一篇文章入门Netty

2,480 阅读4分钟

什么是Netty?Netty是一个框架。或者说是一个工具集。封装了网络编程方面java的API。

Netty有哪些核心组件?

  1. Channel:java nio的基本构造,代表一个实体(硬件设备、文件、网路套接字等)的开放连接。用作传入(入站)或者传出(出站)数据

  2. 回调:封装操作完成后需要做的事情的方法

  3. Future: 提供异步操作的结果访问

  4. 事件和ChannelHandler:程序运行过程中发生的事情抽象(事件)交给ChannerHandler来处理,channelHandler类似于为了响应特定事件而执行的回调

Netty的Hello world

server

public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }
    public static void main(String[]args)throws Exception{
        new EchoServer(8888).start();
    }
    public void start() throws Exception{
        final EchoServerHandler handler = new EchoServerHandler();
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            ServerBootstrap b = new ServerBootstrap();
            b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(handler);
                        }
                    });
            ChannelFuture f = b.bind().sync();
            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully().sync();
        }
    }
}

serverHandler

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        System.out.printf("Server get:"+in.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将目前暂存于ChannelOutboundBuffer中的消息在下一次flush或者writeAndFlush的时候冲刷到远程并关闭这个channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
       cause.printStackTrace();
       ctx.close();
    }
}

client

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 Exception{
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)//指定NIO的传输方式
                    .remoteAddress(new InetSocketAddress(host,port))//指定远程地址
                    .handler(new ChannelInitializer<SocketChannel>() {//向channel的pipeline添加handler
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());//channelHander交给pipeline
                        }
                    });
            ChannelFuture f = b.connect().sync();//连接到远程节点,阻塞直到连接完成
            System.out.println("wait");
            f.channel().closeFuture().sync();//阻塞直到连接关闭
            System.out.println("over");
        }finally {
            System.out.println("shutdown");
            group.shutdownGracefully().sync();//关闭线程池并且释放资源
        }
    }
    public static void main(String[]args) throws Exception{
        new EchoClient("localhost",8888).start();
    }
}

clientHandler

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello world",CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("Client get:"+msg.toString(CharsetUtil.UTF_8));
    }
}

Channel、Eventloop 、ChannelFuture 是什么关系?

它们是netty对网络的抽象组件。

Channel本身用来提供基本的IO操作(bind/connect/read/write),连接建立之后通过EventLoop来处理所发生的事情,它们之间的对应关系是1个channel只能有1个EventLoop,但是一个EventLoop对应多个Channel。EventLoop整个生命周期中只和一个Thread绑定,对应来讲一个Channel中的所有IO操作也都是在一个线程中执行的。执行的结果则是通过ChannelFuture来获取

Channel有什么特征?

  1. channel是传输API的核心,每一个都会被分配一个ChannelPipeline和ChannelConfig,ChannelConfig包含了Channel的所有配置,并且支持热更新
  2. 每个channel都是独一无二的,channel之间的顺序通过Comparable来实现比较
  3. channel的实现是线程安全的

pipeline和handler是什么关系?

pipeline用来管理数据流,handler(client和server)是用来处理逻辑。

ChannelHandler接收事件,对事件进行逻辑处理,并将数据传给链(多个按照一定顺序定义的ChannelHandler)中的下一个ChannelHandler。ChannelPipLine就是ChannelHandler的编排顺序(二者建立关系的时机是ChannelInitializer执行initChannel的时候ChannelPipline组装自定义的channelHandler)。出于方便ChannelHandler会提供一些适配实现类让用户专注于处理自己的业务逻辑。

bootstrap是什么?

有两种类型,客户端(简称BootStrap)和服务端(简称ServerBootStrap)。区别有两点

  1. BootStrap用来连接远程节点,ServerBootStrap则是绑定本地端口监听连接
  2. 客户端需要一个EventLoopGroup(存多个EventLoop的东西),服务端需要两个(可以是同一个实例),一个用来绑定到本地端口代表已经监听了(分配一个EventLoop),另一个则是处理客户端的连接(再分配一个EventLoop)

Netty支持的传输类型有哪些?

  • NIO:基于java api的selector机制实现。 大致原理是:当channel状态发生变化的时得到通知,执行状态变化相应的任务,响应结束后,选择器重置,再次重复这个过程

  • Epoll:只能在linux系统中使用,高负载情况下,性能优于JDK的NIO。 大致原理是:I/O多路复用【通过一个文件符管理多个文件描述符(针对epoll可以看做无限制)】,当一个文件描述符可读或者可写的时候,收到通知,立马执行【边沿触发】
  • OIO(旧的阻塞IO):基于java.net的阻塞IO。 大致原理是:单线程监听一个socket,任何I/O操作在任意的时间节点上都有可能被阻塞
  • Local:同一个JVM上运行的客户端和服务端之间的通信
  • Embedded:使用channel,但不需要真正意义上的网络传输,一般用于测试

附录

Netty In Action