1. Netty模型

Netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模型有多个Reactor。

简版图:

说明:

  1. BossGroup线程维护Selector,只关注Accept
  2. 当接收到Accept事件,获取到对应的SocketChannel,封装成NIOSocketChannel并注册到Worker线程(事件循环),并进行维护
  3. 当Worker线程监听到selector中通道发生自己感兴趣的事件后,就进行处理(就由handler),注意,handler已经加入到channel

Netty模型进阶版图:

详细版:

模型说明:

  1. Netty抽象出2组线程池:

    • BossGroup:专门接受客户端的连接
    • WorkerGroup:专门负责网络的读写
  2. BossGroup和WorkerGroup类型都是NioEventLoopGroup
  3. NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
  4. NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通讯
  5. NioEventLoop可以有多个线程,即可以含有多个NioEventLoop
  6. 每个Boss NioEventLoop循环执行的步骤有3步:
    • 轮询accept事件
    • 处理accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个worker
    • 处理任务队列的任务,即runAllTasks
  7. 每个Worker NIOEventLoop循环执行的步骤:
    • 轮询read,write事件
    • 处理I/O事件,即read,write事件,在对应NioSocketChannel处理
    • 处理任务队列的任务,即runAllTasks
  8. 每个Worker NIOEventLoop处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应channel,管道中维护了很多的处理器

2. Netty实现简单服务端与客户端交互

NettyServer.java

package com.tang.netty.simple;

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) {
// 创建 BossGroup 和 WorkerGroup
// 1.创建 2 个线程组 boosGroup 和 workerGroup
// 2.bossGroup只是处理连接请求,真正与客户端的业务处理,会交给workerGroup
// 3.两个都是无限循环
// 4.bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数,默认为实际 CPU 核数*2
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 创建服务器端的启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap(); // 使用链式编程来进行设置
bootstrap.group(bossGroup, workerGroup) // 设置2个线程组
.channel(NioServerSocketChannel.class) // 使用 NioServerSocketChannel 作为服务器通道的实现
.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接数
.childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道测试对象(匿名对象)
// 给pipeline 设置处理器
// 使用的自己实现的 NettyServerHandler
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
}); // 给我们 workerGroup 的 EventLoop 对应的管道设置处理器 System.out.println("....Server is ready..."); try {
// 绑定一个端口并且同步,生成了一个 ChannelFuture 对象
// 启动服务器
ChannelFuture cf = bootstrap.bind(6668).sync(); // 对关闭通道进行监听
cf.channel().closeFuture().sync(); } catch (InterruptedException e){
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
} }
}

NettyServerHandler.java

