netty之处理连接源码分析
写在前面
在这篇文章看了netty服务是如何启动的,服务启动成功后,也就相当于是迎宾工作都已经准备好了,那么当客人来了怎么招待客人呢?也就是本文要看的处理连接的工作。
1:正文
先启动源码example模块的echoserver,后启动echoclient进行debug调试。
从哪里开始呢,是从EventLoop方法中开始,在EventLoop中有一个run方法(注意不是Thread的run方法),通过死循环的方式不断的轮询selector的事件,这里自然是轮询op_accept事件了,故事就从这里开始了,如下:
// io.netty.channel.nio.NioEventLoop#run
// 系循环方式处理事件,相当用户死循环selector.select
@Override
protected void run() {int selectCnt = 0;for (;;) {try {// ...if (ioRatio == 100) {// ...} else if (strategy > 0) {final long ioStartTime = System.nanoTime();try {// 处理select到的事件processSelectedKeys();} finally {// ...}} else {// ...}// ...}
}
processSelectedKeys如下:
// io.netty.channel.nio.NioEventLoop#processSelectedKeys
private void processSelectedKeys() {// processSelectedKeysOptimized是netty的优化版本(使用了反射等),更少的gc,1%~2%的效率提升等if (selectedKeys != null) {processSelectedKeysOptimized();} else {// 这里直接使用Java NIO的方式来获取发生发的事件了(一般不走)processSelectedKeysPlain(selector.selectedKeys());}
}
这里执行processSelectedKeysOptimized:
// io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized
private void processSelectedKeysOptimized() {for (int i = 0; i < selectedKeys.size; ++i) {final SelectionKey k = selectedKeys.keys[i]; // 事件对应的selection key// null out entry in the array to allow to have it GC'ed once the Channel close// See https://github.com/netty/netty/issues/2363selectedKeys.keys[i] = null;final Object a = k.attachment(); // 这里的attachment就是serversocketchannel了,在serversocketchannle绑定到selector时指定的if (a instanceof AbstractNioChannel) { // 这里自然是true了,因为我们使用的是Java NIO的方式processSelectedKey(k, (AbstractNioChannel) a);} else {// ...}// ...}
}
注意代码final Object a = k.attachment();获取到对应server socket channel,比较重要,接着看代码processSelectedKey(k, (AbstractNioChannel) a);:
// io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();// ...try {// ...// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead// to a spin loop 接受连接的话会执行到这里if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {unsafe.read();}} catch (CancelledKeyException ignored) {unsafe.close(unsafe.voidPromise());}
}
这里的readyOps自然就是op_read,1了,unsafe.read();,执行到io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages:
// io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages
protected int doReadMessages(List<Object> buf) throws Exception {SocketChannel ch = SocketUtils.accept(javaChannel()); // 内部会accept连接生成socketchannel类 serverSocketChannel.accept();try {if (ch != null) {buf.add(new NioSocketChannel(this, ch)); // 存储到buf,后续会从buf中拿return 1; // 代表buf中元素的数量}} catch (Throwable t) {// ...}return 0;
}
SocketChannel ch = SocketUtils.accept(javaChannel());就accept了,接着执行代码:
// io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read
public void read() {// ...try {// ...int size = readBuf.size();for (int i = 0; i < size; i ++) {readPending = false;pipeline.fireChannelRead(readBuf.get(i)); // 通过pipeline开始处理连接请求,主要看ServerBootstrapAcceptor,其他的都是辅助的handler,如日志打印等}// ...} finally {// ...}
}
pipeline.fireChannelRead(readBuf.get(i));主要看ServerBootstrapAcceptor,其他的都是辅助的handler,如日志打印等:
// io.netty.bootstrap.ServerBootstrap.ServerBootstrapAcceptor#channelRead
public void channelRead(ChannelHandlerContext ctx, Object msg) {// ...try {// 完成注册,不管是serversocketchannel还是socketchannel,都是注册到selector中childGroup.register(child).addListener(new ChannelFutureListener() {// ...});} catch (Throwable t) {forceClose(child, t);}
}
childGroup.register:
// io.netty.channel.AbstractChannel.AbstractUnsafe#register
public final void register(EventLoop eventLoop, final ChannelPromise promise) {// ...// eventLoop.inEventLoop()判断是否为eventgrou线程,这里不是,如果是main启动的话,则此时是main threadlogger.info("当前的线程是: " + Thread.currentThread().getName());if (eventLoop.inEventLoop()) {register0(promise);} else { // 使用eventloop的线程执行注册到seletor的工作try {eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});} catch (Throwable t) {// ...}}
}
此处在这篇文章已经看过了,只不过这里是将socketchannel注册到selector而已。因为注册op事件比较重要,所以这里再来看下:
// io.netty.channel.AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {try {// ...// Only fire a channelActive if the channel has never been registered. This prevents firing// multiple channel actives if the channel is deregistered and re-registered.if (isActive()) {if (firstRegistration) {pipeline.fireChannelActive();} else if (config().isAutoRead()) {// ...}}} catch (Throwable t) {// ...}
}
pipeline.fireChannelActive();最终执行到:
// io.netty.channel.nio.AbstractNioChannel#doBeginRead
protected void doBeginRead() throws Exception {// Channel.read() or ChannelHandlerContext.read() was calledfinal SelectionKey selectionKey = this.selectionKey;if (!selectionKey.isValid()) {return;}readPending = true;// 完成op事件的注册,对于serversocketchannel就是op_acceptfinal int interestOps = selectionKey.interestOps();if ((interestOps & readInterestOp) == 0) {logger.info("注册op事件:{}", (interestOps | readInterestOp));selectionKey.interestOps(interestOps | readInterestOp);}
}
自然这里注册的op_code就是1,op_read了。
2:流程总结
1:event loop死循环执行selectfor (;;) {}
2:监听到op_accept事件,acceept连接,创建socketchannelSocketChannel ch = SocketUtils.accept(javaChannel())
3:绑定op_read事件等待读取数据selectionKey.interestOps(interestOps | readInterestOp);
3:两个线程
其中一个是boss group对应的eventloop中的线程,另一个是worker group对应的event loop中的线程:
boss线程:轮询selector,accept连接,创建socket channel为读写客户端数据做准备初始化socket channel,从worker group中选择一个event loop
worker线程:绑定socketchannel到selector中注册socketchannel的op_read事件到selector中,准备读取数据
写在后面
参考文章列表
netty之是如何做好服务准备的。
相关文章:
netty之处理连接源码分析
写在前面 在这篇文章看了netty服务是如何启动的,服务启动成功后,也就相当于是迎宾工作都已经准备好了,那么当客人来了怎么招待客人呢?也就是本文要看的处理连接的工作。 1:正文 先启动源码example模块的echoserver&a…...
Dockerfile文件编写
1、打nginx原始包 登录后复制 ROM nginxENV LANG zh_CN.UTF-8 ENV LC_ALL zh_CN.UTF-8 ENV TZ Asia/Singapore# 设置时区,同样保持在一层 RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \echo "${TZ}" > /etc/timezoneRUN apt-get …...
Oracle SQL 使用 ROWNUM 分页查询速度太慢的问题及解决方案!
在使用 Oracle 数据库进行数据查询时,分页查询是一种常见的需求。传统上,开发者常常使用 ROWNUM 来实现分页功能。 然而,当数据量较大时,使用 ROWNUM 进行分页查询可能会导致性能问题。本文将深入探讨这一问题的原因,并提供多种解决方案,以提高分页查询的性能。 一、RO…...
Django3 + Vue.js 前后端分离书籍添加项目Web开发实战
文章目录 Django3后端项目创建切换数据库创建Django实战项目App新建Vue.js前端项目 Django3后端项目创建 创建Django项目,采用Pycharm或者命令行创建皆可。此处,以命令行方式作为演示,项目名为django_vue。 django-admin startproject djang…...
楼梯区域分割系统:Web效果惊艳
楼梯区域分割系统源码&数据集分享 [yolov8-seg-FocalModulation&yolov8-seg-GFPN等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Global Al l…...
Day10加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 class Solution {public int[] plusOne(int[] di…...
UTF-8简介
UTF-8 UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。它可以用一至四个字节对Unicode字符集中的所有有效编码点进行编码,属于Unicode标准的一部分,最初由肯汤普逊…...
基于Openwrt系统架构,实现应用与驱动的实例。
一、在openwrt系统架构,编写helloworld的应用程序。 第一步先创建目录,项目代码要放在 openwrt根目下的 package 目录中,这里源码写在了 hellworld 的 src 目录下,因为外层还有需要编写的文件。 mkdir -p ~/openwrt/package/hel…...
SQL进阶技巧:如何利用三次指数平滑模型预测商品零售额?
目录 0 问题背景 1 数据准备 2 问题解决 2.1 模型构建 (1)符号规定 (2)基本假设...
HTB:Cicada[WriteUP]
目录 连接至HTB服务器并启动靶机 使用nmap对靶机进行开放端口扫描 使用nmap对靶机开放端口进行脚本、服务信息扫描 首先尝试空密码连接靶机SMB服务 由于不知道账户名,这里我们使用crackmapexec对smb服务进行用户爆破 通过该账户连接至靶机SMB服务器提取敏感信…...
小张求职记四
学校食堂的装修富丽堂皇,像个金碧辉煌的宫殿,可实际上却充斥着廉价的塑料制品和刺鼻的消毒水味。这金玉其外败絮其中的景象,与食堂承包商的“精明算计”如出一辙。 小张和小丽应约来到了一个档口下,“红烧肉”,之前就是…...
适用于 c++ 的 wxWidgets框架源码编译SDK-windows篇
本文章记录了下载wxWidgets源码在windows 11上使用visual Studio 2022编译的全过程,讲的不详细请给我留言,让我知道错误并改进。 本教程是入门级。有更深入的交流可以留言给我。 如今互联网流行现在大家都忘记了这块桌面的开发,我认为桌面应用还是有用武之地,是WEB无法替代…...
flink 内存配置(二):设置TaskManager内存
TaskManager在Flink中运行用户代码。根据需要配置内存使用,可以极大地减少Flink的资源占用,提高作业的稳定性。 注意下面的讲解适用于TaskManager 1.10之后的版本。与JobManager进程的内存模型相比,TaskManager内存组件具有类似但更复杂的结构…...
【C++ 算法进阶】算法提升八
复杂计算 (括号问题相关递归套路 重要) 题目 给定一个字符串str str表示一个公式 公式里面可能有整数 - * / 符号以及左右括号 返回最终计算的结果 题目分析 本题的难点主要在于可能会有很多的括号 而我们直接模拟现实中的算法的话code会难写 要考虑…...
阿里云实时数据仓库HologresFlink
1. 实时数仓Hologres特点 专注实时场景:数据实时写入、实时更新,写入即可见,与Flink原生集成,支持高吞吐、低延时、有模型的实时数仓开发,满足业务洞察实时性需求。 亚秒级交互式分析:支持海量数据亚秒级交…...
生成式语言模型的文本生成评价指标(从传统的基于统计到现在的基于语义)
文本生成评价指标 以 BLEU 为代表的基于统计的文本评价指标基于 BERT 等预训练模型的文本评价指标 1.以 BLEU 为代表的基于统计的文本评价指标 1.BLEU(Bilingual Evaluation Understudy, 双语评估辅助工具) 所有评价指标的鼻祖,核心思想是比较 候选译文 和 参考…...
【网安案例学习】暴力破解攻击(Brute Force Attack)
### 案例与影响 暴力破解攻击在历史上曾导致多次重大安全事件,特别是在用户数据泄露和账户被盗的案例中。随着计算能力的提升和密码管理技术的进步,暴力破解的威胁虽然有所减弱,但仍需警惕,特别是在面对高价值目标时。 【故事一…...
时间序列预测(十八)——实现配置管理和扩展命令行参数解析器
如图,这是一个main,py文件,在此代码中,最开始定义了许多模型参数,为了使项目更加灵活和可扩展,便于根据不同的需求调整参数和配置,可以根据实际需要扩展参数和配置项。 下面是如何实现配置管理和扩展命令行…...
Vue问题汇总解决
作者:fyupeng 技术专栏:☞ https://github.com/fyupeng 项目地址:☞ https://github.com/fyupeng/distributed-blog-system-api 留给读者 我们经常在使用Vue开发遇到一些棘手的问题,解决后通常要进行总结,避免下次重复…...
Spark学习
Spark简介 1.Spark是什么 首先spark是一个计算引擎,而不是存储工具,计算引擎有很多: 第一代:MapReduce廉价机器实现分布式大数据处理 第二代:Tez基于MR优化了DAG,性能比MR快一些 第三代:Spark…...
top50 BF16算力(TFLOPS) 显卡排行榜 天梯图
排名显卡型号BF16算力(TFLOPS)售价(元)单TFLOPS价格(元)1B200(SXM)45002200000488.892H200(SXM)19801200000606.063MI300X1307750000573.834H100 SXM519501100000564.105RTX PRO 6000 Blackwell1150780000678.266H100 PCIe 80GB1560850000544.877RTX 50906803400050.008A100 80…...
从入门到上岗,Java+AI 复合型人才养成攻略
当下编程行业格局正在悄然改变,纯 Java 后端岗位内卷日趋严重,薪资增长逐步放缓;纯粹的 AI 算法岗门槛居高不下,对学历、数理功底要求严苛,普通开发者很难入局。 而Java+AI 复合型开发顺势成为行业刚需岗位,既依托成熟的 Java 体系承接业务开发,又能融入人工智能技术实…...
硬件答辩问题总结
一、电源纹波是什么,为什么LDO的小,DCDC的大1.电源纹波电源纹波 是指直流电源输出电压上叠加的 交流波动成分,表现为电压在理想直流值附近上下波动。2.LDO 纹波小原理LDO 内部是一个 调整管(可变电阻) 串联在输入和输出…...
从Office功能区的“局外人“到“掌控者“:Office RibbonX Editor深度指南
从Office功能区的"局外人"到"掌控者":Office RibbonX Editor深度指南 【免费下载链接】office-ribbonx-editor An overhauled fork of the original Custom UI Editor for Microsoft Office, built with WPF 项目地址: https://gitcode.com/g…...
雪球网md5__1038参数逆向解析与Node.js复现
1. 这不是“破解”,而是对前端加密逻辑的常规逆向还原你打开雪球网任意一只股票详情页,F12 打开开发者工具,切到 Network 面板,刷新页面——很快就能在 XHR 请求里捕获到类似这样的接口:https://xueqiu.com/stock/cube…...
3步深度解锁:网络设备权限管理工具的实战手册
3步深度解锁:网络设备权限管理工具的实战手册 【免费下载链接】zteOnu A tool that can open ZTE onu device factory mode 项目地址: https://gitcode.com/gh_mirrors/zt/zteOnu 你是否曾面对功能受限的网络设备感到束手无策?当默认配置锁死了硬…...
Postgresql基础实践教程(九)
⭐️⭐️⭐️⭐️⭐️ 完整数据详见 练习数据免费 ⭐️⭐️⭐️⭐️⭐️ 七十二、WITH查询(公用表表达式CTE) 1. SELECT 中的 WITH 2. 递归查询 3. 公用表表达式的物化 4. WITH中的数据修改语句 WITH提供了一种在主查询中写辅助语句的方法。这些语…...
CSharpVerbalExpressions常见问题解答:解决开发者遇到的10个典型挑战
CSharpVerbalExpressions常见问题解答:解决开发者遇到的10个典型挑战 【免费下载链接】CSharpVerbalExpressions 项目地址: https://gitcode.com/gh_mirrors/cs/CSharpVerbalExpressions CSharpVerbalExpressions是一个强大的C#库,它通过类自然语…...
TorchDynamo与TorchInductor:PyTorch编译器生态的完整解析
TorchDynamo与TorchInductor:PyTorch编译器生态的完整解析 【免费下载链接】torchdynamo A Python-level JIT compiler designed to make unmodified PyTorch programs faster. 项目地址: https://gitcode.com/gh_mirrors/to/torchdynamo TorchDynamo 是一个…...
用Azure Kinect DK和Body Tracking SDK,5分钟实现一个实时人体骨骼点检测Demo(C++版)
5分钟实战:用Azure Kinect DK实现实时人体骨骼点追踪(C版) 当你第一次拿到Azure Kinect DK时,最令人兴奋的莫过于它强大的人体追踪能力。这款深度相机不仅能捕捉高清彩色图像,更能通过AI算法实时重建人体骨骼关节点。本…...
