分布式消息队列的功能
- 应用解耦
- 流量消峰
应用系统流量瞬间猛增,这个时候如果没有缓冲机制,不可能承受住短时大流 量的冲击。通过利用消息队列,把大量的请求暂存起来,分散到相对长的一段时间内 处理,能大大提高系统的稳定性和用户体验 。
- 消息分发
- 保持最终一致性
- 方便动态扩容
RocketMQ
RocketMQ四个角色
Producer
Consumer
Broker
NameServer
启动 RocketMQ 的顺序是先启动 NameServer,再启动 Broker,这时候消息队列已经可以提供服务了,想发送消息就使用 Producer来发送,想接收消息就使用Consumer来接收 。
很多应用程序既要发送,又要接收,可以启动多个Producer 和 Consumer 来发送多种消息,同时接收多种消息 。
为了消除单点故障,增加可靠性或增大吞吐量 ,可以在多台机器上部署多个NameServer 和Broker,为每个Broker部署一个或多个 Slave。
生产者和消费者
根据使用者对读取操作的控制情况,消费者分为两种类型:
DefaultMQPushConsumer
: 由系统控制读取操作,收到消息后自动调用传入的处理方法来处理;DefaultMQPullConsumer
: 读取操作中的大部分功能由使用者自主控制 。
DefaultMQPushConsumer
DefaultMQPushConsumer
需要设置三个参数:
- Consumer的GroupName
Consumer的GroupName用于把多个Consumer组织到一起,提高并发处理能力,GroupName需要和消息模式(MessageModel)配合使用。
RocketMQ支持两种消息模式:Clustering和Broadcasting。
Clustering:同一个ConsumerGroup(GroupName相同)里的每个consumer只消费所订阅消息的一部分内容,同一个ConsumerGroup里所有的Consumer消费的内容合起来才是所订阅Topic内容的整体,从而达到负载均衡的目的。
Broadcasting:同一个ConsumerGroup里的每个Consumer都能消费到所订阅Topic的全部消息,也就是一个消息会被多次分发,被多个Consumer消费。
- NameServer的地址和端口号
可以填写多个,用分号隔开,达到消除单点故障的目的 - Topic的名称
用来标识消息类型,需要提前创建。
如果不需要消费某个Topic下的所有消息,可以通过指定消息的Tag进行消息过滤。如:Consumer.subscribe (”TopicTest”,tag1 || tag2 || tag3”)。
DefaultMQPushConsumer
通过“长轮询”方式达到push效果,长轮询方式既有pull的优点,又兼具push方式的实时性。
push方式是server端接收到消息后,主动把消息推送给Client端,实时性高。 push方式的弊端:
- 加大Server端的工作量,进而影响Server的性能
- Client的处理能力各不相同,Client的状态不受Server控制,如果Client不能及时处理server推送过来的消息,会造成各种潜在问题。
pull方式是client端循环地从server端拉取消息,主动权在client手里,自己拉取到一定量的消息后,处理妥当了再接着取。 pull方式的问题主要是循环拉取的间隔不好确定,间隔太短就处在一个“忙等”的状态,浪费资源;
每个pull的时间间隔太长,Server端有消息到来时,有可能没有被及时处理。
“长轮询”的核心是, Broker端HOLD 住客户端过来的请求一小段时间 requestHeader.setSuspendTimeoutMillis (brokerSus- pendMaxTimeMillis)
,在这个时间内有新消息到达,就利用现有的连接立刻返回消息给 Consumer。
“长轮询”的 主动权还是掌握在 Consumer 手中, Broker 即使有大量消息积压 ,也不会主动推送给 Consumer 。
长轮询方式的局限性,是在 HOLD 住 Consumer 请求的时候需要占用资源,它适合用在消息队列这种客户端连接数可控的场景中。
PushConsumer的核心还是Pull方式。
DefaultMQPullConsumer
因为PullConsumer需要用户自己处理遍历Message Queue、保持Offset,所以有更多的自主性和灵活性。
主要处理额外三件事:
-
获取Message Queue并遍历
一个Topic包括多个Message Queue,如果这个Consumer需要获取Topic下所有的消息,就要遍历所有的Message Queue。特殊情况也可以选择某些特定的Message Queue来获取消息。 -
维护Offset store
从一个Message Queue里拉取消息的时候,要传人Offset参数( long类型 的值),随着不断读取消息 ,Offset会不断增长。这个时候由用户负责把Offset存储下来,根据具体情况可以存到内存里、写到磁盘或者数据库里等。 -
根据不同的消息状态做不同的处理
拉取消息的请求发出后,会返回:FOUND
、NO MATCHED MSG
、NO NEW MSG
、OFFSET ILLEGAL
四种状态,需要根据每个状态做不同的处理 。
比较重要的两个状态是FOUNT
和NO NEW MSG
,分别表示获取到消息和没有新的消息 。
生产者向消息队列写入消息,不同的业务场景需要生产者采用不同的写入策略,比如同步发送、异步发送、延迟发送、发送事务性消息等。
DefaultMQProducer
生产者发送消息默认使用DefaultMQProducer
,发送消息主要经过5个步骤:
- 设置Producer的GroupName
- 设置InstanceName
- 设置发送失败重试次数
- 设置NameServer地址
- 组装消息并发送
消息发送的返回状态有四种:
public enum SendStatus {
SEND_OK,
FLUSH_DISK_TIMEOUT,
FLUSH_SLAVE_TIMEOUT,
SLAVE_NOT_AVAILABLE;
private SendStatus() {
}
}
发送延迟消息
RockerMQ支持发送延迟消息,Broker收到这类消息后,延迟一段时间再处理,使消息在规定的一段时间后生效。
延迟消息的使用方法是在创建 Message对象时调用
setDelayTimeLevel ( int level)
方法设置延迟时间, 然后再把这个消息发送 出去。 目前延迟的时间不支持任意设置,仅支持预设值的时间长度 ( 1s/5s/1Os/30s/Im/2m/3m/4m/5m/6m/ 7m/8m/9m/1Om/20m/30m/1h/2h)。比如 setDelayTimeLevel(3)表示延迟10s。
事务消息
RocketMQ的事务消息,是指发送消息事件和其他事件需要同时成功或同时失败 。
RocketMQ事务性消息
Offset
RocketMQ 中, 一种类型的消息会放到一 个Topic 里,为了能够并行,一般一个Topic会有多个Message Queue (也可以设置成一个), Offset是指某个Topic下的一条消息在某个Message Queue里的位置,通过Offset的值可以定位到这条消息,或者指示 Consumer从这条消息 开始向后继续处理 。