消息队列面试解析 - 传输协议

应用程序之间要想互相通信,一起配合来实现业务功能,还需传输协议支持。

传输协议就是应用程序之间对话的语言。设计传输协议,并没有太多规范和要求,只要是通信双方的应用程序都能正确处理这个协议,并且没有歧义即可。

1 断句

分隔符

传输协议也是种语言,在传输数据的的时候,首先要解决的就是断句。
对于传输层来说,收到的数据是什么样的?就是一段一段的字节,但是,因为网络的不确定性,你收到的分段并不一定是我们发出去的分段。
在协议中也加上“标点符号”不就行了?而且,并不需要像自然语言中那么多种标点符号,只需要定义一个分隔符即可。

这办法可行,很多传输协议采用这种方法,比如HTTP1协议,它的分隔符是换行(\r\n)。但这个办法有个问题难处理,在自然语言中,标点符号是专用的,它没有别的含义,和文字天然区分。

在数据传输过程,无论你定义什么字符作为分隔符,理论上都有可能会在传输的数据中出现。为了区分“数据内的分隔符”和真正的分隔符,得在发送数据阶段,加上分隔符之前,把数据内的分隔符做转义,收到数据之后再转义回来。这是个麻烦过程,还损失一些性能。

预置长度

更加实用方法,给每句话前面加一个表示这句话长度的数字,收到数据时,按长度读。比如:
03下雨天03留客天02天留03我不留
这里固定使用2位数字来存放长度,每句话最长可支持到99个字。
接收后的处理就比较简单,先读取2位数字03,知道接下来3个字是第一句话,那我们接下来就等着这3个字都收到了,就可以作为第一句话,接下来再按照这个方法来读第二句话、第三句话。

这种方法很好解决断句问题,并且实现起来要比分隔符方法简单,性能也更好,是普遍采用的一种分隔数据方法。

前置长度是不是也有类似的问题,03也可能是正常文字里的内容,也是需要转义吧?
你可以想一下最好自己实现一下接收数据进行解析的代码,你就会明白,前置长度是不需要转义的。
因为在解析的时候,可以明确的知道当前读到的这个位置应该是长度还是真正的数据,它是不需要根据数据流中的内容来确定的。

2 双工收发

单工通信就是,任何一个时刻,数据只能单向传输,一个人说的时候,另外一个人只能听。
HTTP1协议,就是这样,客户端与服务端建立一个连接后,客户端发送一个请求,直到服务端返回响应或者请求超时,这段时间内,这个连接通道上不能再发其他请求。
这种单工通信效率低,很多浏览器和App为解决问题,只能同时在服务端和客户端间创建多连接。

单工通信时,一句对一句,请求和响应是按照顺序依次收发,有一个天然的对应关系。就像被女朋友质问时,女朋友问一句,你才敢答一句。

而TCP连接是全双工的通道,可同时进行数据的双向收发,互相不会受影响。要提高吞吐量,应用层协议须支持双工通信。

双工通信,就是说不管是客户端还是服务端建立好链接之后,双方都可以基于该socket进行收发消息就好了,而不是说服务器只能accept到message之后再做一些处理。

如果说你和你对象有边听边说的本事,换成双工协议后,基本就是在和女人讲道理,你们就会混乱到分不清到底在回答问题还是称述观点。在并发下,顺序也没有办法保证。在实际设计协议时,一般不关心顺序,只要需要确保请求和响应能够正确对应即可。
可以这样解决:发送请求的时候,给每个请求加一个序号,这个序号在本次会话内保证唯一,然后在响应中带上请求的序号,这样就可以把请求和响应对应上了!
加上序号后,即使是抢答一般混乱,其实也分得清到底在说啥。

你和你对象就可以对自己发出去的请求来编号,回复对方响应的时候,带上对方请求的编号就可以了。这就解决了双工通信的问题。

总结

设计传输协议的时候,只要双方应用程序能够识别传输协议,互相交流就可以了,并没有什么一定要遵循的规范。

在设计传输协议的时候,需要解决如何断句的问题,有“分隔符”和“前置长度”两种断句方案可以选用。

redis 的 aof 文件好像就是前置长度哦,经典无处不在。

另外“使用ID来标识请求与响应对应关系”的方法,是一种比较通用的实现双工通信的方法,可以有效提升数据传输的吞吐量。

解决了断句问题,实现了双工通信,配合专用的序列化方法,就可以实现一套高性能的网络通信协议,实现高性能的进程间通信。很多MQ、RPC框架都是用这种方式来实现它们自己的私有应用层传输协议。

面试题快问快答

  • 实现一个简单的高性能通信程序。功能就是你和你对象三组对话,服务端是你对象,客户端是你自己,我们让俩人在客厅碰见一百万次,记录下总共耗时。
    https://github.com/WangYangA9/netty-FullDuplex-example
    https://sourcegraph.com/github.com/swgithub1006/mqlearning/-/tree/src/main/java/org/coffee/mqlearning

  • 在一次会话过程中,开头的先是唯一序列号么。然后后面跟的是数据长度,再然后是内容么。那接到消息的一方,该如何分辨序列号的长度大小,做到区分序列号和内容前的数据长度信息?
    开头是数据长度,序号也是数据的一部分,所以应该在长度之后。

猜你喜欢

转载自blog.csdn.net/qq_33589510/article/details/107853572