Demo代码

  • 使用Maven的话请在pom.xml中注入netty依赖

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

    package com.ronnie.netty.sample;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel; public class NettyClient { public static void main(String[] args) throws InterruptedException { // 客户端需要一个事件循环组
    NioEventLoopGroup group = new NioEventLoopGroup(); try {
    // 创建客户端启动对象
    // 注意: 客户端使用的不是 ServerBootstrap 而是 Bootstrap
    Bootstrap bootstrap = new Bootstrap(); // 设置相关参数
    bootstrap.group(group) //设置线程组
    .channel(NioSocketChannel.class) // 设置客户端通道实现类(反射)
    .handler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(new NettyClientHandler()); // 加入自己的处理器
    }
    });
    System.out.println("Client side is ready......"); // 启动客户端去连接服务器端
    // channelFuture涉及到netty的异步模型
    ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); // 给关闭通道进行监听
    channelFuture.channel().closeFuture().sync();
    } catch (InterruptedException e) {
    e.printStackTrace();
    } finally {
    group.shutdownGracefully();
    }
    }
    }
  • NettyClientHandler

    package com.ronnie.netty.sample;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.util.CharsetUtil; public class NettyClientHandler extends ChannelInboundHandlerAdapter { // 当同道就绪, 就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("Client " + ctx);
    ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, server: ROFL", CharsetUtil.UTF_8)); } /**
    * 当通道有读取事件时会触发
    * @param ctx
    * @param msg
    * @throws Exception
    */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg;
    System.out.println("The message server responded: " + buf.toString(CharsetUtil.UTF_8));
    System.out.println("Server address: " + ctx.channel().remoteAddress());
    } @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    }
    }
  • NettyServer

    package com.ronnie.netty.sample;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer {
    public static void main(String[] args) throws InterruptedException { // 创建 BossGroup 与 WorkerGroup
    /**
    * 1. 创建两个线程组 bossGroup 和 workerGroup
    * 2. bossGroup 只是处理连接请求, 真正的和客户端业务处理, 会交给workGroup完成
    * 3. 两个都是无限循环
    * 4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
    * 为默认实际 cpu核数 * 2
    */ NioEventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup(); try {
    // 创建服务器端的启动对象, 配置参数
    ServerBootstrap bootstrap = new ServerBootstrap(); // 使用链式编程来进行设置
    bootstrap.group(bossGroup, workerGroup) // 设置两个线程组
    .channel(NioServerSocketChannel.class) // 使用NioSocketChannel 作为服务器的通道实现
    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接的个数
    .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
    .childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象(匿名对象)
    /**
    * 给pipeline设置处理器
    * @param ch
    * @throws Exception
    */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(new NettyServerHandler());
    }
    }); // 给workerGroup的 EventLoop 设置处理器 System.out.println("Server is ready......"); // 绑定一个端口并且同步, 生成了一个ChannelFuture对象
    // 启动服务器(并绑定端口)
    ChannelFuture cf = bootstrap.bind(6668).sync(); // 对关闭通道进行监听
    cf.channel().closeFuture().sync();
    } catch (InterruptedException e) {
    e.printStackTrace();
    } finally {
    // 优雅地关闭
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
    } }
    }
  • NettyServerHandler

    package com.ronnie.netty.sample;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.channel.ChannelPipeline;
    import io.netty.util.CharsetUtil; /**
    * 1. 自定义一个Handler需要继承 netty 规定好的某个 HandlerAdapter(适配器模式)
    * 2. 这时我们自定义一个Handler, 才能称为一个handler
    */
    public class NettyServerHandler extends ChannelInboundHandlerAdapter { /**
    * 读取数据事件(这里我们可以读取客户端发送的消息)
    * ChannelHandlerContext ctx: 上下文对象, 含有管道 pipeline, 通道 channel, 地址 address
    * Object msg: 就是客户端发送的数据 默认Object
    * @param ctx
    * @param msg
    * @throws Exception
    */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("The server is reading thread: " + Thread.currentThread().getName());
    System.out.println("server ctx = " + ctx);
    System.out.println("Check the relationship between channel and pipeline");
    Channel channel = ctx.channel();
    ChannelPipeline pipeline = ctx.pipeline(); // 本质是一个双向链表, 涉及到出栈入栈问题
    // 将 msg转成一个 ByteBuf(是netty提供的, 不是NIO的 ByteBuffer, 性能更高)
    ByteBuf buf = (ByteBuf) msg;
    System.out.println("The message that client send: " + buf.toString(CharsetUtil.UTF_8));
    System.out.println("The address of client: " + ctx.channel().remoteAddress());
    } /**
    * 数据读取完毕
    * @param ctx
    * @throws Exception
    */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // write + flush, 将数据写入到缓冲并刷新
    // 一般来说, 我们对发送的数据进行编码
    ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, dear client, Kappa", CharsetUtil.UTF_8)); } /**
    * 处理异常, 一般需要关闭通道
    * @param ctx
    * @param cause
    * @throws Exception
    */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    ctx.close();
    }
    }

