Netty(三)Netty模型
1. Netty模型
Netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模型有多个Reactor。
简版图:

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

详细版:

模型说明:
- Netty抽象出2组线程池:
- BossGroup:专门接受客户端的连接
- WorkerGroup:专门负责网络的读写
- BossGroup和WorkerGroup类型都是NioEventLoopGroup
- NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
- NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通讯
- NioEventLoop可以有多个线程,即可以含有多个NioEventLoop
- 每个Boss NioEventLoop循环执行的步骤有3步:
- 轮询accept事件
- 处理accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个worker
- 处理任务队列的任务,即runAllTasks
- 每个Worker NIOEventLoop循环执行的步骤:
- 轮询read,write事件
- 处理I/O事件,即read,write事件,在对应NioSocketChannel处理
- 处理任务队列的任务,即runAllTasks
- 每个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模型的更多相关文章
- Netty IO线程模型学习总结
Netty框架的 主要线程是IO线程.线程模型的好坏直接决定了系统的吞吐量.并发性和安全性. Netty的线程模型遵循了Reactor的基础线程模型.以下我们先一起看下该模型 Reactor线程模型 ...
- Netty Reactor 线程模型笔记
引用: https://www.cnblogs.com/TomSnail/p/6158249.html https://www.cnblogs.com/heavenhome/articles/6554 ...
- netty之==线程模型
1.1 netty线程模型本质遵循了Reactor的基础线程模型,所以得先介绍Reactor模型 1.2 Reactor模型 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模 ...
- 面试官:Netty的线程模型可不只是主从多Reactor这么简单
笔者看来Netty的内核主要包括如下图三个部分: 其各个核心模块主要的职责如下: 内存管理 主要提高高效的内存管理,包含内存分配,内存回收. 网通通道 复制网络通信,例如实现对NIO.OIO等底层JA ...
- Netty服务器线程模型概览
一切从ServerBootstrap开始 ServerBootstrap负责初始话netty服务器,并且开始监听端口的socket请求. bootstrap bootstrap =newServerB ...
- SDN三种模型解析
数十年前,计算机科学家兼网络作家Andrew S. Tanenbaum讽刺标准过多难以选择,当然现在也是如此,比如软件定义网络模型的数量也很多.但是在考虑部署软件定义网络(SDN)或者试点之前,首先需 ...
- Javascript事件模型系列(一)事件及事件的三种模型
一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加 ...
- Atitit.web三编程模型 Web Page Web Forms 和 MVC
Atitit.web三编程模型 Web Page Web Forms 和 MVC 1. 编程模型是 Web Forms 和 MVC (Model, View, Controller). 2. W ...
- NLP —— 图模型(零):EM算法简述及简单示例(三硬币模型)
最近接触了pLSA模型,该模型需要使用期望最大化(Expectation Maximization)算法求解. 本文简述了以下内容: 为什么需要EM算法 EM算法的推导与流程 EM算法的收敛性定理 使 ...
- PHP.23-ThinkPHP框架的三种模型实例化-(D()方法与M()方法的区别)
三种模型实例化 原则上:每个数据表应对应一个模型类(Home/Model/GoodsModel.class.php --> 表tp_goods) 1.直接实例化 和实例化其他类库一样实例化模型类 ...
随机推荐
- LVS负载均衡(4)-- LVS FWM防火墙标记
防火墙标记的作用是:借助于防火墙标记来分类报文,然后基于标记定义集群服务:可将多个不同的应用使用同一个集群服务进行调度. 实现方法: 在Director主机打标记,作用在mangle表的PREROUT ...
- Windows 上安装 PostgreSQL详细图文教程
转载于微信公众号:SQL数据库运维,如需转载请注明出处,谢谢! PostgreSQL 的 Slogan 是 "世界上最先进的开源关系型数据库". 这里使用 Enterprise D ...
- leaflet 基本案例-图层控件:基础图层+业务图层
可执行示例一个,如下: <!DOCTYPE html> <html> <head> <title>Layers Control Tutorial - L ...
- Linux(四):Linux的打包和压缩详解
关于Linux的文件操作,这里汇总一下打包和压缩的一些命令,以及命令使用的详情. 打包(归档)和压缩 归档,也称为打包,指的是一个文件或目录的集合,而这个集合被存储在一个文件中.归档文件没有经过压缩, ...
- .net core 微信支付-微信小程序支付(服务端C#代码)
前言 前段时间研究了下微信支付-小程序支付的功能.但微信支付文档中关于.net C#的语言的sdk没有,只有java go 和php版本的,当然社区也有很多已经集成好的微信支付.net core sd ...
- Nokia 5GC 产品概览
目录 文章目录 目录 Nokia SR OS Nokia NSP NFM-P Nokia 7750 SR-MG 5G User Plane Forwarding Mobile Gateway Non- ...
- OAI SDR LTE 基站部署
目录 文章目录 目录 硬件设备要求 物料购买 部署架构图 安装 LTE/EPC 前期准备 运维相关 操作系统要求 内核要求 CPU Frequency scaling,将 CPU 频率打满 eNode ...
- es命令大全,elasticsearch命令详解
参考链接 Relational DB Elasticsearch 数据库(database) 索引(indices) 表(tables) types 行(rows) documents 字段(colu ...
- Django——form组件的全局钩子
前面提到过的都是针对单个字段的校验,如果想同时对多个字段进行校验,就可以使用全局钩子(编写全局钩子预留的clean方法,可以获取到多个字段并进行校验) 注意: ---- 默认的校验.自定义正则规则的校 ...
- 从零开始写 Docker(十六)---容器网络实现(上):为容器插上”网线”
本文为从零开始写 Docker 系列第十六篇,利用 linux 下的 Veth.Bridge.iptables 等等相关技术,构建容器网络模型,为容器插上"网线". 完整代码见:h ...