摘要:主從多線程該模型將客戶端連接那一塊的線程也改為多線程,稱為主線程。同時(shí)也是多個(gè)子線程來(lái)處理事件響應(yīng),這樣無(wú)論是連接還是事件都是高性能的。多線程提高并發(fā)效率。
前言
在之前的 SpringBoot 整合長(zhǎng)連接心跳機(jī)制 一文中認(rèn)識(shí)了 Netty。
但其實(shí)只是能用,為什么要用 Netty?它有哪些優(yōu)勢(shì)?這些其實(shí)都不清楚。
本文就來(lái)從歷史源頭說(shuō)道說(shuō)道。
傳統(tǒng) IO在 Netty 以及 NIO 出現(xiàn)之前,我們寫 IO 應(yīng)用其實(shí)用的都是用 java.io.* 下所提供的包。
比如下面的偽代碼:
ServeSocket serverSocket = new ServeSocket(8080); Socket socket = serverSocket.accept() ; BufferReader in = .... ; String request ; while((request = in.readLine()) != null){ new Thread(new Task()).start() }
大概是這樣,其實(shí)主要想表達(dá)的是:這樣一個(gè)線程只能處理一個(gè)連接。
如果是 100 個(gè)客戶端連接那就得開(kāi) 100 個(gè)線程,1000 那就得 1000 個(gè)線程。
要知道線程資源非常寶貴,每次的創(chuàng)建都會(huì)帶來(lái)消耗,而且每個(gè)線程還得為它分配對(duì)應(yīng)的棧內(nèi)存。
即便是我們給 JVM 足夠的內(nèi)存,大量線程所帶來(lái)的上下文切換也是受不了的。
并且傳統(tǒng) IO 是阻塞模式,每一次的響應(yīng)必須的是發(fā)起 IO 請(qǐng)求,處理請(qǐng)求完成再同時(shí)返回,直接的結(jié)果就是性能差,吞吐量低。Reactor 模型
因此業(yè)界常用的高性能 IO 模型是 Reactor。
它是一種異步、非阻塞的事件驅(qū)動(dòng)模型。
通常也表現(xiàn)為以下三種方式:
單線程從圖中可以看出:
它是由一個(gè)線程來(lái)接收客戶端的連接,并將該請(qǐng)求分發(fā)到對(duì)應(yīng)的事件處理 handler 中,整個(gè)過(guò)程完全是異步非阻塞的;并且完全不存在共享資源的問(wèn)題。所以理論上來(lái)說(shuō)吞吐量也還不錯(cuò)。
但由于是一個(gè)線程,對(duì)多核 CPU 利用率不高,一旦有大量的客戶端連接上來(lái)性能必然下降,甚至?xí)写罅空?qǐng)求無(wú)法響應(yīng)。多線程
最壞的情況是一旦這個(gè)線程哪里沒(méi)有處理好進(jìn)入了死循環(huán)那整個(gè)服務(wù)都將不可用!
因此產(chǎn)生了多線程模型。
其實(shí)最大的改進(jìn)就是將原有的事件處理改為了多線程。
可以基于 Java 自身的線程池實(shí)現(xiàn),這樣在大量請(qǐng)求的處理上性能提示是巨大的。
雖然如此,但理論上來(lái)說(shuō)依然有一個(gè)地方是單點(diǎn)的;那就是處理客戶端連接的線程。
因?yàn)榇蠖鄶?shù)服務(wù)端應(yīng)用或多或少在連接時(shí)都會(huì)處理一些業(yè)務(wù),如鑒權(quán)之類的,當(dāng)連接的客戶端越來(lái)越多時(shí)這一個(gè)線程依然會(huì)存在性能問(wèn)題。
于是又有了下面的線程模型。
主從多線程該模型將客戶端連接那一塊的線程也改為多線程,稱為主線程。
同時(shí)也是多個(gè)子線程來(lái)處理事件響應(yīng),這樣無(wú)論是連接還是事件都是高性能的。
Netty 實(shí)現(xiàn)以上談了這么多其實(shí) Netty 的線程模型與之的類似。
我們回到之前 SpringBoot 整合長(zhǎng)連接心跳機(jī)制TCP-Heartbeat/) 中的服務(wù)端代碼:
private EventLoopGroup boss = new NioEventLoopGroup(); private EventLoopGroup work = new NioEventLoopGroup(); /** * 啟動(dòng) Netty * * @return * @throws InterruptedException */ @PostConstruct public void start() throws InterruptedException { ServerBootstrap bootstrap = new ServerBootstrap() .group(boss, work) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(nettyPort)) //保持長(zhǎng)連接 .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new HeartbeatInitializer()); ChannelFuture future = bootstrap.bind().sync(); if (future.isSuccess()) { LOGGER.info("啟動(dòng) Netty 成功"); } }
其實(shí)這里的 boss 就相當(dāng)于 Reactor 模型中處理客戶端連接的線程池。
work 自然就是處理事件的線程池了。
那么如何來(lái)實(shí)現(xiàn)上文的三種模式呢?其實(shí)也很簡(jiǎn)單:
單線程模型:
private EventLoopGroup group = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap() .group(group) .childHandler(new HeartbeatInitializer());
多線程模型:
private EventLoopGroup boss = new NioEventLoopGroup(1); private EventLoopGroup work = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap() .group(boss,work) .childHandler(new HeartbeatInitializer());
主從多線程:
private EventLoopGroup boss = new NioEventLoopGroup(); private EventLoopGroup work = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap() .group(boss,work) .childHandler(new HeartbeatInitializer());
相信大家一看也明白。
總結(jié)其實(shí)看過(guò)了 Netty 的線程模型之后能否對(duì)我們平時(shí)做高性能應(yīng)用帶來(lái)點(diǎn)啟發(fā)呢?
我認(rèn)為是可以的:
接口同步轉(zhuǎn)異步處理。
回調(diào)通知結(jié)果。
多線程提高并發(fā)效率。
無(wú)非也就是這些,只是做了這些之后就會(huì)帶來(lái)其他問(wèn)題:
異步之后事務(wù)如何保證?
回調(diào)失敗的情況?
多線程所帶來(lái)的上下文切換、共享資源的問(wèn)題。
這就是一個(gè)博弈的過(guò)程,想要做到一個(gè)盡量高效的應(yīng)用是需要不斷磨合試錯(cuò)的。
上文相關(guān)的代碼:
https://github.com/crossoverJie/netty-action
歡迎關(guān)注公眾號(hào)一起交流:
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/71483.html
摘要:的選擇器允許單個(gè)線程監(jiān)視多個(gè)輸入通道。一旦執(zhí)行的線程已經(jīng)超過(guò)讀取代碼中的某個(gè)數(shù)據(jù)片段,該線程就不會(huì)在數(shù)據(jù)中向后移動(dòng)通常不會(huì)。 1、引言 很多初涉網(wǎng)絡(luò)編程的程序員,在研究Java NIO(即異步IO)和經(jīng)典IO(也就是常說(shuō)的阻塞式IO)的API時(shí),很快就會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題:我什么時(shí)候應(yīng)該使用經(jīng)典IO,什么時(shí)候應(yīng)該使用NIO? 在本文中,將嘗試用簡(jiǎn)明扼要的文字,闡明Java NIO和經(jīng)典IO之...
摘要:接下來(lái)的兩篇文章,我將從源碼角度為大家深入淺出的剖析的線程模型工作機(jī)制。我們看一下的源碼通過(guò)的代碼發(fā)現(xiàn),實(shí)現(xiàn)了接口,其內(nèi)部會(huì)通過(guò)指定的默認(rèn)線程工廠來(lái)創(chuàng)建線程,并執(zhí)行相應(yīng)的任務(wù)。至此,初始化完成了。下一篇我們將詳細(xì)介紹,敬請(qǐng)期待。 我們都知道Netty的線程模型是基于React的線程模型,并且我們都知道Netty是一個(gè)高性能的NIO框架,那么其線程模型必定是它的重要貢獻(xiàn)之一。 在使用ne...
摘要:如果什么事都沒(méi)得做,它也不會(huì)死循環(huán),它會(huì)將線程休眠起來(lái),直到下一個(gè)事件來(lái)了再繼續(xù)干活,這樣的一個(gè)線程稱之為線程。而請(qǐng)求處理邏輯既可以使用單獨(dú)的線程池進(jìn)行處理,也可以跟放在讀寫線程一塊處理。 Netty到底是什么 從HTTP說(shuō)起 有了Netty,你可以實(shí)現(xiàn)自己的HTTP服務(wù)器,F(xiàn)TP服務(wù)器,UDP服務(wù)器,RPC服務(wù)器,WebSocket服務(wù)器,Redis的Proxy服務(wù)器,MySQL的P...
摘要:搞懂了這部分后,我們將明白在世界中扮演的角色進(jìn)擊的此圖展示的已經(jīng)算是優(yōu)化后的了用到了線程池。多線程將這種處理操作分隔出來(lái),非型操作業(yè)務(wù)操作配備以線程池,進(jìn)化成多線程模型這樣的架構(gòu),系統(tǒng)瓶頸轉(zhuǎn)移至部分。 Channel定位 注意:如無(wú)特別說(shuō)明,文中的Channel都指的是Netty Channel(io.netty.channel) 一周時(shí)間的Channel家族學(xué)習(xí),一度讓我懷疑人生——...
摘要:它使用了事件通知以確定在一組非阻塞套接字中有哪些已經(jīng)就緒能夠進(jìn)行相關(guān)的操作。目前,可以把看作是傳入入站或者傳出出站數(shù)據(jù)的載體。出站事件是未來(lái)將會(huì)觸發(fā)的某個(gè)動(dòng)作的操作結(jié)果,這些動(dòng)作包括打開(kāi)或者關(guān)閉到遠(yuǎn)程節(jié)點(diǎn)的連接將數(shù)據(jù)寫到或者沖刷到套接字。 netty的概念 定義 Netty 是一款異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架,支持快速地開(kāi)發(fā)可維護(hù)的高性能的面向協(xié)議的服務(wù)器和客戶端。我們可以很簡(jiǎn)單的...
閱讀 1900·2021-09-27 13:35
閱讀 3439·2019-08-30 14:16
閱讀 2491·2019-08-30 10:52
閱讀 871·2019-08-29 16:35
閱讀 1424·2019-08-29 15:22
閱讀 3651·2019-08-23 18:21
閱讀 3144·2019-08-23 18:00
閱讀 3129·2019-08-23 16:50