初学Netty

初学Netty


前言

前面提到了NIO操作,但是其代码比较复杂,直接使用NIO去开发是比较困难的而且问题较多,比如Epoll Bug,于是就基于NIO产生了一个比较健壮的开源框架-Netty。

一、什么是Netty

Netty 是由 JBOSS 提供的一个基于NIO的网络编程框架,Netty是由JAVA语言实现的,提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序。很多框架像Dobbo、Elasticsearch等框架都是基于Netty实现的。

在这里插入图片描述
看Netty官网给出的文档介绍以及架构图Netty使用零拷贝技术最小化不必要的内存复制,同时也减少资源的消耗;具有更高的吞吐量以及低延迟,让其性能是非常高的。

二、Netty线程模型

1.Reactor 模型

Reactor 模式指的是通过一个或多个输入同时传递给服务处理器的模式,服务器端程序处理传入的多个请求,并将它们同步分派到相应的处理线程,因此 Reactor 模式也叫 Dispatcher模式.。Reactor 模式使用IO 复用监听事件,收到事件后,分发给某个线程(进程),Reactor 模式是网络服务器高并发处理关键。

  • Selector可以实现应用程序通过一个阻塞对象对多路连接请求的监听。
  • Reactor 对象通过 Selector监控客户端请求事件,收到事件后通过 Dispatch 进行分发。
  • 建立连接请求事件后,由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理业务。
  • Handler对象Read读取数据后将业务分配到Worker线程池中。
  • Worker线程池处理完业务后将结果返回给Hander。
  • Hander收到结果后在Send返回给客户端。

当然后面线程池不属于Reactor 模型,现在基本都是多核CPU,为了充分利用CPU提高性能一般在后面加线程池去分别处理业务。
在这里插入图片描述

2.Netty线程模型

Netty就是基于Reactor模型设计的。
在这里插入图片描述
Netty中石油两组线程池的,BossGroup和WorkerGroup,他们都是多线程的,都有一个Selector用来监听事件。

  • BossGroup: 负责专门和客户端建立链接,轮询注册的ServerSocketChannel的请求,并建立连接生成NIOSocketChannel注册到WorkerGroup中。
  • WorkerGroup: 负责专门处理连接的业务,轮询注册的NIOSocketChannel,处理对应的业务。

三、示例代码

服务端的代码:

/**
  *  客户端创建两个线程池组分别为 boss线程组和工作线程组
  */
 // 用于接受客户端连接的请求 (并没有处理请求)
 NioEventLoopGroup bossGroup = new NioEventLoopGroup();
 // 用于处理客户端连接的读写操作
 NioEventLoopGroup workGroup = new NioEventLoopGroup();
 // 用于创建我们的ServerBootstrap
 ServerBootstrap serverBootstrap = new ServerBootstrap();
 serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
         .childHandler(new ChannelInitializer<SocketChannel>() {
             @Override
             protected void initChannel(SocketChannel socketChannel) throws Exception {
                 // 设置我们分割最大长度为1024
                 socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                 // 获取数据的结果为string类型,编码和解码
                 socketChannel.pipeline().addLast(new StringEncoder());
                 socketChannel.pipeline().addLast(new ServerHandler());
             }
         });
 //  绑定我们的端口号码
 try {
     // 绑定端口号,同步等待成功
     ChannelFuture future = serverBootstrap.bind(8080).sync();
     System.out.println("服务器启动成功:" + 8080);
     // 等待服务器监听端口
     future.channel().closeFuture().sync();
 } catch (Exception e) {
     e.printStackTrace();

 } finally {
     // 关闭连接
     bossGroup.shutdownGracefully();
     workGroup.shutdownGracefully();
 }

Handler

//这里可以加泛型的接受参数就直接使用String
public class ServerHandler extends SimpleChannelInboundHandler {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
        // 接受我们的数据
        ByteBuf byteBuf = (ByteBuf) o;
        String request = byteBuf.toString(CharsetUtil.UTF_8);
        System.out.println("request:" + request);
        // 响应内容:
        ctx.writeAndFlush(Unpooled.copiedBuffer("平均突破3万月薪\n", CharsetUtil.UTF_8));
    }
}

客户端代码:

 //创建nioEventLoopGroup
NioEventLoopGroup group = new NioEventLoopGroup();
  Bootstrap bootstrap = new Bootstrap();
  bootstrap.group(group).channel(NioSocketChannel.class)
          .remoteAddress(new InetSocketAddress("127.0.0.1", 8080))
          .handler(new ChannelInitializer<SocketChannel>() {
              @Override
              protected void initChannel(SocketChannel ch) throws Exception {
                  // 设置我们分割最大长度为1024
                  ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
                  // 获取数据的结果为string类型
                  ch.pipeline().addLast(new StringEncoder());
                  ch.pipeline().addLast(new ClientHandler());
              }
          });
  try {
      // 发起同步连接
      ChannelFuture sync = bootstrap.connect().sync();
      sync.channel().closeFuture().sync();
  } catch (Exception e) {
...
  } finally {
      group.shutdownGracefully();
  }
}

客户端只创建一个就可以了,只有BossGroup,用不到WordGroup。
客户端handler

public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    /**
     * 活跃通道可以发送消息
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        // 发送数据
        for (int i = 0; i < 10; i++) {
            ctx.writeAndFlush(Unpooled.copiedBuffer("mayikt\n", CharsetUtil.UTF_8));
        }
    }

    /**
     * 读取消息
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("resp:" + msg.toString(CharsetUtil.UTF_8));
    }
}

内容来源:
蚂蚁课堂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值