• Netty:初识Netty


    前文总结了NIO的内容,有了NIO的一些基础之后,我们就可以来看下Netty。Netty是Java领域的高性能网络传输框架,RPC的技术核心就是网络传输和序列化,所以Netty给予了RPC在网络传输领域巨大的支持。 

    一个简单的Netty代码实现

    网络传输基于的是TCP协议,所以会有服务端和客户端之分,而Netty是网络传输框架,所以一个完整的Netty代码至少是有服务端和客户端的。本文代码基于Netty4.1.15。

    服务端:

    public class DemoServer {
        public static void main(String[] args) throws Exception {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(bossGroup,workerGroup).
                        channel(NioServerSocketChannel.class).
                        childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                                ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
                                ch.pipeline().addLast(new DemoServerHandler());
                            }
                        });
    
                ChannelFuture future = serverBootstrap.bind(8899).sync();
                future.channel().closeFuture().sync();
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    public class DemoServerHandler extends SimpleChannelInboundHandler<String> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println("" + ctx.channel().remoteAddress() + "," + msg);
            ctx.channel().writeAndFlush("from server" + UUID.randomUUID());
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

    客户端:

    public class DemoClient {
        public static void main(String[] args) throws Exception{
            EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    
            try{
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                        ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
                        ch.pipeline().addLast(new DemoClientHandler());
                    }
                });
    
                ChannelFuture channelFuture = bootstrap.connect("localhost",8899).sync();
                channelFuture.channel().closeFuture().sync();
    
    
            }finally {
                eventLoopGroup.shutdownGracefully();
            }
        }
    }
    public class DemoClientHandler extends SimpleChannelInboundHandler<String> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println("" + ctx.channel().remoteAddress());
            System.out.println("client output:" + msg);
            ctx.writeAndFlush("from client" + LocalDateTime.now());
        }
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            String msg = "来自客户端的问候!";
            ctx.writeAndFlush(msg);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

    来看下这个简单的代码做了什么:

    1、服务端启动后,客户端先向服务端发起连接建立;

    2、连接建立后,触发客户端的channelActive方法,该方法向服务端发出了一条信息,这条信息默认在网络中是会转成字节的形式来传输的,因为TCP的数据传输都是基于字节的,这个过程也叫做编码;

    3、服务端收到信息后,会被服务端的Handler,其实就是StringDecoder先做处理,从字节变成了字符,这个过程也叫做解码;

    4、此时对于DemoServerHandler来说,信息已经变成了符合自己的String类型,所以channelRead0方法会被调用,输出信息,同时向客户端发出信息,信息又会转成字节的信息传向客户端;

    5、客户端收到信息后,会被客户端解码成字符,触发客户端的channelRead0方法,输出客户端地址和收到的信息,再向服务端发送时间戳;

    6、循环往复上述3-5,死循环;

    模块化

    从上述这个简单的DEMO中,我们可以提取出Netty的核心模块:

    1、Channel、EventLoop、ChannelFuture

    Channel接口:基本的IO操作(bind()/connect()/read()/write())依赖于底层网络传输所提供的原语。在我们这个DEMO中,能看到NioServerSocketChannel和NioSocketChannel,NioServerSocketChannel使用基于NIO选择器的实现来接受新连接,NioSocketChannel使用基于NIO选择器的实现来建立和处理新连接。

    EventLoop接口:EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。EventLoop是协调设计的一部分,采用了两个基本的API:并发和网络编程。在我们这个DEMO中,能看到NioEventLoop,NioEventLoop就是一个Reactor,是整个Netty的一个核心。

    ChannelFuture接口:Netty中的所有的IO操作都是异步的,因为一个操作可能不会立即返回,所以我们需要一种用于在之后某个时间点确定其结果的方法。为此,Netty提供了ChannelFuture接口,其addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成是得到通知。

    2、ChannelHandler、ChannelPipeline

    ChannelHandler接口:从应用程序开发人员的角度来看,ChannelHandler是Netty的主要组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器,因为ChannelHandler的方法是由事件来触发的。在我们这个DEMO中,DemoClientHandler、DemoServerHandler就是两个自定义的ChannelHandler,DemoClientHandler在连接一建立的时候,就触发了channelActive方法,然后DemoServerHandler在channelRead0方法中读取了其传输的信息。

    ChannelPipeline接口:ChannelPipeline为ChannelHandler链提供了容器,并定义了用于该链上传播入站和出站事件流的API。当Channel被创建时,它会被自动的分配到它所专属的ChannelPipeline。在我们的DEMO中,一个ChannelInitializer的实例被注册到ServerBootStrap或者BootStrap,当它的initChannel方法被调用的时候,ChannelInitializer将在ChannelPipeline中安装一组自定义的ChannelHandler,最后ChannelInitializer将它自己从ChannelPipeline中移除。

    3、ByteBuf

    网络数据的基本单位是字节,NIO提供了ByteBuffer作为网络数据的字节容器,但是ByteBuffer本身设计并不优雅,使用繁琐,Netty使用ByteBuf来替代ByteBuffer,在我们的DEMO中,不能直接看到ByteBuf,但是在Netty提供的内置编解码器StringDecoder/StringEncoder中,操作的对象就是ByteBuf。

    4、ServerBootStrap、BootStrap

    ServerBootStrap和BootStrap是一个非常抽象的概念。ServerBootStrap是Netty创建服务器的辅助类,负责封装服务启动的一系列操作。和ServerBootStrap一样,Bootstrap也是封装客户端向服务端发送请求的一系列操作。

    5、Codec

    通过Netty发送和接收一个消息的时候,就会发生一次数据转换,入站消息会被解码,也就是从字节转换为原本的形式,如果是出站消息,就会从一种形式变成字节,这个就是编码,编解码的根本原因就是因为网络数据就是一系列的字节。在我们的DEMO中,StringEncoder和StringDecoder就是编解码器。

    最后总结一下:

    1、通过一个简单的DEMO,介绍了一下Netty,站在应用的角度看了下Netty是如何运行的;

    2、从DEMO中提取出Netty的重要的模块,简单介绍一下各个模块,在后续的文章中将详细介绍每个模块组件。

    参考资料:

    https://netty.io/wiki/user-guide-for-4.x.html

  • 相关阅读:
    plsql developer中各个window的作用【转】
    回忆java输入输出流,走出误区
    JDBC中的元数据
    对于Oracle、mysql和sql server中的部分不同理解
    我对数据库事务的理解(MYSQL中)
    关于mysql的备份和恢复
    mysql触发器学习
    mysql存储过程学习
    JavaScript位运算符
    【JavaScript】数组随机排序 之 Fisher–Yates 洗牌算法
  • 原文地址:https://www.cnblogs.com/iou123lg/p/12615937.html
Copyright © 2020-2023  润新知