Constant

Each thread will take between 320K (32 bit JVM) and 1024K (64 bit JVM) memory for its stack.

处理1M的连接需要ITB的内存

阻塞式服务器

SocketFile中读取字节流信息,然后通过MessageReader分解成一致连续的信息块
NIO服务器线程模型[note]
阻塞式线程(利用BIO)中一个线程只负责处理一个Socket连接(对应一个TCP连接),在等待连接过程中,不得处理其他Socket连接。

之后演变出来的线程池模型:
NIO服务器线程模型[note]
TCP连接进入队列,由从线程池拿出的线程进行处理,保证服务器吞吐率的前提为:
所有连接必须都是活跃的。假如大量不活跃的连接(只建立TCP连接不发送消息)充斥
在队列中,那么服务器响应性指标下降。
扩展性上分析,动态线程池虽然可以动态增大(scale out),但是基于其阻塞本质,一个线程只能处理一个连接,即使没有数据也要无限等待,只要TCP连接不断开,其最大性能只有
16GB/1024K=10000个连接。

服务器会主动断开不活跃的TCP连接等优化手段也有,性能有一定幅度提升

底层基础决定上层建筑啊,一点毛病没有。阻塞式处理方式很长一段时间发挥作用。Tomcat就是阻塞式的吧。之后不是出现了Netty等NIO式服务器吗?

非阻塞式服务器

NIO服务器线程模型[note]
非阻塞式实现起来复杂,因为MessageReader线程和连接不再是一对一。那么消息切分就不能像阻塞式那么切,因为线程已经不是服务于一个TCP连接了。在内存中弄一个定长的缓冲区,消息的到来是这样滴:
NIO服务器线程模型[note]
当时一对一的时候,因为只处理一个TCP连接,上面最长的这块,假如来了只有一部分。可以设定一个足够大的缓冲区,等后面一部分来,怼成一个消息,这没有任何问题。

但是一个线程对多个TCP连接,假如来了只有一部分,下面再来,不一定就是这个TCP连接的消息了。这部分字节按理来说不该丢,因为发送方认为已经发送完了,而且正常的话确认帧已经回去,就不得不在应用层实现复杂网络逻辑,这就很烦。

这位大哥总结的很精辟:

There are two challenges in handling partial messages:

  1. Detecting a full message in the data block.
  2. What to do with partial messages until the rest of the message arrives.

就这两个问题,检测一个完整消息就是鉴别消息边界,这个不是TCP帧边界,而是应用特定的,比如Kafka消息格式<key,value,timestamp>,虽然几个消息有可能在一个TCP帧里。

第二个问题就是多余的一部分不完整消息怎么办?这个作者想的是给一个Channel附加一个
MessageReader,存储不完整信息。让一个MessageReader实例(不是一个线程)负责处理一个Channel的不完整信息。而线程只负责引导字节流。这是一个可行的解决方案。

Reference List

相关文章: