什么是netty--通俗易懂

一.Netty介紹

1.什么是netty

Netty 是由 JBOSS 提供的一個(gè) Java 開源框架钾唬。Netty 提供異步的堰乔、基于事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架偏化,用以快速開發(fā)高性能、高可靠性的網(wǎng)絡(luò) IO 程序,是目前最流行的 NIO 框架镐侯,Netty 在互聯(lián)網(wǎng)領(lǐng)域侦讨、大數(shù)據(jù)分布式計(jì)算領(lǐng)域、游戲行業(yè)、通信行業(yè)等獲得了廣泛的應(yīng)用韵卤,知名的 Elasticsearch 骗污、Dubbo 框架內(nèi)部都采用了 Netty。

2.為什么要用netty

原生 NIO 存在問(wèn)題:

1.NIO 的類庫(kù)和 API 繁雜

2.需要熟悉 Java 多線程編程怜俐,因?yàn)?NIO 編程涉及到 Reactor 模式,必須對(duì)多線程和網(wǎng)絡(luò)編程非常熟悉邓尤, 才能編寫出高質(zhì)量的 NIO 程序

3.開發(fā)工作量和難度都非常大拍鲤。例如客戶端面臨斷連重連、網(wǎng)絡(luò)閃斷汞扎、半包讀寫季稳、失敗緩存、網(wǎng)絡(luò)擁塞和異常 流的處理等等處理起來(lái)難度會(huì)比較大澈魄。

4.JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug景鼠,它會(huì)導(dǎo)致 Selector 空輪詢,最終導(dǎo)致 CPU 100%痹扇。直到 JDK 1.7 版本該問(wèn)題仍舊存在铛漓,沒(méi)有被根本解決。

3.Netty的優(yōu)點(diǎn)

Netty 對(duì) JDK 自帶的 NIO 的 API 進(jìn)行了封裝鲫构,解決了上述問(wèn)題浓恶。

1.設(shè)計(jì)優(yōu)雅:適用于各種傳輸類型的統(tǒng)一 API 阻塞和非阻塞 Socket;基于靈活且可擴(kuò)展的事件模型结笨,可以清晰地分離關(guān)注點(diǎn)包晰;高度可定制的線程模型 - 單線程,一個(gè)或多個(gè)線程池.

2.使用方便:詳細(xì)記錄的 Javadoc炕吸,用戶指南和示例伐憾;沒(méi)有其他依賴項(xiàng),JDK 5(Netty 3.x)或 6(Netty 4.x)就足夠了赫模。

3.高性能树肃、吞吐量更高:延遲更低;減少資源消耗瀑罗;最小化不必要的內(nèi)存復(fù)制扫外。

4.安全:完整的 SSL/TLS 和 StartTLS 支持。

5.社區(qū)活躍廓脆、不斷更新:社區(qū)活躍筛谚,版本迭代周期短,發(fā)現(xiàn)的 Bug 可以被及時(shí)修復(fù)停忿,同時(shí)更多的新功能會(huì)被加入

二.Reactor三種線程模型

1.現(xiàn)有的三種線程模型

不同的線程模式驾讲,對(duì)程序的性能有很大影響,目前存在的線程模型有:

①.傳統(tǒng)阻塞 I/O 服務(wù)模型

②.Reactor 模式

Reactor 模式又有 3 種典型的實(shí)現(xiàn)

單 Reactor 單線程;

單 Reactor 多線程吮铭;

主從 Reactor 多線程

Netty 的線程模型是主要是基于主從 Reactor 多線程模型改成了主從 Reactor 多線程模型有多個(gè) Reactor模式

2.傳統(tǒng)阻塞 I/O 服務(wù)模型介紹

特點(diǎn):

采用阻塞IO模式獲取輸入的數(shù)據(jù)

每個(gè)連接都需要?jiǎng)?chuàng)建單獨(dú)的線程完成數(shù)據(jù)的輸入时迫,業(yè)務(wù)處理和數(shù)據(jù)的返回

缺點(diǎn):

當(dāng)并發(fā)數(shù)很大,就會(huì)創(chuàng)建大量的線程谓晌,占用很大系統(tǒng)資源掠拳,在線程開銷和上下文切換上降低處理性能

