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

歡迎訪問 生活随笔!

生活随笔

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

windows

Netty实战 IM即时通讯系统(二)Netty简介

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

##

Netty實戰 IM即時通訊系統(二)Netty簡介

零、 目錄

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

    二、 Netty簡介

  • 回顧IO編程
  • 場景: 客戶端每隔兩秒發送一個帶有時間戳的“hello world”給服務端 , 服務端收到之后打印。

  • 代碼:

    IOServer.java/*** @author 閃電俠*/public class IOServer {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8000);// (1) 接收新連接線程new Thread(() -> {while (true) {try {// (1) 阻塞方法獲取新的連接Socket socket = serverSocket.accept();// (2) 每一個新的連接都創建一個線程,負責讀取數據new Thread(() -> {try {int len;byte[] data = new byte[1024];InputStream inputStream = socket.getInputStream();// (3) 按字節流方式讀取數據while ((len = inputStream.read(data)) != -1) {System.out.println(new String(data, 0, len));}} catch (IOException e) {}}).start();} catch (IOException e) {}}}).start();}}IOClient.java/*** @author 閃電俠*/public class IOClient {public static void main(String[] args) {new Thread(() -> {try {Socket socket = new Socket("127.0.0.1", 8000);while (true) {try {socket.getOutputStream().write((new Date() + ": hello world").getBytes());Thread.sleep(2000);} catch (Exception e) {}}} catch (IOException e) {}}).start();}}
  • IO編程,模型在客戶端較少的場景下運行良好 , 但是客戶端比較多的業務來說 , 單機服務端可能需要支撐成千上萬的連接, IO模型可能就不太合適了 , 原因:

  • 在傳統的IO模型中 , 每一個連接創建成功之后都需要一個線程來維護 , 每個線程包含一個while死循環, 那么1W個連接就對應1W個線程 , 繼而1W個死循環。
  • 線程資源受限: 線程是操作系統中非常寶貴的資源 , 同一時刻有大量的線程處于阻塞狀態是非常嚴重的資源浪費,操作系統開銷太大。
  • 線程切換效率低下: 單機CPU核數固定 , 線程爆炸之后操作系統頻繁的進行線程切換 , 應用性能幾句下降
  • IO編程中 , 數據讀寫是以字節流為單位。
  • 為了解決這些問題 , JDK1.4之后提出了NIO

  • NIO 編程
  • NIO 是如何解決一下三個問題。

  • 線程資源受限
  • NIO編程模型中 , 新來一個連接不再創建一個新的線程, 而是可以把這條連接直接綁定在某個固定的線程 , 然后這條連接所有的讀寫都由這個線程來負責 , 那么他是怎么做到的?
  • 如上圖所示,IO 模型中,一個連接來了,會創建一個線程,對應一個 while 死循環,死循環的目的就是不斷監測這條連接上是否有數據可以讀,大多數情況下,1w 個連接里面同一時刻只有少量的連接有數據可讀,因此,很多個 while 死循環都白白浪費掉了,因為讀不出啥數據。
  • 而在 NIO 模型中,他把這么多 while 死循環變成一個死循環,這個死循環由一個線程控制,那么他又是如何做到一個線程,一個 while 死循環就能監測1w個連接是否有數據可讀的呢? 這就是 NIO 模型中 selector 的作用,一條連接來了之后,現在不創建一個 while 死循環去監聽是否有數據可讀了,而是直接把這條連接注冊到 selector 上,然后,通過檢查這個 selector,就可以批量監測出有數據可讀的連接,進而讀取數據,下面我再舉個非常簡單的生活中的例子說明 IO 與 NIO 的區別。
  • 在一家幼兒園里,小朋友有上廁所的需求,小朋友都太小以至于你要問他要不要上廁所,他才會告訴你。幼兒園一共有 100 個小朋友,有兩種方案可以解決小朋友上廁所的問題:
  • 每個小朋友配一個老師。每個老師隔段時間詢問小朋友是否要上廁所,如果要上,就領他去廁所,100 個小朋友就需要 100 個老師來詢問,并且每個小朋友上廁所的時候都需要一個老師領著他去上,這就是IO模型,一個連接對應一個線程。
  • 所有的小朋友都配同一個老師。這個老師隔段時間詢問所有的小朋友是否有人要上廁所,然后每一時刻把所有要上廁所的小朋友批量領到廁所,這就是 NIO 模型,所有小朋友都注冊到同一個老師,對應的就是所有的連接都注冊到一個線程,然后批量輪詢。
  • 這就是 NIO 模型解決線程資源受限的方案,實際開發過程中,我們會開多個線程,每個線程都管理著一批連接,相對于 IO 模型中一個線程管理一條連接,消耗的線程資源大幅減少
  • 線程切換效率低下
  • 由于NIO模型中線程數量大大降低 , 線程切換的效率也因此大幅度提高
  • IO讀寫面向流
  • IO讀寫是面向流的 , 一次性只能從流中讀取一個或多個字節 , 并且讀完之后無法再次讀取 , 你需要自己緩存數據 , 而NIO的讀寫是面向Buffer的 , 你可以隨意讀取里面的任何一個字節數據 , 不需要你自己緩存數據 , 這一切只需要移動讀寫指針即可。
  • 原生NIO 實現

    /*** 服務端* */ class NIO_server_test_01{public static void start () throws IOException {Selector serverSelect = Selector.open();Selector clientSelect = Selector.open();new Thread(() -> {try {ServerSocketChannel socketChannel = ServerSocketChannel.open();socketChannel.socket().bind(new InetSocketAddress(8000)); // 監聽端口socketChannel.configureBlocking(false); // 是否阻塞socketChannel.register(serverSelect, SelectionKey.OP_ACCEPT);while ( true ) {// 檢測是否有新的連接if(serverSelect.select(1) > 0) { // 1 是超時時間 select 方法返回當前連接數量Set<SelectionKey> set = serverSelect.selectedKeys();set.stream().filter(key -> key.isAcceptable()).collect(Collectors.toList()).forEach(key ->{try {// 每次來一個新的連接, 不需要創建新的線程 , 而是注冊到clientSelectorSocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();clientChannel.configureBlocking(false);clientChannel.register(serverSelect, SelectionKey.OP_ACCEPT);}catch(Exception e) {e.printStackTrace();}finally {set.iterator().remove();}});}}}catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {try {// 批量輪詢 有哪些連接有數據可讀while ( true ) {if(clientSelect.select(1) > 0) {clientSelect.selectedKeys().stream().filter(key -> key.isReadable()).collect(Collectors.toList()).forEach(key -> {try {SocketChannel clientChannl = (SocketChannel) key.channel();ByteBuffer bf = ByteBuffer.allocate(1024);// 面向byteBufferclientChannl.read(bf);bf.flip();System.out.println(Charset.defaultCharset().newDecoder().decode(bf).toString());}catch ( Exception e) {e.printStackTrace();}finally {clientSelect.selectedKeys().iterator().remove();key.interestOps(SelectionKey.OP_READ);}});}}}catch (Exception e) {e.printStackTrace();}}).start();}}
  • 通常NIO 模型中會有兩個線程每個線程中綁定一個輪詢器selector , 在我們的例子中serverSelector負責輪詢是否有新的連接 , clientSelector 負責輪詢連接中是否有數據可讀。
  • 服務端檢測到新的連接之后 , 不在創建一個新的線程 , 而是直接將連接注冊到clientSelector中
  • clientorSelector 被一個while死循環抱著 , 如果在某一時刻有多個連接數據可讀 ,數據將會被clientSelector.select() 方法輪詢出來。 進而批量處理 。
  • 數據的讀寫面向buffer 而不是面向流。
  • 原生NIO 進行網絡開發的缺點:

  • JDK 的NIO 編程需要了解很多概念, 編程復雜 , 對NIO 入門很不友好 , 編程模型不友好 , ByteBuffer的API簡直反人類 (這是書里這么說的 , 不要噴我)。
  • 對NIO 編程來說 , 一個比較適合的線程模型能充分發揮它的優勢 , 而JDK沒有給你實現 , 你需要自己實現 , 就連簡單的協議拆包都要自己實現 (我感覺這樣才根據創造力呀 )
  • JDK NIO 底層由epoll 實現 , 該實現飽受詬病的空輪訓bug會導致cpu 飆升100%
  • 項目龐大之后 , 自己實現的NIO 很容易出現各類BUG , 維護成本高 (作者怎么把自己的過推向JDK haha~)
  • 正因為如此 , 我連客戶端的代碼都懶得給你寫了 (這作者可真夠懶的) , 你可以直接使用IOClient 和NIO_Server 通信
  • JDK 的NIO 猶如帶刺的玫瑰 , 雖然美好 , 讓人向往 , 但是使用不當會讓你抓耳撓腮 , 痛不欲生 , 正因為如此 , Netty橫空出世!(作者這才華 嘖嘖嘖~)

  • Netty 編程
  • Netty到底是何方神圣(被作者吹上天了都) , 用依據簡單的話來說就是: Netty 封裝了JDK 的NIO , 讓你使用更加干爽 (干爽???) , 你不用在寫一大堆復雜的代碼了 , 用官方的話來說就是: Netty是一個異步事件驅動的網絡應用框架 , 用于快速開發可維護的高性能服務器和客戶端。
  • Netty 相比 JDK 原生NIO 的優點 :
  • 使用NIO 需要了解太多概念, 編程復雜 , 一不小心 BUG 橫飛
  • Netty 底層IO模型隨意切換 , 而這一切只需要小小的改動 , 改改參數 , Netty樂意直接從NIO模型轉換為IO 模型 。
  • Netty 自帶的拆包解包 , 異常檢測可以讓你從NIO 的繁重細節中脫離出來 , 讓你只關心業務邏輯 。
  • Netty 解決了JDK 的很多包括空輪訓在內的BUG
  • Netty社區活躍 , 遇到問題可以輕松解決
  • Netty 已經經歷各大RPC 框架 , 消息中間價 , 分布式通信中間件線上的廣泛驗證 , 健壯性無比強大
  • 代碼實例
  • maven 依賴

    <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.6.Final</version> </dependency>
  • NettyServer

    /*** @author outman* */class Netty_server_02 {public void start () {ServerBootstrap serverBootstrap = new ServerBootstrap();NioEventLoopGroup boss = new NioEventLoopGroup();NioEventLoopGroup woker = new NioEventLoopGroup();serverBootstrap.group(boss ,woker).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {@Overrideprotected void channelRead0(ChannelHandlerContext cxt, String msg) throws Exception {System.out.println(msg);}});}}).bind(8000);}}
  • 這么一小段代碼就實現了我們前面NIO 編程中所有的功能 , 包括服務端啟動 , 接收新連接 , 打印客戶端傳來的數據。
  • 將NIO 中的概念與IO模型結合起來理解:
  • boss 對應 IOServer 中接收新連接創建線程 , 主要負責創建新連接
  • worker 對應 IOServer 中負責讀取數據的線程 , 主要用于數據讀取語句業務邏輯處理 。
  • 詳細邏輯會在后續深入討論
  • NettyClient

    /**
    * @author outman
    * */
    class Netty_client_02 {

    public static void main(String[] args) throws InterruptedException {Bootstrap bootstrap = new Bootstrap();NioEventLoopGroup group = new NioEventLoopGroup();bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) {ch.pipeline().addLast(new StringEncoder());}});Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();while (true) {channel.writeAndFlush(new Date() + ": hello world!");Thread.sleep(2000);}}

    }

  • 在客戶端程序中 , group 對應了我們IOClient 中 新起的線程。

  • 剩下的邏輯 我們在后文中詳細分析 , 現在你可以把 Netty_server_02 和Netty_client_02 復制到 你的IDE 中 運行起來 感受世界 的美好 (注意 先啟動 服務端 再啟動客戶端 )

  • 使用Netty 之后 整個世界都美好了, 一方面 Netty 對NIO 封裝的如此完美 , 另一方面 , 使用Netty 之后 , 網絡通信這塊的性能問題幾乎不用操心 , 盡情的讓Netty 榨干你的CPU 吧~~

  • 總結

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

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