created_at | updated_at | slug | tags | |
---|---|---|---|---|
2020-08-15 16:39:42 -0700 |
2021-02-16 15:40:19 -0800 |
how-queue-polling-message |
|
同事:阿里云MNS获取消息的API使用起来不够方便,需要不断手动长轮询,看起来有点原始。 我:em。。。那其它消息队列使用什么方式?难道不是长轮询?
在使用阿里云MNS(Message Notification Service)服务时,意识到其从服务端获取消息的方式是手动长轮询。写代码时可能会用到while-true看似危险的控制结构,而查看手册这也是官方推荐的使用方式。于是想找找其它消息队列拉取消息的原理。
在此之前,我们首先做一些基础知识准备——什么是长轮询? 而要回答这个问题之前,我们需要区分:长连接、短链接、长轮询、短轮询。
长短连接,指的的TCP连接。当客户端通过socket连接到服务端时,就建立了一个TCP连接,当双方都不关闭该连接时,它就是长连接。而如果在一次请求-响应后,关闭了这个TCP连接,则称这个连接为短链接。
在TCP协议中,并没有长连接、短连接的概念,这完全是根据使用方法区分。
需要注意TCP长连接会因为网络波动、操作系统环境等问题出现连接丢失的问题,需要进行额外的连接保活工作,如发送定时心跳(心跳保活机制在很多场景下都有使用,比如Zookeeper;比如AMQP)。
轮询,即客户端与客户端不断重复 请求数据-消费数据 这一过程。有两种策略
- 客户端请求,服务端有数据则返回数据,没有数据则返回空。
- 客户端请求,服务端有数据则返回数据,没有数据则将连接保持,等有数据时再返回。
TCP是传输层协议,而HTTP是应用层协议。而头部Connection: Keep-Alive表示维持TCP连接,即我们常说的HTTP长连接。 Keep-Alive的工作原理如下:
- 客户端请求携带Connection: Keep-Alive头部,服务端接收后,会在响应该条HTTP请求后继续保持TCP连接;否则关闭。
- 客户端在收到的服务端响应后,如果有携带Connection: Keep-Alive头部,则会使用同一个TCP连接发送下一个HTTP请求。
此外,Keep-Alive还可以设置timeout参数,用于指定预期的连接超时时间。
注意HTTP协议本身是一个请求-响应协议,无状态,即一个事务在请求-响应后就结束了,再次请求就是一个新事务。而HTTP协议中定义了几种HTTP连接:
- 常规连接:即请求响应后即关闭TCP连接。
- 持久连接(又称长连接):即可以传输多个事务的连接,传输后不关闭TCP连接,下次传输继续使用。对应Keep-Alive。
- 管道式连接:即在一个TCP连接上一次发送多个事务的请求,再依次接收多个事务的请求。传统的是一个事务的请求-响应完成后再进行下一个事务。
阿里云MNS的数据传输走的是HTTP协议,意味着无论是点对点、还是发布-订阅模式,都要通过HTTP来进行。这就导致了文章一开始所说的使用方式的问题,由于HTTP协议本身无状态,只能是客户端请求-服务端响应,客户端拉取数据时只能采用轮询的方式。而为了拉取操作过于频繁带来的损失,采用的长轮询:客户端请求,服务端没有消息时,挂起该连接,等到有消息时再响应。
也许你很好奇,如果走HTTP协议,那客户端如何订阅主题,即客户端如何被动收到消息?答案是设置回调地址;或将主题消息推送到另一个队列,然后长轮询该队列。
总之阿里云MNS获取消息的方式因为其采用HTTP传输协议而受到了限制。不过这也有一个好处,就是任何语言都能使用MNS服务,甚至不需要SDK,手动调起HTTP接口即可。
RabbitMQ走的是AMQP协议,它和HTTP一样,也是应用层协议,构建于TCP之上。与HTTP不同的是,它专门设计用于消息传输。客户端和服务端采用TCP长连接,前面说过,只要建立了TCP连接,数据就能双向传输。这样就能做到客户端不需要主动轮询,服务端推送消息到客户端的效果。使用起来方便很多。对AMQP协议,就是Basic.Consume。
既然说到了AMQP的双向传输功能,就联想到了WebSocket,它是如何实现双向传输的呢?在翻看一些资料后,发现它也是建立了一个持久的TCP连接,利用TCP双向传输的特性,实现服务端的消息推送。
通常说WebSocket时,是对比HTTP协议,HTTP协议无状态,事务间隔离,建立在请求-响应这样的被动机制下,在应用上无法实现服务端主动推送消息的场景。WebSocket的出现就是为了解决该问题,
如果把WebSocket和AMQP进行对比,在服务端主动发送消息到客户端这一点来说,二者原理一致。甚至可以说WebSocket是AMQP的功能子集。当然也不能这么说,毕竟他们目标不同,WebSocket聚焦于推送消息,而AMQP聚焦于消息模型的抽象,顺带解决消息传输的问题。且就消息推送这一点来说,尽管原理一致,实现方式可能也是不同的。
当然消息队列除了上述两个,还有很多其它的,比如流行的Kafka、RocketMQ、ActiveMQ等。由于他们并不是基于AMQP协议,因此具体方式还不知道。不过我想,传输层都是TCP,实现双向传输的机制应该逃不过TCP长连接的。