写点什么

netty 系列之: 使用 netty 实现支持 http2 的服务器

发布于: 41 分钟前

简介上一篇文章中,我们提到了如何在 netty 中配置 TLS,让他支持 HTTP2。事实上 TLS 并不是 https 的一个必须要求,它只是建议的标准。那么除了 TLS 之外,还需要如何设置才能让 netty 支持 http2 呢?一起来看看吧。


基本流程 netty 支持 http2 有两种情况,第一种情况是使用 tls,在这种情况下需要添加一个 ProtocolNegotiationHandler 来对握手之后的协议进行协商,在协商之后,需要决定到底使用哪一种协议。


上一篇文章,我们已经介绍 TLS 支持 http2 的细节了,这里不再赘述,感兴趣的朋友可以查看我之前的文章。


如果不使用 tls,那么有两种情况,一种是直接使用 http1.1 了,我们需要为 http1.1 添加一个 ChannelInboundHandler 即可。


另一种情况就是使用 clear text 从 HTTP1.1 升级到 HTTP2。


HTTP/2 ClearText 也叫做 h2c,我们看一个简单的升级请求,首先是客户端请求:


GET /index HTTP/1.1Host: server.flydean.comConnection: Upgrade, HTTP2-SettingsUpgrade: h2cHTTP2-Settings: (SETTINGS payload)然后是服务器端的响应,如果服务器端不支持升级,则返回:


HTTP/1.1 200 OKContent-length: 100Content-type: text/html


(... HTTP/1.1 response ...)如果服务器支持升级,则返回:


HTTP/1.1 101 Switching ProtocolsConnection: UpgradeUpgrade: h2c


(... HTTP/2 response ...)CleartextHttp2ServerUpgradeHandler 有了上面的基本流程,我们只需要在 netty 中提供对应的 handler 类就可以解决 netty 对 http2 的支持了。


不过上面的升级流程看起来比较复杂,所以 netty 为我们提供了一个封装好的类:CleartextHttp2ServerUpgradeHandler 来实现 h2c 的功能。


这个类需要传入 3 个参数,分别是 HttpServerCodec、HttpServerUpgradeHandler 和 ChannelHandler。


HttpServerCodec 就是处理 http server 的编码类,一般我们使用 HttpServerCodec。


HttpServerUpgradeHandler 是从 http1.1 升级到 http2 的处理类。


netty 也提供了一个现成的类:HttpServerUpgradeHandler,来处理升级的编码。


HttpServerUpgradeHandler 需要两个参数,一个是 sourceCodec,也就是 http 原始的编码类 HttpServerCodec,一个是用来返回 UpgradeCodec 的工厂类,返回 netty 自带的 Http2ServerUpgradeCodec。


public HttpServerUpgradeHandler(SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory) {    this(sourceCodec, upgradeCodecFactory, 0);}
复制代码


ChannelHandler 是真正处理 HTTP2 的 handler,我们可以根据需要对这个 handler 进行自定义。


有了 UpgradeHandler,将其加入 ChannelPipeline 即可。


Http2ConnectionHandler 不管是 HttpServerUpgradeHandler,还是 CleartextHttp2ServerUpgradeHandler,都需要传入一个真正能够处理 http2 的 handler。这个 handler 就是 Http2ConnectionHandler。


Http2ConnectionHandler 是一个实现类,它已经实现了处理各种 inbound frame events 的事件,然后将这些事件委托给 Http2FrameListener。


所以 Http2ConnectionHandler 需要跟 Http2FrameListener 配合使用。


这里要详细讲解一下 Http2FrameListener,它主要处理 HTTP2 frame 的各种事件。


先来看下 http2FrameListener 中提供的 event trigger 方法:


从上图可以看到,主要是各种 frame 的事件触发方法,其中 http2 中有这样几种 frame:


DATA frameHEADERS framePRIORITY frameRST_STREAM frameSETTINGS acknowledgment frameSETTINGS framePING framePING acknowledgmentPUSH_PROMISE frameGO_AWAY frameWINDOW_UPDATE frameUnknown Frame 这几种 frame 基本上列举了 http2 frame 中所有的类型。


我们要做的就是自定义一个 handler 类,继承 Http2ConnectionHandler,然后实现 Http2FrameListener 接口即可。


public final class CustHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener
复制代码


在使用 clear text 从 HTTP1.1 升级到 HTTP2 的过程中,我们需要处理两个事情,第一个事情就是处理 http1.1 使用 http 头升级到 http2,可以重写继承自 Http2ConnectionHandler 的 userEventTriggered 方法,通过判断 event 的类型是否是 UpgradeEvent,来触发对应的 Http2FrameListener 接口中的方法,比如这里的 onHeadersRead:


/** * 处理HTTP upgrade事件 */@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {    if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {        HttpServerUpgradeHandler.UpgradeEvent upgradeEvent =                (HttpServerUpgradeHandler.UpgradeEvent) evt;        onHeadersRead(ctx, 1, upgradeToHttp2Headers(upgradeEvent.upgradeRequest()), 0 , true);    }    super.userEventTriggered(ctx, evt);}
复制代码


upgradeToHttp2Headers 方法将传入的 FullHttpRequest,转换成为 Http2Headers:


private static Http2Headers upgradeToHttp2Headers(FullHttpRequest request) {    CharSequence host = request.headers().get(HttpHeaderNames.HOST);    Http2Headers http2Headers = new DefaultHttp2Headers()            .method(HttpMethod.GET.asciiName())            .path(request.uri())            .scheme(HttpScheme.HTTP.name());    if (host != null) {        http2Headers.authority(host);    }    return http2Headers;}
复制代码


还有一个要实现的方法,就是 sendResponse 方法,将数据写回给客户端,回写需要包含 headers 和 data 两部分,如下所示:


/** * 发送响应数据到客户端 */private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {    Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());    encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());    encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());}
复制代码


总结到此,一个处理 clear text 从 HTTP1.1 升级到 HTTP2 的 handler 就做好了。加上之前讲解的 TLS 扩展协议的支持,就构成了一个完整的支持 http2 的 netty 服务器。


本文的例子可以参考:learn-netty4


本文已收录于 http://www.flydean.com/27-netty-http2/


最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 41 分钟前阅读数: 2
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:使用netty实现支持http2的服务器