时空域联合约束技术在 Netty 中的应用
我们有一款面向对象存储的安全网关服务,用于实时加密、解密文件数据流,对使用公有云安全存储文件起到很大的作用。
项目初期在构造 ChannelInitializer 时采用了常用的构造方式:
1 | protected void initChannel(SocketChannel socketChannel) throws Exception { |
这种方式使用起来很简单,通过 HttpObjectAggregator 将请求数据包装成 FullHttpRequest
对象,使用时直接通过 headers()
和 content()
方法获取 header 和 body 内容。通过业务逻辑处理更新相应的 header 和 body 内容后转发至对应的云存储服务。接收响应后,HttpObjectAggregator 再次将响应数据包装成 FullHttpResponse
对象,业务逻辑处理完成后响应至 Client 端。
从上述描述可知,如果请求的数据内容很大,在构造 FullHttpRequest
或 FullHttpResponse
时会产生较大的内存消耗和延时:
HttpObjectAggregator
构造时必须指定所能处理的最大 content 值,因为 content 保存了从 socket 中读取到的所有字节数据,不指定该值势必会造成内存的暴增导致 OutOfMemory 异常- 构造
FullHttpRequest
或FullHttpResponse
是一个等待的过程,必须等待数据完全读取完毕才可以使用,延时增加导致并发量下降
在这种情形下必然需要解决这个问题。
时空域联合约束技术充分利用 Netty 特性,在处理非结构化数据时,采用分段读取的方式,边读取、边处理、边转发,充分利用网络带宽,避免数据阻塞。
应用时空域技术后, ChannelInitializer 构造方式如下:
1 | protected void initChannel(SocketChannel socketChannel) throws Exception { |
对比发现少了向 pipeline 中添加 HttpObjectAggregator
,数据处理流程变成了如下方式:
ChannelInboundHandlerAdapter
处理请求时会首先接收到 HttpRequest
,接着是 HttpContent
,最后是 LastHttpContent
。
ChannelOutboundHandlerAdapter
处理响应时首先接收到 HttpResonse
,接着是 HttpContent
,最后是 LastHttpContent
。
从请求流程图可以看出,在通过 HttpObjectDecoder 从 socket 读取数据时,先读取 HTTP 头部分,并将之分发至业务层,接着分段读取 body 内容,并依次分发至业务层,业务层可以由此实时处理请求/响应数据,充分利用带宽。因为少了 HttpObjectAggregator
包装数据,流式数据分段为 KB 级别,极大的节省内存占用,单个请求的处理时间、内存占用降低,充分利用时间和空间,提高了吞吐量。
通过压测观察,应用该技术后,性能提高 3倍以上,吞吐量更多的取决于带宽能力。
欢迎拍砖。