netty系列之:性能为王!创建多路复用http2服务器

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"简介在之前的文章中,我们提到了在netty的客户端通过使用Http2FrameCodec和Http2MultiplexHandler可以支持多路复用,也就是说在一个连接的channel基础上创建多个子channel,通过子channel来处理不同的stream,从而达到多路复用的目的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然客户端可以做到多路复用,同样的服务器端也可以,今天给大家介绍一下如何在netty的服务器端打造一个支持http2协议的多路复用服务器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"多路复用的基础netty中对于http2多路复用的基础类是Http2FrameCodec、Http2MultiplexHandler和Http2MultiplexCodec。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Http2FrameCodec是将底层的HTTP/2 frames消息映射成为netty中的Http2Frame对象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了Http2Frame对象就可以通过Http2MultiplexHandler对新创建的stream开启不同的channel。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Http2MultiplexCodec是Http2FrameCodec和Http2MultiplexHandler的结合体,但是已经不再被推荐使用了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为Http2FrameCodec继承自Http2ConnectionHandler,而Http2MultiplexHandler继承自Http2ChannelDuplexHandler,所以这两个类可以同时在客户端和服务器端使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客户端使用Http2FrameCodecBuilder.forClient().build()来获得Http2FrameCodec,而服务器端通过Http2FrameCodecBuilder.forServer().build()来获得Http2FrameCodec。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"多路复用在server端的使用配置TLS处理器对于服务器端,同样需要处理TLS和普通clear text两种情况。对于TLS来说,我们需要自建ProtocolNegotiationHandler继承自ApplicationProtocolNegotiationHandler,然后实现configurePipeline方法,在其中分别处理http2和http1.1的连接:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {\n if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {\n //添加多路复用支持\n ctx.pipeline().addLast(Http2FrameCodecBuilder.forServer().build());\n ctx.pipeline().addLast(new Http2MultiplexHandler(new CustMultiplexHttp2Handler()));\n return;\n }\n\n if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {\n ctx.pipeline().addLast(new HttpServerCodec(),\n new HttpObjectAggregator(MAX_CONTENT_LENGTH),\n new CustHttp1Handler(\"ALPN Negotiation\"));\n return;\n }\n\n throw new IllegalStateException(\"未知协议: \" + protocol);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先添加Http2FrameCodec,然后添加Http2MultiplexHandler。因为Http2MultiplexHandler已经封装了多路复用的细节,所以自定义的handler只需要实现正常的消息处理逻辑即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为Http2FrameCodec已经对消息进行了转换成为HTTP2Frame对象,所以只需要处理具体的Frame对象:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {\n if (msg instanceof Http2HeadersFrame) {\n onHeadersRead(ctx, (Http2HeadersFrame) msg);\n } else if (msg instanceof Http2DataFrame) {\n onDataRead(ctx, (Http2DataFrame) msg);\n } else {\n super.channelRead(ctx, msg);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置clear text upgrade对于h2c的升级来说,需要向pipline中传入sourceCodec和upgradeHandler两个处理器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sourceCodec可以直接使用HttpServerCodec。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"upgradeHandler可以使用HttpServerUpgradeHandler。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HttpServerUpgradeHandler的构造函数需要传入一个sourceCodec和一个upgradeCodecFactory。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sourceCodec我们已经有了,再构造一个upgradeCodecFactory即可:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private static final UpgradeCodecFactory upgradeCodecFactory = protocol -> {\n if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {\n return new Http2ServerUpgradeCodec(\n Http2FrameCodecBuilder.forServer().build(),\n new Http2MultiplexHandler(new CustMultiplexHttp2Handler()));\n } else {\n return null;\n }\n};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从代码中可以看出,upgradeCodecFactory内部又调用了Http2FrameCodec和Http2MultiplexHandler。这和使用TLS的处理器是一致的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" final ChannelPipeline p = ch.pipeline();\n final HttpServerCodec sourceCodec = new HttpServerCodec();\n p.addLast(sourceCodec);\n p.addLast(new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory));\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"总结通过上述方式,就可以创建出支持多路复用的http2 netty服务器了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文的例子可以参考:learn-netty4","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文已收录于 http://www.flydean.com/33-netty-multiplex-http2server/","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章