當(dāng)連接創(chuàng)建后,如果當(dāng)前線程暫時(shí)沒(méi)有數(shù)據(jù)可讀纸肉,該線程會(huì)阻塞在read 操作溺欧,造成線程資源的浪費(fèi)。

img

黃色的框表示對(duì)象柏肪, 藍(lán)色的框表示線程 白色的框表示方法(API)

3. Reactor 模式

針對(duì)傳統(tǒng)阻塞 I/O 服務(wù)模型的 2 個(gè)缺點(diǎn)姐刁,解決方案:

I/O 復(fù)用模型:多個(gè)連接共用一個(gè)阻塞對(duì)象,應(yīng)用程序只需要在一個(gè)阻塞對(duì)象上等待烦味,無(wú)需阻塞等待所有連接聂使。當(dāng)某個(gè)連接有新的數(shù)據(jù)可以處理時(shí),操作系統(tǒng)通知應(yīng)用程序谬俄,線程從阻塞狀態(tài)返回柏靶,開始進(jìn)行業(yè)務(wù)處理。

Reactor 對(duì)應(yīng)的叫法: 1. 反應(yīng)器模式 2. 分發(fā)者模式(Dispatcher) 3. 通知者模式(notifier)

基于線程池復(fù)用線程資源模式:不必再為每個(gè)連接創(chuàng)建線程溃论,將連接完成后的業(yè)務(wù)處理任務(wù)分配給線程進(jìn)行處理宿礁,一個(gè)線程可以處理多個(gè)連接的業(yè)務(wù)。

I/O 復(fù)用結(jié)合線程池蔬芥,就是 Reactor 模式基本設(shè)計(jì)思想

img

Reactor 模式梆靖,通過(guò)一個(gè)或多個(gè)輸入同時(shí)傳遞給服務(wù)處理器的模式,(基于事件驅(qū)動(dòng))

服務(wù)器端程序處理傳入的多個(gè)請(qǐng)求,并將它們同步分派到相應(yīng)的處理線程, 因此Reactor模式也叫 Dispatcher模式

Reactor 模式使用IO復(fù)用監(jiān)聽事件, 收到事件后笔诵,分發(fā)給某個(gè)線程(進(jìn)程), 這點(diǎn)就是網(wǎng)絡(luò)服務(wù)器高并發(fā)處理關(guān)鍵

4.單 Reactor 單線程

1.工作原理:

①Select 是前面 I/O 復(fù)用模型介紹的標(biāo)準(zhǔn)網(wǎng)絡(luò)編程 API返吻,可以實(shí)現(xiàn)應(yīng)用程序通過(guò)一個(gè)阻塞對(duì)象監(jiān)聽多路連接請(qǐng)求

②Reactor 對(duì)象通過(guò) Select 監(jiān)控客戶端請(qǐng)求事件,收到事件后通過(guò) Dispatch 進(jìn)行分發(fā)

③如果是建立連接請(qǐng)求事件乎婿,則由 Acceptor 通過(guò) Accept 處理連接請(qǐng)求测僵,然后創(chuàng)建一個(gè) Handler 對(duì)象處理連接完成后的后續(xù)業(yè)務(wù)處理

④如果不是建立連接事件,則 Reactor 會(huì)分發(fā)調(diào)用連接對(duì)應(yīng)的 Handler 來(lái)響應(yīng)

⑤Handler 會(huì)完成 Read→業(yè)務(wù)處理→Send 的完整業(yè)務(wù)流程

2.優(yōu)點(diǎn):

模型簡(jiǎn)單谢翎,沒(méi)有多線程捍靠、進(jìn)程通信、競(jìng)爭(zhēng)的問(wèn)題森逮,全部都在一個(gè)線程中完成

3.缺點(diǎn):

①性能問(wèn)題榨婆,只有一個(gè)線程,無(wú)法完全發(fā)揮多核 CPU 的性能褒侧。

②可靠性問(wèn)題良风,線程意外終止谊迄,或者進(jìn)入死循環(huán),會(huì)導(dǎo)致整個(gè)系統(tǒng)通信模塊不可用烟央,不能接收和處理外部消息统诺,造成節(jié)點(diǎn)故障

