什么是消息中间件?
可以把消息中间件比作一个存放消息的容器,当需要使用消息的时候可以从中取出消息。 消息队列是分布式系统中的一个重要组件。它主要通过异步处理提高系统性能和削峰,降低系统耦合性。
常见的消息中间件有activeMQ、rabbitMQ、rabbitMQ、kafka。
我们知道队列是先进先出的数据结构,那么消费者消费消息也是按照顺序进行消费的。但是也有可能在一些情况下产生不按顺序消费的情况,所以我们需要保证消费队列被消费的正确性。
采用消息队列要考虑如何保证消不被重复消费,如何保证消息不丢失。所以虽然有好处,但是它也会同样带来问题,比如导致系统的可用性降低,提高系统复杂度,另外还需要保证一致性。
JMS
JMS是java message service 的简称,jms客户端可以通过jms服务进行异步的消息传输。
ActiveMQ就是基于JMS规范实现的
JMS中的2种消息模型
-
点对点模型
Queue作为消息载体,满足生产者消费者模式,**一个消息只能被一个消费者消费。**消息将在队列中等待被消费直到超时。
如果你希望发送的每个消息都应该被成功处理的话,那么你需要P2P模式 -
发布/订阅模型
Topic作为消息载体,类似广播模式,发布者发布一条消息,通过主题传递给所有的订阅者。
这种模式会出现重复消费的场景
JMS中消费消息的不同形式
在JMS中,消息的产生和消息是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。
-
同步
订阅者或接收者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞
-
异步
订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。
JMS五种不同的消息正文
ActiveMQ
ActiveMQ是基于JMS的消息中间件。
ActiveMQ消息签收机制
客戶端成功接收一条消息的标志是一条消息被签收,成功应答。
消息的签收情形分两种:
-
带事务的session
如果session带有事务,并且事务成功提交,则消息被自动签收。如果事务回滚,则消息会被再次传送。
-
不带事务的session
Activemq支持以下三種模式:
-
Session.AUTO_ACKNOWLEDGE
消息自动签收
-
Session.CLIENT_ACKNOWLEDGE
客戶端调用acknowledge方法手动签收
-
Session.DUPS_OK_ACKNOWLEDGE
不是必须签收,消息可能会重复发送。在第二次重新传送消息的时候,消息只有在被确认之后,才认为已经被成功地消费了。
-
ActiveMQ中的持久化机制
ActiveMQ支持持久化,可以防止在生产者未消费的时候,mq挂掉重启之后消息丢失。
为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一般都会采用持久化机制,即:若MQ挂了,消息不会消失的机制 。
ActiveMQ的消息持久化机制JDBC、AMQ、KahaDB、LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。
在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或远程数据库等再试图将消息发送给接收者,成功则将消息从存储中删除,失败则继续尝试发送。消息中心启动后首先要检查指定的存储位置,若有未发送成功的消息,则需要把消息发送出去。
使用ActiveMQ注意事项
- 消费者代码不要抛出异常,否则activqmq默认有重试机制。
- 如果代码发生异常,需要发布版本才可以解决的问题,不要使用重试机制,采用日志记录方式,定时Job进行补偿。
- 如果不需要发布版本解决的问题,可以采用重试机制进行补偿。
ActiveMQ如何保证消息幂等性,不被重复消费。
产生原因:网络延迟传输中或者其他情况引起的消费者长时间没有收到消息,会造成进行MQ重试,在重试过程中,可能会造成重复消费。
解决办法: 使用全局MessageID 判断是同一个消息就不重复处理 ,解决幂等性。
SpringBoot整合activemq简单demo
- pom引入的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency> <!--springboot 2.1+以上的版本使用这个连接池jar--> <dependency> <groupId>org.messaginghub</groupId> <artifactId>pooled-jms</artifactId> </dependency>
- application.properties
spring.activemq.user=admin spring.activemq.password=admin spring.activemq.broker-url=tcp://localhost:61616 spring.activemq.in-memory=true spring.activemq.pool.enabled=false #定义queue的名称 mq.myqueue=myqueue
- Controller作为消息生产者
@RestController public class MessageSendController { @Value("${mq.myqueue}") String queue; //也可以注入JmsTemplate,JmsMessagingTemplate对JmsTemplate进行了封装 @Autowired JmsMessagingTemplate messagingTemplate; @RequestMapping("/sendMessage") public String sendMessage(String message){ messagingTemplate.convertAndSend(queue, message); return "success"; } }
- 消息消费者
@Service public class MQConsumerService { @JmsListener(destination = "${mq.myqueue}") public void getMessageFromMQ(String text) { System.out.println("从myqueue获取到消息:" + text); } }
RocketMQ
RocketMQ 是一款分布式、队列模型的消息中间件,具有以下特点: 能够保证严格的消息顺序 提供丰富的消息拉取模式 高效的订阅者水平扩展能力 实时的消息订阅机制 亿级消息堆积能力
RocketMQ包含的组件
NameServer:单点,供Producer和Consumer获取Broker地址
Producer:生产和送消息
Consumer:接受并消费消息
Broker:消息暂存,消息转发
RocketMQ优点
1.强调集群无单点,可扩展
2.任意一点高可用,水平可扩展
3.海量消息堆积能力,消息堆积后,写入低延迟。
4.支持上万个队列
5.消息失败重试机制
6.消息可查询
7.开源社区活跃
8.成熟度(经过双十一考验)