文章目录
对《muduo》封面提出疑问的一些解答
-
TCP协议真的有所谓的“粘包问题”吗?该如何设计消息帧的协议?又该如何编码实现分包才不会掉到陷阱里?
TCP本身不存在“粘包问题”,因为TCP是基于字节流的协议,没有所谓的“包”。实际上“粘包问题”是上层协议消息转成字节流发送之后,接收方如何正确处理字节流到上层协议消息的unmashal问题。
设计(上层协议)消息帧最基础的原则就是正确能从字节流中unmashal出消息数据包,例如在上层消息数据包加上帧头、消息协议编号,帧尾(或者帧头,数据包长度,消息协议编号)。另外优秀的设计也要兼顾mashal和unmashal的效率和可扩展性,例如Protobuf
分包要解决的问题:生命周期管理;出错如何处理;如何处理一次收到半条消息、一条消息、一条半消息、两条消息。
-
要不改成用现成的libevent网络库吧,怎么查询一下数据库就把其他连接上的请求给耽误了?
这里指MySQL等只提供同步操作API的数据库,实际上API操作的表现往往同步且阻塞。解决的办法就是多开一条线程专门处理数据库的I/O。
-
再用一个线程池吧。万一发回响应的时候对方已经断开连接了怎么办?会不会串话?
网络编程所谓“串话”实际是指程序尝试使用已释放的描述符进行通信。于是乎问题就来到如何维护释放描述符的问题,遇到资源释放问题,那么解决办法就是使用RAII,用shared_ptr维护一个socket对象。这样一旦断开连接,描述符也会被自动回收。
-
分布式系统跟单机多进程到底有什么本质区别?心跳协议为什么是必需的,该如何实现?
分布式系统的出错状况比单机要复杂:例如远程机子断联,我们无法知道是进程宕机还是网络中断。
心跳协议是必需的。心跳协议用于判断对方进程是否能正常工作。(心跳间隔往往大于网络时延,以便区别到底是网络问题还是进程问题)
网络库设计思路
网络库是介于socket层和应用层之间一层抽象,实际上它也应属于应用层的一部分,它为应用层解决网络的三个半事件:建立连接、断开连接、消息到达、消息发送完毕(半个)。 对于网络服务器来说,因为它需要处理多个连接的收发,而且需要高效地处理多个连接收发。优秀的网络库必须要解决以下问题:
- 高性能
- 多线程 多线程安全
- 网络I/O尽量不阻塞于某个线程
- 尽可能少的内存切换
- 代码复用
- 高内聚 低耦合
代码复用
Douglas C. Schmidt给我们总结了一个网络库设计可以解耦成几个部分:事件处理模型、并发模型和连接模型。 二十多年来模型也依旧如此,仍能解决现今绝大部分网络性能问题,说明网络库设计已成定式,实际运用少有突破。
事件处理模型
从UNP v.1我们可以学习到单线程非阻塞select编程实例,多线程阻塞accept编程实例,以及对应线程池版阻塞accept编程实例。根据UNP v.1统计,单线程非阻塞select编程实例的I/O执行效率最高(足以解决c10k问题)。而它唯一缺点就是代码量大,因此我们对其进行代码封住复用(归功D. C. Schmidt),而单线程非阻塞select编程实例封装之后就是Reactor模型了。
事件处理模型主要有Reactor(反应式)和Proactor(前摄式)两种模式。
-
Reactor
Reactor是基于I/O multiplexting的一个同步事件循环模型,关键结构包括:-
Demultiplexer:解复用,一般和select(poll, epoll)内聚,用于处理select(poll, epoll)状态。
-
Event Handler:事件句柄调度,根据Demultiplexer返回handle调用对应event。
-
Dispatcher:事件循环主体,用于初始化,注册Event Handler。
一个Reactor事件循环:
-