③服務(wù)器端用一個(gè)線程通過(guò)多路復(fù)用搞定所有的 IO 操作(包括連接,讀疑俭、寫等)粮呢,編碼簡(jiǎn)單,清晰明了钞艇,但是如果客戶端連接數(shù)量較多時(shí)啄寡,當(dāng)對(duì)應(yīng)多個(gè)讀時(shí),還是會(huì)出現(xiàn)阻塞現(xiàn)象香璃,當(dāng)這種情況發(fā)生時(shí)將無(wú)法支撐高并發(fā)的場(chǎng)景这难。

4.應(yīng)用場(chǎng)景:

客戶端的數(shù)量有限舟误,業(yè)務(wù)處理非称厦耄快速(比如 Redis在業(yè)務(wù)處理的時(shí)間復(fù)雜度 O(1) 的情況)

img

5.單Reactor多線程

1.工作原理:

①Reactor 對(duì)象通過(guò)select 監(jiān)控客戶端請(qǐng)求事件, 收到事件后,通過(guò)dispatch進(jìn)行分發(fā)

②如果是建立連接請(qǐng)求, 則由Acceptor 通過(guò)accept 處理連接請(qǐng)求, 然后創(chuàng)建一個(gè)Handler對(duì)象處理完成連接后的各種事件

③如果不是連接請(qǐng)求嵌溢,則由Reactor分發(fā)調(diào)用連接對(duì)應(yīng)的handler 來(lái)處理

④handler 只負(fù)責(zé)響應(yīng)事件眯牧,不做具體的業(yè)務(wù)處理, 通過(guò)read 讀取數(shù)據(jù)后,會(huì)分發(fā)給后面的worker線程池的某個(gè)線程處理業(yè)務(wù)赖草。

⑤worker 線程池會(huì)分配獨(dú)立線程完成真正的業(yè)務(wù)学少,并將結(jié)果返回給handler,handler收到響應(yīng)后,通過(guò)send 將結(jié)果返回給client.

2.優(yōu)點(diǎn):

可以充分的利用多核cpu 的處理能力

3.缺點(diǎn):

多線程數(shù)據(jù)共享和訪問(wèn)比較復(fù)雜,Reactor處理所有的事件的監(jiān)聽和響應(yīng)秧骑,在單線程運(yùn)行時(shí)版确,在高并發(fā)場(chǎng)景容易出現(xiàn)性能瓶頸.

img

6.主從 Reactor 多線程

1.工作原理:

①Reactor主線程 MainReactor 對(duì)象通過(guò)select 監(jiān)聽連接事件, 收到事件后,通過(guò)Acceptor 處理連接事件

②當(dāng) Acceptor 處理連接事件后乎折,MainReactor 將連接分配給SubReactor

③subReactor 將連接加入到連接隊(duì)列進(jìn)行監(jiān)聽,并創(chuàng)建handler進(jìn)行各種事件處理

④當(dāng)有新事件發(fā)生時(shí)绒疗, subreactor 就會(huì)調(diào)用對(duì)應(yīng)的handler處理

⑤handler 通過(guò)read 讀取數(shù)據(jù),分發(fā)給后面的worker 線程處理

⑥worker 線程池分配獨(dú)立的worker 線程進(jìn)行業(yè)務(wù)處理骂澄,并返回結(jié)果

⑦h(yuǎn)andler 收到響應(yīng)的結(jié)果后吓蘑,再通過(guò)send 將結(jié)果返回給client

⑧Reactor 主線程可以對(duì)應(yīng)多個(gè)Reactor 子線程, 即MainRecator 可以關(guān)聯(lián)多個(gè)SubReactor

img

三.Netty線程模型

1.工作原理

Netty抽象出兩組線程池 BossGroup 專門負(fù)責(zé)接收客戶端的連接, WorkerGroup 專門負(fù)責(zé)網(wǎng)絡(luò)的讀寫

BossGroup 和 WorkerGroup 類型都是 NioEventLoopGroup

NioEventLoopGroup 相當(dāng)于一個(gè)事件循環(huán)組, 這個(gè)組中含有多個(gè)事件循環(huán) ,每一個(gè)事件循環(huán)是 NioEventLoop

NioEventLoop 表示一個(gè)不斷循環(huán)的執(zhí)行處理任務(wù)的線程坟冲, 每個(gè)NioEventLoop 都有一個(gè)selector , 用于監(jiān)聽綁定在該通道上的socket的網(wǎng)絡(luò)通訊

NioEventLoopGroup 可以有多個(gè)線程, 即可以含有多個(gè)NioEventLoop

