<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Netty到底是個啥

          共 6346字,需瀏覽 13分鐘

           ·

          2022-05-10 07:26

          Netty是什么?

          ?

          Netty 是一個利用Java的高級網(wǎng)絡(luò)能力,隱藏其(Java API)背后的復雜性而提供一個易于使用的 NIO 客戶端/服務端框架。


          它極大地簡化并優(yōu)化了 TCP 和 UDP 套接字服務器等網(wǎng)絡(luò)編程,并且性能以及安全性等很多方面甚至都要更好。


          支持多種協(xié)議 如 FTP,SMTPHTTP 以及各種二進制和基于文本的傳統(tǒng)協(xié)議。

          ?

          用官方的總結(jié)就是:Netty 成功地找到了一種在不妥協(xié)可維護性和性能的情況下實現(xiàn)易于開發(fā),性能,穩(wěn)定性和靈活性的方法。

          ?

          為什么要用Netty

          ?

          Netty作為一款優(yōu)秀的網(wǎng)絡(luò)框架,自然有令人折服的特點:

          ?

          設(shè)計:針對多種傳輸類型的同一接口。簡單但更強大的線程模型。真正的無連接的數(shù)據(jù)報套接字支持。鏈接邏輯復用。

          ?

          性能Netty的高性能是它被廣泛使用的一個重要的原因,我們可能都認為Java不太適合 編寫游戲服務端程序,但Netty的到來無疑是降低了懷疑的聲音。

          ?

          較原生Java API有更好的吞吐量,較低的延時。資源消耗更少(共享池和重用)。減少內(nèi)存拷貝。

          ?

          健壯性:原生NIO的客戶端/服務端程序編寫較為麻煩,如果某個地方處理的不好,可能會 導致一些意料之外的異常,如內(nèi)存溢出,死循環(huán)等等,而Netty則為我們簡化了原生API 的使用,這使得我們編寫出來的程序不那么容易出錯。

          ?

          社區(qū):Netty快速發(fā)展的一個重要的原因就是它的社區(qū)非常活躍,這也使得采用它的開發(fā)者越來越多。


          ? ? ? ? ? ? ?


          Netty的簡單使用



          左邊是服務端代碼,右邊是客戶端代碼。

          ?

          上面的代碼基本就是模板代碼,每次使用都是這一個套路,唯一需要我們開發(fā)的部分是 handler(…) childHandler() 方法中指定的各個 handler,如 EchoServerHandler EchoClientHandler,當然 Netty 源碼也給我們提供了很多的 handler,比如上面的 LoggingHandler,它就是 Netty 源碼中為我們提供的,需要的時候直接拿過來用就好了。

          ?

          我們先來看一下上述代碼中涉及到的一些內(nèi)容:

          ?

          ServerBootstrap 類用于創(chuàng)建服務端實例,Bootstrap 用于創(chuàng)建客戶端實例。

          ?

          兩個 EventLoopGroup:bossGroup workerGroup,它們涉及的是 Netty 的線程模型,可以看到服務端有兩個 group,而客戶端只有一個,它們就是 Netty 中的線程池。

          ?

          Netty 中的 Channel,沒有直接使用 Java 原生的 ServerSocketChannel SocketChannel,而是包裝了 NioServerSocketChannel NioSocketChannel 與之對應。

          ?

          當然,也有對其他協(xié)議的支持,如支持 UDP 協(xié)議的 NioDatagramChannel,本文只關(guān)心 TCP 相關(guān)的。

          ?

          左邊 handler(…) 方法指定了一個 handlerLoggingHandler),這個 handler 是給服務端收到新的請求的時候處理用的。右邊 handler(...) 方法指定了客戶端處理請求過程中需要使用的 handlers

          ?

          如果你想在 EchoServer 中也指定多個 handler,也可以像右邊的 EchoClient 一樣使用 ChannelInitializer

          ?

          左邊 childHandler(…) 指定了 childHandler,這邊的 handlers 是給新創(chuàng)建的連接用的,我們知道服務端 ServerSocketChannel accept 一個連接以后,需要創(chuàng)建 SocketChannel 的實例,childHandler() 中設(shè)置的 handler 就是用于處理新創(chuàng)建的 SocketChannel 的,而不是用來處理 ServerSocketChannel 實例的。

          ?

          pipeline:handler 可以指定多個(需要上面的 ChannelInitializer 類輔助),它們會組成了一個 pipeline,它們其實就類似攔截器的概念,現(xiàn)在只要記住一點,每個 NioSocketChannel NioServerSocketChannel 實例內(nèi)部都會有一個 pipeline 實例。pipeline 中還涉及到 handler 的執(zhí)行順序。

          ?

          ChannelFuture:這個涉及到 Netty 中的異步編程,和 JDK 中的 Future 接口類似。

          ?

          Netty核心組件

          ?


          Bytebuf(字節(jié)容器)


          網(wǎng)絡(luò)通信最終都是通過字節(jié)流進行傳輸?shù)摹yteBuf 就是 Netty 提供的一個字節(jié)容器,其內(nèi)部是一個字節(jié)數(shù)組。當我們通過 Netty 傳輸數(shù)據(jù)的時候,就是通過 ByteBuf 進行的。

          ?

          我們可以將 ByteBuf 看作是 Netty Java NIO 提供了 ByteBuffer 字節(jié)容器的封裝和抽象。

          ?

          有很多小伙伴可能就要問了 :為什么不直接使用 Java NIO 提供的 ByteBuffer 呢?

          ?

          因為 ByteBuffer 這個類使用起來過于復雜和繁瑣。

          ?

          Bootstrap 和 ServerBootstrap(啟動引導類)


          Bootstrap 是客戶端的啟動引導類/輔助類,具體使用方法如下:

          ?

                  EventLoopGroup group = new NioEventLoopGroup();        try {            //創(chuàng)建客戶端啟動引導/輔助類:Bootstrap            Bootstrap b = new Bootstrap();            //指定線程模型            b.group(group).                    ......            // 嘗試建立連接            ChannelFuture f = b.connect(host, port).sync();            f.channel().closeFuture().sync();        } finally {            // 優(yōu)雅關(guān)閉相關(guān)線程組資源            group.shutdownGracefully();        }


          ServerBootstrap 客戶端的啟動引導類/輔助類,具體使用方法如下:

          ?

                  // 1.bossGroup 用于接收連接,workerGroup 用于具體的處理?????   EventLoopGroup?bossGroup?=?new?NioEventLoopGroup(1);        EventLoopGroup workerGroup = new NioEventLoopGroup();        try {            //2.創(chuàng)建服務端啟動引導/輔助類:ServerBootstrap            ServerBootstrap b = new ServerBootstrap();            //3.給引導類配置兩大線程組,確定了線程模型            b.group(bossGroup, workerGroup).                   ......            // 6.綁定端口            ChannelFuture f = b.bind(port).sync();            // 等待連接關(guān)閉            f.channel().closeFuture().sync();        } finally {            //7.優(yōu)雅關(guān)閉相關(guān)線程組資源            bossGroup.shutdownGracefully();            workerGroup.shutdownGracefully();        }    }

          從上面的示例中,我們可以看出:

          ?

          Bootstrap 通常使用 connet() 方法連接到遠程的主機和端口,作為一個 Netty TCP 協(xié)議通信中的客戶端。另外,Bootstrap 也可以通過 bind() 方法綁定本地的一個端口,作為 UDP 協(xié)議通信中的一端。


          ServerBootstrap通常使用 bind() 方法綁定本地的端口上,然后等待客戶端的連接。


          Bootstrap 只需要配置一個線程組— EventLoopGroup ,ServerBootstrap需要配置兩個線程組— EventLoopGroup ,一個用于接收連接,一個用于具體的 IO 處理。

          ?

          Channel(網(wǎng)絡(luò)操作抽象類)


          Channel 接口是 Netty 對網(wǎng)絡(luò)操作抽象類。通過 Channel 我們可以進行 I/O 操作。

          ?

          一旦客戶端成功連接服務端,就會新建一個 Channel 同該用戶端進行綁定,示例代碼如下:

          ?

             //  通過 Bootstrap 的 connect 方法連接到服務端   public Channel doConnect(InetSocketAddress inetSocketAddress) {        CompletableFuture completableFuture = new CompletableFuture<>();        bootstrap.connect(inetSocketAddress).addListener((ChannelFutureListener) future -> {            if (future.isSuccess()) {                completableFuture.complete(future.channel());            } else {                throw new IllegalStateException();            }        });        return completableFuture.get();    }

          比較常用的Channel接口實現(xiàn)類是 :

          ?

          NioServerSocketChannel(服務端)

          NioSocketChannel(客戶端)


          這兩個 Channel 可以和 BIO 編程模型中的ServerSocket以及Socket兩個概念對應上。


          ? ? ? ??


          EventLoop(事件循環(huán))


          EventLoop 介紹


          這么說吧!EventLoop(事件循環(huán))接口可以說是 Netty 中最核心的概念了!

          ?

          《Netty 實戰(zhàn)》這本書是這樣介紹它的:

          ?

          EventLoop 定義了 Netty 的核心抽象,用于處理連接的生命周期中所發(fā)生的事件。

          ?

          是不是很難理解?說實話,我學習 Netty 的時候看到這句話是沒太能理解的。

          ?

          說白了,EventLoop 的主要作用實際就是責監(jiān)聽網(wǎng)絡(luò)事件并調(diào)用事件處理器進行相關(guān) I/O 操作(讀寫)的處理。


          ? ? ? ? ? ? ??

          ?

          Channel 和 EventLoop 的關(guān)系


          那 Channel 和 EventLoop 直接有啥聯(lián)系呢?

          ?

          Channel 為 Netty 網(wǎng)絡(luò)操作(讀寫等操作)抽象類,EventLoop 負責處理注冊到其上的Channel I/O 操作,兩者配合進行 I/O 操作。

          ?

          EventloopGroup 和 EventLoop 的關(guān)系


          EventLoopGroup 包含多個 EventLoop(每一個 EventLoop 通常內(nèi)部包含一個線程),它管理著所有的 EventLoop 的生命周期。

          ?

          并且,EventLoop 處理的 I/O 事件都將在它專有的 Thread 上被處理,即 Thread EventLoop 屬于 1 : 1 的關(guān)系,從而保證線程安全。

          ?

          下圖是 Netty NIO 模型對應的 EventLoop 模型。通過這個圖應該可以將EventloopGroupEventLoopChannel三者聯(lián)系起來。



          ChannelHandler(消息處理器) 和 ChannelPipelineChannelHandler 對象鏈表)


          下面這段代碼使用過 Netty 的小伙伴應該不會陌生,我們指定了序列化編解碼器以及自定義的 ChannelHandler 處理消息。

                  b.group(eventLoopGroup)                .handler(new ChannelInitializer() {                    @Override                    protected void initChannel(SocketChannel ch) {                        ch.pipeline().addLast(new NettyKryoDecoder(kryoSerializer, RpcResponse.class));                        ch.pipeline().addLast(new NettyKryoEncoder(kryoSerializer, RpcRequest.class));                        ch.pipeline().addLast(new KryoClientHandler());                    }                });

          ChannelHandler 是消息的具體處理器,主要負責處理客戶端/服務端接收和發(fā)送的數(shù)據(jù)。

          ?

          當 Channel 被創(chuàng)建時,它會被自動地分配到它專屬的 ChannelPipeline。一個Channel包含一個 ChannelPipelineChannelPipeline ChannelHandler 的鏈,一個 pipeline 上可以有多個 ChannelHandler

          ?

          我們可以在 ChannelPipeline 上通過 addLast() 方法添加一個或者多個ChannelHandler (一個數(shù)據(jù)或者事件可能會被多個 Handler 處理) 。當一個 ChannelHandler 處理完之后就將數(shù)據(jù)交給下一個 ChannelHandler

          ?

          當 ChannelHandler 被添加到的 ChannelPipeline 它得到一個 ChannelHandlerContext,它代表一個 ChannelHandler ChannelPipeline 之間的“綁定”。ChannelPipeline 通過 ChannelHandlerContext來間接管理 ChannelHandler


          ?

          ChannelFuture(操作執(zhí)行結(jié)果)


          public interface ChannelFuture extends Future {    Channel channel();
          ChannelFuture addListener(GenericFutureListener> var1); ......
          ChannelFuture sync() throws InterruptedException;}

          Netty 是異步非阻塞的,所有的 I/O 操作都為異步的。


          Netty實際上是不支持異步io的,真正的異步io需要底層操作系統(tǒng)的支持,異步是說數(shù)據(jù)準備好之后由系統(tǒng)通知應用程序你可以來操作數(shù)據(jù)了,而netty所謂的異步是另起一個用戶線程等待數(shù)據(jù)就緒并通過回調(diào)處理,并不是真正意義上的異步io

          ?

          因此,我們不能立刻得到操作是否執(zhí)行成功,但是,你可以通過 ChannelFuture 接口的 addListener() 方法注冊一個 ChannelFutureListener,當操作執(zhí)行成功或者失敗時,監(jiān)聽就會自動觸發(fā)返回結(jié)果。

          ChannelFuture f = b.connect(host, port).addListener(future -> {  if (future.isSuccess()) {    System.out.println("連接成功!");  } else {    System.err.println("連接失敗!");  }}).sync();


          并且,你還可以通過ChannelFuture 的 channel() 方法獲取連接相關(guān)聯(lián)的Channel

          Channel channel = f.channel();

          另外,我們還可以通過 ChannelFuture 接口的 sync()方法讓異步的操作編程同步的。

          //bind()是異步的,但是,你可以通過?`sync()`方法將其變?yōu)橥健?/span>ChannelFuture f = b.bind(port).sync();


          本文參考:https://www.javadoop.com/post/netty-part-1

          本文參考:https://github.com/Snailclimb/netty-practical-tutorial


          結(jié)束語


          感謝大家能夠做我最初的讀者和傳播者,請大家相信,只要你給我一份愛,我終究會還你們一頁情的。


          Captain會持續(xù)更新技術(shù)文章,和生活中的暴躁文章,歡迎大家關(guān)注


          哦對了,后續(xù)所有的文章都會更新到這里


          https://github.com/DayuMM2021/Java




          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲一逼 | 青春草在线无码 | 一级黄色AA | 国产高清一区 | 丰满的大屁股一区二区 |