文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
马士兵:https://github.com/bjmashibing/InternetArchitect
一、消息中间件功能
1.应用解耦
AB应用不存在相互依赖
2.流量削峰
2.1限流算法弊端:
- 流量达到高峰的时候,通常使用限流算法来控制流量的涌入,避免系统被击瘫,但是这种方式损失了一部分请求
- 为了提高消费效率,需要增加消费服务时,限流算法是无法感知服务的增加,无法通过增加服务来提高消费效率。而MQ可以
- 不够灵活,请求超时时间无法设定,而MQ可以
注意:在使用MQ进行流量削峰时,MQ前的请求接入层一定得hold住海量请求
接入层:CDN+LVS+Nginx集群
2.2 消息中间件的作用:
此时可以用消息中间件来缓冲大量请求,进行匀速消费,当消息队列中堆积消息过多时,我们可以动态线上增加消费端,来保证不丢失重要请求
3.大数据处理
消费端拿出MQ中的信息,进行分析从而可以实现 预警/风控/推荐/排名
消息中间件可以把各个模块中产生的管理员操作日志、用户行为、系统状态等数据文件作为消息搜集到主题中,数据使用方可以订阅自己感兴趣的数据内容
二、RocketMQ原理
1.四大角色:
默认情况下这些角色的消息都是存在内存中
MQ注册调度中心:NameServer,集群环境下每个nameserver数据都是独立的不会相互共享
MQ服务端:Broker
生产者:Producer
消费者:Consumer
broker:
- broker面向producer和consumer接受和发送消息
- 向nameserver提交自己信息
- 是消息中间件的存储、转发服务器
- 每个broker节点,在启动时,都会遍历nameserver列表,与每个nameserver建立长连接,注册自己的信息,之后定时上报
broker集群:
- broker高可用,可用配置成master/slave结构,master可读可写,slave只可用读,master将写入的数据同步给slave
一个master可用同时对应多个slave,但是一个slave只能对应一个master
master与broker对应关系通过指定相同的brokername和不同的brokerid来定义,brokerid=0 表示为master,非0表示slave - master多机负载,可用部署多个broker
每个broker与nameserver集群中的所有节点建立长连接,定时注册所有topic信息到素有nameserver
producer
- 消息的生产者
- 通过nameserver集群中的一个节点(随机选择)建立长连接,获得topic的路由信息,包括topic下面有哪些queue,这些queue分布在哪些broker上
- 接下来向提供topic服务的broker master建立长连接,且定时向master发送心跳,不能与broker的slave进行连接
consumer
- 消息的消费者
- 通过nameserver集群获取topic的路由信息,连接到对应的broker上消费信息
- 注意:由于master和slave都是可以读取消息,因此comsumer会与master和slave都建立连接
nameserver
- 底层由netty实现
- 提供了路由管理、服务注册、服务发现的功能。是一个无状态节点
- nameserver是服务发现者,集群中各个角色(broker、producer、comsumer)都需要向nameserver上报自己的状态.以便互相发现彼此,超时不上报的话nameserver会把它从列表中剔除(无自我保护机制)
- namerserver可以部署多个,当多个nameserver存在的时候,其他角色同时向他们上报信息,以保证高可用
- nameserver集群间相互不通信,没有主备的概念
- nameserver是内存式存储,nameserver中的broker、topic默认都不会持久化
为什么不用zookeeper? rocketmq希望为了提高性能、cap定理、客户端负载均衡
2.原理图
消费一条消息的原理:
1.启动NameServer
2.启动Broker 并注册到NameServer
3.生产者Producer根据Topic去NameServer找
4.NameServer返回broker地址A
5.Producer带着Topic将消息写到broker A
6.消费者Consumer需要订阅哪个Topic消息去NameServer找
7.NameServer根据Topic找到是哪个Broker A
8.消费者Consumer从Broker A上读取消息
三、RocketMQ消息
发送消息
1.同步消息,会等待:
消息发送中进入同步等待状态,可以保证消息投递一定送达(broker返回ack)后才会执行完毕
2.异步消息,回调或重试:
想要快速发送消息,又不想消息丢失可以使用异步消息
new SendCallBack()
3.单向消息,效率最高:
只发送消息,不等待服务器响应。发送消息过程耗时非常短,一般为微妙级别,有可能会丢消息
producer.sendOneway(msg);
4.批量消息发送
可以将多个topic消息进行打包成List,一次发送,减少网络传输次数提高效率
,单次发送消息大小不能超过1MB,如超过,需要分批发送。
消费消息:
1.消息消费模式
消息消费模式有消费者来决定,默认为集群模式(一条消息消息只能被一个消费者消费)
注意:同一组的消费者,需要指定同一种消费模式
两种消费模式:
广播消费
广播模式:consumer.setMessageNodel(MessageModel.BROADCASTING);
消息会向所有同一个group的consumer发送消息
当使用广播消息时,MQ会将每条消息推送给所有注册过的客户端,保证消息至少被每台消息消费一次
特点:
- 消费进度由consumer维护
- 保证每个消费者消费一次所有消息
- 消费失败的消息不会重投
集群消费
集群模式:consumer.setMessageNodel(MessageModel.CLUSTERING);
代表一组consumer,同一组的consumer为一个集群,消息只会发给集群(group)中的某一个
特点:
- 每条消息只需要被处理一次,broker只会把消息发送给消费集群中的一个消费者
- 在消息重投时,不会保证路由到同一台机器上
- 消费状态是有broker维护
批量消息发送:
可以多条消息打包一起发送,减少网络传输次数来提高效率。一次发送消息最大不能超过1MB,所以当单次传输消息很大的时候,可以将list分批发送
- 批量消息要求必须要具有同一topic、相同消息配置
- 不支持延迟消息
- 建议一个批量消息最好不要超过1MB大小
- 如果不确定是否超过限制,可以手动计算大小分批发送
5.消息过滤
1.TAG 消息分组:
可以在producer、consumer中使用tag来过滤
1.在producer中使用tag:
new Massage(“topicTest”,“TagA”,“Hello RocketMQ”.getBytes(RemotingHelper.DEFAULT_CAHRSET));
2.在consumer中使用tag:
sonsumer.subscribe(“topic”,“TagA|TagB”); // 代表订阅topic下的所有消息
key:关联其他消息,用来找消息,是与业务进行绑定。如用户需要关联订单,那么就可以把key设置为用户id或用户名
tag:消息分组
2.SQL表达式过滤:
相比tag过滤,selector的过滤更为精细,两者不能同时使用
消费者将受到包含TagA或TagB的消息,但限制是一条消息只能有一个标签,而这对于复杂的情况可能无效。
在这种情况下,可以使用sql表达式筛选出消息。
需要在broker中配置:enablePropertyFilter=true
启动时指定broker.conf:…/bin/mqbroker - n 192.168.150.113:9876 -c broker.conf
producer实例:
message.setUserProperty(“age”,25);
message.setUserProperty(“sex”,“男”);
设置多个property时,消费时,可以根据多个property进行过滤
consumer实例:
MessageSelector selector = MessageSelector.bySql(“order > 5”);
consumer.subscribe(“xxx003”,selector)
语法:
RocketNQ只定义了一些基本语法支持这些功能,也可以很容易的进行扩展:
1.数字比较:>,>=,<,<=,BETWEEN,=
2.字符串比较:=, <>,INS
3.IS NULL 或 IS NOT NULL;
4.逻辑运算 AND、OR、NOT;
常量类型:
1.数字:123,4,5,644
2.字符串:‘abd’,'d’必须使用单引号
3.NULL,特殊常数
4.布尔常量:TRUE或FALSE
注意:tag selector在同一个group中的消费者,都不能随便变,要保持统一,不然会消费不到
6.延迟消息:
RocketMQ可以使用messageDelayLevel设置延迟投递
默认延迟消息配置等级为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
需要在broker.conf文件中添加:messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
7.顺序消息:
队列先天就支持FIFO模型,单一生产和消费者下只要保证使用messageListenerOrderly监听即可
顺序消费标示消息的顺序同生产者为每个消息队列发送的顺序一致,所以如果正在处理全局顺序是强制性的场景,需要确保使用的主题只有一个消息队列。
并行消费不在保证消息顺序,消费的最大并行数量受每个消费者客户端指定的线程池限制。
那么主要顺序发送消息,再保证一个线程去消费一个队列上的消息,那么就是有序的。
跟普通消息相比,顺序消息的使用需要在producer的send方法中添加MesageQueueSelector接口实现类
并重写select选择使用的队列,因为顺序消息局部顺序,需要将所有消息指定发送到同一队列中。
保证有序参与因素
- FIFO
- 队列保证有序
- 消费线程控制为1个
8.事务消息:
分布式系统中的事务可以使用TCC,2PC来解决分布式系统中消息的原子性。
RocketMQ通过2PC可以达到分布式消息的最终一致性
RocketMQ分布式事务实现方式:
Half Message:处理消息,当broker受到此类消息后,会存储在RMQ_SYS_TRANS_HALF_TOPIC的消息消费队列中
检查事务状态:broker会开启一个定时任务,消费RMQ_SYS_TRANS_HALF_TOPIC队列中的消息,每次执行任务会向消息发送者确认事务的执行状态(提交/回滚/未知),如果是未知则等待下一次回调;
超时:如果通过回查次数,默认回滚消息
TransacionListener中两个方法:
1.executeLocalTransaction:半消息发送成功触发次方法来执行本地事务(producer方在本方法中执行本地事务)
2.checkLocalTransaction:broker将发送检查消息来检查事务状态,并条用次方法来获取本地事务状态(broker方执行此方法)
本地事务执行状态:
LocalTransactionState.COMMIT_MESSAGE:执行事务成功,确认提交
LocalTransactionState.ROLLBACK_MESSAGE:回滚消息,broker端会自动删除消息
LocalTransactionState.UNKNOWN:暂时未未知状态,等待broker回查
broker check事务方法的时候,定时任务回调的时间,阶梯状:1s 5s 10s …
分布式事务:一般两种技术实现:
理解两种方式粗略原理:
2PC: 两阶段提交(rocketMq以这种方式实现) (比较简单)
1.尝试提交
2.确认ok
TCC:
分为3个阶段步骤:try,confirm,cancel
9.消息重试机制:
producer
默认超时时间:
consumer
消费超时,单位分钟:consumer.setConsumerTimeout();
发送ack,消费失败:RECONSUMER_LATER
broker 投递:
只有在消息模式为MessageModel.CLUSTERING集群模式时,broker才会自动进行重试,广播模式不重试
重投使用messageDelayLevel,默认等级为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
四、RocketMQ 面试FAQ
1.消息丢失:
SendResult
producer在发送同步/异步会笑洗后,会接收到sendresult,表示消息发送成功
sendresult其中属性sendStatus表示了broker是否真正的完成了消息存储
当sendStatus!=“ok”时,应该重新发送消息,避免丢失
当producer.setRetryAnotherBrokerWhenNotStoreOk
2.消息重复消费问题:
影响消息正常发送和消费的重要因素是网络的不确定性
引起重复消费的重要原因:
ACK:
正常情况下在consumer真正消费完消息后应该发送ACK给broker,通知broker该消息已经消费,应从queue中剔除
当ack因为网络原因无法发送到broker,broker会认为此条消息还没有被消费,此后会开启消息重头机制把消息再次投递到consumer
GROUP:
在CLUSTERING模式下,消息在broker中会保证相同的group的consumer消费一次,但是针对同一topic的不同group的consumer会多次推送
解决方案:
数据库表:
处理消息前,使用消息主键在表中带有约束的字段中insert
MAP:
单机时可以使用map concurrentHashMap - > putIfAbsent guavaCache
Redis:
使用主键或SET操作
3.如何让RocketMQ保证消息的顺序消费
消息顺序发送和顺序消费
保证条件:
1.同一个topic(1个topic由4个无限大的数组)
2.同一个QUEUE(是QUEUE保证了FIFO)
3.发送消息是同一个线程发送消息(需要手动控制)
4.消费的时一个线程消费一个queue的消息
5.同一tpoic,多个queue时,只能保证queue里的顺序,并不能保证消费多个queue的顺序