日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Netty实战 IM即时通讯系统(五)客户端启动流程

發布時間:2024/4/30 windows 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Netty实战 IM即时通讯系统(五)客户端启动流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

##

Netty實戰 IM即時通訊系統(五)客戶端啟動流程

零、 目錄

  • IM系統簡介
    • Netty 簡介
    • Netty 環境配置
    • 服務端啟動流程
    • 實戰: 客戶端和服務端雙向通信
    • 數據傳輸載體ByteBuf介紹
    • 客戶端與服務端通信協議編解碼
    • 實現客戶端登錄
    • 實現客戶端與服務端收發消息
    • pipeline與channelHandler
    • 構建客戶端與服務端pipeline
    • 拆包粘包理論與解決方案
    • channelHandler的生命周期
    • 使用channelHandler的熱插拔實現客戶端身份校驗
    • 客戶端互聊原理與實現
    • 群聊的發起與通知
    • 群聊的成員管理(加入與退出,獲取成員列表)
    • 群聊消息的收發及Netty性能優化
    • 心跳與空閑檢測
    • 總結
    • 擴展

    五、 客戶端啟動流程

  • 客戶端啟動demo

    /*** 客戶端啟動流程* */public class Test_05_客戶端啟動流程 {public static void main(String[] args) {NioEventLoopGroup workerGroup = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap// 指定線程模型.group(workerGroup)// 指定IO 模型.channel(NioSocketChannel.class)// 指定業務處理邏輯.handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {}});// 建立連接bootstrap.connect("127.0.0.1" , 8000).addListener(future ->{if(future.isSuccess()) {System.out.println("連接成功");}else {System.out.println("連接失敗");}});}}
  • 從上面的代碼可以看出 , 客戶端的啟動引導類是BootStrap , 負責啟動客戶端以及連接服務端 , 而上面一小節我們在描述服務端啟動的時候, 這個引導類是 ServerBootStrap , 引導類創建完成之后我們描述一下客戶端啟動流程:
  • 首先和服務端的啟動流程一樣 , 我們需要給特指定線程模型, 驅動著連接的數據讀寫
  • 然后我們指定IO 模型為 NioSocketChannel , 表示IO模型為 NIO
  • 接著, 我們給引導類指定一個handler , 這里主要就是定義連接的業務處理邏輯 , 不理解沒有關系 , 我們在后面會詳解
  • 配置完線程模型 , IO 模型 , 業務處理邏輯之后 , 調用connect() 方法進行連接 , 可以看到connect() 方法有兩個參數 , 第一個參數可以填寫IP或域名 ,第二個參數填寫的是端口號 , 由于connect() 方法返回的是一個Future , 也就是說這個方法是異步的 , 我們通過addListener方法可以監聽到連接是否成功 , 進而打印連接狀態
  • 到這里一個客戶端的demo 就完成了 , 其實只要和客戶端Socket 編程模型對應起來 , 這里的三個概念就會顯得非常簡單
  • 失敗重連

  • 在網絡差的情況下 , 客戶端第一次連接可能會失敗 , 這個時候我們可能會嘗試重新連接 , 重新連接的邏輯寫在連接失敗的邏輯塊里

    // 建立連接bootstrap.connect("127.0.0.1" , 8000).addListener(future ->{if(future.isSuccess()) {System.out.println("連接成功");}else {System.out.println("連接失敗");//TODO: 重新連接邏輯}});
  • 重新連接時依然是調用相同的邏輯 , 所以我們把連接的代碼抽取出來, 實現代碼復用 , 在連接失敗的情況下使用遞歸的方法 實現重連

    public static void connect(Bootstrap bootstrap, String IP, int port) {// 建立連接bootstrap.connect(IP, port).addListener(future -> {if (future.isSuccess()) {System.out.println("連接成功");} else {System.out.println("連接失敗,執行重連");connect(bootstrap, IP, port);}});}
  • 以上代碼就實現了重連機制 , 但是在通常情況下連接失敗不會立即重連 , 而是通過一個指數退避的方式 , 比如 每隔1秒、2秒、4秒、8秒 , 以2的次冪來實現建立連接 , 然后到達一定次數之后就放棄重連

    connect(bootstrap , "127.0.0.1" , 8000 , 5);public static void connect(Bootstrap bootstrap, String IP, int port ,int maxRetry , int... retryIndex) {// 建立連接bootstrap.connect(IP, port).addListener(future -> {// 由于閉包特性 不能修改外部的變量 所有需要在閉包內定義一個相同的變量 拷貝外部變量的值int[] finalRetryIndex ;if (future.isSuccess()) {System.out.println("連接成功");} else if(maxRetry == 0){System.out.println("到達重試最大次數,放棄重連");}else {// 初始化 重試計數if(retryIndex.length == 0) {finalRetryIndex = new int[] {0};}else {finalRetryIndex = retryIndex;}//計算時間間隔int delay = 1 << finalRetryIndex[0];// 執行重試System.out.println(new Date()+"連接失敗,剩余重連次數:"+maxRetry+","+delay+"秒后執行第"+(finalRetryIndex[0]+1)+"次重連...");bootstrap.config().group().schedule(()->{connect(bootstrap, IP, port , maxRetry-1 , finalRetryIndex[0]+1);}, delay, TimeUnit.SECONDS);}});}執行結果:Thu Dec 27 11:04:19 CST 2018連接失敗,剩余重連次數:5,1秒后執行第1次重連...Thu Dec 27 11:04:21 CST 2018連接失敗,剩余重連次數:4,2秒后執行第2次重連...Thu Dec 27 11:04:24 CST 2018連接失敗,剩余重連次數:3,4秒后執行第3次重連...Thu Dec 27 11:04:29 CST 2018連接失敗,剩余重連次數:2,8秒后執行第4次重連...Thu Dec 27 11:04:38 CST 2018連接失敗,剩余重連次數:1,16秒后執行第5次重連...到達重試最大次數,放棄重連
  • 從上面的代碼中我們可以看到 , 通過判斷是否連接成功以及剩余重試次數 , 分別執行不同的邏輯
  • 如果連接成功則打印連接成功的消息
  • 如果連接失敗 , 但是重試次數已經用完則放棄連接
  • 如果連接失敗 , 但是連接沒有用完則計算下一次重試時間間隔 , 然后定時重連
  • 從上面代碼中我們可以看到 , 定時任務是調用bootstrap.config().group().schedule() , 其中bootStrap.config()這個方法返回的是BootStrapConfig , 他是對BootStrap 參數配置的抽象 , 然后ootstrap.config().group() 返回的就是我們一開始設置的線程模型workerGroup , 最后調用schedule() 方法就可以實現定時任務邏輯了。
  • 客戶端啟動其他方法

  • attr(): attr()方法可以給客戶端channel也就是NioSocketChannel綁定自定義屬性 , 然后我們通過channel.attr()取出屬性。 說白了就是給NioSocketChannel維護一個Map 而已

    //設置屬性bootstrap.attr(AttributeKey.newInstance("clientName"), "NettyClient");//業務邏輯bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {// 取出屬性Attribute<Object> attr = ch.attr(AttributeKey.valueOf("clientName"));System.out.println("客戶端名稱:"+attr.get());}});
  • option(): option()可以給連接設置一些TCP底層的相關屬性 : (ChannelOption相關參數詳解在 上一節《服務端啟動流程》中有連接地址)

    // 設置TCP 相關的屬性// 設置連接超時時間bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);// 開啟TCP 心跳機制bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
  • 總結:

  • 在本節中我們學習了Netty客戶端啟動的流程 , 一句話來說就是: 創建一個引導類 , 然后給他指定線程 , IO模型 , 指定業務邏輯 ,連接特定的IP:port 客戶端就啟動起來了
  • 然后我們學習到 connect()方法時異步的 , 我們可以通過異步回調機制來實現指數退避重連機制。
  • 最后我們討論了Netty客戶端啟動的額外參數 , 只要包括給客戶端Channel 綁定自定義屬性 , 設置TCP底層參數。
  • 疑問:

  • 客戶端Channel設置的attr是否會被服務端接收到,并且以此進行必要的參數傳遞?
  • 答: 客戶端Channel 會被服務端接收到。
  • 總結

    以上是生活随笔為你收集整理的Netty实战 IM即时通讯系统(五)客户端启动流程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。