我们有一款面向对象存储的安全网关服务,用于实时加密、解密文件数据流,对使用公有云安全存储文件起到很大的作用。

项目初期在构造 ChannelInitializer 时采用了常用的构造方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void initChannel(SocketChannel socketChannel) throws Exception {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpContentCompressor());
pipeline.addLast(new HttpObjectAggregator(10485760));
pipeline.addLast(new ChunkedWriteHandler());

// 业务处理完毕后返回数据的 ChannelOutboundHandlerAdapter
pipeline.addLast(outbound);

// 业务层处理请求的 ChannelInboundHandlerAdapter
pipeline.addLast(inbound);

}

这种方式使用起来很简单,通过 HttpObjectAggregator 将请求数据包装成 FullHttpRequest 对象,使用时直接通过 headers()content() 方法获取 header 和 body 内容。通过业务逻辑处理更新相应的 header 和 body 内容后转发至对应的云存储服务。接收响应后,HttpObjectAggregator 再次将响应数据包装成 FullHttpResponse 对象,业务逻辑处理完成后响应至 Client 端。

从上述描述可知,如果请求的数据内容很大,在构造 FullHttpRequestFullHttpResponse 时会产生较大的内存消耗和延时:

  • HttpObjectAggregator 构造时必须指定所能处理的最大 content 值,因为 content 保存了从 socket 中读取到的所有字节数据,不指定该值势必会造成内存的暴增导致 OutOfMemory 异常
  • 构造 FullHttpRequestFullHttpResponse 是一个等待的过程,必须等待数据完全读取完毕才可以使用,延时增加导致并发量下降

在这种情形下必然需要解决这个问题。

时空域联合约束技术充分利用 Netty 特性,在处理非结构化数据时,采用分段读取的方式,边读取、边处理、边转发,充分利用网络带宽,避免数据阻塞。

应用时空域技术后, ChannelInitializer 构造方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void initChannel(SocketChannel socketChannel) throws Exception {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpContentCompressor());
pipeline.addLast(new ChunkedWriteHandler());

// 业务处理完毕后返回数据的 ChannelOutboundHandlerAdapter
pipeline.addLast(outbound);

// 业务层处理请求的 ChannelInboundHandlerAdapter
pipeline.addLast(inbound);

}

对比发现少了向 pipeline 中添加 HttpObjectAggregator,数据处理流程变成了如下方式:

netty-data-flow

ChannelInboundHandlerAdapter 处理请求时会首先接收到 HttpRequest ,接着是 HttpContent,最后是 LastHttpContent

ChannelOutboundHandlerAdapter 处理响应时首先接收到 HttpResonse,接着是 HttpContent,最后是 LastHttpContent

从请求流程图可以看出,在通过 HttpObjectDecoder 从 socket 读取数据时,先读取 HTTP 头部分,并将之分发至业务层,接着分段读取 body 内容,并依次分发至业务层,业务层可以由此实时处理请求/响应数据,充分利用带宽。因为少了 HttpObjectAggregator 包装数据,流式数据分段为 KB 级别,极大的节省内存占用,单个请求的处理时间、内存占用降低,充分利用时间和空间,提高了吞吐量。

通过压测观察,应用该技术后,性能提高 3倍以上,吞吐量更多的取决于带宽能力。

欢迎拍砖。