主頁 > 後端開發 > 你要的Netty常見面試題總結,我面試回來整理好了!

你要的Netty常見面試題總結,我面試回來整理好了!

2020-10-07 23:15:33 後端開發

最近經常會再面試中碰到 Netty 相關的問題,

全文采用大家喜歡的與面試官對話的形式展開, 如果大家覺得總結的不錯的話,不妨點一個贊鼓勵一下!這是我繼續堅持很重要的動力來源,

概覽:

  • Netty 是什么?
  • 為什么要用 Netty?
  • Netty 應用場景了解么?
  • Netty 核心組件有哪些?分別有什么作用?
  • EventloopGroup 了解么?和 EventLoop 啥關系?
  • Bootstrap 和 ServerBootstrap 了解么?
  • NioEventLoopGroup 默認的建構式會起多少執行緒?
  • Netty 執行緒模型了解么?
  • Netty 服務端和客戶端的啟動程序了解么?
  • Netty 長連接、心跳機制了解么?
  • Netty 的零拷貝了解么?

Netty 是什么?

面試官 :介紹一下自己對 Netty 的認識吧!小伙子,

 :好的!那我就簡單用 3 點來概括一下 Netty 吧!

  1. Netty 是一個 基于 NIO 的 client-server(客戶端服務器)框架,使用它可以快速簡單地開發網路應用程式,
  2. 它極大地簡化并優化了 TCP 和 UDP 套接字服務器等網路編程,并且性能以及安全性等很多方面甚至都要更好,
  3. 支持多種協議 如 FTP,SMTP,HTTP 以及各種二進制和基于文本的傳統協議,

用官方的總結就是:Netty 成功地找到了一種在不妥協可維護性和性能的情況下實作易于開發,性能,穩定性和靈活性的方法,

除了上面介紹的之外,很多開源專案比如我們常用的 Dubbo、RocketMQ、Elasticsearch、gRPC 等等都用到了 Netty,

網路編程我愿意稱 Netty 為王 ,

為什么要用 Netty?

面試官 :為什么要用 Netty 呢?能不能說一下自己的看法,

 :因為 Netty 具有下面這些優點,并且相比于直接使用 JDK 自帶的 NIO 相關的 API 來說更加易用,

  • 統一的 API,支持多種傳輸型別,阻塞和非阻塞的,
  • 簡單而強大的執行緒模型,
  • 自帶編解碼器解決 TCP 粘包/拆包問題,
  • 自帶各種協議堆疊,
  • 真正的無連接資料包套接字支持,
  • 比直接使用 Java 核心 API 有更高的吞吐量、更低的延遲、更低的資源消耗和更少的記憶體復制,
  • 安全性不錯,有完整的 SSL/TLS 以及 StartTLS 支持,
  • 社區活躍
  • 成熟穩定,經歷了大型專案的使用和考驗,而且很多開源專案都使用到了 Netty, 比如我們經常接觸的 Dubbo、RocketMQ 等等,
  • ......

Netty 應用場景了解么?

面試官 :能不能通俗地說一下使用 Netty 可以做什么事情?

 :憑借自己的了解,簡單說一下吧!理論上來說,NIO 可以做的事情 ,使用 Netty 都可以做并且更好,Netty 主要用來做網路通信 :

  1. 作為 RPC 框架的網路通信工具 :我們在分布式系統中,不同服務節點之間經常需要相互呼叫,這個時候就需要 RPC 框架了,不同服務節點之間的通信是如何做的呢?可以使用 Netty 來做,比如我呼叫另外一個節點的方法的話,至少是要讓對方知道我呼叫的是哪個類中的哪個方法以及相關引數吧!
  2. 實作一個自己的 HTTP 服務器 :通過 Netty 我們可以自己實作一個簡單的 HTTP 服務器,這個大家應該不陌生,說到 HTTP 服務器的話,作為 Java 后端開發,我們一般使用 Tomcat 比較多,一個最基本的 HTTP 服務器可要以處理常見的 HTTP Method 的請求,比如 POST 請求、GET 請求等等,
  3. 實作一個即時通訊系統 :使用 Netty 我們可以實作一個可以聊天類似微信的即時通訊系統,這方面的開源專案還蠻多的,可以自行去 Github 找一找,
  4. 實作訊息推送系統 :市面上有很多訊息推送系統都是基于 Netty 來做的,
  5. ......

