Netty 初识

作者: Always_July | 来源:发表于2021-04-01 11:54 被阅读0次

    Netty 是什么

    Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.

    'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.

    官网介绍是:
    Netty是一个NIO客户端服务器框架,可以快速、轻松地开发网络应用程序,如协议服务器和客户端。它极大地简化和简化了网络编程,如TCP和UDP套接字服务器。

    介绍中提到了NIO这个关键字,英文全称是Non blocking IO,非阻塞IO。学习过Java Socket编程的人应该会了解到阻塞IO。

    Linux 网络I/O 模型简介

    阻塞I/O

    image.png

    非阻塞I/O

    image.png

    轮询检查状态

    I/O 复用

    Linux 提供select/poll,进程将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样select/poll 可以帮我们侦测多个fd是否处于就绪状态。select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。Linux提供了一个epoll系统调用,epoll使用基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。

    image.png
    select/poll 缺点
    1. 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
    2. 每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
    3. select支持的文件描述符数量太少了,默认是1024。
    epoll
    1. 内核与用户空间共享一块内存。
    2. 通过回调解决遍历问题。
    3. fd没有限制,可以支撑10万连接。

    信号驱动 I/O

    首先开启套接口信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主函数处理数据。


    image.png

    异步I/O

    告知内核启动某个操作,并让内核再整个操作完成后(包含将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是:信号驱动I/O由内核通知我们何时可以开始一个I/O操作;异步I/O通知我们I/O操作何时已完成。


    image.png

    Netty 的优势

    quick and easy

    Design

    • Unified API for various transport types-blocking and non-blocking socket
    • Flexible to use
    • Simple but powerful thread-model
    • True connectionless datagram socket support
    • Chaining of logics to make reuse easy

    Easy to Use

    • Well-documented Javadoc and plenty of examples provided
    • No additional dependencies except JDK 1.6 (or above). Some features are supported only in Java 1.7 and above. Other features may have other dependencies, but these are optional

    Perfermance

    • Better throughput; lower latency than core Java APIs
    • Less resource consumption because of pooling and reuse
    • Minimized unnecessary memory copy

    Robobustness

    • No more OutOfMemoryError due to fast, slow, or overloaded connection.
    • No more unfair read/write ratio often found in a NIO application in high-speed

    Security

    • Complete SSL/TSL And Start TLS support
    • Runs in a restricted environment such as Applet or OSGI

    Community

    • Release early,release often
    • Active

    Netty Demo

    maven dependency

        <dependencies>
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-all</artifactId>
                <version>4.1.32.Final</version>
            </dependency>
        </dependencies>
    

    Server

    import io.netty.bootstrap.ServerBootstrap;
    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.NioServerSocketChannel;
    
    import java.net.InetSocketAddress;
    
    
    public class EchoServer {
        private final int port;
    
        public EchoServer(int port) {
            this.port = port;
        }
        public void start() throws InterruptedException {
            EventLoopGroup group = new NioEventLoopGroup();
            try{
                ServerBootstrap b = new ServerBootstrap();
                b.group(group)
                        .channel(NioServerSocketChannel.class)
                        .localAddress(new InetSocketAddress(port))
                        // when a connection is accepted,which create a child channel.
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch){
                                // channelPipeline hold all off the different  ChannelHandlers
                                ch.pipeline().addLast(new EchoServerHandler());
                            }
                        });
                ChannelFuture f = b.bind().sync();
                System.out.println(EchoServer.class.getName() +" started and listen on " + f.channel().localAddress());
                // sync() method will cause this to block until the server is bound
                f.channel().closeFuture().sync();
            }catch(Exception e){
                e.getMessage();
            }finally {
                group.shutdownGracefully().sync();
            }
        }
    
        public static void main(String[] args) throws Exception {
            new EchoServer(9999).start();
        }
    }
    

    EchoServerHandler

    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.util.CharsetUtil;
    
    public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ByteBuf in = (ByteBuf) msg;
            System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
            ctx.write(in);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx)  {
            //  Flush all previous written messages (that are pending) to the remote peer, and close the channel after the operation is complete.
            ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                    .addListener(ChannelFutureListener.CLOSE);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)  {
            cause.printStackTrace();
            ctx.close();
        }
    }
    
    

    Client

    
    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;
    
    import java.net.InetSocketAddress;
    
    
    public class EchoClient {
        private final String host;
        private final int port;
    
        public EchoClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public void start() throws InterruptedException {
            EventLoopGroup group = new NioEventLoopGroup();
            try{
                Bootstrap bootstrap = new Bootstrap();
    
                bootstrap.group(group)
                        .channel(NioSocketChannel.class)
                        .remoteAddress(new InetSocketAddress(host,port))
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch){
                                ch.pipeline().addLast(new EchoClientHandler());
                            }
                        });
                ChannelFuture f = bootstrap.connect().sync();
                f.channel().closeFuture().sync();
            }catch(Exception e){
                e.getMessage();
            }finally {
                group.shutdownGracefully().sync();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            EchoClient echoClient = new EchoClient("127.0.0.1",9999);
            echoClient.start();
        }
    }
    
    

    EchoClientHandler

    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.ByteBufUtil;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandler;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.util.CharsetUtil;
    
    
    public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            // must Flush
            ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
        }
    
        @Override
        public void channelRead0(ChannelHandlerContext ctx,
                                 ByteBuf in) {
           System.out.println("Client received: " + ByteBufUtil
                    .hexDump(in.readBytes(in.readableBytes())));
        }
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx,
                                    Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
    
    

    Server console

    com.daxiyan.study.netty.chapter2.EchoServer started and listen on /0:0:0:0:0:0:0:0:9999
    Server received: Netty rocks!
    

    Client console

    Client received: 4e6574747920726f636b7321
    

    参考文档及书籍

    Netty_ Home
    Netty权威指南(第2版)
    Netty In Action

    相关文章

      网友评论

        本文标题:Netty 初识

        本文链接:https://www.haomeiwen.com/subject/ovhtkltx.html