一篇文章入門Netty

爬蜥發表於2018-04-25

什麼是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狀態發生變化的時得到通知,執行狀態變化相應的任務,響應結束後,選擇器重置,再次重複這個過程

一篇文章入門Netty

  • Epoll:只能在linux系統中使用,高負載情況下,效能優於JDK的NIO。 大致原理是:I/O多路複用【通過一個檔案符管理多個檔案描述符(針對epoll可以看做無限制)】,當一個檔案描述符可讀或者可寫的時候,收到通知,立馬執行【邊沿觸發】
  • OIO(舊的阻塞IO):基於java.net的阻塞IO。 大致原理是:單執行緒監聽一個socket,任何I/O操作在任意的時間節點上都有可能被阻塞
  • Local:同一個JVM上執行的客戶端和服務端之間的通訊
  • Embedded:使用channel,但不需要真正意義上的網路傳輸,一般用於測試

附錄

Netty In Action

相關文章