package com.tang.netty.simple;

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; import java.nio.ByteBuffer; /**
* 我们自定义一个Handler 需要继承netty 规定好的某个 HandlerAdapter(规范)
* 这个 ChannelInboundHandlerAdapter 是 Pipeline 里的 ChannelHandler 的一个实现
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter { // 读取数据(这里我们可以读取客户端发送的消息) /**
1.ChannelHandlerContext ctx:上下文对象,含有管道 pipeline,通道 channel,地址
2.Object msg:就是客户端发送的数据,默认Object
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
System.out.println("服务器读取线程 " + Thread.currentThread().getName());
System.out.println("server ctx = " + ctx);
System.out.println("查看 channel 与 pipeline 的关系");
Channel channel = ctx.channel();
ChannelPipeline pipeline = ctx.pipeline(); // 本质是一个双向链表,出战入站 // 将 msg 转为 ByteBuf
// ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送的消息是:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:" + channel.remoteAddress()); } // 数据读取完毕
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception{ // writeAndFlush 是 write + flush
// 将数据写入到缓冲,并刷新
// 一般讲,我们对这个发送的数据进行编码
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,客户端~ 收到", CharsetUtil.UTF_8));
} // 处理异常,一般是需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}

NettyClient.java

package com.tang.netty.simple;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
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) {
// 客户端需要一个事件循环组
EventLoopGroup eventExecutors = new NioEventLoopGroup(); try{
// 创建客户端启动对象
// 注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
Bootstrap bootstrap = new Bootstrap(); // 设置相关参数
bootstrap.group(eventExecutors) // 设置线程组
.channel(NioSocketChannel.class) // 设置客户端Channel的实现类
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler()); // 加入自己的处理器
}
}); System.out.println("客户端 ok..."); // 启动客户端去连接服务器端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); // 给关闭通道进行监听
channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e){
e.printStackTrace();
} finally {
eventExecutors.shutdownGracefully();
} }
}

NettyClientHandler.java

package com.tang.netty.simple;

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 { // 当 Channel 就绪时,就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client " + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello, server, this is client", CharsetUtil.UTF_8)); } // 当 Channel 有读取事件时,会触发
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址:" + ctx.channel().remoteAddress());
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

------------------------------------

此 Netty 笔记为学习尚硅谷韩老师讲的 Netty 整理完成,原视频讲解十分详细,建议对 Netty 框架感兴趣的同学们可以看一遍原视频:

https://www.bilibili.com/video/BV1DJ411m7NR

Netty(三)Netty模型的更多相关文章

  1. Netty IO线程模型学习总结

    Netty框架的 主要线程是IO线程.线程模型的好坏直接决定了系统的吞吐量.并发性和安全性. Netty的线程模型遵循了Reactor的基础线程模型.以下我们先一起看下该模型 Reactor线程模型 ...

  2. Netty Reactor 线程模型笔记

    引用: https://www.cnblogs.com/TomSnail/p/6158249.html https://www.cnblogs.com/heavenhome/articles/6554 ...

  3. netty之==线程模型

    1.1 netty线程模型本质遵循了Reactor的基础线程模型,所以得先介绍Reactor模型  1.2  Reactor模型 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模 ...

  4. 面试官:Netty的线程模型可不只是主从多Reactor这么简单

    笔者看来Netty的内核主要包括如下图三个部分: 其各个核心模块主要的职责如下: 内存管理 主要提高高效的内存管理,包含内存分配,内存回收. 网通通道 复制网络通信,例如实现对NIO.OIO等底层JA ...

  5. Netty服务器线程模型概览

    一切从ServerBootstrap开始 ServerBootstrap负责初始话netty服务器,并且开始监听端口的socket请求. bootstrap bootstrap =newServerB ...

  6. SDN三种模型解析

    数十年前,计算机科学家兼网络作家Andrew S. Tanenbaum讽刺标准过多难以选择,当然现在也是如此,比如软件定义网络模型的数量也很多.但是在考虑部署软件定义网络(SDN)或者试点之前,首先需 ...

  7. Javascript事件模型系列(一)事件及事件的三种模型

    一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加 ...

  8. Atitit.web三编程模型 Web Page Web Forms 和 MVC

    Atitit.web三编程模型 Web Page    Web Forms 和 MVC 1. 编程模型是 Web Forms 和 MVC (Model, View, Controller). 2. W ...

  9. NLP —— 图模型(零):EM算法简述及简单示例(三硬币模型)

    最近接触了pLSA模型,该模型需要使用期望最大化(Expectation Maximization)算法求解. 本文简述了以下内容: 为什么需要EM算法 EM算法的推导与流程 EM算法的收敛性定理 使 ...

  10. PHP.23-ThinkPHP框架的三种模型实例化-(D()方法与M()方法的区别)

    三种模型实例化 原则上:每个数据表应对应一个模型类(Home/Model/GoodsModel.class.php --> 表tp_goods) 1.直接实例化 和实例化其他类库一样实例化模型类 ...

随机推荐

  1. Echarts立体地图加3D柱图可点击可高亮选中的开发

    注意 echarts请使用v5.1.0以上版本,低版本会无法显示,或者无法触发点击事件. 若有闪屏bug,不要设置temporalSuperSampling属性. 注意图层顺序. 实现原理 借助 ec ...

  2. 使用NSSM将.exe程序安装成windows服务

    1.下载NSSM:NSSM - the Non-Sucking Service Manager 2.cmd方式安装服务 将下载的压缩包解压,找到nssm.exe,以管理员身份打开cmd,在cmd中定位 ...

  3. 【简说Python WEB】Bootstrap

    目录 [简说Python WEB]Bootstrap Bootstrap的导航组件应用 404,500错误页面定制化 系统环境:Ubuntu 18.04.1 LTS Python使用的是虚拟环境:vi ...

  4. MindSpore反向传播配置关键字参数

    技术背景 在MindSpore深度学习框架中,我们可以向construct函数传输必备参数或者关键字参数,这跟普通的Python函数没有什么区别.但是对于MindSpore中的自定义反向传播bprop ...

  5. python教程6.1-模块和包

    模块分类 1.内置标准模块(⼜称标准库)执⾏help('modules')查看所有python⾃带模块列表 2.第三⽅开源模块,可通过pip install 模块名 联⽹安装 3.⾃定义模块 模块导入 ...

  6. jeecg-boot中分页接口用自定义sql和list实现

    1.controller中 @ApiOperation(value="分析仪工作状态和报警-3列-分页", notes="分析仪工作状态和报警状态-分页") @ ...

  7. MSP 通过 Splashtop SOS 远程支持非托管设备

    RMM 是 MSP 的绝佳工具.它们使 MSP 可以通过集中控制台来管理其所有客户计算机,通常使他们能够远程访问任何计算机以提供远程支持. 但是,这样做的一个很大限制是--并非所有客户设备都在 MSP ...

  8. 在 Chromebook 上使用 Word 的最佳方法

    Splashtop 允许您从 Chromebook 远程控制 Windows 和 Mac 计算机,从而可以访问 Word 的桌面版本和所有文件. 对于远程工作者和学生,Chromebook 可以是一种 ...

  9. Hello Laravel! 准备

    Hello Laravel! 准备 目录 Hello Laravel! 准备 什么是 Laravel? 为什么选择 Laravel? 优雅的语法 丰富的功能 强大的社区支持 安全性 易于扩展 Lara ...

  10. containerd 源码分析:启动注册流程

    0. 前言 containerd 是一个行业标准的容器运行时,其强调简单性.健壮性和可移植性.本文将从 containerd 的代码结构入手,查看 containerd 的启动注册流程. 1. 启动注 ...