需要细究的几个点

  1. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数为默认实际 cpu核数 * 2。

    • 如果传入的为空, 默认传入0

          public NioEventLoopGroup() {
      this(0);
      }
          public NioEventLoopGroup(int nThreads) {
      this(nThreads, (Executor) null);
      }
          public NioEventLoopGroup(int nThreads, Executor executor) {
      this(nThreads, executor, SelectorProvider.provider());
      }
          public NioEventLoopGroup(
      int nThreads, Executor executor, final SelectorProvider selectorProvider) {
      this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
      }
          public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
      final SelectStrategyFactory selectStrategyFactory) {
      super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
      }
          /**
      * @see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object...)
      */
      protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
      // 如果传入的线程数为0, 就让线程数为默认事件循环线程数, 否则就使用传入的线程数
      super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
      }
    • 一路追踪源码, 最终你会追踪到:

      DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
      "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
      • DEFAULT_EVENT_LOOP_THREADS 最小为1, 通常是可获取的cpu核数 * 2
    • 我们可以写个程序测试一下

      package com.ronnie.netty.sample;
      
      import io.netty.util.NettyRuntime;
      
      public class Test {
      public static void main(String[] args) {
      System.out.println(NettyRuntime.availableProcessors());
      }
      }
      • 打印结果为12, 我的cpu为 i7 8700, 6核12线程, 结论成立。
  2. Pipeline中含有其对应的Channel的属性, Channel中也有其对应的Pipeline的属性, 两者是互相包容的, 而ChannelHandlerContext则包含了此两者, 是个很复杂的接口。

Netty 模型的更多相关文章

  1. netty模型简介

    Netty工作原理图 netty抽象出了两组线程池,BossGroup专门负责客户端 的连接,WorkerGroup专门负责网络读写. BossGroup和WorkerGroup 类型都是NioEve ...

  2. Netty模型

  3. Netty是什么,Netty为什么速度这么快,线程模型分析

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,现在下着大雨看来是去 ...

  4. Netty原理剖析

    1. Netty简介 Netty是一个高性能.异步事件驱动的NIO框架,基于JAVA NIO提供的API实现.它提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作 ...

  5. Netty原理

    1. Netty简介Netty是一个高性能.异步事件驱动的NIO框架,基于JAVA NIO提供的API实现.它提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都 ...

  6. 史诗级最强教科书式“NIO与Netty编程”

    史诗级最强教科书式“NIO与Netty编程” 1.1 概述 1.2 文件IO 1.2.1 概述和核心API 1.2.2 案例 1.3 网络IO 1.3.1 概述和核心API 3.4 AIO编程 3.5 ...

  7. Netty 基本原理

    转载. https://blog.csdn.net/qq_27641935/article/details/86543578 之前在看rocketmq源码时,发现底层用了Netty,顺便学习了一下,网 ...

  8. netty的原理

    1. Netty简介 Netty是一个高性能.异步事件驱动的NIO框架,基于JAVA NIO提供的API实现.它提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作 ...

  9. Netty | 第1章 Java NIO 网络编程《Netty In Action》

    目录 前言 1. Java 网络编程 1.1 Javs NIO 基本介绍 1.2 缓冲区 Buffer 1.2 通道 Channel 1.3 选择器 Selector 1.4 NIO 非阻塞网络编程原 ...

随机推荐

  1. HashMap与HashTable源码学习及效率比较分析

    一.个人学习后的见解: 首先表明学习源码后的个人见解,后续一次依次进行分析: 1.线程安全:HashMap是非线程安全的,HashTable是线程安全的(HashTable中使用了synchroniz ...

  2. Spring注解@Qualifier、@Autowired、@Primary

    @Qualifier 1.当一个接口有多个实现类,且均已注入到Spring容器中了,使用@AutoWired是byType的,而这些实现类类型都相同,此时就需要使用@Qualifier明确指定使用那个 ...

  3. Python学习笔记005

    if if     ==    : xxxx elif     : xxxx else: xxxx 输入字符串 input() 字符串转数值 int() 数值转字符串 str() 输出 print() ...

  4. STM32的程序升级

    IAP基础参考http://www.eeworld.com.cn/mcu/2018/ic-news112042038.html https://blog.csdn.net/tq384998430/ar ...

  5. mysql8 Could not retrieve transation read-only status server

    想回顾下ssm,看着网上别的帖子手动搭了一个,一直报Could not retrieve transation read-only status server , java.sql.SQLExcept ...

  6. kafka的搭建,命令

    a)kafka搭建 1.解压 2.修改配置/software/kafka_2.11-0.11.0.3/config/server.properties broker.id=0 log.dirs=/va ...

  7. jdbc学习over

    这次的代码和之前学习到一般的代码主要就是将一些很常见的操作(建立连接.清除连接)不管做什么操作都需要用到它们,所以将它们单独放到另一个工具类里面去. 用到的术语: 1.事务:https://www.c ...

  8. 「CF1004E」Sonya and Ice Cream

    题目描述 给定一个 \(N\) 个点的树,要选出一条所含点的个数不超过 \(K\) 的一条路径,使得路径外的点到这条路径的距离的最大值最小. 数据范围:\(1\le K \le N \le 10^5\ ...

  9. JS垂直落体回弹原理

    /* *JS垂直落体回弹原理 */ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...

  10. div背景图片自适应

    对背景图片设置属性:background-size:cover;可以实现背景图片适应div的大小. background-size有3个属性: auto:当使用该属性的时候,背景图片将保持100% 的 ...