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

30次阅读

共计 3841 个字符,预计需要花费 10 分钟才能阅读完成。

简介

上一篇文章中,咱们提到了如何在 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.1
Host: server.flydean.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c 
HTTP2-Settings: (SETTINGS payload) 

而后是服务器端的响应,如果服务器端不反对降级,则返回:


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

(... HTTP/1.1 response ...)

如果服务器反对降级,则返回:

HTTP/1.1 101 Switching Protocols 
Connection: Upgrade
Upgrade: 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 frame
  • HEADERS frame
  • PRIORITY frame
  • RST_STREAM frame
  • SETTINGS acknowledgment frame
  • SETTINGS frame
  • PING frame
  • PING acknowledgment
  • PUSH_PROMISE frame
  • GO_AWAY frame
  • WINDOW_UPDATE frame
  • Unknown 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 事件
     */
    @Override
    public 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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

正文完
 0