Netty 核心組件有哪些?分別有什么作用?

面試官 :Netty 核心組件有哪些?分別有什么作用?

 :表面上,嘴上開始說起 Netty 的核心組件有哪些,實則,內心已經開始 mmp 了,深度懷疑這面試官是存心搞我啊!

1.Channel

Channel 介面是 Netty 對網路操作抽象類,它除了包括基本的 I/O 操作,如 bind()、connect()、read()、write() 等,

比較常用的Channel介面實作類是NioServerSocketChannel(服務端)和NioSocketChannel(客戶端),這兩個 Channel 可以和 BIO 編程模型中的ServerSocket以及Socket兩個概念對應上,Netty 的 Channel 介面所提供的 API,大大地降低了直接使用 Socket 類的復雜性,

2.EventLoop

這么說吧!EventLoop(事件回圈)介面可以說是 Netty 中最核心的概念了!

《Netty 實戰》這本書是這樣介紹它的:

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

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

說白了,EventLoop 的主要作用實際就是負責監聽網路事件并呼叫事件處理器進行相關 I/O 操作的處理,

那 Channel 和 EventLoop 直接有啥聯系呢?

Channel 為 Netty 網路操作(讀寫等操作)抽象類,EventLoop 負責處理注冊到其上的Channel 處理 I/O 操作,兩者配合參與 I/O 操作,

3.ChannelFuture

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

因此,我們不能立刻得到操作是否執行成功,但是,你可以通過 ChannelFuture 介面的 addListener() 方法注冊一個 ChannelFutureListener,當操作執行成功或者失敗時,監聽就會自動觸發回傳結果,

并且,你還可以通過ChannelFuture 的 channel() 方法獲取關聯的Channel

public interface ChannelFuture extends Future<Void> {
    Channel channel();

    ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> var1);
     ......

    ChannelFuture sync() throws InterruptedException;
}

另外,我們還可以通過 ChannelFuture 介面的 sync()方法讓異步的操作變成同步的,

4.ChannelHandler 和 ChannelPipeline

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

        b.group(eventLoopGroup)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @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 是訊息的具體處理器,他負責處理讀寫操作、客戶端連接等事情,

ChannelPipeline 為 ChannelHandler 的鏈,提供了一個容器并定義了用于沿著鏈傳播入站和出站事件流的 API ,當 Channel 被創建時,它會被自動地分配到它專屬的 ChannelPipeline,

我們可以在 ChannelPipeline 上通過 addLast() 方法添加一個或者多個ChannelHandler ,因為一個資料或者事件可能會被多個 Handler 處理,當一個 ChannelHandler 處理完之后就將資料交給下一個 ChannelHandler ,

EventloopGroup 了解么?和 EventLoop 啥關系?

面試官 :剛剛你也介紹了 EventLoop,那你再說說 EventloopGroup 吧!和 EventLoop 啥關系?

 :

EventLoopGroup 包含多個 EventLoop(每一個 EventLoop 通常內部包含一個執行緒),上面我們已經說了 EventLoop 的主要作用實際就是負責監聽網路事件并呼叫事件處理器進行相關 I/O 操作的處理,

并且 EventLoop 處理的 I/O 事件都將在它專有的 Thread 上被處理,即 Thread 和 EventLoop 屬于 1 : 1 的關系,從而保證執行緒安全,

上圖是一個服務端對 EventLoopGroup 使用的大致模塊圖,其中 Boss EventloopGroup 用于接收連接,Worker EventloopGroup 用于具體的處理(訊息的讀寫以及其他邏輯處理),

從上圖可以看出:當客戶端通過 connect 方法連接服務端時,bossGroup 處理客戶端連接請求,當客戶端處理完成后,會將這個連接提交給 workerGroup 來處理,然后 workerGroup 負責處理其 IO 相關操作,

Bootstrap 和 ServerBootstrap 了解么?

