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…...
京东 SPU/SKU 数据接口全解读:商品详情 API 文档(2026 最新版)
京东商品详情 API 体系以SPU(标准产品单元)聚合、SKU(库存单元)明细为核心设计,覆盖商家开放平台(JOS)、京东联盟两大核心场景,支持单品 / 批量查询、全字段 / 指定字段返回…...
vite-plugin-federation实战:构建React+Vue混合应用完整教程
vite-plugin-federation实战:构建ReactVue混合应用完整教程 【免费下载链接】vite-plugin-federation Module Federation for vite & rollup 项目地址: https://gitcode.com/gh_mirrors/vi/vite-plugin-federation 想要在Vite项目中实现模块联邦…...
Oak安全最佳实践:10个防范常见Web攻击的终极指南
Oak安全最佳实践:10个防范常见Web攻击的终极指南 【免费下载链接】oak A middleware framework for handling HTTP with Deno 🐿️ 🦕 项目地址: https://gitcode.com/gh_mirrors/oa/oak Oak是一个基于Deno的现代化中间件框架…...
一键切换模型:OpenClaw同时管理多个SecGPT-14B实例
一键切换模型:OpenClaw同时管理多个SecGPT-14B实例 1. 为什么需要管理多个模型实例 去年我在搭建本地AI安全分析系统时,遇到了一个典型困境:当SecGPT-14B模型需要版本升级时,整个服务必须停机。更糟的是,有次模型推理…...
百考通:AI精准驱动数据分析,让数据价值高效落地
在数字化浪潮席卷各行各业的今天,数据已成为核心生产要素,但如何从海量数据中挖掘价值、辅助决策,始终是企业与个人面临的核心难题。传统数据分析流程繁琐、技术门槛高、周期漫长,让许多非专业人士望而却步。百考通(ht…...
仅用200行代码重构内存管理模块:某AI平台将GPU服务器月成本压至$1,840的独家策略(限时开源)
第一章:Python 智能体内存管理策略Python 的内存管理并非由开发者直接操控,而是由解释器内置的智能体协同完成——包括引用计数、循环垃圾回收器(GC)和内存池机制三者构成动态平衡系统。这一“智能体”在运行时持续感知对象生命周…...
FPGA开发流程全解析:从Verilog代码到硬件实现的7个关键步骤
FPGA开发实战指南:从代码到硬件的全流程精要 在电子设计自动化领域,FPGA开发因其灵活性和高性能优势,正成为越来越多工程师的首选方案。不同于传统ASIC开发的漫长周期和高昂成本,FPGA允许设计者在硬件层面进行快速迭代和验证&…...
事件驱动:为AI原生应用领域注入新活力
事件驱动:为AI原生应用领域注入新活力关键词:事件驱动、AI原生应用、事件流、实时响应、异步架构、微服务、事件溯源摘要:本文将带你走进「事件驱动」与「AI原生应用」的交叉领域,通过生活案例、技术原理解析和实战代码࿰…...
Azure IoT Hub AMQP传输层深度解析与嵌入式实践
1. Azure IoT Hub AMQP 传输层技术深度解析Azure IoT Hub 是微软面向物联网场景构建的高可靠、可扩展云平台,其核心能力依赖于多种协议栈的协同支持。在众多通信协议中,AMQP(Advanced Message Queuing Protocol)因其固有的消息可靠…...
comsol复合相变墙体保温隔热,comsol论文复现建模仿真 模拟室外温度变化复合墙体温度变化过程
comsol复合相变墙体保温隔热,comsol论文复现建模仿真 模拟室外温度变化复合墙体温度变化过程,对比普通墙体的保温隔热性能大夏天顶着40度高温站阳台收衣服的时候,总想着要是墙体能像冰柜门一样隔热该多好。最近用COMSOL折腾了个复合相变墙体模…...