每個(gè)Boss NioEventLoop 循環(huán)執(zhí)行的步驟有3步

輪詢accept 事件

處理accept 事件 , 與client端建立連接 , 生成NioScocketChannel , 并將其注冊(cè)到某個(gè)worker NIOEventLoop 上的 selector 上

處理任務(wù)隊(duì)列的任務(wù) 磨镶, 即 runAllTasks

每個(gè) Worker NIOEventLoop 循環(huán)執(zhí)行的步驟

輪詢r(jià)ead, write 事件

處理i/o事件, 即read , write 事件健提,在對(duì)應(yīng)NioScocketChannel 處理

處理任務(wù)隊(duì)列的任務(wù) 琳猫, 即 runAllTasks

每個(gè)Worker NIOEventLoop 處理業(yè)務(wù)時(shí),會(huì)使用pipeline(管道), pipeline 中包含了boss group上NioEventLoop注冊(cè)到worker 的selector 的channel , 即通過(guò)pipeline 可以獲取到對(duì)應(yīng)通道, 管道中維護(hù)了很多的處理器

img

四.Netty入門

1.引入java包

(JDK 5(Netty 3.x)或 6(Netty 4.x))
<dependency>
 <groupId>io.netty</groupId>
 <artifactId>netty-all</artifactId>
 <version>4.1.20.Final</version>
</dependency>

2.hello world 編寫

入門的編寫一共需要4個(gè)類

netty server

netty server handler

netty client

netty client handler

2.1.netty server 端編寫

package com.zpb.netty.netty.helloWorld;
?
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
?
/**
 * @dec : netty入門
 * @Date: 2019/11/24
 * @Auther: pengbo.zhao
 * @version: 1.0
 * @demand:
 *
 *    {@link #main(String[] args)}
 *
 */
public class NettyServer {
?
 public static void main(String[] args) throws  Exception{
?
 //1.創(chuàng)建BossGroup 和 WorkerGroup
 //1.1 創(chuàng)建2個(gè)線程組
 //bossGroup只處理連接請(qǐng)求
 //workerGroup 處理客戶端的業(yè)務(wù)邏輯
 //2個(gè)都是無(wú)限循環(huán)
 EventLoopGroup bossGroup = new NioEventLoopGroup();
 EventLoopGroup workerGroup = new NioEventLoopGroup();
?
 //2.創(chuàng)建服務(wù)端的啟動(dòng)對(duì)象,可以為服務(wù)端啟動(dòng)配置一些服務(wù)參數(shù)
 ServerBootstrap bootStrap = new ServerBootstrap();
?
 //2.1使用鏈?zhǔn)骄幊虂?lái)配置服務(wù)參數(shù)
 bootStrap.group(bossGroup,workerGroup)                          //設(shè)置2個(gè)線程組
 .channel(NioServerSocketChannel.class)                 //使用NioServerSocketChannel作為服務(wù)器的通道
 .option(ChannelOption.SO_BACKLOG,128)            //設(shè)置線程等待的連接個(gè)數(shù)
 .childOption(ChannelOption.SO_KEEPALIVE,Boolean.TRUE) //設(shè)置保持活動(dòng)連接狀態(tài)
 .childHandler(new ChannelInitializer<SocketChannel>() {
 //給PipeLine設(shè)置處理器
 @Override
 protected void initChannel(SocketChannel socketChannel) throws Exception {
 //通過(guò)socketChannel得到pipeLine私痹,然后向pipeLine中添加處理的handle
 socketChannel.pipeline().addLast(new NettyServerHandle());
 }
 }); //給workerGroup 的EventLoop對(duì)應(yīng)的管道設(shè)置處理器(可以自定義/也可使用netty的)
 System.err.println("server is ready......");
?
 //啟動(dòng)服務(wù)器沸移,并綁定1個(gè)端口且同步生成一個(gè)ChannelFuture 對(duì)象
 ChannelFuture channelFuture = bootStrap.bind(8888).sync();
?
 //對(duì)關(guān)閉通道進(jìn)行監(jiān)聽(netty異步模型)
 //當(dāng)通道進(jìn)行關(guān)閉時(shí)痪伦,才會(huì)觸發(fā)這個(gè)關(guān)閉動(dòng)作
 channelFuture.channel().closeFuture().sync();
?
 }
}

