消息中间件RocketMQ


提示:以下是本篇文章正文内容,下面案例可供参考

马士兵: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的顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值