一些Web应用对实时消息比较敏感,HTTP协议是比较慢的协议,频繁的通信效率不高。若有服务器向用户发送通知的情景,并且有对时间的要求,可以考虑服务器推送技术。任何技术的使用都要基于需求出发,并不是最好的技术就是好的方案,过犹不及。

HTTP协议里最适合做推送的就数WebSocket,这是一种新的全双工连接协议,需要服务器和客户端浏览器的支持。还有一种单向服务器推送机制是SSE。这两种协议都需要保持长时间的连接,服务器维护大量的连接对性能和内存和网络都是挑战。

TCP是面向连接的协议,每建立一个连接都会占用客户端和服务端各一个端口。服务端要维护大量的连接和端口的映射。IP端口的范围是有限的,端口号最大是65536,通常服务器会保留1024一下的端口给特殊服务,再剩下的端口范围内再保留用户手动指定使用的端口,剩下的六万多个端口是可以分配给进程的。实际上一个端口可以服务多个不同地址的客户端,端口号与客户端的映射关系比较复杂,可以再详细讲。

在linux系统中,一个进程能够打开的文件句柄是有软硬限制的,默认是1024,可以调整。网络端口也属于文件句柄。整个系统支持的文件句柄总和也是有限的,根据系统内存和启动参数确定,属于硬件限制,通过提升硬件水平,文件句柄可以达到百万级别。所以系统的限制还是比较小的。

应用层面要考虑的就是接受和保持连接对资源的消耗。建立连接要快,保持连接要减少内存的消耗。基本思路就是异步IO和多线程。连接的建立对处理器有一定要求,尽量使用多核心多线程并行处理。

综合考虑,单台机器8G内存的话维持10万个长连接是可行的。网上有人做到百万并发的,不知是否使用集群环境。这种情况下应该使用多个负载均衡服务器分配到多个后端处理,保持稳定性。

使用socket.io做服务器,两核心处理器,10G内存,两个线程,使用socket.io官网示例,三台测试机用websocket-bench测试。当连接数达到3万以上时出现少量无法第一次连接,经过重连可以达到全部保持正常连接。连接数达到5万时出现3千多连接无法连接,反复有客户端掉线,然后重连,无法全部连接。建立连接对处理器的消耗比保持连接大得多,保持连接对内存消耗很少,这里只测试保持连接,没有发送任何数据。处理器成为了瓶颈,内存很宽裕。维持连接的网络流量也很小,在几十k的范围。

测试没有任何业务,单纯为了得到最大连接数。当连接达到3万以上的时候连接质量下降,有可能是网络问题,也有可能是websocket-bench运行异常,websocket-bench相当占内存,8G最多开2万个连接,或许可以考虑使用其他测试软件。

服务器理论上还能增加连接数,不过连接效率变差,推送能力也有疑问。或许还会准备更详细的测试,不过脱离业务实际的测试并没有参考意义。也不应该让服务器在重负荷下持续运行,更多的考虑是稳定和快速。

原文链接:https://marskid.net/2017/12/03/web-server-push/