面試官 :你再說說自己對 Bootstrap 和 ServerBootstrap 的了解吧!

 :

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

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

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

        // 1.bossGroup 用于接收連接,workerGroup 用于具體的處理
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //2.創建服務端啟動引導/輔助類:ServerBootstrap
            ServerBootstrap b = new ServerBootstrap();
            //3.給引導類配置兩大執行緒組,確定了執行緒模型
            b.group(bossGroup, workerGroup).
                   ......
            // 6.系結埠
            ChannelFuture f = b.bind(port).sync();
            // 等待連接關閉
            f.channel().closeFuture().sync();
        } finally {
            //7.優雅關閉相關執行緒組資源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

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

  1. Bootstrap 通常使用 connet() 方法連接到遠程的主機和埠,作為一個 Netty TCP 協議通信中的客戶端,另外,Bootstrap 也可以通過 bind() 方法系結本地的一個埠,作為 UDP 協議通信中的一端,
  2. ServerBootstrap通常使用 bind() 方法系結本地的埠上,然后等待客戶端的連接,
  3. Bootstrap 只需要配置一個執行緒組— EventLoopGroup ,而 ServerBootstrap需要配置兩個執行緒組— EventLoopGroup ,一個用于接收連接,一個用于具體的處理,

NioEventLoopGroup 默認的建構式會起多少執行緒?

面試官 :看過 Netty 的原始碼了么?NioEventLoopGroup 默認的建構式會起多少執行緒呢?

 :嗯嗯!看過部分,

回顧我們在上面寫的服務器端的代碼:

// 1.bossGroup 用于接收連接,workerGroup 用于具體的處理
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

為了搞清楚NioEventLoopGroup 默認的建構式 到底創建了多少個執行緒,我們來看一下它的原始碼,

    /**
     * 無參建構式,
     * nThreads:0
     */
    public NioEventLoopGroup() {
        //呼叫下一個構造方法
        this(0);
    }

    /**
     * Executor:null
     */
    public NioEventLoopGroup(int nThreads) {
        //繼續呼叫下一個構造方法
        this(nThreads, (Executor) null);
    }

    //中間省略部分建構式

    /**
     * RejectedExecutionHandler():RejectedExecutionHandlers.reject()
     */
    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
       //開始呼叫父類的建構式
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }

一直向下走下去的話,你會發現在 MultithreadEventLoopGroup 類中有相關的指定執行緒數的代碼,如下:

    // 從1,系統屬性,CPU核心數*2 這三個值中取出一個最大的
    //可以得出 DEFAULT_EVENT_LOOP_THREADS 的值為CPU核心數*2
    private static final int DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

    // 被呼叫的父類建構式,NioEventLoopGroup 默認的建構式會起多少執行緒的秘密所在
    // 當指定的執行緒數nThreads為0時,使用默認的執行緒數DEFAULT_EVENT_LOOP_THREADS
    protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
    }

綜上,我們發現 NioEventLoopGroup 默認的建構式實際會起的執行緒數為 CPU核心數*2

另外,如果你繼續深入下去看建構式的話,你會發現每個NioEventLoopGroup物件內部都會分配一組NioEventLoop,其大小是 nThreads, 這樣就構成了一個執行緒池, 一個NIOEventLoop 和一個執行緒相對應,這和我們上面說的 EventloopGroup 和 EventLoop關系這部分內容相對應,

Netty 執行緒模型了解么?

面試官 :說一下 Netty 執行緒模型吧!

 :大部分網路框架都是基于 Reactor 模式設計開發的,

Reactor 模式基于事件驅動,采用多路復用將事件分發給相應的 Handler 處理,非常適合處理海量 IO 的場景,

在 Netty 主要靠 NioEventLoopGroup 執行緒池來實作具體的執行緒模型的 ,

我們實作服務端的時候,一般會初始化兩個執行緒組:

  1. bossGroup :接收連接,
  2. workerGroup :負責具體的處理,交由對應的 Handler 處理,

下面我們來詳細看一下 Netty 中的執行緒模型吧!

1.單執行緒模型 :

