消息中间件-RabbitMQ
rabbitmq分布式消息队列,即用来使不同的服务间消息传递,在此过程中分为发送者、接收者。
优点:
- 异步处理: 相比于传统的串行、并行方式,提高了系统吞吐量。
- 应用解耦 :系统间通过消息通信,不用关心其他系统的处理。
- 流量削锋 : 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
- 日志处理:解决大量日志传输。
- 消息通讯:消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
详情:https://blog.csdn.net/thinkwon/article/details/104588612/
详图:来自 https://blog.csdn.net/qq_35387940/article/details/100514134
使用RabbitMQ,服务将进行如下几个步骤:
- 建立连接:可以使用ConnectionFactory来构建Connection。
- 创建通道:Channel
- 发送 / 接收消息:此过程使用队列
- 关闭通道、关闭连接
RabbitMQ 发送消息时(消息有很多),将消息放在队列(先进先出)中,接收者从队列获取发送者发送的消息。
接收者接收完毕后,会为发送者发送一个成功信号,发送者收到信号后才将消息从队列中删除。
因为不加此机制的话,如果在接收者在接收消息时挂了,发送者删除消息将导致消息无法被处理。加了此机制后,挂掉的接收者无法向发送者发送成功消息,所以发送者不会删除消息。
当然如果当前接收者挂了,一定时间后,它会将消息交给另一个服务来处理。
发布订阅者模式:发送者发送一条消息后,所以可以接收消息的服务都能收到。
消息分组
默认情况下,rabbitmq采用发布-定阅模式,所有启动的服务都能收到消息,但是不是所有服务都需要这个消息,因此我们需要进行消息分组,组外的服务就无法收到这个消息了。
springboot整合RabbitMQ
首先我们需要安装并启动RabbitMQ server,服务既可以收消息又可以发消息,下面就已仅收、仅发来讲解rabbitmq的使用。
启动后可访问:http://127.0.0.1:15672 进入控制台,用户密码均为:guest
1.服务引入RabbitMQ
1.项目依赖:
<!--rabbitmq--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
2.yml配置:
spring: rabbitmq: host: localhost port: 5672 username: rabbit password: rabbit virtual-host: myhost #虚拟机名,如果没添加,直接用 / 即可
注意:账号rabbit是在控制台新加的,默认账号不支持远程连接
2.发送者(仅发送):
1.交换机配置:注意是amqp包下的
@Configuration public class RabbitConfig { /** * new Queue(): * 参数一:队列名称 * 参数二:是否持久化队列,默认true * 参数三:是否为当前连接专属,连接关闭后销毁,默认false * 参数四:是否自动删除,无服务使用时自动删除,默认false * */ @Bean//队列 public Queue theQueue(){ return new Queue("theQueue",true); } @Bean//交换机 public DirectExchange theDirectExchange(){ return new DirectExchange("theDirectExchange",true,false);//名字、持久化、自动删除 } @Bean//绑定队列、交换机,匹配值为key public Binding bindingDirect() { //监听key的服务会收到消息 return BindingBuilder.bind(theQueue()).to(theDirectExchange()).with("key"); } @Bean public Binding bindingDirectAll() { //监听Key开头的服务会收到消息 return BindingBuilder.bind(theQueue()).to(theDirectExchange()).with("key.#"); } }
最后2个方法是绑定队列和交换机并指定一个可被监听的key,因此可以实现多个组合。
2.消息发送:
@RestController @RequestMapping("/rabbit") public class RabbitController { @Autowired private RabbitTemplate rabbitTemplate; @PostMapping public void sendMessage(){//交换机、绑定键、消息 rabbitTemplate.convertAndSend("theDirectExchange","key","hello world!"); } }
接收者(仅接收):
队列监听,并接收
@Component @RabbitListener(queues = "theQueue")//监听的队列名 public class RabbitListen { @RabbitHandler public void getMessage(String message){ System.out.println(message); } }
使用注解 @RabbitListener 监听指定队列,使用 @RabbitHandler 标记接收方法。
Stream整合RabbitMQ(SpringCloud)
1.项目依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<!--生产者-->
<dependency>
<groupId>org.sshengcpringframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!--消费者-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
2.yml配置
spring:
application:
name: rabbitmq-service #指定服务名
rabbitmq:
addresses: 127.0.0.1
username: itcast
password: itcast
virtual-host: myhost
cloud:
stream:
bindings:
output: #发送通道
destination: itcast-default #指定消息发送目的地
input: #消费通道
destination: itcast-default #指定消息来源
#contentType: text/plain #消息类型
binders: #配置绑定器
defaultRabbit:
type: rabbit
发送:
1.定义发送通道:output
public interface Source { @Output("output")//发送通道接口 MessageChannel myoutput(); }
2.发送:
@Component @EnableBinding(Source.class)//绑定通道 public class MessageSender { @Autowired private MessageChannel messageChannel ; //发送消息 public void send(Object o) { messageChannel.send(MessageBuilder.withPayload(o).build()); } }
2.接收:
1.接收通道
public interface Sink { @Input("input") SubscribableChannel input(); }
2.接收
@Component @EnableBinding(Sink.class)//绑定通道 public class GetMessageSender { @StreamListener("input")//监听通道 private MessageChannel messageChannel ; //接收消息 public void getMessage(String message) { System.out.println( message); } }
几种消息模式
1.收发模式:
生产者将消息放入队列,消费者从队列消费。如果消费者消费后向队列发送ack,队列收到ack后才删除消息。
多个消费者存在资源竞争,可以加锁解决。
2.发布订阅模式:
每个消费者监听自己的队列,生产者的消息通过交换机发送给每个队列。
3.路由模式
交换机根据路由字符串来匹配决定将消息发送到哪个队列。
主体模式
也是路由模式,只不过是根据表达式来匹配。
RabbitMQ 高可用集群
单台RabbitMQ服务到达瓶颈后,需要开启多个RabbitMQ server来分摊压力。
RabbitMQ不支持负载均衡,可以通过nginx实现负载均衡。
RabbitMQ集群各节点之间共享队列,但各队列之间默认数据不同步,仅收到消息的队列有消息,所以需要开启镜像模式,才能同步并实现高可用。