文章

Netty应用规划

Netty应用规划

引言

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
—— 摘自netty.io

Netty是一款基于Java NIOReactor的异步事件驱动型网络应用框架‌,由JBOSS开源维护,现为GitHub独立项目,用于快速开发高性能、高可靠性的网络服务器与客户端程序,大幅简化了TCP/UDP等场景下的网络编程工作。

Netty应用

高并发Web应用

Web应用中,通讯协议都是HTTP协议,后台都是用的REST接口,看NettyHTTP场景下的使用场景有哪些?

Web应用的并发量有高有低,高并发应用要么是大厂ToC应用(ToB的并发量都不高),要么是基础型组件(或者是中台性质的组件,比如消息推送、服务总线等)。为了分析,我们把应用做分级,分别看下十万及以下QPS吞吐量级别和百万QPS吞吐量级别。接入层和服务层之间的通讯协议,是HTTP协议。

Desktop View 图片来自网络

Desktop View 图片来自网络

思考下,
在这种架构中,Netty能够解决什么问题?又是怎么使用Netty的呢?
如果你的技术栈是SpringCloud,它搭建的微服务是执行在内嵌的Tomcat服务器上的,Tomcat服务器有哪些问题?
Netty是高性能的,Tomcat能不能替换成Netty?如果能替換,是在哪些环节做替换?

对比下,TomcatNetty不一样的地方,

Desktop View Tomcat和Netty对比

Tomcat也用了反应器这种线程模型(高性能的IO线程模型),这个模型并不是Netty的专利,所以Tomcat也是高性能的,但缺陷在于IO模型用的不是epoll,而是selectepollselect大的模型是一致的,都属于IO多路复用模型,但在性能上selectepoll低了很多。

另一个核心的性能劣势是在内存的管理上,Tomcat并没有用池化内存,也没有使用堆外内存。

刚好Tomcat的劣势,就是Netty的优势。

Desktop View Netty和Tomcat对比

Netty在反应器线程模型上面,可以切换到更高性能的epoll。内存管理上用了池化的内存,并且使用了池化的堆外内存,使用堆外内存进行IO操作,性能高了好几个量级。零复制,使用堆外内存,会少好几次CPU的内存复制。这两大优势一对比,Netty的性能就高很多。

Netty的劣势,技术难度比Tomcat大,而且在Web领域没有形成开发生态。

Tomcat一直专注于Web开发,在Web领域比如SpringMVCStrust等这些技术都是围绕Tomcat展开的,在Web开发这块,Tomcat已经组建了自己的生态,大量开箱即用的组件可以直接拿来用。Netty没有,如果要用NettyWeb开发,可以选择的一个组件是webFlux(一种响应式编程的模式),这个组件下就得使用响应式编程的代码组织方式,比如Fluxmono这种流式编程,和之前MVC的代码不兼容,如果传统老项目要做改造,工作量很大。另外,这种代码的组织方式(流式编程的风格),和传统SpringMVC的顺序调用的风格也完全不同,调试定位分析的复杂度都有相应的增加。Spring官方对这种处理也不友善,对程序不是特别友好,没有解决传统程序使用Netty替换Tomcat的问题。

网关层面,webFluxSpringCloud Gateway的底层是用了的。

传统的SpringBoot应用或者Tomcat应用,如果要把Tomcat替换为Netty,只有一种选择,就是自己动手。用NettyWeb服务器,把这个Netty Web服务器放到SpringBoot应用里,也能正常运行,只不过自己动手做这种基础组件,要不断地持续投入,有些企业考虑成本,不太愿意做这些事情。到目前为止,没有一个成熟的工业级的Web服务器能够替代Tomcat

实际上,普通的Java服务并发量不会太高,为什么呢?因为一般都是面向数据库编程,数据库的吞吐量本身就很低,所以就导致Tomcat服务吞吐量也是上不来,这样就没有改造Tomcat的必要性,Tomcat支撑这种吞吐量是足够的,这也是大家没有花时间去升级和改造这一块的原因。只要做好合理的配置,微服务使用TomcatWeb服务器是没有问题的。