一個執行緒需要執行處理所有的 accept、read、decode、process、encode、send 事件,對于高負載、高并發,并且對性能要求比較高的場景不適用,

對應到 Netty 代碼是下面這樣的

使用 NioEventLoopGroup 類的無參建構式設定執行緒數量的默認值就是 CPU 核心數 *2 ,

  //1.eventGroup既用于處理客戶端連接,又負責具體的處理,
  EventLoopGroup eventGroup = new NioEventLoopGroup(1);
  //2.創建服務端啟動引導/輔助類:ServerBootstrap
  ServerBootstrap b = new ServerBootstrap();
            boobtstrap.group(eventGroup, eventGroup)
            //......

2.多執行緒模型

一個 Acceptor 執行緒只負責監聽客戶端的連接,一個 NIO 執行緒池負責具體處理:accept、read、decode、process、encode、send 事件,滿足絕大部分應用場景,并發連接量不大的時候沒啥問題,但是遇到并發連接大的時候就可能會出現問題,成為性能瓶頸,

對應到 Netty 代碼是下面這樣的:

// 1.bossGroup 用于接收連接,workerGroup 用于具體的處理
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
  //2.創建服務端啟動引導/輔助類:ServerBootstrap
  ServerBootstrap b = new ServerBootstrap();
  //3.給引導類配置兩大執行緒組,確定了執行緒模型
  b.group(bossGroup, workerGroup)
    //......

3.主從多執行緒模型

從一個 主執行緒 NIO 執行緒池中選擇一個執行緒作為 Acceptor 執行緒,系結監聽埠,接收客戶端連接的連接,其他執行緒負責后續的接入認證等作業,連接建立完成后,Sub NIO 執行緒池負責具體處理 I/O 讀寫,如果多執行緒模型無法滿足你的需求的時候,可以考慮使用主從多執行緒模型 ,

// 1.bossGroup 用于接收連接,workerGroup 用于具體的處理
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
  //2.創建服務端啟動引導/輔助類:ServerBootstrap
  ServerBootstrap b = new ServerBootstrap();
  //3.給引導類配置兩大執行緒組,確定了執行緒模型
  b.group(bossGroup, workerGroup)
    //......

Netty 服務端和客戶端的啟動程序了解么?

服務端

        // 1.bossGroup 用于接收連接,workerGroup 用于具體的處理
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //2.創建服務端啟動引導/輔助類:ServerBootstrap
            ServerBootstrap b = new ServerBootstrap();
            //3.給引導類配置兩大執行緒組,確定了執行緒模型
            b.group(bossGroup, workerGroup)
                    // (非必備)列印日志
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 4.指定 IO 模型
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ChannelPipeline p = ch.pipeline();
                            //5.可以自定義客戶端訊息的業務處理邏輯
                            p.addLast(new HelloServerHandler());
                        }
                    });
            // 6.系結埠,呼叫 sync 方法阻塞知道系結完成
            ChannelFuture f = b.bind(port).sync();
            // 7.阻塞等待直到服務器Channel關閉(closeFuture()方法獲取Channel 的CloseFuture物件,然后呼叫sync()方法)
            f.channel().closeFuture().sync();
        } finally {
            //8.優雅關閉相關執行緒組資源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

簡單決議一下服務端的創建程序具體是怎樣的:

1.首先你創建了兩個 NioEventLoopGroup 物件實體:bossGroup 和 workerGroup,

  • bossGroup : 用于處理客戶端的 TCP 連接請求,
  • workerGroup :負責每一條連接的具體讀寫資料的處理邏輯,真正負責 I/O 讀寫操作,交由對應的 Handler 處理,

舉個例子:我們把公司的老板當做 bossGroup,員工當做 workerGroup,bossGroup 在外面接完活之后,扔給 workerGroup 去處理,一般情況下我們會指定 bossGroup 的 執行緒數為 1(并發連接量不大的時候) ,workGroup 的執行緒數量為 CPU 核心數 *2 ,另外,根據原始碼來看,使用 NioEventLoopGroup 類的無參建構式設定執行緒數量的默認值就是 CPU 核心數 *2 ,

2.接下來 我們創建了一個服務端啟動引導/輔助類:ServerBootstrap,這個類將引導我們進行服務端的啟動作業,

3.通過 .group() 方法給引導類 ServerBootstrap 配置兩大執行緒組,確定了執行緒模型,

通過下面的代碼,我們實際配置的是多執行緒模型,這個在上面提到過,

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();

4.通過channel()方法給引導類 ServerBootstrap指定了 IO 模型為NIO

  • NioServerSocketChannel :指定服務端的 IO 模型為 NIO,與 BIO 編程模型中的ServerSocket對應
  • NioSocketChannel : 指定客戶端的 IO 模型為 NIO, 與 BIO 編程模型中的Socket對應5.通過 .childHandler()給引導類創建一個ChannelInitializer ,然后制定了服務端訊息的業務處理邏輯 HelloServerHandler 物件6.呼叫 ServerBootstrap 類的 bind()方法系結埠

客戶端

        //1.創建一個 NioEventLoopGroup 物件實體
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //2.創建客戶端啟動引導/輔助類:Bootstrap
            Bootstrap b = new Bootstrap();
            //3.指定執行緒組
            b.group(group)
                    //4.指定 IO 模型
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            // 5.這里可以自定義訊息的業務處理邏輯
                            p.addLast(new HelloClientHandler(message));
                        }
                    });
            // 6.嘗試建立連接
            ChannelFuture f = b.connect(host, port).sync();
            // 7.等待連接關閉(阻塞,直到Channel關閉)
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }

