RabbitMQ+Springboot2.x项目搭建
前言:
最初只在普通maven项目中单独配置过rabbitMQ,并简单的实现了各种模式的操作,但总归不是实际开发的配置,甚至于连自己的项目都无法融入。使用别人已经写好的配置直接调用,也不能掌握其中的细节,百度了一番,也没有看到合适的初学者的文章,故写一篇文章来培训自己,也给后来人一点参考。
一、前期准备
必须安装好rabbitmq客户端并配置了用户与虚拟主机信息,才可以进行以下操作
1、application配置文件增加连接到rabbitmq的信息
#配置自己的rabbitmq地址与端口号,这里采用的是application.yml的方式
spring:
application:
name: springboot-rabbitMQ
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /tempHost
#项目运行时的端口号,默认8080
server:
port: 8888
2、添加pom文件
在测试过程中,出现了项目跑起来,也创建了队列,但是访问不到controller的情况,就是因为忘了加pom文件…
<!-- spring的web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- rabbitmq的注解支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、简单模式
1、添加配置类
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
public class RabbitMQConfig {
public static String SQFinishRegister = "SQ_FinishRegister";
//简单模式只需要队列
//不同的模式单独设置内部类,调用时可区分使用的属性与类
//全为public static final,放开权限,可在注解中访问到属性值,否则注解无法使用
//simple_queue_FinishRegister的命名方式容易出现大量冗余名称,降低可读性,故采用简称SQ_业务名称
//假设完成注册,发送注册成功的短信
public static class Simple {
public static class SQueueField {
public static final String SQFinishRegister = "SQ_FinishRegister";
}
//创建bean放在模式配置类中,可以访问所有的内部类属性
//需要使用这个bean时,只需要调用方法,就可以获取对应的队列类
@Bean
public Queue createSimpleQueueWhenRegister() {
return QueueBuilder.durable(SQueueField.SQFinishRegister).build();
}
}
//对外暴露队列名,使某些方法中调用可以简化代码
public static String SQFinishRegister() {
return Simple.SQueueField.SQFinishRegister;
}
}
2、创建简单模式生产者
import com.mq.rabbitmq.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SimpleProvider {
//不同于使用redisTemplate时那样,单独设置bean信息注入bean,
//配置文件中填写了ip账号等信息,spring就会自动注入好相关信息
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/simple_FinishRegister")
public void simpleFinishRegister() {
//参数1:队列名称
//参数2,发送的消息数据
rabbitTemplate.convertAndSend(RabbitMQConfig.SQFinishRegister(), "this is simple msg");
}
}
3、创建简单模式消费者
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import static com.mq.rabbitmq.config.RabbitMQConfig.Simple.SQueueField.SQFinishRegister;
@Component//需要注册进入ioc才能进行监听
public class SimpleCustomer {
/**
* simple消费者
* 参数@Queue:队列:
* 参数value:队列名称,不填则使用临时队列
* 参数declare:是否持久化,默认为true
* 参数exclusive:是否是独占队列,默认为false
* 参数autoDelete:是否自动删除,默认为false
* @param msg
*/
@RabbitListener(queuesToDeclare = @Queue(value = SQFinishRegister, declare = "true", exclusive = "false", autoDelete = "false")
)
public void simpleFinishRegister(String msg) {
System.out.println("simpleFinishRegister = " + msg);
}
}
运行简单模式
当项目运行,并访问localhost:8888/simple_FinishRegister时,控制台会输出
simpleFinishRegister = this is simple msg
就代表简单模式与Springboot的合并创建好了
三、direct路由直连模式
在简单模式中创建的RabbitMQConfig配置类中添加direct类的配置
public class RabbitMQConfig {
//上面是简单模式的配置信息...
//路由直连模式设置队列,路由值,交换机
//假设完成购买商品,发送短信和邮箱
//创建bean放在模式配置类中,可以访问所有的内部类属性
public static class Direct {
public static class DQueueField {
public static final String DQBuyGoods = "DQ_BuyGoods";
}
//路由Key不需要创建bean
public static class DRoutKeyField {
public static final String DRBuyGoods = "DR_BuyGoods";
}
public static class DExchangeField {
public static final String DEBuyGoods = "DE_BuyGoods";
}
@Bean
public static Queue directQueueWhenBuyGoods() {
return QueueBuilder.durable(DQueueField.DQBuyGoods).build();
}
@Bean
//不要创建Exchange,要指定是哪种交换机(DirectExchange,FanoutExchange,TopicExchange),不然设置bing连接时会返回值不对应
public static DirectExchange directExchangeWhenBuyGoods() {
return ExchangeBuilder.directExchange(DExchangeField.DEBuyGoods).build();
}
@Bean
public static Binding directBindingWhenBuyGoods() {
return BindingBuilder.bind(directQueueWhenBuyGoods()).to(directExchangeWhenBuyGoods()).with(DRoutKeyField.DRBuyGoods);
}
}
//提供对外暴露字段名的方法,简化调用属性时的代码编写
public static String DQBuyGoods() {
return Direct.DQueueField.DQBuyGoods;
}
public static String DRBuyGoods() {
return Direct.DRoutKeyField.DRBuyGoods;
}
public static String DEBuyGoods() {
return Direct.DExchangeField.DEBuyGoods;
}
}
2、创建直连模式生产者
import com.mq.rabbitmq.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DirectProvider {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/direct_BuyGoods")
public void directBuyGoods() {
//idea中ctrl+p可以看到当前方法的参数列表
//生产者不需要绑定队列,由交换机决定消息发送给谁
//参数1:交换机名称
//参数2:路由key
//参数3:发送的消息数据,已封装可以用object,会自动转换为byte
for (int i = 0; i < 4; i++) {
rabbitTemplate.convertAndSend(RabbitMQConfig.Direct.DExchangeField.DEBuyGoods, RabbitMQConfig.DRBuyGoods(), "this is direct msg" + i);
}
}
}
3、创建直连模式消费者
import com.mq.rabbitmq.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class DirectCustomer {
/**
* direct消费者
*
* @param msg
*/
//direct消费者需要bind绑定交换机
//QueueBinding参数:
//value绑定队列名,如果不声明名称,则创建临时队列,
//exchange绑定交换机,type指定路由模式的类型(direct或topic),默认direct,name为交换机名称,不指定则使用默认交换机
//key为路由key,可用{}绑定多个路由key
@RabbitListener(bindings = @QueueBinding(
value = @Queue(RabbitMQConfig.Direct.DQueueField.DQBuyGoods),
exchange = @Exchange(
type = "direct",
name = RabbitMQConfig.Direct.DExchangeField.DEBuyGoods),
key = {RabbitMQConfig.Direct.DRoutKeyField.DRBuyGoods})
)
public void directBuyGoods(String msg) {
System.out.println("directBuyGoods = " + msg);
}
}
运行直连模式
重启项目,访问localhost:8888/direct_BuyGoods,看到控制台输出以下信息就代表本地配置成功了
directBuyGoods = this is direct msg0
directBuyGoods = this is direct msg1
directBuyGoods = this is direct msg2
directBuyGoods = this is direct msg3
思考
假设现在为一个direct模式的交换机绑定了两个消费者,会有什么效果呢?
广播模式?工作模式?
增加一个直连模式消费者
@RabbitListener(bindings = @QueueBinding(
value = @Queue(RabbitMQConfig.Direct.DQueueField.DQBuyGoods),
exchange = @Exchange(
type = "direct",
name = RabbitMQConfig.Direct.DExchangeField.DEBuyGoods),
key = {RabbitMQConfig.Direct.DRoutKeyField.DRBuyGoods})
)
public void directBuyGoods2(String msg) {
System.out.println("directBuyGoods2 = " + msg);
}
重启项目,再次访问localhhost:8888/direct_BuyGoods,发现控制台输出了
directBuyGoods2 = this is direct msg0
directBuyGoods = this is direct msg1
directBuyGoods2 = this is direct msg2
directBuyGoods = this is direct msg3
当直连模式不使用临时队列,而使用声明后的队列,并绑定了两个消费者后,变成了带路由的工作模式
回想一下work模式应该不难理解,单条队列绑定两个消费者,就是work模式,平均分配消息,只不过这里多了个路由key
总结,其实RabbitMQ的模式中,其实交换机和队列是分开设计的
在路由系列中(fanout,direct,topic),
direct和topic实际上只关注了交换机的路由编写方式,与队列上有多少个消费者无关,
fanout也是只关注谁绑定了交换机,究竟有多少个消费者绑定,生产者并不关心
在非路由系列中(simple,work)
关注的是消费者如何与消息队列绑定,消息队列如何分发消息给消费者
其实在工作当中不必按照固定的模式,而是应该按照实际需求,灵活的配置消息分发方式。
2020-09-26 pm
展望1——完成其余模式的编写
展望2——改进代码
未完待续...