netty实战

  Netty是一款异步的事件驱动的网络应用程序框架,支持快速开发可维护的高性能面向协议的服务器和客户端

  1、pom依赖

<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.32.Final</version>
</dependency>

2、实战:客户端与服务端双向通信

     客户端/服务端连接读写逻辑处理均是启动阶段通过给逻辑处理链 Pipeline 添加逻辑处理器实现连接数据的读写逻辑.

     客户端连接成功回调逻辑处理器channelActive()方法,客户端/服务端接收连接数据调用channelRead()方法.
     写数据调用writeAndFlush()方法,客户端与服务端交互的二进制数据传输载体为ByteBuf,ByteBuf通过连接的内存管理器创建即ctx.alloc().buffer(),通过writeBytes()方法将字节数据填充到ByteBuf 写到对端.
Server端
@Data
public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoServer(8089).start();
    }

    public void start() throws InterruptedException {
        final EchoServerHandler echoServerHandler = new EchoServerHandler();
        //创建EventLoopGroup,处理事件
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker)
                    //指定所使用的NIO传输 Channel
                    .channel(NioServerSocketChannel.class)
                    //使用指定的端口设置套接字地址
                    .localAddress(new InetSocketAddress(port))
                    //添加一个EchoServerHandler到子Channel的ChannelPipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(echoServerHandler);
                        }
                    });
            //异步的绑定服务器,调用sync()方法阻塞等待直到绑定完成
            ChannelFuture channelFuture = bootstrap.bind().sync();
            // 等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            //关闭EventLoopGroup,释放所有的资源
            boss.shutdownGracefully().sync();
            worker.shutdownGracefully().sync();
        }
    }

}

 服务端创建流程:

      

  • 创建 ServerBootStrap实例
  • 设置并绑定 NioEventLoopGroup线程池
  • 通过 ServerBootStrapchannel方法设置并绑定服务端 Channel
  • 创建并初始化 ChannelPipeline
  • 添加并设置 ChannelHandler
  • 绑定并启动监听端口

  Channel是 Netty中的网络操作抽象类,对应JDK底层的Socket,它除了包含基本的I/O操作,如 bind(),connect(),read(),write()之外,还包括了Netty框架相关的一些功能,如获取 Channel的EventLoop。

  EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。EventLoop 为Channel处理I/O操作

一个EventLoopGroup 包含一个或者多个EventLoop
一个 EventLoop 在它的生命周期内只和一个Thread绑定
所有由 EventLoop处理的 I/O事件都将在它专有的Thread上被处理
一个 Channel 在它的生命周期内只注册一个EventLoop
一个 EventLoop 可能会被分配给一个或多个 Channel

   EventLoopGroup实际上就是处理I/O操作的线程池,负责为每个新注册的Channel分配一个EventLoop,Channel在整个生命周期都有其绑定的 EventLoop来服务。

   NioEventLoop 就是 EventLoop的一个重要实现类,NioEventLoop 是Netty内部的I/O线程,而 NioEventLoopGroup是拥有 NioEventLoop的线程池,在Netty服务端中一般存在两个这样的NioEventLoopGroup线程池,一个 “Boss” 线程池,用于接收客户端连接,实际上该线程池中只有一个线程,一个 “Worker”线程池用于处理每个连接的读写。而Netty客户端只需一个线程池即可,主要用于处理连接中的读写操作。

  ChannelHandler是Netty的主要组件,主要用于对出站和入站数据进行处理,有两个重要的子接口:

  • ChannelInboundHandler——处理入站数据以及各种状态变化
  • ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作

  ChannelPipeline提供 ChannelHandler链的容器,换句话说,就是一个逻辑处理链,用于拦截流经Channel的入站和出站事件的ChannelHandler。就是当 Channel被创建时,会被自动地分配到它的专属的 ChannelPipeline。

  当一个消息或者任何其他的入站事件被读取时,会从 ChannelPipeline的头部开始流动,并被传递给第一个 ChannelInboundHandler,第一个处理完成之后传递给下一个 ChannelInboundHandler,一直到ChannelPipeline的尾端,与之对应的是,当数据被写出时,数据从 ChannelOutboundHandler 链的尾端开始流动,直到它到达链的头部为止。

  Netty中所有I/O操作都是异步的,使用ChannelFuture可以获取操作完成的结果,其 addListener()方法注册了一个 ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。

  Netty提供的启动辅助类,帮助Netty客户端或服务端的Netty初始化,服务端对应的是 ServerBootStrap引导类。

package com.smart.netty.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

//标识一个 ChannelHandler可以被多个Channel安全地共享
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println("接收到消息:" + byteBuf.toString(CharsetUtil.UTF_8));
        //将接受到消息回写给发送者
        ctx.write(byteBuf);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

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

}

客户端

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 loopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(loopGroup)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect().sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            loopGroup.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient("127.0.0.1", 8089).start();
    }

}

 

@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("客户端接收消息:" + msg.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks", CharsetUtil.UTF_8));
    }

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

 

  

 

 

posted on 2019-11-28 22:52  溪水静幽  阅读(427)  评论(0)    收藏  举报