繼續分析一下客戶端的創建流程:

1.創建一個 NioEventLoopGroup 物件實體

2.創建客戶端啟動的引導類是 Bootstrap

3.通過 .group() 方法給引導類 Bootstrap 配置一個執行緒組

4.通過channel()方法給引導類 Bootstrap指定了 IO 模型為NIO

5.通過 .childHandler()給引導類創建一個ChannelInitializer ,然后制定了客戶端訊息的業務處理邏輯 HelloClientHandler 物件

6.呼叫 Bootstrap 類的 connect()方法進行連接,這個方法需要指定兩個引數:

  • inetHost : ip 地址
  • inetPort : 埠號
    public ChannelFuture connect(String inetHost, int inetPort) {
        return this.connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
    }
    public ChannelFuture connect(SocketAddress remoteAddress) {
        ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
        this.validate();
        return this.doResolveAndConnect(remoteAddress, this.config.localAddress());
    }

connect 方法回傳的是一個 Future 型別的物件

public interface ChannelFuture extends Future<Void> {
  ......
}

也就是說這個方是異步的,我們通過 addListener 方法可以監聽到連接是否成功,進而列印出連接資訊,具體做法很簡單,只需要對代碼進行以下改動:

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

什么是 TCP 粘包/拆包?有什么解決辦法呢?

面試官 :什么是 TCP 粘包/拆包?

 :TCP 粘包/拆包 就是你基于 TCP 發送資料的時候,出現了多個字串“粘”在了一起或者一個字串被“拆”開的問題,比如你多次發送:“你好,你真帥啊!哥哥!”,但是客戶端接收到的可能是下面這樣的:

面試官 :那有什么解決辦法呢?

 :

1.使用 Netty 自帶的解碼器

  • LineBasedFrameDecoder : 發送端發送資料包的時候,每個資料包之間以換行符作為分隔,LineBasedFrameDecoder 的作業原理是它依次遍歷 ByteBuf 中的可讀位元組,判斷是否有換行符,然后進行相應的截取,
  • DelimiterBasedFrameDecoder : 可以自定義分隔符解碼器,LineBasedFrameDecoder 實際上是一種特殊的 DelimiterBasedFrameDecoder 解碼器,
  • FixedLengthFrameDecoder: 固定長度解碼器,它能夠按照指定的長度對訊息進行相應的拆包,
  • LengthFieldBasedFrameDecoder

2.自定義序列化編解碼器

在 Java 中自帶的有實作 Serializable 介面來實作序列化,但由于它性能、安全性等原因一般情況下是不會被使用到的,