2.2.netty server handler編寫

package com.zpb.netty.netty.helloWorld;
?
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.util.CharsetUtil;
?
/**
 * @dec :
 * @Date: 2019/11/24
 * @Auther: pengbo.zhao
 * @version: 1.0
 * @demand:
 */
public class NettyServerHandle extends ChannelInboundHandlerAdapter {
 /**
 * 讀取數(shù)據(jù)
 *
 * @param: 1.ChannelHandlerContext ctx:上下文對(duì)象, 含有 管道 pipeline , 通道 channel, 地址
 * @param: 2\. Object msg: 就是客戶端發(fā)送的數(shù)據(jù) 默認(rèn) Object
 */
 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 System.err.println("服務(wù)器讀取線程 " + Thread.currentThread().getName());
 System.out.println("server ctx =" + ctx);
 System.out.println("看看 channel 和 pipeline 的關(guān)系");
?
 Channel channel = ctx.channel();
 ChannelPipeline pipeline = ctx.pipeline(); //本質(zhì)是一個(gè)雙向鏈接, 出站入站
?
 //將 msg 轉(zhuǎn)成一個(gè) ByteBuf,ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.
 ByteBuf buf = (ByteBuf) msg;
 System.out.println("客戶端發(fā)送消息是:" + buf.toString(CharsetUtil.UTF_8));
 System.out.println("客戶端地址:" + channel.remoteAddress());
 }
?
 /**
 * 讀取數(shù)據(jù)完成后
 *
 * @param:
 * @return:
 * @auther:
 * @date:
 */
 @Override
 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
 //writeAndFlush 是 write + flush
 //將數(shù)據(jù)寫入到緩存雹锣,并刷新
 //一般講网沾,我們對(duì)這個(gè)發(fā)送的數(shù)據(jù)進(jìn)行編碼
 ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客戶端~(>^ω^<)喵", CharsetUtil.UTF_8));
 }
?
 //處理異常, 一般是需要關(guān)閉通道
 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
 ctx.close();
 }
}

2.3.netty client端編寫

package com.zpb.netty.netty.helloWorld;
?
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
?
?
/**
 * @dec :
 * @Date: 2019/11/24
 * @Auther: pengbo.zhao
 * @version: 1.0
 * @demand:
 */
public class NettyClient {
?
 public static void main(String[] args) throws Exception {
?
 //1.客戶端定義一個(gè)循環(huán)事件組
 EventLoopGroup group = new NioEventLoopGroup();
?
 try {
?
 //2.創(chuàng)建客戶端啟動(dòng)對(duì)象
 Bootstrap bootstrap = new Bootstrap();
 bootstrap.group(group)                      //設(shè)置線程組
 .channel(NioSocketChannel.class)   //設(shè)置客戶端通道實(shí)現(xiàn)類
 .handler(new ChannelInitializer<SocketChannel>() {
 @Override
 protected void initChannel(SocketChannel socketChannel) throws Exception {
 socketChannel.pipeline().addLast(new NettyClientHandle());
 }
 });
 System.err.println("client is ready......");
?
 //3.啟動(dòng)客戶端去連接服務(wù)端
 ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).sync();
?
 //4.設(shè)置通道關(guān)閉監(jiān)聽(當(dāng)監(jiān)聽到通道關(guān)閉時(shí),關(guān)閉client)
 channelFuture.channel().closeFuture().sync();
 } finally {
 group.shutdownGracefully();
 }
 }
}

2.4.netty client handler端編寫

package com.zpb.netty.netty.helloWorld;
?
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
?
/**
 * @dec :
 * @Date: 2019/11/24
 * @Auther: pengbo.zhao
 * @version: 1.0
 * @demand:
 */
public class NettyClientHandle extends ChannelInboundHandlerAdapter{
?
 //如果client 端服務(wù)啟動(dòng)完成后
 @Override
 public void channelActive(ChannelHandlerContext ctx) throws Exception {
?
 System.err.println("client "+ctx);
 ctx.writeAndFlush(Unpooled.copiedBuffer("hello,netty server...",CharsetUtil.UTF_8));
 }
?
 //當(dāng)通道有讀事件時(shí)
 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
?
 ByteBuf byteBuf = (ByteBuf) msg;
 System.err.println("服務(wù)器端回復(fù)消息:"+byteBuf.toString(CharsetUtil.UTF_8));
 System.err.println("服務(wù)器端地址是:"+ctx.channel().remoteAddress());
 }
