图解Netty5.0
前言:
如果知识不能够在短时间内应用那么就很难记住,换句话说没有应用的知识都是你没有掌握的!图解系列相当于在大脑中建立了一个模型,这个模型和头脑中的原有知识可以形成高速公路,而这样的高速公路越多就越容易找到这个模型,这样以后真正应用的时候可以快速遍历知识体系而做到有的放矢。
言归正传,上一篇我们回顾了一下Java NIO,使用环形传送带和管道检查点以及管道形象化了这些抽象的概念。然而,Java NIO的操作步骤着实复杂,简单写一个程序都需要特别多的步骤。我们还没有考虑到可靠性问题,即超时处理、心跳检测、网络故障处理等,想要写一个完整的高性能Java NIO,这些模块我们都要自己实现。Netty NIO恰好实现了这些模块,免去了我们重复造轮子的麻烦
1:Netty结构和处理流程:
高清图下载:Netty结构和流程高清图

A:从外到内阐述结构:
内存:计算机内存,4g 8g 16g等等
ByteBuf:字节缓冲区。想想JavaNIO里面的ByteBuffer效果一样,操作更加人性化了!ByteBuf在内存中分为直接内存,堆内存和混合内存,其中堆内存较常用!
客户端:想象成一个客户。包含一个工作线程组,绑定IP和端口后就可以收发TCP等协议的消息了
服务端:想象成一个工厂。
a:包含老板和工人两个线程组,老板负责监听分派任务,工人们负责去车间干活(Java NIO2即AIO里,我们不可以指定工人线程,而是由Java内部自动从线程池里取出线程去工作)。
b:流水线:所有代加工的材料(收到的字节数组)都需要按照顺序经过流水线上的每个车间处理加工。
b1:车间:Handler,车间实际上由工人线程们操控,是真正干活儿的部分,我们的代码一般都写到车间里
b2:车间流水线连接器:联通车间和流水线,让车间有调用下一个车间的能力,实际上这个连接器还连接着通道,可以从通道读写
启动器:想象成公司董事会。分为客户端启动器和服务端启动器,想想Java NIO,每次启动都需要各种open操作,这里直接一个Bootstrap就可以让工厂运行了
B:现实世界映射:
Server公司(服务端)打算成立一个项目组生产服装,可以接收Client(客户端)这样的公司的各种订单
首先,S公司的董事会(ServerBootstrap服务端启动器)召开董事会,决定接手项目,并分配了几个包工头(boss = new NioEventLoopGroup())和一群工人(workers = new NioEventLoopGroup())来处理这个项目。项目开工之前可以先设定一下工厂的一些规则,比如同时接受多少个C这样类型的公司的原材料(backlog),由哪些车间进行生产(childHandler)等
然后,项目按照预定计划开工(bootstrap.group(boss,workers))
然后,boss开始接收到C公司订单,然后把原材料扔到生产线上,由工厂派出worker线程去生产线(ChannelPipeLine)上的车间(ChannelHander)工作
然后,某个车间工作完成之后可以把产品(response)通过连接器(ChannelHanlerContext)放回到管道(Channel.write)里交给客户端,也可以通过连接器把产品交给下一个车间(ctx.fireNext)。
最后,所有工作都完成,优雅的让工人和老板退出生产线(shutdownGracefully()),而不是直接赶走。
C:代码体现:(服务端)
public class Server {
private int port;
public Server(int port) {
this.port = port;
}
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于处理服务器端接收客户端连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 进行网络通信(读写)
try {
ServerBootstrap bootstrap = new ServerBootstrap(); // 辅助工具类,用于服务器通道的一系列配置
bootstrap.group(bossGroup, workerGroup) // 绑定两个线程组
.channel(NioServerSocketChannel.class) // 指定NIO的模式
.childHandler(new ChannelInitializer() { // 配置具体的数据处理方式
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ServerHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128) // 设置TCP缓冲区
.option(ChannelOption.SO_SNDBUF, 32 * 1024) // 设置发送数据缓冲大小
.option(ChannelOption.SO_RCVBUF, 32 * 1024) // 设置接受数据缓冲大小
.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:总结:
本篇博客意在让大家对Netty有一个结构和流程上的认识,基于本博客可以快速了解Netty两端的数据交互流程,以及Netty的重要工作组件。后续还会继续为大家奉上Netty各个组件的详细使用方法。欢迎大家提出宝贵意见!