通常情況下,我們使用 Protostuff、Hessian2、json 序列方式比較多,另外還有一些序列化性能非常好的序列化方式也是很好的選擇:

  • 專門針對 Java 語言的:Kryo,FST 等等
  • 跨語言的:Protostuff(基于 protobuf 發展而來),ProtoBuf,Thrift,Avro,MsgPack 等等

由于篇幅問題,這部分內容會在后續的文章中詳細分析介紹~~~

Netty 長連接、心跳機制了解么?

面試官 :TCP 長連接和短連接了解么?

 :我們知道 TCP 在進行讀寫之前,server 與 client 之間必須提前建立一個連接,建立連接的程序,需要我們常說的三次握手,釋放/關閉連接的話需要四次揮手,這個程序是比較消耗網路資源并且有時間延遲的,

所謂,短連接說的就是 server 端 與 client 端建立連接之后,讀寫完成之后就關閉掉連接,如果下一次再要互相發送訊息,就要重新連接,短連接的有點很明顯,就是管理和實作都比較簡單,缺點也很明顯,每一次的讀寫都要建立連接必然會帶來大量網路資源的消耗,并且連接的建立也需要耗費時間,

長連接說的就是 client 向 server 雙方建立連接之后,即使 client 與 server 完成一次讀寫,它們之間的連接并不會主動關閉,后續的讀寫操作會繼續使用這個連接,長連接的可以省去較多的 TCP 建立和關閉的操作,降低對網路資源的依賴,節約時間,對于頻繁請求資源的客戶來說,非常適用長連接,

面試官 :為什么需要心跳機制?Netty 中心跳機制了解么?

 :

在 TCP 保持長連接的程序中,可能會出現斷網等網路例外出現,例外發生的時候, client 與 server 之間如果沒有互動的話,它們是無法發現對方已經掉線的,為了解決這個問題, 我們就需要引入 心跳機制 ,

心跳機制的作業原理是: 在 client 與 server 之間在一定時間內沒有資料互動時, 即處于 idle 狀態時, 客戶端或服務器就會發送一個特殊的資料包給對方, 當接收方收到這個資料報文后, 也立即發送一個特殊的資料報文, 回應發送方, 此即一個 PING-PONG 互動,所以, 當某一端收到心跳訊息后, 就知道了對方仍然在線, 這就確保 TCP 連接的有效性.

TCP 實際上自帶的就有長連接選項,本身是也有心跳包機制,也就是 TCP 的選項:SO_KEEPALIVE,但是,TCP 協議層面的長連接靈活性不夠,所以,一般情況下我們都是在應用層協議上實作自定義心跳機制的,也就是在 Netty 層面通過編碼實作,通過 Netty 實作心跳機制的話,核心類是 IdleStateHandler ,

Netty 的零拷貝了解么?

面試官 :講講 Netty 的零拷貝?

 :

維基百科是這樣介紹零拷貝的:

零復制(英語:Zero-copy;也譯零拷貝)技術是指計算機執行操作時,CPU 不需要先將資料從某處記憶體復制到另一個特定區域,這種技術通常用于通過網路傳輸檔案時節省 CPU 周期和記憶體帶寬,

在 OS 層面上的 Zero-copy 通常指避免在 用戶態(User-space) 與 內核態(Kernel-space) 之間來回拷貝資料,而在 Netty 層面 ,零拷貝主要體現在對于資料操作的優化,

Netty 中的零拷貝體現在以下幾個方面:

  1. 使用 Netty 提供的 CompositeByteBuf 類, 可以將多個ByteBuf 合并為一個邏輯上的 ByteBuf, 避免了各個 ByteBuf 之間的拷貝,
  2. ByteBuf 支持 slice 操作, 因此可以將 ByteBuf 分解為多個共享同一個存盤區域的 ByteBuf, 避免了記憶體的拷貝,
  3. 通過 FileRegion 包裝的FileChannel.tranferTo 實作檔案傳輸, 可以直接將檔案緩沖區的資料發送到目標 Channel, 避免了傳統通過回圈 write 方式導致的記憶體拷貝問題.

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/162015.html

標籤:Java

上一篇:Maven Eclipse各種操作

下一篇:我的第一個Java程式:自動關機、取消自動關機程式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more