?
 //當(dāng)通道有異常時(shí)
?
 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
 cause.printStackTrace();
 ctx.close();
 }
}

五.Netty三種任務(wù)隊(duì)列的使用

當(dāng)我們?cè)谔幚淼膆andle中如果出現(xiàn)了阻塞的情況蕊爵,或者處理業(yè)務(wù)邏輯比較耗時(shí)辉哥,我們不能讓程序處于阻塞,

當(dāng)有客戶端請(qǐng)求時(shí)攒射,我們想讓程序定時(shí)的去執(zhí)行業(yè)務(wù)邏輯醋旦,

當(dāng)需要對(duì)一些用戶需要進(jìn)行推送活動(dòng)時(shí),根據(jù)用戶標(biāo)識(shí)会放,找到對(duì)應(yīng)的 Channel 引用饲齐,向該用戶推送特定消息時(shí)

可以采用以下三種任務(wù)隊(duì)列:

1.提交到execute(Runnable command)中時(shí)

ctx.channel().eventLoop().execute(new Runnable() { })
?
業(yè)務(wù)邏輯交給線程去處理,線程不會(huì)阻塞在這里,而是直接返回咧最,直到有數(shù)據(jù)才返回給客戶端捂人,如果有多個(gè)線程runnable需要處理,那么只能等上一個(gè)處理完才會(huì)處理下一個(gè)矢沿,(假如第1個(gè)任務(wù)需要10S,第2個(gè)需要20s,執(zhí)行完共需30S)

2.提交到 scheduledTaskQueue中

schedule(Runnable command, long delay, TimeUnit unit)
?
①Runnable command:執(zhí)行業(yè)務(wù)邏輯處理的線程
?
② long delay:定時(shí)時(shí)長(zhǎng)
?
③TimeUnit unit:定時(shí)類型
?
業(yè)務(wù)邏輯交給定時(shí)線程去處理滥搭。

3.通過(guò)傳輸?shù)膬?nèi)容的標(biāo)識(shí)

在解碼客戶端發(fā)送的內(nèi)容中,讀取到客戶端的特殊標(biāo)識(shí)捣鲸,利用這個(gè)標(biāo)識(shí)來(lái)進(jìn)行推送消息處理瑟匆,這個(gè)在粘包、拆包中進(jìn)行說(shuō)明
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末栽惶,一起剝皮案震驚了整個(gè)濱河市愁溜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌外厂,老刑警劉巖冕象,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異酣衷,居然都是意外死亡交惯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門穿仪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)席爽,“玉大人,你說(shuō)我怎么就攤上這事啊片≈欢停” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵紫谷,是天一觀的道長(zhǎng)齐饮。 經(jīng)常有香客問(wèn)我捐寥,道長(zhǎng),這世上最難降的妖魔是什么祖驱? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任握恳,我火速辦了婚禮,結(jié)果婚禮上捺僻,老公的妹妹穿的比我還像新娘乡洼。我一直安慰自己,他們只是感情好匕坯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布束昵。 她就那樣靜靜地躺著,像睡著了一般葛峻。 火紅的嫁衣襯著肌膚如雪锹雏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天术奖,我揣著相機(jī)與錄音礁遵,去河邊找鬼。 笑死腰耙,一個(gè)胖子當(dāng)著我的面吹牛榛丢,可吹牛的內(nèi)容都是我干的铲球。 我是一名探鬼主播挺庞,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼稼病!你這毒婦竟也來(lái)了选侨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤然走,失蹤者是張志新(化名)和其女友劉穎援制,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芍瑞,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晨仑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拆檬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洪己。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖竟贯,靈堂內(nèi)的尸體忽然破棺而出答捕,到底是詐尸還是另有隱情,我是刑警寧澤屑那,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布拱镐,位于F島的核電站艘款,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沃琅。R本人自食惡果不足惜哗咆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望益眉。 院中可真熱鬧岳枷,春花似錦、人聲如沸呜叫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)朱庆。三九已至盛泡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娱颊,已是汗流浹背傲诵。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留箱硕,地道東北人拴竹。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像剧罩,于是被迫代替她去往敵國(guó)和親栓拜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容