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. C语言入门---第七章 C语言函数

    函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写大量重复的代码.函数可以提前保存起来,并给它起一个独一无二的名字,只要知道它的名字就能使用这段代码.函数还可以接收数据, ...

  2. Linux内核5.4正式将华为EROFS超级文件系统合入主线

    导读 近期,Linux内核5.4系列宣布全面可用,添加了许多新功能,更强的安全性和更新的驱动程序,以提供更好的硬件支持.Linux内核5.4增加对微软exFAT文件系统的支持,另外还支持内核锁定功能, ...

  3. Sping IOC容器

    Sping IOC容器 package servlet; import org.springframework.context.ApplicationContext; import org.sprin ...

  4. EC20的指令

    AT+QURCCFG="urcport","usbat" :当设置在主串口时且用主串口进行AT交互时开机会收到一串状态的信息,默认USBAT就不会 AT+IPR ...

  5. redis 之redis集群与集群配置

    一.为什么要用集群 redis3.0集群采用P2P模式,完全去中心化,将redis所有的key分成了16384个槽位,每个redis实例负责一部分slot,集群中的所有信息通过节点数据交换而更新. r ...

  6. UIWindow的获取

    注意:还是直接用下面这个比较靠谱.尤其是iOS11之后. [UIApplication sharedApplication].keyWindow;   1.下面这种是比较严谨的方式 - (UIWind ...

  7. 【剑指Offer面试编程题】题目1514:数值的整数次方---九度OJ

    题目描述: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 输入: 输入可能包含多个测试样例. 对于每个输入文件,第一行输入一个整数T, ...

  8. 认识系统服务 (daemons)

      daemon(守护进程:后台程序)与服务:   系统为了某些功能必须要提供一些服务 (不论是系统本身还是网络方面),这个服务就称为 service .但是 service 的提供总是需要程序的运作 ...

  9. 02-02Android 学习进度报告二

    今天我主要学习了Android的UI基础布局知识,主要是学习View与ViewGroup的概念以及其区别. 首先是代码说明: <?xml version="1.0" enco ...

  10. tomcat在windows下的监测

    一些老旧的系统部署在了windwos服务器上,而且总是容易挂,新机器申请不到,只能.. %tomcat%bin下有service.bat的tomcat版本 service.bat install se ...