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. nodeJS - 定义全局变量

    定义 : global.变量名=‘xxxx’; 取出 :    global.变量名

  2. Linux centosVMware 自动化运维认识自动化运维、启动salt相关服务、saltstack配置认证、salt-key命令用法、saltstack远程执行命令、saltstack - grains、saltstack – pillar

    一.认识自动化运维 传统运维效率低,大多工作人为完成 传统运维工作繁琐,容易出错 传统运维每日重复做相同的事情 传统运维没有标准化流程 传统运维的脚本繁多,不能方便管理 自动化运维就是要解决上面所有问 ...

  3. Linux centosVMware Tomcat介绍、安装jdk、安装Tomcat

    一.Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同开 ...

  4. 「JSOI2010」满汉全席

    前言 由于蒟蒻才刚开始学 \(\text{2-SAT}\),所以题解中有的地方可能不够精炼,望多包涵! 题目描述 题目意思很简单,标准的\(\text{2-SAT}\)问题模型.那么我们就先来介绍一下 ...

  5. 「CF126B」Password

    题目描述 给定一个字符串 \(S\),我们规定一个字符串 \(P\) 是可行解,\(P\) 需要满足: \(P\) 是 \(S\) 的前缀 \(P\) 是 \(S\) 的后缀 \(P\) 出现在 \( ...

  6. 六 一对多关联查询&关联查询小结

    一对多关联查询:基于用户表关联查询订单表 在pojo中,一的一方方式多的一方的集合 在代理映射中配置查询方法,ResultMap一对多关系(注意:当两表有字段重名时,在一方字段设置别名,以免造成查询混 ...

  7. jforum发表文章和回复文章显示中文乱码问题

    jforum安装时,在数据库创建jforum数据库的时候,设置了字符集(CREATE DATABASE jforum DEFAULT CHARACTER SET utf8 COLLATE utf8_g ...

  8. json object string互转

    参考: https://www.cnblogs.com/guangshan/p/4459436.html JsonArray和JsonObject遍历方法 参考:https://blog.csdn.n ...

  9. vue配置config ‘./.../.../***/**.vue’路径别名

    cli-4的脚手架配置 因为组件的引用,经常会遇到import * from  '../../../components/common/***.vue‘这样的引入格式,太复杂了,所以可以在vue里面配 ...

  10. VMwara虚拟机三种网络模式

    虚拟机:虚拟机是能够让用户在一台物理机上模拟出多个操作系统的软件其本质是通过中间层实现计算机资源的管理和再分配让系统资源的利用率最大化VMware即是一款虚拟机软件注意:虚拟机和操作系统的区别,虚拟机 ...