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":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章