MQ(message queue),本质是个队列,FIFO先入先出,队列中存放的内容是message,是一种跨进程的通讯机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用MQ后,消息发送上游只需要依赖MQ,不用依赖其他服务。
流量消峰 :举个例子,如果订单系統最多能处理一万次订单,这个处理能力应付正常时段的下单时绰绰有余,正常时段我们下单—秒后就能返回结果。但是在高峰期,如果有两万次下单操作系统是处理不了的,只能限制订单超过一万后不允许用户下单。使用消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这时有些用户可能在下单十几秒后才能收到下单成功的操作,但是比不能下单的体验要好。
MQ可以将系统的超量
请求暂存其中,以便系统后期可以慢慢进行处理,从而避免了请求的丢失或系统被压垮。应用解耦 :以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合 调用库存系统、物流系统、支付系统,仼何—个子系统出了故障,都会造成下单操作异常。当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统因为发生故障,需要几分钟来修复。在这几分钟的时间里,物流系统要处理的内存被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理订单信息即可,中单用户感受不到物流系统的故障,提升系统的可用性。
异步处理: 有些服务间调用是异步的,例如A调需要花费很长时间执行,但是A需要知道B什么时候可
行完,以前一般有两种方式,A过一段时间去调用B的查询api查询。或者A提供一个 callback apiB执行完之后调用叫通知A服务。这两种方式都不是很优雅,使用消息总线,可以很方便解決这个问题A调用B服务后,只需要监听B处理完成的消息,当B处理完成后,会发送一条消息给MQ,MQ会将此消息转发给A服务。这样A服务既不用循环调用B的查询api,也不用提供 callback api。同样B服务也不用做这些操作。A服务还能及时的得到异步处理成功的消息
1.消息(Message)
- 消息是指,消息系统所传输信息的物理载体/生产和消费数据的最小单位,每条消息必须属于一个主题。
2.主题(Topic)
- Topic:表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是 RocketMQ进行消
息订阅的基本单位- 一个生产者可以同时发送多种 Topic的消息;而一个消费者只对某种特定的 Topics感兴趣,即只可以订阅和
消费一种 Topic的消息。
3.标签(Tag)
- 为消息设置的标签,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的淸晰度和连贯性,并优化 RocketMQ提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。
4.队列(Queue)
- 存储消息的物理实体。一个 Topic中可以包含多个 Queue,每个 Queue中存放的就是该 Topic的消息。一个Topick的 Queuet被称为一个 Topic中消息的分区。
5.消息标志(MessageId/Key)
- RocketMQ中每个消息拥有唯一的 Messaged,且可以携带具有业务标识的Key,以方便对消息的查询。不
过需要注意的是, Messaged有两个:在生产者 send(0消息时会自动生成一个 Messaged(msgId),当消息到达 Broker后, Broker会自动生成一个 Messaged(offsetMsgId)。 msgld、 offsetMsgld与key都称为消息
标识。
- msgld:由 producer端生成,其生成规则为:
producer+进程pid+ MessageclientIDSetter类的 Classloader的hashcode+当前时间+ AutomicInteger自增计数器
- offsetMsgld:由 broker端生成,其生成规则为: brokerIp+物理分区的 offset
- key:由用户指定的业务相关的唯一标识
1. Product
- 消息生产者,负责生产消息。 Producer过MQ的负载均衡模块选择相应的 Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。
例如:业务系统产生的日志,用户提交的秒杀请求- RocketMQ中的消息生产者都是以生产者组( Producer Group)的形式出现的。生产者组是同一类生产者的集合,这类 Producer发送相同Topic类型的消息。一个生产者可以同时发送多个主题的消息。
2.Consumer
- 消息消费者,负责消费消息。一个消息消费者会从 Broker服务器中获取到消息,并对消息进行相关业务处理。
例如:读取解析日志;读取到秒杀请求,并对请求进行处理的过程- Rocket消息消费者都是以消费者组( Consumer group)的形式出现的。消费者组是同一类消费者consumer消费的是同一个Topi类型的消息。消费者组使得在消息消费方面,实现负载均衡(将一个 Topic中的不同的 Oueue平均分配给同一个 Consumer group的不同的 Consumer,注意,并不是将消息负载均衡)和容错(一个 Consumer 挂了,该 Consumer Group中的其它 Consumer可以接着消费原Consumer消费的 Oueue)的目标变得非常容易。
- 消费者组中 Consumer的数量应该小于等于订阅 Topic的 Queue数量。如果超出 Queue数量,则多出的Consumer将不能消费消息。不过,一个 Topic类型的消息可以被多个消费者组同时消费
3.Name Server
功能介绍:
Name server是一个 Broker与 Topic路由的注册中心,支持 Broker的动态注册与发现。
Rocketmq的思想来自于Kafka,而Kafka是依赖了 Zookeeper的。所以,在 Rocketmq的早期版本,即在
Meta v1.0与v2.0版本中,也是依赖于 Zookeeper的。从 Meta v3.0,即 Rocketman开始去掉了 Zookeeper
依赖,使用了自己的 Name Server。主要包括两个功能:
Broker管理:接受 Broker集群的注册信息并且保存下来作为路由信息的基本数据;提供心跳检测机
制,枪查 Broker是否还存活。路由信息管理:每个 NameServer中都保存着 Broker集群的整个路由信息和用于客户端查询的队列信
息。 Producer和 Conumser通过 NameServer可以获取整个 Broker集群的路由信息,从而进行消息的
投递和消费。路由注册:
- NameServer通常也是以集群的方式部署,不过, NameServer是无状态的,即 Nameserver集群中的各个节点间是无差异的,各节点间相互不进行信息通讯。那各节点中的数据是如何进行数据同步的呢?在Broker节点启动时,轮询Name Server列表,与毎个 NameServer节点建立长连接,发起注册请求。在NameServer内部维护着一个Broker列表,用来动态存储 Broker的信息。
- Broker节点为了证明自己是活着的,为了维护与 NameServer间的长连接,会将最新的信息以心跳包的方
式上报给 Nameserver,每30秒发送一次心跳。心跳包中包含 Brokered、 Broker地址(IP+Port)、 Broker名称、 Broker所属集群名称等等。 Name Server在接收到心跳包后,会更新心时间戳,记录这个 . Broker的最新存活时间。路由剔除:
- 由于 Broker关机、宕机或网络抖动等原因, NameServer没有收到 Broker的心跳, NameServer可能会将其从 Broker列表中剔除。
- NameServer中有一个定时任务,毎隔10秒就会扫描一次 Broke表,查看每一个 Broker的最新心跳时间戳
距离当前时间是否超过120秒,如果超过,则会判定Broker失效,然后将其从 Broker列表中剔除。路由发现:
- RocketMQ的路由发现采用的是Pull(拉取)模型。当 Topic路由信息出现变化时, NameServer不会主动推送给客户端,而是客户端定时拉取主题最新的路由。默认客户端毎30秒会拉取一次最新的路由。
客户端 NameServer选择策略:
- 客户端在配置时必须要写上Name Server集群的地址,那么客户端到底连接的是哪个 NameServer节点呢,客户端首先会首先一个随机数,然后再与NameServer节点数量取模,此时得到的就是所要连接的节点索引,然后就会进行连接。如果连接失败,则会采用 round- robin策略,逐个尝试着去连接其它节点。
首先采用的是随机策略进行的选择,失败后采用的是轮询策略。
4.Broker
功能介绍
- Broker充当着消息中转角色,负责存储消息、转发消息。 Broker在 RocketMQ系统中负责接收并存储从生产者发送来的消息,同时为消费者的拉取请求作准备。 Broker同时也存储着消息相关的元数据,包括消费者组消费进度偏移 offset、主题、队列等。
模块构成
Remoting Module
: 整个 Broker的实体,负责处理来自 Clients端的请求。而这个 Broker实体则由以下模块。Client Manager
: 客户端管理器。负责接收、解析客户端( Producer/, Consumer请求,管理客户端。例
如,维护 Consumer的 Topic订阅信息。Store service
: 存储服务。提供方便简单的API接口,处理消息存储到物理硬盘和消息査询功能。HA Service
: 高可用服务,提供 Master broker和 Slave broker之间的数据同步功能。Index service
: 索引服务。根据特定的 Message key,对投递到 Broker的消息进行索引服务,同时也提供
根据 Message Key对消息进行快速查询的功能。集群部署
- 为了增强 Broker性能与吞吐量, Broker-般都是以集群形式岀现的。各集群节点中可能存放着相同 Topic的不同 Queue。不过,这里有个问题,如果某 Broker节点宕机,如何保证数据不丢失呢?其解决方案是,
将毎个 Broker集群节点进行横向扩展,即将 Broker节点再建为一个HA集群,解决单点问题。- Broker节点集群是一个主从集群,即集群中具有 Master与 Slave两种角色。 Master负责处理读写操作请求, Slave负责对 Master中的数据进行备份。当 Master挂掉了, Slave则会自动切换为 Master去工作。所以这个 Broker集群是主备集群。一个 Master可以包含多个 Slave,但一个 Slave只能隶属于一个 Master,Master与 Slave的对应关系是通过指定相同的 BrokerName、不同的 Brokered来确定的。 Brokered为0表示Master,非0表示 Slave。每个 Broker与 NameServer集群中的所有节点建立长连接,定时注册 Topic信息到所有 Name server
5. 工作流程:
- 启动 NameServer, Name Server启动后开始监听端口,等待 Broker、 Producer、 Consumer连接。
- 启动 Broker时, Broker会与所有的 Name Serve建立并保持长连接,然后每30秒向 Name Server定时发送心跳包。
- 发送消息前,可以先创建 Topic,创建 Topic时需要指定该 Topic要存储在哪些 Broker上,当然,在创建Topic时也会将 Topic与 Broker的关系写入到 Name Server中。不过,这步是可选的,也可以在发送消息时自动创建 Topic。
- Producer发送消息,启动时先跟 Nameserver集群中的其中一台建立长连接,并从 NameServer中获取路由信息,即当前发送的 Topic消息的 Queue与 Broker的地址(IP+Port)的映射关系。然后根据算法策略从队选择一个 Queue,与队列所在的 Broke建立长连接从而向 Broker发消息。当然,在获取到路由信息后, Producer会首先将路由信息缓存到本地,再每30秒从 NameServer更新一次路由信息。
- Consumer跟 Producer类似,跟其中一台 NameServer建立长连接,获取其所订阅 Topic的路由信息,然后根据算法策略从路由信息中获取到其所要消费的queue,然后直接跟 Broker建立长连接,开始消费其中的消息。 Consumer在获取到路由信息后,同样也会毎30秒从 Name Server更新一次路由信息。不过不同于Producer的是, Consumer还会向 Broker发送心跳,以确保 Broker的存活状态。