Web应用中,虽然没有直接使用Netty,但是很多组件间接用到了Netty使用的线程模型——Reactor反应器模型。比如Nginx,它就是Reactor的事件处理模型(或者说线程处理模型),再比如Redis也用了Reactor模型,Tomcat也用了Reactor线程处理模型(不要认为Tomcat是一个请求一个线程这种古老的模式)。

以上是在高并发场景下Netty怎么切入和使用,在百万级QPS场景下,服务这一层主要是考虑怎么做横向扩展,有一部分是运维的工作,有一部分是架构师的工作,对于开发来说,只要做好一个实例的调优,其他进行参数的复制就可以了。

非web应用

HTTP的典型应用,是高并发IM。高并发IM只是非HTTP应用的其中一种落地方案,一种细分场景。随着网络带宽越来越大,也随之诞生了大流量的应用,比如视频会议、直播(语音流、视频流)、消息推送等,这些都不是基于HTTP协议,HTTP协议并不是传输性能很高的协议,使用的传输性能高的协议都是非HTTP的,比如基于TCP/UDP这种四层传输层协议。

在这些应用中,Netty开发的价值就很高了,Java应用基本在这些场景下必须要用到Netty,包括IMIM是一直存在的应用场景,时间比较久远。虽然像视频会议这种其他业务场景,和IM不太一样,但在系统架构(设计和开发)的维度,用到的思想、组件、原理都和IM类似。了解IM更大的价值,在于TCP层或者UDP层这种非HTTP场景的高并发应用的开发架构模式。

简单看下,怎么用Netty服务器做高并发IM架构集群的?

我们知道,理论上,从底层IO系统调用层面,epoll可以支持同时监听处理百万级连接。假设给一台服务器规划100w的连接,资源占用情况是怎么样的呢?

先从内存的角度触发,用命令查看一个连接的写缓冲和读缓冲,

1
2
3
4
$ cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4194304
$ cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 6291456

这三个值分别表示最小分配值、默认分配值和最大分配值。

简单计算,按照一个连接按照接收缓冲区4K,发送缓冲区4K(最小是4K),有些说做了预分配,有些说没做预分配,涉及到内核的处理,操作系统不同,分配的方式也不一样,做架构从悲观的角度,就认为做了分配的话,一个连接占8K内存(不算业务资源的话),100w连接什么都不干,占多少内存?

可以估算下,8K * 100w,约等于10 * 1K * 100w,即10G

一个连接包含两个缓冲区(发送、接收),再加上其他的一些内存(堆外、堆内存),百万连接,不算业务处理内存,也不算其他内存,只算连接管理内存就快到10G。所以,单体服务器可以管理上百万的连接,但是也需要很多资源,其实不推荐一个服务器搞这么多连接,如果服务器资源足够,比如一台服务器几百G内存,那也没关系。

再来看端口规划,一个TCP连接由2个端口组成,发送端口和接收端口,这俩有一个不一样,就是一个新连接。当然,发送端口和接收端口不能相同。

如果要规划100w连接,最容易想到的方案是,启动十个客户端,每个客户端10w连接,然后去连接同一个服务器端口,这样做无疑是比较麻烦的。可以稍微改进下,服务端开启100个监听端口,比如20000-20099,客户端可以针对每个服务端的端口,开1w个连接,每一个服务端的同一个端口,客户端的1w个端口都可以连一次,相乘就是100w

假设内存32G,按上面的数据理论来推(架构就是数学思维),30W就可以使用3G内存来做连接管理,没有问题。300w就用10Netty服务器,3000w就用100个。亿级高并发IM,需要一个很大的Netty集群。

Desktop View IM集群架构简图

对于单台服务器来说,需要打破系统内核的连接数限制配置,才能达到预期效果,否则会由于系统内核的限制报各种错误。

本文由作者按照 CC BY 4.0 进行授权

© ManShouyuan. 保留部分权利。

本站总访问量 本站访客数人次

🚩🚩🚩🚩🚩🚩