前言:
如果知識(shí)不能夠在短時(shí)間內(nèi)應(yīng)用那么就很難記住席怪,換句話說沒有應(yīng)用的知識(shí)都是你沒有掌握的生兆!圖解系列相當(dāng)于在大腦中建立了一個(gè)模型苦始,這個(gè)模型和頭腦中的原有知識(shí)可以形成高速公路调榄,而這樣的高速公路越多就越容易找到這個(gè)模型维咸,這樣以后真正應(yīng)用的時(shí)候可以快速遍歷知識(shí)體系而做到有的放矢壤追。
言歸正傳撩轰,上一篇我們回顧了一下Java NIO坟乾,使用環(huán)形傳送帶和管道檢查點(diǎn)以及管道形象化了這些抽象的概念。然而枪孩,Java NIO的操作步驟著實(shí)復(fù)雜横蜒,簡(jiǎn)單寫一個(gè)程序都需要特別多的步驟。我們還沒有考慮到可靠性問題销凑,即超時(shí)處理、心跳檢測(cè)仅炊、網(wǎng)絡(luò)故障處理等斗幼,想要寫一個(gè)完整的高性能Java NIO,這些模塊我們都要自己實(shí)現(xiàn)抚垄。Netty NIO恰好實(shí)現(xiàn)了這些模塊蜕窿,免去了我們重復(fù)造輪子的麻煩
1:Netty結(jié)構(gòu)和處理流程:
高清圖下載:Netty結(jié)構(gòu)和流程高清圖
A:從外到內(nèi)闡述結(jié)構(gòu):
內(nèi)存:計(jì)算機(jī)內(nèi)存,4g 8g 16g等等
ByteBuf:字節(jié)緩沖區(qū)呆馁。想想JavaNIO里面的ByteBuffer效果一樣桐经,操作更加人性化了!ByteBuf在內(nèi)存中分為直接內(nèi)存浙滤,堆內(nèi)存和混合內(nèi)存阴挣,其中堆內(nèi)存較常用!
客戶端:想象成一個(gè)客戶纺腊。包含一個(gè)工作線程組畔咧,綁定IP和端口后就可以收發(fā)TCP等協(xié)議的消息了
服務(wù)端:想象成一個(gè)工廠。
????????a:包含老板和工人兩個(gè)線程組揖膜,老板負(fù)責(zé)監(jiān)聽分派任務(wù)誓沸,工人們負(fù)責(zé)去車間干活(Java NIO2即AIO里,我們不可以指定工人線程壹粟,而是由Java內(nèi)部自動(dòng)從線程池里取出線程去工作)拜隧。
????????b:流水線:所有代加工的材料(收到的字節(jié)數(shù)組)都需要按照順序經(jīng)過流水線上的每個(gè)車間處理加工。
? ? ? ????????b1:車間:Handler趁仙,車間實(shí)際上由工人線程們操控洪添,是真正干活兒的部分,我們的代碼一般都寫到車間里
? ? ? ????????b2:車間流水線連接器:聯(lián)通車間和流水線幸撕,讓車間有調(diào)用下一個(gè)車間的能力薇组,實(shí)際上這個(gè)連接器還連接著通道,可以從通道讀寫
啟動(dòng)器:想象成公司董事會(huì)坐儿。分為客戶端啟動(dòng)器和服務(wù)端啟動(dòng)器律胀,想想Java NIO宋光,每次啟動(dòng)都需要各種open操作,這里直接一個(gè)Bootstrap就可以讓工廠運(yùn)行了
B:現(xiàn)實(shí)世界映射:
Server公司(服務(wù)端)打算成立一個(gè)項(xiàng)目組生產(chǎn)服裝炭菌,可以接收Client(客戶端)這樣的公司的各種訂單
首先罪佳,S公司的董事會(huì)(ServerBootstrap服務(wù)端啟動(dòng)器)召開董事會(huì),決定接手項(xiàng)目黑低,并分配了幾個(gè)包工頭(boss = new NioEventLoopGroup())和一群工人(workers = new NioEventLoopGroup())來處理這個(gè)項(xiàng)目赘艳。項(xiàng)目開工之前可以先設(shè)定一下工廠的一些規(guī)則,比如同時(shí)接受多少個(gè)C這樣類型的公司的原材料(backlog)克握,由哪些車間進(jìn)行生產(chǎn)(childHandler)等
然后蕾管,項(xiàng)目按照預(yù)定計(jì)劃開工(bootstrap.group(boss,workers))
然后,boss開始接收到C公司訂單菩暗,然后把原材料扔到生產(chǎn)線上掰曾,由工廠派出worker線程去生產(chǎn)線(ChannelPipeLine)上的車間(ChannelHander)工作
然后,某個(gè)車間工作完成之后可以把產(chǎn)品(response)通過連接器(ChannelHanlerContext)放回到管道(Channel.write)里交給客戶端停团,也可以通過連接器把產(chǎn)品交給下一個(gè)車間(ctx.fireNext)旷坦。
最后,所有工作都完成佑稠,優(yōu)雅的讓工人和老板退出生產(chǎn)線(shutdownGracefully())秒梅,而不是直接趕走。
C:代碼體現(xiàn):(服務(wù)端)
public class Server {
private int port;
public Server(int port) {
? ? ? ?this.port = port;
}
public void run() {
????????EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于處理服務(wù)器端接收客戶端連接
????????EventLoopGroup workerGroup = new NioEventLoopGroup(); // 進(jìn)行網(wǎng)絡(luò)通信(讀寫)
????????try {
????????????????ServerBootstrap bootstrap = new ServerBootstrap(); // 輔助工具類舌胶,用于服務(wù)器通道的一系列配置
????????????????bootstrap.group(bossGroup, workerGroup) // 綁定兩個(gè)線程組
? ? ? ? ? ? ? ? .channel(NioServerSocketChannel.class) // 指定NIO的模式
????????????????.childHandler(new ChannelInitializer() { // 配置具體的數(shù)據(jù)處理方式
? ? ? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? ? ? protected void initChannel(SocketChannel socketChannel) throws Exception {
? ? ? ? ? ? ? ? ? ? ? ? ? ? socketChannel.pipeline().addLast(new ServerHandler());
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }).option(ChannelOption.SO_BACKLOG, 128) // 設(shè)置TCP緩沖區(qū)
? ? ? ? ? ? ? ? ? ? .option(ChannelOption.SO_SNDBUF, 32 * 1024) // 設(shè)置發(fā)送數(shù)據(jù)緩沖大小
? ? ? ? ? ? ? ? ? ? .option(ChannelOption.SO_RCVBUF, 32 * 1024) // 設(shè)置接受數(shù)據(jù)緩沖大小
? ? ? ? ? ? ? ? ? ? .childOption(ChannelOption.SO_KEEPALIVE, true); // 保持連接
? ? ? ? ? ? ChannelFuture future = bootstrap.bind(port).sync();
? ? ? ? ? ? future.channel().closeFuture().sync();
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? workerGroup.shutdownGracefully();
? ? ? ? ? ? bossGroup.shutdownGracefully();
? ? ? ? }
? ? }
? ? public static void main(String[] args) {
? ? ? ? new Server(8379).run();
? ? }
}
public class ServerHandler extends ChannelHandlerAdapter {
? @Override
? public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
????????ByteBuf buf = (ByteBuf)msg;
????????byte[] data = new byte[buf.readableBytes()];
????????buf.readBytes(data);
????????String request = new String(data, "utf-8");
????????System.out.println("Server: " + request); //寫給客戶端
????????ctx.writeAndFlush(Unpooled.copiedBuffer("888".getBytes())); //.addListener(ChannelFutureListener.CLOSE);
????}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
????cause.printStackTrace(); ctx.close();
????}
}
2:總結(jié):
????????本篇博客意在讓大家對(duì)Netty有一個(gè)結(jié)構(gòu)和流程上的認(rèn)識(shí)捆蜀,基于本博客可以快速了解Netty兩端的數(shù)據(jù)交互流程,以及Netty的重要工作組件辆琅。后續(xù)還會(huì)繼續(xù)為大家奉上Netty各個(gè)組件的詳細(xì)使用方法漱办。歡迎大家提出寶貴意見!