从0到1落地自研网关项目,掌握更底层的高阶技能
download:
3w ukoou com
网关在架构设计时最为关键点,就是网关在接收到请求,调用后端服务时不能阻塞 Block,否则网关的吞吐量很难上去,因为最耗时的就是调用后端服务这个远程调用过程。
如果这里是阻塞的,Tomcat 的工作线程都 block 住了,在等待后端服务响应的过程中,不能去处理其他的请求,这个地方一定要异步。
架构图如下:
这版我们实现单独的 Push 层,作为网关收到响应后,响应客户端时,通过这层实现,和后端服务的通信是 HttpNioClient,对业务的支持黑白名单,流控,鉴权,API 发布等功能。
但是这版只是功能上达到网关的要求,处理能力很快就成了瓶颈,单机 QPS 到 5K 的时候,就会不停的 Full GC。
后面通过 Dump 线上的堆分析,发现全是 Tomcat 缓存了很多 HTTP 的请求,因为 Tomcat 默认会缓存 200 个 requestProcessor,每个 prcessor 都关联了一个 request。
还有就是 Servlet 3.0 Tomcat 的异步实现会出现内存泄漏,后面通过减少这个配置,效果明显。
但性能肯定就下降了,总结了下,基于 Tomcat 做为接入端,有如下几个问题。
Tomcat 自身的问题:
1)缓存太多,Tomcat 用了很多对象池技术,内存有限的情况下,流量一高很容易触发 GC;
2)内存 Copy,Tomcat 的默认是用堆内存,所以数据需要读到堆内,而我们后端服务是 Netty,有堆外内存,需要通过数次 Copy;
3)Tomcat 还有个问题是读 body 是阻塞的, Tomcat 的 NIO 模型和 reactor 模型不一样,读 body 是 block 的。
这里再分享一张 Tomcat buffer 的关系图:
通过上面的图,我们可以看出,Tomcat 对外封装的很好,内部默认的情况下会有三次 copy。
HttpNioClient 的问题: 获取和释放连接都需要加锁,对应网关这样的代理服务场景,会频繁的建连和关闭连接,势必会影响性能。
基于 Tomcat 的存在的这些问题,我们后面对接入端做改造,用 Netty 做接入层和服务调用层,也就是我们的第二版,能彻底解决上面的问题,达到理想的性能。
4、第2版:Netty+全异步
基于 Netty 的优势,我们实现了全异步,无锁,分层的架构。
先看下我们基于 Netty 做接入端的架构图: