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. springdata框架

    SpringData jpa:    JPA(Java Persistence API)是当年的 Sun 官方提出的 Java 持久化规范.    Spring Data JPA 是 Spring 基 ...

  2. 02-05Android学习进度报告五

    今天主要学习了关于Android 开发的关于进度条和拖动条的知识. 主要学习了一些关于进度条的基本属性: android:max:进度条的最大值 android:progress:进度条已完成进度值 ...

  3. 在ng-repeat 中使用 ng-click

    angular 中使用 ng-repeat  过程中,有时需要绑定 事件 click ,使用ng-click ,但是并没有效果,此时应该使用 data-ng-click 替代 ng-click ,并且 ...

  4. 输出简单图形(StringBuilder代替双重循环)

    在有些题目中打印简单图形必须使用StringBuilder或者StringBuffer,否则会运行超时(用String都会超时). 因为在题目的要求中说到输入的n是小于1000的,用双重循环就会超时, ...

  5. 设计模式课程 设计模式精讲 14-2 组合模式coding

    1 代码演练 1.1 代码演练1(组合模式1) 1.2 代码演练2(组合模式1之完善) 1 代码演练 1.1 代码演练1(组合模式1) 需求: 打印出木木网的课程结构, 我们用一个组建类作为接口,课程 ...

  6. iis下发布MVC网站

    1.首先检查有没有安装iis,没有的话先安装iis 2. 3.选择应用程序池的时候看有没有asp.net 4.0 如果没有先安装. 首先以管理员身份打开“运行”输入cd C:\Windows\Micr ...

  7. linux sed命令(擅长输出行)(转)

    linux命令总结sed命令详解 Sed 简介 sed 是一种新型的,非交互式的编辑器.它能执行与编辑器 vi 和 ex 相同的编辑任务.sed 编辑器没有提供交互式使用方式,使用者只能在命令行输入编 ...

  8. Linux CentOS7 VMware 文件和目录权限chmod、更改所有者和所属组chown、umask、隐藏权限lsattr/chattr

    一.文件和目录权限chmod u User,即文件或目录的拥有者:g Group,即文件或目录的所属群组:o Other,除了文件或目录拥有者或所属群组之外,其他用户皆属于这个范围:a All,即全部 ...

  9. lunix下的redis安装

    https://blog.csdn.net/qq_35992900/article/details/82950157

  10. c++中的全排列

    next_permutation函数 组合数学中经常用到排列,这里介绍一个计算序列全排列的函数:next_permutation(start,end),和prev_permutation(start, ...