图解Netty5.0

前言:

如果知识不能够在短时间内应用那么就很难记住,换句话说没有应用的知识都是你没有掌握的!图解系列相当于在大脑中建立了一个模型,这个模型和头脑中的原有知识可以形成高速公路,而这样的高速公路越多就越容易找到这个模型,这样以后真正应用的时候可以快速遍历知识体系而做到有的放矢。

言归正传,上一篇我们回顾了一下Java NIO,使用环形传送带和管道检查点以及管道形象化了这些抽象的概念。然而,Java NIO的操作步骤着实复杂,简单写一个程序都需要特别多的步骤。我们还没有考虑到可靠性问题,即超时处理、心跳检测、网络故障处理等,想要写一个完整的高性能Java NIO,这些模块我们都要自己实现。Netty NIO恰好实现了这些模块,免去了我们重复造*的麻烦

1:Netty结构和处理流程:

高清图下载:Netty结构和流程高清图

图解Netty5.0
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各个组件的详细使用方法。欢迎大家提出宝贵意见!