Netty网络编程
入门案例
1、服务器端代码
public class HelloServer {public static void main(String[] args) {// 1、启动器,负责装配netty组件,启动服务器new ServerBootstrap()// 2、创建 NioEventLoopGroup,可以简单理解为 线程池 + Selector.group(new NioEventLoopGroup())// 3、选择服务器的 ServerSocketChannel 实现.channel(NioServerSocketChannel.class)// 4、child 负责处理读写,该方法决定了 child 执行哪些操作// ChannelInitializer 处理器(仅执行一次)// 它的作用是待客户端SocketChannel建立连接后,执行initChannel以便添加更多的处理器.childHandler(//channel 代表和客户端进行数据读写的通道,InitiaLizer 初始化,负责添加别的handlernew ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {// 5、SocketChannel的处理器,使用StringDecoder解码,ByteBuf=>StringnioSocketChannel.pipeline().addLast(new StringDecoder());// 6、SocketChannel的业务处理,使用上一个处理器的处理结果//SimpleChannelInboundHandler 自定义的handlernioSocketChannel.pipeline().addLast(new SimpleChannelInboundHandler<String>() {@Override//读事件protected void channelRead(ChannelHandlerContext channelHandlerContext, String s) throws Exception {//打印上一步转换好的字符串System.out.println(s);}});}// 7、ServerSocketChannel绑定8080端口}).bind(8080);}
}
2、客户端代码
public class HelloClient {public static void main(String[] args) throws InterruptedException {new Bootstrap().group(new NioEventLoopGroup())// 选择客户 Socket 实现类,NioSocketChannel 表示基于 NIO 的客户端实现.channel(NioSocketChannel.class)// ChannelInitializer 处理器(仅执行一次)// 它的作用是待客户端SocketChannel建立连接后,执行initChannel以便添加更多的处理器.handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel channel) throws Exception {// 消息会经过通道 handler 处理,这里是将 String => ByteBuf 编码发出channel.pipeline().addLast(new StringEncoder());}})// 指定要连接的服务器和端口.connect(new InetSocketAddress("localhost", 8080))// Netty 中很多方法都是异步的,如 connect// 这时需要使用 sync 方法等待 connect 建立连接完毕.sync()// 获取 channel 对象,它即为通道抽象,可以进行数据读写操作.channel()// 写入消并清空缓冲区,向客户端发送数据.writeAndFlush("hello world");}
}
组件解释
- channel 可以理解为数据的通道
- msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 中的各个 handler 加工,会变成其它类型对象,最后输出又变成 ByteBuf
- handler 可以理解为数据的处理工序
- 工序有多道,合在一起就是 pipeline(传递途径),pipeline 负责发布事件(读、读取完成…)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
- pipeline 中有多个 handler,处理时会依次调用其中的 handler
- handler 分 Inbound 和 Outbound 两类
- Inbound 入站
- Outbound 出站
- 工序有多道,合在一起就是 pipeline(传递途径),pipeline 负责发布事件(读、读取完成…)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
- eventLoop 可以理解为处理数据的工人
- eventLoop 可以管理多个 channel 的 io 操作,并且一旦 eventLoop 负责了某个 channel,就会将其与channel进行绑定,以后该 channel 中的 io 操作都由该 eventLoop 负责
- eventLoop 既可以执行 io 操作,也可以进行任务处理,每个 eventLoop 有自己的任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
- eventLoop 按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每个 handler 指定不同的 eventLoop
三、组件
1、EventLoop
事件循环对象 EventLoop
EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理一个或多个 Channel 上源源不断的 io 事件
它的继承关系如下
- 继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法
- 继承自 netty 自己的 OrderedEventExecutor
- 提供了 boolean inEventLoop(Thread thread) 方法判断一个线程是否属于此 EventLoop
- 提供了 EventLoopGroup parent() 方法来看看自己属于哪个 EventLoopGroup
事件循环组 EventLoopGroup
EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
- 继承自 netty 自己的 EventExecutorGroup
- 实现了 Iterable 接口提供遍历 EventLoop 的能力
- 另有 next 方法获取集合中下一个 EventLoop
处理普通与定时任务
public class TestEventLoop {public static void main(String[] args) {// 创建拥有两个EventLoop的NioEventLoopGroup,对应两个线程EventLoopGroup group = new NioEventLoopGroup(2);// 通过next方法可以获得下一个 EventLoopSystem.out.println(group.next());System.out.println(group.next());// 通过EventLoop执行普通任务group.next().execute(()->{System.out.println(Thread.currentThread().getName() + " hello");});// 通过EventLoop执行定时任务group.next().scheduleAtFixedRate(()->{System.out.println(Thread.currentThread().getName() + " hello2");}, 0, 1, TimeUnit.SECONDS);// 优雅地关闭group.shutdownGracefully();}
}
输出结果如下
io.netty.channel.nio.NioEventLoop@7bb11784 io.netty.channel.nio.NioEventLoop@33a10788
nioEventLoopGroup-2-1 hello
nioEventLoopGroup-2-2 hello2
nioEventLoopGroup-2-2 hello2
nioEventLoopGroup-2-2 hello2
关闭 EventLoopGroup
优雅关闭 shutdownGracefully
方法。该方法会首先切换 EventLoopGroup
到关闭状态从而拒绝新的任务的加入,然后在任务队列的任务都处理完成后,停止线程的运行。从而确保整体应用是在正常有序的状态下退出的
处理IO任务
服务器代码
public class MyServer {public static void main(String[] args) {new ServerBootstrap().group(new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));}});}}).bind(8080);}
}
客户端代码
public class MyClient {public static void main(String[] args) throws IOException, InterruptedException {Channel channel = new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new StringEncoder());}}).connect(new InetSocketAddress("localhost", 8080)).sync().channel();System.out.println(channel);// 此处打断点调试,调用 channel.writeAndFlush(...);System.in.read();}
}
分工
Bootstrap的group()方法可以传入两个EventLoopGroup参数,分别负责处理不同的事件
public class MyServer {public static void main(String[] args) {new ServerBootstrap()// 两个Group,第一个负责Accept连接事件,第二个负责读写事件//默认可以不用传 第一个还是1 第二个是电脑核心线程*2.group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))...}
}
多个客户端分别发送 hello
结果
nioEventLoopGroup-3-1 hello1
nioEventLoopGroup-3-2 hello2
nioEventLoopGroup-3-1 hello3
nioEventLoopGroup-3-2 hello4
nioEventLoopGroup-3-2 hello4
可以看出,一个EventLoop可以负责多个Channel,且EventLoop一旦与Channel绑定,则一直负责处理该Channel中的事件
增加自定义EventLoopGroup
当有的任务需要较长的时间处理时,可以使用非NioEventLoopGroup,避免同一个NioEventLoop中的其他Channel在较长的时间内都无法得到处理
public class MyServer {public static void main(String[] args) {// 增加自定义的非NioEventLoopGroup 独立的EventLoopGroupEventLoopGroup group = new DefaultEventLoopGroup();new ServerBootstrap().group(new NioEventLoopGroup(1), new NioEventLoopGroup(2)).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// 增加两个handler,第一个使用NioEventLoopGroup处理,第二个使用自定义EventLoopGroup处理socketChannel.pipeline().addLast("nioHandler",new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));// 将消息传给下一个handlerctx.fireChannelRead(msg);}})// 该handler绑定自定义的Group线程.addLast(group, "myHandler", new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));}});}}).bind(8080);}
}
启动四个客户端发送数据
nioEventLoopGroup-4-1 hello1
defaultEventLoopGroup-2-1 hello1
nioEventLoopGroup-4-2 hello2
defaultEventLoopGroup-2-2 hello2
nioEventLoopGroup-4-1 hello3
defaultEventLoopGroup-2-3 hello3
nioEventLoopGroup-4-2 hello4
defaultEventLoopGroup-2-4 hello4
可以看出,客户端与服务器之间的事件,被nioEventLoopGroup和defaultEventLoopGroup分别处理
切换的实现
不同的EventLoopGroup切换的实现原理如下
由上面的图可以看出,当handler中绑定的Group不同时,需要切换Group来执行不同的任务
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);// 获得下一个EventLoop, excutor 即为 EventLoopGroupEventExecutor executor = next.executor();// 如果下一个EventLoop 在当前的 EventLoopGroup中//当前header中的线程是否和EventLoop是同一线程if (executor.inEventLoop()) {// 使用当前 EventLoopGroup 中的 EventLoop 来处理任务next.invokeChannelRead(m);} else {// 否则让另一个 EventLoopGroup 中的 EventLoop 来创建任务并执行executor.execute(new Runnable() {public void run() {next.invokeChannelRead(m);}});}
}
- 如果两个 handler 绑定的是同一个EventLoopGroup,那么就直接调用
- 否则,把要调用的代码封装为一个任务对象,由下一个 handler 的 EventLoopGroup 来调用
相关文章:
Netty网络编程
入门案例 1、服务器端代码 public class HelloServer {public static void main(String[] args) {// 1、启动器,负责装配netty组件,启动服务器new ServerBootstrap()// 2、创建 NioEventLoopGroup,可以简单理解为 线程池 Selector.group(n…...
层三交换机解析(Layer 3 Switch)层3交换机
文章目录 层三交换机解析1. 概述1.1 什么是层三交换机1.2 层三交换机与路由器、二层交换机的比较 2. 层三交换机的工作原理2.1 基于MAC地址的转发2.2 基于IP地址的转发 3. 层三交换机的配置4. 常见问题与解答4.1 我应该使用路由器还是层三交换机?4.2 层三交换机可以…...
expect自动化交互
目录 1. expect作用: 2. expect语言用法: 3. 实例 1. expect作用: 是建立在tcl语言基础上的一个工具,常被用于进行自动化控制和测试,解决shell脚本中交互的相关问题。 2. expect语言用法: spawn开启免…...
探究两个互联网时代的差异,Web 2.0 与 Web 3.0 区别
Web 2.0 的特征 首先我们来了解一下 Web 2.0 的特征都有哪些。 用户生成内容:Web 2.0 时代以用户生成内容为特征,用户可以轻松地在网络上分享、创建和编辑信息。社交媒体平台、博客等网站的兴起使得用户成为信息的创造者,网络逐渐从被动浏览…...
c++基本常见错误总结
我们无论是在学习中还是在工作当中,总是会遇到各种各样的c编译错误问题,经常会有一种情况就是上一次好像遇到过这种问题,但是就是想不起来了(我就是这样)所以下面这一篇文章就是总结自己遇到的编译以及运行错误。 注意…...
鸿蒙原生应用/元服务开发-AGC分发如何生成密钥和和证书请求文件
HarmonyOS通过数字证书(.cer文件)和Profile文件(.p7b文件)等签名信息来保证应用的完整性,应用如需上架到华为应用市场必须通过签名校验。因此,开发者需要使用发布证书和Profile文件对应用进行签名后才能发布…...
Python自动化——driver.switch_to的用法
driver.switch_to的用法 from selenium import webdriver driver webdriver.Chrome(/path/to/chromedriver) # Chrome driver.get(https://www.example.com) # 打开网页 element driver.find_element_by_id(element_id) # 查找元素记得在不需要使用 driver 时,…...
【Java技术专题】「入门到精通系列教程」深入探索Java特性中泛型技术体系的原理和实战开发指南
深入探索Java特性中泛型技术体系的原理和实战开发指南 前提介绍泛型的介绍泛型带来的好处泛型引发的问题错觉:泛型也可以实现多态 泛型的特点类型擦除概念定义案例分析隐患问题开发要点类型擦除的过程案例代码 实例分析类型转换问题 通配符与上下界通配符List<Ob…...
基于Java SSM邮局订报管理系统
尽管电子读物越来越普及,但还是有很多读者对纸质刊物情有独钟,所以邮局的报刊征订业务一直非常受欢迎。邮局订报管理系统就是对客户在邮局订阅报刊进行管理,包括查询报刊、订阅报刊、订阅信息的查询、统计等的处理,系统的主要业务…...
【优选算法系列】【专题一双指针】第四节.15. 三数之和和18. 四数之和
文章目录 前言一、三数之和 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写 1.2.3 题目总结二、四数之和 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写 …...
字符集——带你了解UTF-8的前世今生
文章目录 字符集的来历汉字和字母的编码特点Unicode字符集字符集小结编码和解码开发约定 字符集的来历 计算机是美国人发明的,由于计算机能够处理的数据只能是0和1组成的二进制数据,为了让计算机能够处理字符,于是美国人就把他们会用到的每一…...
数据分析工具比较:Excel vs Python vs R
写在开头 在数据分析的世界里,选择合适的工具至关重要。本篇博客将深入比较常用的数据分析工具,包括Excel、Python和R,以帮助读者更好地选择适合自己需求的工具。 1.Excel:经典易用的电子表格 优势: 用户友好&…...
Java基础数据类型
Java有八种基础的数据类型,它们被分为两个主要的类别:原始类型和引用类型。原始类型又被分为四类:整型、浮点型、字符型和布尔型。 整型(Integral Types): 这些类型用于存储整数。它们包括: ○…...
Linux-Linux安装JDK及配置环境 及 遇到的问题
下载linux环境对应的JDK的tar.gz包 配置JDK环境:编辑 sudo vim /etc/profile 在文件的最下方,填写 export JAVA_HOME/usr/local/src/software/jdk1.8 export CLASSPATH.:$JAVA_HOME/lib/tools.jar export PATH$JAVA_HOME/bin:$PATH 执行生效命令&…...
后端架构的一些知识
目录 一.抖音 二.大型网站是如何管理海量的数据的 三.大型网站停机一天会造成多大损失 四.如何设计一套安全,健壮,可扩展,稳定性强的后端系统 五.如何在不影响原来代码的基础上进行功能更新 六.大型网站一年都不停机吗 七.线上业务出现…...
golang使用sip实现语音通话
在使用 github.com/cloudwebrtc/sip 这个 Go 语言库时,要实现通话,您需要处理 SIP 协议的一系列操作,包括建立和终止呼叫、处理媒体传输等。以下是一个简化的示例代码,演示如何使用该库来处理 SIP 通话的基本流程: pac…...
【1day】蓝凌OA 系统custom.jsp 接口任意文件读取漏洞学习
注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录 一、漏洞概述 二、影响版本 三、资产测绘 四、漏洞复现...
OWASP Web 安全测试指南-Web 应用程序安全测试
Web 应用程序安全测试 4.0 简介和目标 4.1 信息收集 4.2 配置和部署管理测试 4.3 身份管理测试 4.4 身份验证测试 4.5 授权测试 4.6 会话管理测试 4.7 输入验证测试 4.8 错误处理测试 4.9 弱密码测试 4.10 业务逻辑测试 4.11 客户端测试 4.0 简介和目标 本节介绍 O…...
oracle FUNCTION(任意两个时间 之间的工作小时)
写函数计算 任意两个时间 之间的工作小时 每天工作时间(8:00 - 20:00 共12小时),没有休息日 CREATE OR REPLACE FUNCTION SC_YD_DESI.CALCULATE_WORK_HOURS_FUNC (p_current_time IN DATE,p_order_time IN DATE ) RETURN NUMBER ASp_work_hou…...
【“C++ 精妙之道:解锁模板奇谭与STL精粹之门“】
【本节目标】 1. 泛型编程 2. 函数模板 3. 类模板 4. 什么是STL 5. STL的版本 6. STL的六大组件 7. STL的重要性 8. 如何学习STL 9.STL的缺陷 1. 泛型编程 如何实现一个通用的交换函数呢? void Swap(int& left, int& right) {int temp left;lef…...
el-date-picker时间控制范围为过去时间不可选
<el-date-picker :picker-options"startPickerOptions()" value-format"yyyy-MM-dd HH:mm:ss" v-model"form.applyFixPlan" type"datetime" placeholder"选择日期时间"> </el-date-picker> 在method中定义star…...
音视频技术开发周刊 | 322
每周一期,纵览音视频技术领域的干货。 新闻投稿:contributelivevideostack.com。 超级AI不会主宰人类,但人工智能必须开源!LeCun最新采访引全网300万人围观 LeCun最新访谈视频中,再次坦露了自己对开源AI的看法。超级AI…...
面试就是这么简单,offer拿到手软(三)—— 常见中间件框架面试题,es,redis,dubbo,zookeeper kafka 等
面试就是这么简单,offer拿到手软(一)—— 常见非技术问题回答思路 面试就是这么简单,offer拿到手软(二)—— 常见65道非技术面试问题 面试就是这么简单,offer拿到手软(三ÿ…...
【Spring系列】DeferredResult异步处理
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
使用晶振遇到的两个问题
并联电阻的问题 在一些方案中,晶振并联1MΩ电阻时,程序运行正常,而在没有1MΩ电阻的情况下,程序运行有滞后及无法运行现象发生。 原因分析: 在无源晶振应用方案中,两个外接电容能够微调晶振产生的时钟频率…...
手写promise A+、catch、finally、all、allsettled、any、race
目录 手写promise 同步版 1.Promise的构造方法接收一个executor(),在new Promise()时就立刻执行executor回调 2.executor()内部的异步任务被放入宏/微任务队列,等待执行 3.状态与结果的管理 状态只能变更一次 4.then()调用成功/失败回调 catch是…...
【原神游戏开发日志1】缘起
【原神游戏开发日志1】缘起 版权声明 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明 文章内容不得删减、修改、演绎 相关学习资源见文末 大家好,最近看到原神在TGA上频频获奖,作为一个14年经验的游戏开…...
leetcode5 最长公共前缀三种python解法
14. 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""。 示例 1: 输入:strs ["flower","flow","flight"] 输出:"fl"示…...
对小程序的初了解
WXML和HTML的区别 标签名称不同 HTML:div、a、span、img WXML:view、text、image、navigator 属性节点不同 <a href"#">超链接</a> <navigator url"/pages/home/home"></navigator> 提供了类似vue的…...
QLineEdit 的 InputMask掩码
QLineEdit 的 InputMask掩码 A:只能输入字母,且不可省略 a:只能输入字母,可以省略 N:只能输入 字母和数字,且不可省略 n:只能输入 字母和数字,可以省略 X:可以输入任意字…...
深圳做网站龙华新科/百度关键词查询
一、日常关注的问题1、我们的日志生成速度?1、每天生成多少日志、产生多少redo logmysql> show global status like Innodb_os_log_written;-------------------------------| Variable_name | Value |-------------------------------| Innodb_os_log_written |…...
dwcs2018怎么做动态网站/高明公司搜索seo
EG:打印文件结果打印出一片空白 原因:使用了null的数据源而不是JREmptyDataSource 以下为正确代码 public <T> List<JasperPrint> createJasperPrint_1(List<T> list, Map<String, Object> imgMap, Map<String, Object>…...
张家港seo建站/seo排名优化什么意思
苏生不惑第266 篇原创文章,将本公众号设为星标,第一时间看最新文章。之前分享过签到太麻烦?全自动一键签到网易云音乐,b站,京东,百度贴吧等网站 和账号升级攻略:每天自动升级b站和网易云音乐&am…...
静态网站建设/dz论坛seo设置
前言:上一篇讲解了linux下使用命令行操作数据库,这篇继续讲解怎么使用c语言实现linux数据库的操作。使用c语言实现环境搭建:既然我们要使用c语言实现linux数据库操作,那么首先我们得先把数据库源码的文件打包成库文件.so放到ubunt…...
淄博网站建设给力臻动传媒/友情链接交换平台源码
如今Docker的使用已经非常普遍,特别在一线互联网公司。使用Docker技术可以帮助企业快速水平扩展服务,从而到达弹性部署业务的能力。在云服务概念兴起之后,Docker的使用场景和范围进一步发展,如今在微服务架构越来越流行的情况下&a…...
合肥网约车收入怎么样/seo优化策略
要监视原始的传感器数据,你需要实现两个通过SensorEventListener接口暴露的回调方法:onAccuracyChanged()和onSensorChanged()。 传感器数据的速度值,这些值如下: 1.SENSOR_DELAY_GAME : 如果利用传感器开发游戏&#…...