文章目录
1. 文章内容
本文的目的就是在SpringBoot项目中使用RabbitMQ解耦一个模拟的用户注册服务, 进而熟悉RabbitMQ的Fanout模式.
2. 环境搭建(多模块工程)
-
关键词: JDK1.8 + IDEA + Maven + SpringBoot + RabbitMQ(在腾讯云服务器中)
-
主模块:
rabbitmq
-
子模块一:
producer-server
(生产者服务) -
子模块二:
consumer-server
(消费者服务)
-
2.1 主模块搭建- rabbitmq
创建主模块, 填写项目名, java版本等信息点击Next
选择项目所需依赖Web服务和RabbitMQ支持, 这里我选择的SpringBoot版本是2.5.3, 点击Finish
项目创建成功后我们就可以吧主模块的src目录删除, 简化项目结构
在主工程中的pom文件中我们需要加入两个标签, model后面需要填入子模块项目名
2.2 子模块搭建 - producer-server
鼠标右键主模块 --> New–> Module
再次创建SpringBoot项目, 作为子模块
子模块一般无需勾选任何依赖, 直接点击Finish
创建好子模块后将子模块pom文件中的parent信息替换为父模块pom文件的信息
同时在父模块中的Model标签中加入子模块的name
2.3 子模块搭建 - consumer-server
子模块consumer-server的搭建也和 producer-server的搭建一样, 大家可以跟着上一步操作进行搭建
最后只需要改改两个子模块的端口即可
当两个子模块服务都能顺利运行就说明我们的多模块搭建成功了
3. 代码实现 解耦注册服务
3.1 Service实现分析
当我们引入任何消息中间件我们的注册服务应该是这样实现的:
因此我们的程序应该是先 发送短信 --> 再将用户信息入库 --> 最后发送邮件这样串行执行的
; 我们发现这样编写注册服务会让我们的register方法耦合, 耗时长, 用户体验差
, 而且后期若是要加入微信通知等附属服务又得加代码, 扩展性差
; 而且我们可以发现, 其实注册服务的实现只有用户信息入库才是我们的根本目的, 万一邮件服务或者短信服务抛出异常我们的服务回滚会造成注册失败
, 得不偿失, 因此我们才需要引入RabbitMQ为我们的程序解耦.
新的架构:
优点: 横向扩扩展, 服务独立, 异步执行
3.2 代码实现
在SpringBoot中应用RabbitMQ的fanout模式很简单
- 配置连接RabbitMQ信息
编写生产者
, 只需进行用户入库的核心任务, 再通过交换机将信息发送到对应的栈中配置交换机和目标栈的关系
, 信息是由特定的交换机发送到指定的栈中的编写消费者
, 短信消费者和邮件消费者自动从对应的栈中获取消息进行消费
1-配置两个子模块连接信息
#生产者
spring:
application:
name: producer
rabbitmq:
username: [你的RabbitMQ用户名]
password: [你的RabbitMQ用户密码]
virtual-host: /
host: [你的RabbitMQ主机]
port: 5672
## 消费者
spring:
application:
name: producer
rabbitmq:
username: [你的RabbitMQ用户名]
password: [你的RabbitMQ用户密码]
virtual-host: /
host: [你的RabbitMQ主机]
port: 5672
特别注意:
-
远程RabbitMQ服务是否已经启动
-
远程RabbitMQ服务一定要在云服务器安全组中开发对应的端口 5672
-
你注册的RabbitMQ用户要赋予响应的权限(administrator)以及允许访问的虚拟端口 /
# 启动rabbitmq
systemctl start rabbitmq-server
# 设置用户角色,分配操作权限
rabbitmqctl set_user_tags jinchange administrator
# 为用户添加资源权限(授予访问虚拟机根节点的所有权限)
rabbitmqctl set_permissions -p / jinchange ".*" ".*" ".*"
若发生连接异常或IO异常请检查以上几点原因
2-生产者代码
注册业务service实现
@Service
public class FanoutRegisterServer {
@Resource
private RabbitTemplate rabbitTemplate;
public void register(String phone, String userInfo) {
// 1. 将用户信息入库, 完成注册
System.out.println(phone + " 用户已入库 " + userInfo);
// 2. 将用户信息发送给指定交换机(注册的交换机名, 广播的信息)
String routingKey = "";// 匹配条件, Fanout模式无需
rabbitTemplate.convertAndSend(RabbitMQConfig.exchangeName, routingKey, userInfo);
}
}
交换机和栈的定义及绑定
@Configuration
public class RabbitMQConfiguration {
/**
* Fanout模式的交换机/向绑定的所有的栈进行广播
*/
public static final String exchangeName = "login_fanout_exchange";
/**
* 类型为Fanout(广播), 持久化, 非自动删除的交换机
* @return FanoutExchange
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange(exchangeName,true,false);
}
/**
* 创建短信队列 sms.fanout.queue
*/
@Bean
public Queue smsQueue() {
return new Queue("sms.fanout.queue");
}
/**
* 创建邮件队列
*/
@Bean
public Queue mailQueue() {
return new Queue("mail.fanout.queue");
}
/**
* 将交换机和队列进行绑定
*/
@Bean
public Binding smsBinding() {
return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
}
@Bean
public Binding mailBinding() {
return BindingBuilder.bind(mailQueue()).to(fanoutExchange());
}
}
生产者测试代码(使用定义的交换机广播信息)
@SpringBootTest
class ProducerServerApplicationTests {
@Resource
private FanoutRegisterServer fanoutRegisterServer;
/**
* 测试Fanout模式广播信息到绑定的所有队列
*/
@Test
void contextLoads() {
fanoutRegisterServer.register("110", "这是110用户的用户信息");
}
}
生产者通过交换机向队列中发送了消息:
3-消费者代码
短信消费服务
@Service
@RabbitListener(queues = {"sms.fanout.queue"})
public class SMSService {
/**
* 对短信分发消息进行消费
*/
@RabbitHandler
public void sandSMS(String message) {
System.out.println("向 "+message+" 发送了短信");
}
}
邮件消费服务
@Service
@RabbitListener(queues = {"mail.fanout.queue"})
public class MailService {
/**
* 对邮件分发消息进行消费
*/
@RabbitHandler
public void sandMail(String message) {
System.out.println("向 "+message+" 发送了邮件");
}
}
消费者执行效果:
4. 总结
到此我们使用RabbitMQ基于Fanout模式实现了对消息的发布和消费, 将我们的服务结构进行了解耦, 提高了扩展性和提高了系统性能.