这篇blog将具体介绍netty中对http2协议中stream dependency部分的实现,分为两部分,第一部分先介绍下netty http2所参考的HTTP/2 priority implementation in nghttp2 采用的算法,第二部分分析下netty http2中WeightedFairQueueByteDistributor的算法和具体实现。
http2中stream会依赖父stream,父stream优先于子stream发送数据,当父stream不能proceed时候,父stream的分配资源被所有子stream按照所占的权重来共享分配。所有stream形成一颗自顶向下的依赖树,树的根结点是stream 0,算法要解决的问题是将本轮可以发送的数据量在这颗依赖树上所有active streams间进行分配。
有个前提:分配数据量有个最小单元,也称为chunk,这个前提也很容易理解,这个chunk大小的选择要做到效率和公平间平衡,选择的太小的话,效率低,太大的话,不公平。
在前面的UniformStreamByteDistributor中,chunkSize是根据当前最大可写的数据量maxBytes 除以当前active stream数动态计算得到。在这里chunk大小的选择将是影响算法表现的一个重要因素。
在上一篇blog已经大体介绍了。
设计上,他可以说主要是Inbound handler,继承自ByteToMessageDecoder,通过内部decoder来对http2协议进行decode,decoder内部再委托给使用者自己实现的Http2FrameListener对收到的http2消息进行业务处理。这里和netty中常见的decoder decode出协议封装成对象消息交给pipeline下游业务handler的做法不太一样。另外,他在实现上又实现了ChannelOutboundHandler接口,看上去主要是为了实现flush方法,并不是作为一个encoder handler。如果使用方想去向连接上响应http2的response还需要通过他的encoder()方法拿到一个Http2FrameWriter对象,通过Http2FrameWriter的各种writeXXX接口直接write对应的http2消息/帧,可以说非常原始了。
h2 server是基于TLS的ALPN的扩展来start http2的,因此需要先为pipeline配置上ssl handler,然后监听ssl 握手完成事件,得到ALPN的protocol,再根据protocol来配置pipeline。
netty里已经提供好一个ApplicationProtocolNegotiationHandler
基类,通过实现其configurePipeline
方法来根据ALPN扩展里的protocol来配置相应的pipeline
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
ctx.pipeline().addLast(new HelloWorldHttp2HandlerBuilder().build());
return;
}
if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
ctx.pipeline().addLast(new HttpServerCodec(),
new HttpObjectAggregator(MAX_CONTENT_LENGTH),
new HelloWorldHttp1Handler("ALPN Negotiation"));
return;
}
throw new IllegalStateException("unknown protocol: " + protocol);
}