Netty之旅二:口口相傳的高性能Netty到底是什麼?
{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ad/adc61b03ce4460399a0adb2b07e8a74b.png","alt":"d0iosx.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"高清思維導圖原件("},{"type":"codeinline","content":[{"type":"text","text":"xmind/pdf/jpg"}]},{"type":"text","text":")可以關注公衆號:"},{"type":"codeinline","content":[{"type":"text","text":"一枝花算不算浪漫"}]},{"type":"text","text":" 回覆"},{"type":"codeinline","content":[{"type":"text","text":"netty01"}]},{"type":"text","text":"即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"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":"上一篇文章講了"},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"相關的知識點,相比於傳統"},{"type":"codeinline","content":[{"type":"text","text":"IO"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"已經做得很優雅了,爲什麼我們還要使用"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"?"}]},{"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":"上篇文章最後留了很多坑,講了"},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"使用的弊端,也是爲了引出"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"而設立的,這篇文章我們就來好好揭開"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"的神祕面紗。"}]},{"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":"本篇文章的目的很簡單,希望看過後你能看懂"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"的示例代碼,針對於簡單的網絡通信,自己也能用"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"手寫一個開發應用出來!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一個簡單的Netty示例"}]},{"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端的程序,代碼參考自:"},{"type":"codeinline","content":[{"type":"text","text":"http://www.imooc.com/read/82/article/2166"}]}]},{"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":"代碼有點長,主要核心代碼是在"},{"type":"codeinline","content":[{"type":"text","text":"main()"}]},{"type":"text","text":"方法中,這裏代碼也希望大家看懂,後面也會一步步剖析。"}]},{"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":"PS:我是用"},{"type":"codeinline","content":[{"type":"text","text":"mac"}]},{"type":"text","text":"系統,直接在終端輸入"},{"type":"codeinline","content":[{"type":"text","text":"telnet 127.0.0.1 8007"}]},{"type":"text","text":" 即可啓動一個聊天框,如果提示找不到"},{"type":"codeinline","content":[{"type":"text","text":"telnet"}]},{"type":"text","text":"命令,可以通過"},{"type":"codeinline","content":[{"type":"text","text":"brew"}]},{"type":"text","text":"進行安裝,具體步驟請自行百度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * @Description netty簡易聊天室\n *\n * @Author 一枝花算不算浪漫\n * @Date 2020/8/10 6:52 上午\n */\npublic final class NettyChatServer {\n\n static final int PORT = Integer.parseInt(System.getProperty(\"port\", \"8007\"));\n\n public static void main(String[] args) throws Exception {\n // 1. EventLoopGroup\n EventLoopGroup bossGroup = new NioEventLoopGroup(1);\n EventLoopGroup workerGroup = new NioEventLoopGroup();\n try {\n // 2. 服務端引導器\n ServerBootstrap serverBootstrap = new ServerBootstrap();\n // 3. 設置線bootStrap信息\n serverBootstrap.group(bossGroup, workerGroup)\n // 4. 設置ServerSocketChannel的類型\n .channel(NioServerSocketChannel.class)\n // 5. 設置參數\n .option(ChannelOption.SO_BACKLOG, 100)\n // 6. 設置ServerSocketChannel對應的Handler,只能設置一個\n .handler(new LoggingHandler(LogLevel.INFO))\n // 7. 設置SocketChannel對應的Handler\n .childHandler(new ChannelInitializer() {\n @Override\n public void initChannel(SocketChannel ch) throws Exception {\n ChannelPipeline p = ch.pipeline();\n // 可以添加多個子Handler\n p.addLast(new LoggingHandler(LogLevel.INFO));\n p.addLast(new ChatNettyHandler());\n }\n });\n\n // 8. 綁定端口\n ChannelFuture f = serverBootstrap.bind(PORT).sync();\n // 9. 等待服務端監聽端口關閉,這裏會阻塞主線程\n f.channel().closeFuture().sync();\n } finally {\n // 10. 優雅地關閉兩個線程池\n bossGroup.shutdownGracefully();\n workerGroup.shutdownGracefully();\n }\n }\n\n private static class ChatNettyHandler extends SimpleChannelInboundHandler {\n @Override\n public void channelActive(ChannelHandlerContext ctx) {\n System.out.println(\"one conn active: \" + ctx.channel());\n // channel是在ServerBootstrapAcceptor中放到EventLoopGroup中的\n ChatHolder.join((SocketChannel) ctx.channel());\n }\n\n @Override\n protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {\n byte[] bytes = new byte[byteBuf.readableBytes()];\n byteBuf.readBytes(bytes);\n String content = new String(bytes, StandardCharsets.UTF_8);\n System.out.println(content);\n\n if (content.equals(\"quit\\r\\n\")) {\n ctx.channel().close();\n } else {\n ChatHolder.propagate((SocketChannel) ctx.channel(), content);\n }\n }\n\n @Override\n public void channelInactive(ChannelHandlerContext ctx) {\n System.out.println(\"one conn inactive: \" + ctx.channel());\n ChatHolder.quit((SocketChannel) ctx.channel());\n }\n }\n\n private static class ChatHolder {\n static final Map USER_MAP = new ConcurrentHashMap<>();\n\n /**\n * 加入羣聊\n */\n static void join(SocketChannel socketChannel) {\n // 有人加入就給他分配一個id\n String userId = \"用戶\"+ ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);\n send(socketChannel, \"您的id爲:\" + userId + \"\\n\\r\");\n\n for (SocketChannel channel : USER_MAP.keySet()) {\n send(channel, userId + \" 加入了羣聊\" + \"\\n\\r\");\n }\n\n // 將當前用戶加入到map中\n USER_MAP.put(socketChannel, userId);\n }\n\n /**\n * 退出羣聊\n */\n static void quit(SocketChannel socketChannel) {\n String userId = USER_MAP.get(socketChannel);\n send(socketChannel, \"您退出了羣聊\" + \"\\n\\r\");\n USER_MAP.remove(socketChannel);\n\n for (SocketChannel channel : USER_MAP.keySet()) {\n if (channel != socketChannel) {\n send(channel, userId + \" 退出了羣聊\" + \"\\n\\r\");\n }\n }\n }\n\n /**\n * 擴散說話的內容\n */\n public static void propagate(SocketChannel socketChannel, String content) {\n String userId = USER_MAP.get(socketChannel);\n for (SocketChannel channel : USER_MAP.keySet()) {\n if (channel != socketChannel) {\n send(channel, userId + \": \" + content);\n }\n }\n }\n\n /**\n * 發送消息\n */\n static void send(SocketChannel socketChannel, String msg) {\n try {\n ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;\n ByteBuf writeBuffer = allocator.buffer(msg.getBytes().length);\n writeBuffer.writeCharSequence(msg, Charset.defaultCharset());\n socketChannel.writeAndFlush(writeBuffer);\n } catch (Exception e) {\n e.printStackTrace();\n }\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a6/a6b3e3d6d3df87bea45eccf2f02e3b7b.png","alt":"dkeb0s.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"代碼有點長,執行完的效果如上圖所示,下面所有內容都是圍繞着"},{"type":"codeinline","content":[{"type":"text","text":"如何看懂"}]},{"type":"text","text":"以及"},{"type":"codeinline","content":[{"type":"text","text":"如何寫出"}]},{"type":"text","text":"這樣的代碼來展開的,希望你看完 也能輕鬆手寫"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"服務端代碼~。通過簡單demo開發讓大家體驗了"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"實現相比"},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"確實要簡單的多,但優點不限於此,只需要知道選擇Netty就對了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Netty核心組件"}]},{"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":"對應着文章開頭的思維導圖,我們知道"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"的核心組件主要有:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Bootstrap && ServerBootstrap"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventLoopGroup"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventLoop"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ByteBuf"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Channel"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelHandler"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelFuture"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelPipeline"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelHandlerContext"}]}]}]},{"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":"類圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d26a6eeaf925fe55e05d449a1f427f48.png","alt":"dk8ZC9.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Bootstrap & ServerBootstrap"}]},{"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":"一看到"},{"type":"codeinline","content":[{"type":"text","text":"BootStrap"}]},{"type":"text","text":"大家就應該想到"},{"type":"text","marks":[{"type":"strong"}],"text":"啓動類、引導類"},{"type":"text","text":"這樣的詞彙,之前分析過[EurekaServer項目啓動類時][1]介紹過"},{"type":"codeinline","content":[{"type":"text","text":"EurekaBootstrap"}]},{"type":"text","text":", 他的作用就是上下文初始化、配置初始化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"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":"在"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"中我們也有類似的類,"},{"type":"codeinline","content":[{"type":"text","text":"Bootstrap"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"ServerBootstrap"}]},{"type":"text","text":"它們都是"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"程序的引導類,主要用於配置各種參數,並啓動整個"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"服務,我們看下文章開頭的示例代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ServerBootstrap serverBootstrap = new ServerBootstrap();\nserverBootstrap.group(bossGroup, workerGroup) \n .channel(NioServerSocketChannel.class)\n .option(ChannelOption.SO_BACKLOG, 100)\n .handler(new LoggingHandler(LogLevel.INFO))\n .childHandler(new ChannelInitializer() {\n @Override\n public void initChannel(SocketChannel ch) throws Exception {\n ChannelPipeline p = ch.pipeline();\n p.addLast(new LoggingHandler(LogLevel.INFO));\n p.addLast(new ChatNettyHandler());\n }\n });"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Bootstrap"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"ServerBootstrap"}]},{"type":"text","text":"是針對於"},{"type":"codeinline","content":[{"type":"text","text":"Client"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"Server"}]},{"type":"text","text":"端定義的兩套啓動類,區別如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Bootstrap"}]},{"type":"text","text":"是客戶端引導類,而"},{"type":"codeinline","content":[{"type":"text","text":"ServerBootstrap"}]},{"type":"text","text":"是服務端引導類。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Bootstrap"}]},{"type":"text","text":"通常使用"},{"type":"codeinline","content":[{"type":"text","text":"connect()"}]},{"type":"text","text":"方法連接到遠程的主機和端口,作爲一個"},{"type":"codeinline","content":[{"type":"text","text":"TCP客戶端"}]},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ServerBootstrap"}]},{"type":"text","text":"通常使用"},{"type":"codeinline","content":[{"type":"text","text":"bind()"}]},{"type":"text","text":"方法綁定本地的端口,等待客戶端來連接。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ServerBootstrap"}]},{"type":"text","text":"可以處理"},{"type":"codeinline","content":[{"type":"text","text":"Accept"}]},{"type":"text","text":"事件,這裏面"},{"type":"codeinline","content":[{"type":"text","text":"childHandler"}]},{"type":"text","text":"是用來處理"},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":"請求的,我們可以查看"},{"type":"codeinline","content":[{"type":"text","text":"chaildHandler()"}]},{"type":"text","text":"方法的註解:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/26/26ef7f0cbf0694c41594c355df2cdbe7.png","alt":"dk884H.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Bootstrap"}]},{"type":"text","text":"客戶端引導只需要一個"},{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":",但是一個"},{"type":"codeinline","content":[{"type":"text","text":"ServerBootstrap"}]},{"type":"text","text":"通常需要兩個(上面的"},{"type":"codeinline","content":[{"type":"text","text":"boosGroup"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"workerGroup"}]},{"type":"text","text":")。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"EventLoopGroup && EventLoop"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":"及"},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":"這兩個類名稱定義的很奇怪,對於初學者來說往往無法通過名稱來了解其中的含義,包括我也是這樣。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":" 可以理解爲一個線程池,對於服務端程序,我們一般會綁定兩個線程池,一個用於處理 "},{"type":"codeinline","content":[{"type":"text","text":"Accept"}]},{"type":"text","text":" 事件,一個用於處理讀寫事件,看下"},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":"系列的類目錄:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/95/95db84fe7d25dee1eb926fa98f163b96.png","alt":"dU4Roj.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"通過上面的類圖,我們才恍然大悟,我的親孃咧,這不就是一個線程池嘛?(名字氣的犄角拐彎的真是難認)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":"是"},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":"的集合,一個"},{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":" 包含一個或者多個"},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":"。我們可以將"},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":"看做"},{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":"線程池中的一個個工作線程。"}]},{"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":"至於這裏爲什麼要用到兩個線程池,具體的其實可以參考"},{"type":"codeinline","content":[{"type":"text","text":"Reactor"}]},{"type":"text","text":"設計模式,這裏暫時不做過多的講解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個 EventLoopGroup 包含一個或多個 EventLoop ,即 EventLoopGroup : EventLoop = 1 : n "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個 EventLoop 在它的生命週期內,只能與一個 Thread 綁定,即 EventLoop : Thread = 1 : 1 "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有有 EventLoop 處理的 I/O 事件都將在它專有的 Thread 上被處理,從而保證線程安全,即 Thread : EventLoop = 1 : 1"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個 Channel 在它的生命週期內只能註冊到一個 EventLoop 上,即 Channel : EventLoop = n : 1 "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個 EventLoop 可被分配至一個或多個 Channel ,即 EventLoop : Channel = 1 : n "}]}]}]},{"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":"當一個連接到達時,"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":" 就會創建一個 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":",然後從 "},{"type":"codeinline","content":[{"type":"text","text":"EventLoopGroup"}]},{"type":"text","text":" 中分配一個 "},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":" 來給這個 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 綁定上,在該 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 的整個生命週期中都是有這個綁定的 "},{"type":"codeinline","content":[{"type":"text","text":"EventLoop"}]},{"type":"text","text":" 來服務的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ByteBuf"}]},{"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":"在"},{"type":"codeinline","content":[{"type":"text","text":"Java NIO"}]},{"type":"text","text":"中我們有 "},{"type":"codeinline","content":[{"type":"text","text":"ByteBuffer"}]},{"type":"text","text":"緩衝池,對於它的操作我們應該印象深刻,往"},{"type":"codeinline","content":[{"type":"text","text":"Buffer"}]},{"type":"text","text":"中寫數據時我們需要關注寫入的位置,切換成讀模式時我們還要切換讀寫狀態,不然將會出現大問題。"}]},{"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":"針對於"},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"中超級難用的"},{"type":"codeinline","content":[{"type":"text","text":"Buffer"}]},{"type":"text","text":"類, "},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":" 提供了"},{"type":"codeinline","content":[{"type":"text","text":"ByteBuf"}]},{"type":"text","text":"來替代。"},{"type":"codeinline","content":[{"type":"text","text":"ByteBuf"}]},{"type":"text","text":"聲明瞭兩個指針:一個讀指針,一個寫指針,使得讀寫操作進行分離,簡化"},{"type":"codeinline","content":[{"type":"text","text":"buffer"}]},{"type":"text","text":"的操作流程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6f/6f7af1693e734d32bd7eee790ee5f9f3.png","alt":"dkQocV.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"另外"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"提供了發幾種"},{"type":"codeinline","content":[{"type":"text","text":"ByteBuf"}]},{"type":"text","text":"的實現以供我們選擇,"},{"type":"codeinline","content":[{"type":"text","text":"ByteBuf"}]},{"type":"text","text":"可以分爲:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Pooled"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"Unpooled"}]},{"type":"text","text":" 池化和非池化"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Heap 和 Direct,堆內存和堆外內存,NIO中創建Buffer也可以指定"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Safe 和 Unsafe,安全和非安全"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/96/962ee1464dfe1982f16ace358a5b3032.png","alt":"dkJ9TU.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"對於這麼多種創建"},{"type":"codeinline","content":[{"type":"text","text":"Buffer"}]},{"type":"text","text":"的方式該怎麼選擇呢?"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"也爲我們處理好了,我們可以直接使用(真是暖男"},{"type":"codeinline","content":[{"type":"text","text":"Ntetty"}]},{"type":"text","text":"):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;\nByteBuf buffer = allocator.buffer(length);"}]},{"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將最大努力的使用池化、Unsafe、對外內存的方式爲我們創建buffer。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Channel"}]},{"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":"提起"},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":"並不陌生,上一篇講"},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"的三大組件提到過,最常見的就是"},{"type":"codeinline","content":[{"type":"text","text":"java.nio.SocketChannel"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"java.nio.ServerSocketChannel"}]},{"type":"text","text":",他們用於非阻塞的I/0操作。類似於"},{"type":"codeinline","content":[{"type":"text","text":"NIO"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":",Netty提供了自己的"},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":"和其子類實現,用於異步I/0操作和其他相關的操作。"}]},{"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":"在 "},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":" 中, "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 是一個 "},{"type":"codeinline","content":[{"type":"text","text":"Socket"}]},{"type":"text","text":" 連接的抽象, 它爲用戶提供了關於底層 "},{"type":"codeinline","content":[{"type":"text","text":"Socket"}]},{"type":"text","text":" 狀態(是否是連接還是斷開) 以及對 "},{"type":"codeinline","content":[{"type":"text","text":"Socket"}]},{"type":"text","text":" 的讀寫等操作。每當 "},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":" 建立了一個連接後, 都會有一個對應的 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 實例。並且,有父子"},{"type":"codeinline","content":[{"type":"text","text":"channel"}]},{"type":"text","text":"的概念。 服務器連接監聽的"},{"type":"codeinline","content":[{"type":"text","text":"channel"}]},{"type":"text","text":" ,也叫 "},{"type":"codeinline","content":[{"type":"text","text":"parent channel"}]},{"type":"text","text":"。 對應於每一個 "},{"type":"codeinline","content":[{"type":"text","text":"Socket"}]},{"type":"text","text":" 連接的"},{"type":"codeinline","content":[{"type":"text","text":"channel"}]},{"type":"text","text":",也叫 "},{"type":"codeinline","content":[{"type":"text","text":"child channel"}]},{"type":"text","text":"。"}]},{"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":"既然"},{"type":"codeinline","content":[{"type":"text","text":"channel"}]},{"type":"text","text":" 是 Netty 抽象出來的網絡 I/O 讀寫相關的接口,爲什麼不使用"},{"type":"codeinline","content":[{"type":"text","text":" JDK NIO"}]},{"type":"text","text":" 原生的 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 而要另起爐竈呢,主要原因如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":" 的"},{"type":"codeinline","content":[{"type":"text","text":" SocketChannel"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"ServersocketChannel "}]},{"type":"text","text":"沒有統一的 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 接口供業務開發者使用,對一於用戶而言,沒有統一的操作視圖,使用起來並不方便。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":" 的 "},{"type":"codeinline","content":[{"type":"text","text":"SocketChannel "}]},{"type":"text","text":"和 "},{"type":"codeinline","content":[{"type":"text","text":"ScrversockctChannel "}]},{"type":"text","text":"的主要職責就是網絡 I/O 操作,由於他們是"},{"type":"codeinline","content":[{"type":"text","text":" SPI"}]},{"type":"text","text":" 類接口,由具體的虛擬機廠家來提供,所以通過繼承 SPI 功能直接實現 "},{"type":"codeinline","content":[{"type":"text","text":"ServersocketChannel"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"SocketChannel"}]},{"type":"text","text":" 來擴展其工作量和重新"},{"type":"codeinline","content":[{"type":"text","text":" Channel"}]},{"type":"text","text":" 功類是差不多的。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Netty 的 "},{"type":"codeinline","content":[{"type":"text","text":"ChannelPipeline Channel"}]},{"type":"text","text":" 需要夠跟 Netty 的整體架構融合在一起,例如 I/O 模型、基的定製模型,以及基於元數據描述配置化的 TCP 參數等,這些"},{"type":"codeinline","content":[{"type":"text","text":" JDK SocketChannel"}]},{"type":"text","text":" 和"},{"type":"codeinline","content":[{"type":"text","text":" ServersocketChannel "}]},{"type":"text","text":"都沒有提供,需要重新封裝。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自定義的 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" ,功實現更加靈活。"}]}]}]},{"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":"基於上述 4 原因,它的設計原理比較簡單, Netty 重新設計了 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 接口,並且給予了很多不同的實現。但是功能卻比較繁雜,主要的設計理念如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 接口層,相關聯的其他操作封裝起來,採用 "},{"type":"codeinline","content":[{"type":"text","text":"Facade"}]},{"type":"text","text":" 模式進行統一封裝,將網絡 I/O 操作、網絡 I/O 統一對外提供。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 接口的定義儘量大而全,統一的視圖,由不同子類實現不同的功能,公共功能在抽象父類中實現,最大程度上實現接口的重用。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具體實現採用聚合而非包含的方式,將相關的功類聚合在 "},{"type":"codeinline","content":[{"type":"text","text":"Channel "}]},{"type":"text","text":"中,由 "},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":" 統一負責分配和調度,功能實現更加靈活。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Channel "}]},{"type":"text","text":"的實現類非常多,繼承關係複雜,從學習的角度我們抽取最重要的兩個 "},{"type":"codeinline","content":[{"type":"text","text":"NioServerSocketChannel "}]},{"type":"text","text":"和 "},{"type":"codeinline","content":[{"type":"text","text":"NioSocketChannel"}]},{"type":"text","text":"。"}]},{"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":"服務端 "},{"type":"codeinline","content":[{"type":"text","text":"NioServerSocketChannel "}]},{"type":"text","text":"的繼承關係類圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cc/cccc172c897f3a87d1983669f8129040.png","alt":"dUn8G4.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"客戶端 "},{"type":"codeinline","content":[{"type":"text","text":"NioSocketChannel "}]},{"type":"text","text":"的繼承關係類圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/45/45e0a0fd80e8c3fcc75aeaa151498eab.png","alt":"dUnJz9.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"後面文章源碼系列會具體分析,這裏就不進一步闡述分析了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ChannelHandler"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ChannelHandler"}]},{"type":"text","text":" 是"},{"type":"codeinline","content":[{"type":"text","text":"Netty"}]},{"type":"text","text":"中最常用的組件。"},{"type":"codeinline","content":[{"type":"text","text":"ChannelHandler"}]},{"type":"text","text":" 主要用來處理各種事件,這裏的事件很廣泛,比如可以是連接、數據接收、異常、數據轉換等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ChannelHandler"}]},{"type":"text","text":" 有兩個核心子類 "},{"type":"codeinline","content":[{"type":"text","text":"ChannelInboundHandler"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"ChannelOutboundHandler"}]},{"type":"text","text":",其中 "},{"type":"codeinline","content":[{"type":"text","text":"ChannelInboundHandler"}]},{"type":"text","text":" 用於接收、處理入站( "},{"type":"codeinline","content":[{"type":"text","text":"Inbound"}]},{"type":"text","text":" )的數據和事件,而 "},{"type":"codeinline","content":[{"type":"text","text":"ChannelOutboundHandler"}]},{"type":"text","text":" 則相反,用於接收、處理出站( "},{"type":"codeinline","content":[{"type":"text","text":"Outbound"}]},{"type":"text","text":" )的數據和事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c9/c90bd6c99066e919de06a68e16b1edaf.png","alt":"dkJAp9.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"ChannelInboundHandler"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ChannelInboundHandler"}]},{"type":"text","text":"處理入站數據以及各種狀態變化,當"},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":"狀態發生改變會調用"},{"type":"codeinline","content":[{"type":"text","text":"ChannelInboundHandler"}]},{"type":"text","text":"中的一些生命週期方法.這些方法與"},{"type":"codeinline","content":[{"type":"text","text":"Channel"}]},{"type":"text","text":"的生命密切相關。"}]},{"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":"入站數據,就是進入"},{"type":"codeinline","content":[{"type":"text","text":"socket"}]},{"type":"text","text":"的數據。下面展示一些該接口的生命週期"},{"type":"codeinline","content":[{"type":"text","text":"API"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28cc3cad531c2ba344ff849b13617ffb.png","alt":"dUntMR.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"當某個 "},{"type":"codeinline","content":[{"type":"text","text":"ChannelInboundHandler"}]},{"type":"text","text":"的實現重寫 "},{"type":"codeinline","content":[{"type":"text","text":"channelRead()"}]},{"type":"text","text":"方法時,它將負責顯式地釋放與池化的 "},{"type":"codeinline","content":[{"type":"text","text":"ByteBuf"}]},{"type":"text","text":" 實例相關的內存。 Netty 爲此提供了一個實用方法"},{"type":"codeinline","content":[{"type":"text","text":"ReferenceCountUtil.release()"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Sharable\npublic class DiscardHandler extends ChannelInboundHandlerAdapter {\n\t@Override\n\tpublic void channelRead(ChannelHandlerContext ctx, Object msg) {\n\t\tReferenceCountUtil.release(msg);\n\t}\n}"}]},{"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提供了一個"},{"type":"codeinline","content":[{"type":"text","text":"SimpleChannelInboundHandler"}]},{"type":"text","text":",重寫"},{"type":"codeinline","content":[{"type":"text","text":"channelRead0()"}]},{"type":"text","text":"方法,就可以在調用過程中會自動釋放資源."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SimpleDiscardHandler\n\textends SimpleChannelInboundHandler
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.