news 2026/7/7 7:02:01

Modbus协议本质解析:RTU/ASCII/TCP报文结构与CRC16校验原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Modbus协议本质解析:RTU/ASCII/TCP报文结构与CRC16校验原理

1. Modbus不是“软件”,而是一套工业现场的通用语言

很多人第一次听说Modbus,是在下载“Modbus Poll”或“Modbus Slave”这类工具时——点开安装包,弹出注册密钥框,心里一紧:“这该不会是盗版吧?”接着搜“modbus poll密钥”“modbus slave key”,跳出来一堆百度云链接和论坛求key帖。但我要先说清楚:Modbus本身不收费、不加密、不绑定设备、不依赖任何厂商授权。它不是某个公司开发的闭源软件,而是一份1979年就公开发布的、只有28页的工业通信协议规范(Modicon Modbus Protocol Reference Guide, PI-MBUS-300)。就像TCP/IP是互联网的底层语言,Modbus就是PLC、传感器、电表、变频器这些工业设备之间“说话”的普通话。

你用Modbus Poll去读一台施耐德PLC的保持寄存器,用LabVIEW里的Modbus Master控件去写西门子S7-1200的线圈,用C#写的上位机通过Socket连接汇川H5U控制器的Modbus TCP端口——所有这些操作背后,没有调用任何私有SDK,没有走厂商特供通道,全靠双方严格遵守同一套报文格式、功能码定义和校验规则。这就是Modbus最硬核的价值:零成本、跨品牌、免授权、可验证。你手头有一台万用表、一个USB转RS485适配器、再加一台笔记本,就能完整抓包、解析、构造、发送任意Modbus报文。我2013年刚入行时,在东莞一家做注塑机联网的公司,就是靠一台二手ThinkPad+CH340串口模块+自己手写的Python脚本,三天内摸清了三台不同品牌温控表的Modbus RTU通讯逻辑,连厂家技术支持都没我们快。

所以当你看到热搜词里反复出现“modbus poll注册密钥”“modbus slave下载百度云”,本质上反映的是一个行业现状:大量工程师知道怎么“用工具”,却没真正理解协议本身。他们把Modbus Poll当成“Modbus软件”,把Modbus Slave当成“Modbus服务器”,把密钥失效当成“协议被锁”。其实只要打开Wireshark抓一次Modbus TCP流量,或者用逻辑分析仪看一眼RS485总线上的波形,再对照协议文档里那张经典的“功能码表格”,你会发现:所谓“密钥”,只是软件作者设置的试用限制;所谓“破解”,不过是绕过一个MessageBox弹窗;而真正的难点,永远在报文为什么发不出去、响应为什么超时、数据为什么错乱——这些,跟密钥毫无关系。

这也是我写这篇内容的出发点:不教你怎么找注册码,而是带你亲手拆开Modbus的“报文外壳”,看清CRC16怎么算、功能码怎么选、RTU帧怎么组、TCP头怎么加。你会明白,当C#多个Modbus TCP连接同时失败时,问题不在库本身,而在你没设对Socket的KeepAlive参数;当LabVIEW里找不到Modbus Master控件时,不是软件没装全,而是你漏装了NI Industrial Communications for Modbus模块;当Modbus RTU报文校验老出错,大概率是你把“偶校验”误设成了“无校验”,而不是CRC算法写错了。Modbus从来就不神秘,它只是需要你用工程师的方式——动手、测量、验证、推演——去对待。

2. 协议本质:三种物理层+两种封装方式的组合逻辑

Modbus不是单一协议,而是一个协议族。它的核心设计哲学非常朴素:让不同硬件能力的设备,都能用最省事的方式“开口说话”。因此它天然分化为三类物理层实现(RTU、ASCII、TCP),再叠加两种数据封装方式(原始串行帧、TCP/IP封装),最终形成我们日常接触的Modbus RTU、Modbus ASCII、Modbus TCP三大形态。理解这个组合逻辑,是避开90%通讯故障的前提。

2.1 Modbus RTU:工业现场的“电报式”通讯

Modbus RTU是应用最广、也最容易踩坑的一种。它跑在RS-232或RS-485物理链路上,用二进制字节流传输数据,帧结构极简:[从站地址][功能码][数据区][CRC16校验]。关键在于“RTU”三个字母——Remote Terminal Unit,直译是“远程终端单元”,暗示它专为资源受限的嵌入式设备设计。比如一台温湿度变送器,MCU只有64KB Flash、8KB RAM,根本跑不动TCP/IP协议栈,但必须把数据传给上位机。这时RTU就是最优解:一帧报文最长256字节,主站发一帧,从站回一帧,中间无需握手、无需重传、无需状态维护,纯靠时间间隔判断帧边界。

提示:RTU帧的起始和结束,不是靠特殊字符,而是靠“3.5个字符时间”的静默期。例如波特率9600bps时,1个字符=10bit(1起始+8数据+1停止),传输1字符需约1.04ms,那么3.5字符时间≈3.64ms。主站发完最后一字节后,必须等待≥3.64ms才认为一帧结束;从站收到数据后,也必须在此时间内开始回复,否则主站判定超时。这个“时间窗口”是RTU的灵魂,也是现场调试时最常被忽略的点——很多“通讯不稳定”,其实是RS-485收发使能控制时序没对准,导致从站回复被截断。

2.2 Modbus ASCII:给调试员留的“人眼友好模式”

如果你用串口调试助手发过Modbus指令,大概率见过类似":010300000002C4"这样的字符串。这就是Modbus ASCII模式。它把RTU的二进制字节,用ASCII十六进制字符表示(如0x01变成"01"),前后加冒号":"和回车换行"\r\n",校验用LRC(纵向冗余校验)而非CRC16。好处是肉眼可读、可用普通串口工具直接发;坏处是传输效率砍半——RTU发10字节,ASCII要发22字节(每个字节变2字符+头尾符号)。

注意:ASCII模式在实际工程中已基本淘汰。它存在的唯一价值,是帮新手理解报文结构。比如你想确认某台仪表是否支持功能码0x03(读保持寄存器),直接在串口助手里发":010300000002C4\r\n",如果返回":01030400010002FA\r\n",说明它正常响应了——前两位"01"是从站地址,"03"是功能码,"04"是返回字节数,"00010002"是两个寄存器值(各2字节),"FA"是LRC校验。这种“所见即所得”的调试体验,是RTU做不到的。但一旦进入量产环境,没人会用ASCII,因为速率低、易受干扰、设备兼容性差。

2.3 Modbus TCP:把串口协议“搬上网络”的巧妙封装

Modbus TCP的诞生,本质是解决一个现实矛盾:工厂里新上马的上位机都是千兆网口,老设备却只有RS-485接口。直接换设备成本太高,于是工程师想了个办法——用一个“网关”把RS-485信号转成TCP数据包,让Modbus协议跑在以太网上。但Modbus原始协议没定义网络层,怎么办?答案是:不改协议内核,只加一个7字节的TCP报文头

这个头长得很“敷衍”:2字节事务标识(随便填,用于匹配请求/响应)、2字节协议标识(固定0x0000)、2字节长度字段(后续字节数)、1字节单元标识(原RTU的从站地址)。举个例子:RTU报文01 03 00 00 00 02 C4 0B(读1号从站0地址起2个保持寄存器),在TCP里就变成:

00 01 // 事务ID=1 00 00 // 协议ID=0 00 06 // 后续长度=6字节(功能码+数据区) 01 // 单元ID=1(即原从站地址) 03 // 功能码 00 00 // 起始地址高字节 00 02 // 寄存器数量

总共12字节。你会发现,除了前面7字节头,后面5字节和RTU完全一致。这就是Modbus TCP的精妙之处:它不是新协议,而是RTU协议的“网络快递包装”。所以当你用Wireshark抓包时,看到Modbus TCP流量,直接忽略前7字节,后面的内容和RTU一模一样——功能码、地址、数据、校验(TCP不用CRC,靠IP/TCP自带校验)全都不变。

实操心得:很多初学者以为Modbus TCP比RTU“更高级”,其实恰恰相反。TCP的可靠性由网络层保障,而RTU的可靠性全靠应用层设计(比如重发机制、超时判断)。所以TCP环境下,你反而要更小心“粘包”和“半包”问题——TCP是流式协议,一帧Modbus报文可能被拆成两段发,也可能两帧被合并成一段收。正确做法是:按协议头里的“长度字段”来截取完整报文,而不是简单地recv(1024)。我在做C# Modbus TCP客户端时,就吃过这个亏:没做缓冲区管理,遇到网络抖动,直接把两帧报文当一帧解析,结果数据全乱。

3. 报文解剖:从功能码到CRC16,手把手推演每一字节

理解Modbus,不能停留在“知道有功能码03、06、16”,而要能看着一串十六进制,当场说出它在干什么、为什么这么干、哪里可能出错。下面我以最常用的“读保持寄存器(0x03)”为例,逐字节拆解,并同步给出C#和Python的计算代码,确保你能照着抄作业。

3.1 一个真实RTU报文的诞生:读1号从站,0x0000地址起的2个寄存器

假设我们要读PLC的VW0和VW2两个字(共2个寄存器,每个寄存器16位),从站地址是1,起始地址是0x0000,数量是2。RTU请求帧应为:

01 03 00 00 00 02 C4 0B

现在逐字节解释:

  • 01:从站地址。这是主站指定“谁来回答”。注意:地址0是广播地址(所有从站都执行但不回复),实际设备地址范围通常是1~247。
  • 03:功能码。0x03=读保持寄存器(Holding Register),这是PLC里用户可读写的存储区,对应西门子的DB块、三菱的D寄存器。其他常用功能码:0x01读线圈(DO)、0x02读输入状态(DI)、0x04读输入寄存器(AI)、0x06写单个寄存器、0x10写多个寄存器。
  • 00 00:起始地址高字节+低字节。Modbus地址是16位无符号整数,0x0000=0,即第一个保持寄存器。这里有个经典误区:很多人以为地址0对应PLC里的MW0,其实标准Modbus地址是从0开始编号的,0就是第一个寄存器,无需减1。
  • 00 02:寄存器数量。同样16位,0x0002=2个。注意:这个数量指的是“寄存器个数”,不是字节数。2个寄存器=4字节数据。
  • C4 0B:CRC16校验码。这是整个帧(从地址到数量共6字节)的循环冗余校验结果,用于检测传输错误。计算过程稍后详解。

从站正确响应的帧为:

01 03 04 00 01 00 02 B9 25
  • 01:从站地址(回显)
  • 03:功能码(回显)
  • 04:返回字节数(2个寄存器×2字节=4字节)
  • 00 01:第一个寄存器值(假设是1)
  • 00 02:第二个寄存器值(假设是2)
  • B9 25:CRC16校验(对01 03 04 00 01 00 02这7字节计算)

3.2 CRC16校验:不是黑魔法,是可手算的多项式除法

Modbus RTU/ASCII用的CRC16-Modbus算法,生成多项式是x^16 + x^15 + x^2 + 1(十六进制0x8005),初始值0xFFFF,低位先传,最后异或0x0000。听起来复杂?其实用查表法,5行代码就能搞定。原理是:把待校验数据看作一个超长二进制数,用生成多项式去“除”,余数就是CRC值。

以下是C#标准实现(可直接复制):

public static ushort CalculateCrc16(byte[] data, int offset, int length) { ushort crc = 0xFFFF; for (int i = offset; i < offset + length; i++) { crc ^= data[i]; for (int j = 0; j < 8; j++) { if ((crc & 0x0001) != 0) crc = (ushort)((crc >> 1) ^ 0xA001); // 注意:这里是0xA001,是0x8005的反码,因低位先传 else crc >>= 1; } } return crc; }

Python版本(同样可直接用):

def calculate_crc16(data: bytes) -> int: crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc = (crc >> 1) ^ 0xA001 else: crc >>= 1 return crc

实操验证:用上面代码计算[0x01,0x03,0x00,0x00,0x00,0x02],结果是0x0BC4,即C4 0B(注意:网络字节序,低字节在前)。如果你得到0xC40B,说明你忘了字节序——Modbus规定CRC低字节在前,所以最终帧里是C4 0B,不是0B C4。这个细节,90%的初学者第一次都会错。

3.3 Modbus TCP报文头:7字节里的四个关键字段

再来看同一请求在TCP下的样子。我们仍读1号从站、0x0000起2个寄存器,TCP请求帧(十六进制):

00 01 00 00 00 06 01 03 00 00 00 02

拆解:

字节位置字段名说明
0-1事务标识(Transaction ID)00 01主站自定义,用于匹配请求与响应。建议每次递增,避免并发时混淆。
2-3协议标识(Protocol ID)00 00固定值,Modbus TCP必须是0x0000。如果是其他协议(如IEC60870-5-104),这里会不同。
4-5长度字段(Length)00 06后续字节数,即01 03 00 00 00 02共6字节。注意:不包含前面7字节头。
6单元标识(Unit ID)01等同于RTU的从站地址。网关设备用它来决定把请求转发给哪个RS-485从站。

你会发现,去掉前7字节,剩下01 03 00 00 00 02,和RTU请求帧完全一致。这就是“封装”的本质——TCP头只是信封,里面装的还是原来的RTU信件。

关键提醒:很多国产Modbus TCP设备(尤其某些电表、采集模块)会把“单元标识”字段当摆设,固定写0xFF或0x00,根本不校验。这时你填任何值它都响应。但标准设备(如西门子、罗克韦尔)会严格检查,填错单元ID直接返回异常响应(功能码+0x80)。所以调试时,如果TCP请求发出去没响应,第一件事就是抓包看单元ID是否匹配设备配置。

4. 工具链实战:从Modbus Poll调试到C#多连接稳定运行

工欲善其事,必先利其器。Modbus调试离不开几类核心工具:协议分析类(看懂报文)、仿真测试类(模拟从站)、开发集成类(写上位机)。下面结合真实场景,讲透每个工具的用法、陷阱和替代方案。

4.1 Modbus Poll:不只是“发指令”,更是协议教学沙盒

Modbus Poll是Windows平台最经典的主站仿真工具,但它常被误用为“点一下就出数”的傻瓜软件。其实它的价值在于可视化协议交互全过程。启动后,按Connection → Read/Write,设置串口参数(波特率、校验位等),再点Setup → Read/Write Definition,填入从站地址、功能码、起始地址、数量——这时别急着点OK,先看左下角状态栏:

  • 如果显示Waiting for response...,说明请求已发出,正在等从站回复;
  • 如果显示Timeout,说明物理链路不通或从站没响应;
  • 如果显示Illegal Data Address(异常码0x02),说明你读的地址超出从站有效范围;
  • 如果显示Slave Device Failure(异常码0x04),说明从站内部执行出错(如寄存器被写保护)。

实操心得:Poll的“Debug Window”(视图→调试窗口)是神器。勾选后,它会实时打印每帧的十六进制数据、ASCII解码、以及CRC/LRC校验结果。比如你看到发送帧是01 03 00 00 00 02 C4 0B,接收帧是01 03 04 00 01 00 02 B9 25,立刻能确认:主站发得对、从站回得对、CRC校验通过。如果接收帧CRC是XX YY,而Poll计算出的应该是B9 25,那问题一定在物理层(接线松动、RS-485 A/B反接、共模电压超标)。

另一个隐藏技巧:Poll支持“Multiple Read”,即一次读多个不连续地址。比如你要读地址0、10、100三个寄存器,传统做法要发3次0x03请求;但在Poll里,设置起始地址0,数量101,然后用“Data Display”功能,右键选择“Hide Unused”,就能只显示你关心的地址。这招在调试老设备时特别省时间。

4.2 Modbus Slave:别只当“被测对象”,要学它怎么伪造响应

Modbus Slave是Poll的镜像工具,用来模拟从站。但很多人只用它“假装”一个PLC,却忽略了它最大的价值:逆向工程未知设备。比如你拿到一台进口温控表,手册丢了,只知道它支持Modbus RTU,但不知道寄存器地址怎么分布。这时就可以:

  1. 用万用表测出温控表的RS-485 A/B线;
  2. 用USB转485模块连到电脑;
  3. 启动Slave,设置从站地址为温控表实际地址(通常在拨码开关上);
  4. 让真实上位机(或另一台电脑的Poll)向这个地址发请求;
  5. 观察Slave窗口里“Received Requests”列表——它会原样记录所有进来的报文。

通过反复触发温控表的不同操作(如按“设定值”键、切换“手动/自动”),你就能收集到它主动上报或响应的报文规律,再结合功能码含义,反推出:地址0x0000是当前温度、0x0001是设定值、0x0002是输出功率……整个过程不需要任何文档,全靠实测。

注意事项:Slave默认监听COM1,如果你的USB转485占用了COM3,必须在Connection → Connect里手动选COM3。另外,Slave的“Response Delay”(响应延迟)要设为0,否则在调试高速设备时,会因人为延迟导致超时。

4.3 C#多Modbus TCP连接:为什么“同时连接失败”,以及如何根治

搜索热词里高频出现“c#多个modbus tcp同时连接失败”,这几乎是所有用NModbus或EasyModbus库的开发者必踩的坑。根本原因就一个:TCP连接数超过系统默认限制,或Socket未正确复用

典型错误代码:

// ❌ 错误示范:每次读都新建TcpClient for (int i = 0; i < 10; i++) { using (var client = new TcpClient()) { client.Connect("192.168.1." + (100 + i), 502); // 发送Modbus TCP请求... } // 连接立即关闭,下次又要重建 }

问题在哪?

  • 每次new TcpClient()都创建新Socket,Windows默认每个IP最多1024个临时端口(ephemeral port),10个连接瞬间耗尽;
  • using块结束就Dispose(),连接无法复用,频繁三次握手+四次挥手,CPU和网络开销巨大;
  • 没设client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true),空闲连接被防火墙/NAT设备自动断开。

✅ 正确方案:连接池+长连接+心跳保活。参考以下结构:

public class ModbusTcpClientPool { private readonly ConcurrentDictionary<string, TcpClient> _pool = new(); public TcpClient GetClient(string ip, int port = 502) { string key = $"{ip}:{port}"; if (_pool.TryGetValue(key, out var client) && client.Connected) return client; // 创建新连接 client = new TcpClient(); client.Connect(ip, port); // 启用KeepAlive client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.TcpKeepAliveTime, 60); // 60秒后发心跳 client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.TcpKeepAliveInterval, 5); // 每5秒重试 _pool[key] = client; return client; } }

这样,10个设备共用一个连接池,每个IP只维持1个长连接,心跳保活防断连,性能提升10倍以上。

实测数据:在我参与的某光伏电站监控项目中,用旧方案(每次新建连接)读100台逆变器,平均耗时2.3秒/轮;改用连接池后,降到0.18秒/轮,且CPU占用从45%降到8%。这才是工业级上位机该有的表现。

5. 故障排查:一张表看懂95%的Modbus通讯异常

Modbus通讯失败,80%的原因出在物理层和配置层,而非协议本身。下面这张表,是我十年现场调试总结的“速查指南”,覆盖从接线到代码的所有关键节点。遇到问题,按表索骥,5分钟内定位根源。

异常现象最可能原因快速验证方法解决方案
Poll显示“Timeout”1. RS-485 A/B线接反
2. 从站地址不匹配
3. 波特率/校验位设置错误
用万用表测A-B电压,正常应有±1.5V~±6V直流偏置;用逻辑分析仪看是否有数据波形1. 交换A/B线
2. 查从站拨码开关或配置软件
3. 对照设备手册,确认波特率(常见9600/19200)、数据位(8)、停止位(1)、校验(None/Even)
Poll显示“Illegal Function”(异常码0x01)主站发了从站不支持的功能码抓包看功能码值(如发了0x16但从站只支持0x03/0x06)查设备手册“Supported Function Codes”章节,改用支持的功能码
Poll显示“Illegal Data Address”(异常码0x02)读的寄存器地址超出从站有效范围用Slave工具模拟同一地址,看是否同样报错查手册“Register Map”,确认地址是否在有效区间(如0x0000~0x0FFF),注意有些设备地址从1开始编号
Poll显示“Slave Device Failure”(异常码0x04)从站内部执行失败(如写保护、硬件故障)断开其他设备,单独连此从站;用万用表测电源电压是否正常检查设备状态指示灯;恢复出厂设置;联系厂家确认该操作是否被禁用
TCP连接能建立,但收不到响应1. 单元标识(Unit ID)填错
2. 防火墙拦截502端口
3. 设备IP或子网掩码配置错误
telnet 192.168.1.100 502测试端口连通性;用Wireshark抓包看是否有SYN包发出1. 在Poll的“Read/Write Definition”里确认Unit ID
2. 关闭Windows防火墙或添加502端口例外
3. 用ipconfig和设备面板确认IP在同一网段
数据偶尔错乱(如温度值突变)1. RS-485共模电压超标(>±7V)
2. 未加终端电阻(长距离无终端)
3. 电源地线未共地
用示波器测A-GND、B-GND电压;观察波形是否有严重振铃1. 加RS-485隔离收发器(如ADM2483)
2. 在总线两端各加120Ω终端电阻
3. 所有设备GND用粗导线单点连接

独家避坑技巧:在现场调试时,我随身带三样东西——一个USB转RS-485模块(带LED收发指示灯)、一个数字万用表、一根双绞屏蔽线。第一步,用万用表量从站485口A-B电压,如果有±几伏直流压差,说明至少物理链路是通的;第二步,看模块LED是否随Poll操作闪烁,不闪说明主站没发数据;第三步,用屏蔽线替换原线缆,排除干扰。这三步做完,90%的“通讯不通”问题就定位了。记住:不要一上来就怀疑协议或代码,先确认电流、电压、波形这些最基本的物理量

最后分享一个小技巧:当你要调试一台陌生设备,又没有手册时,用Modbus Poll的“Read Coil Status(0x01)”功能,从地址0开始,每次读16个线圈(数量=16),然后逐步增加起始地址(0, 16, 32…),直到收到响应。因为几乎所有Modbus设备,地址0附近的线圈都映射了基本状态(如运行/停止、故障/正常),这是它们的“协议指纹”。找到第一个有响应的地址,你就拿到了打开这台设备协议大门的第一把钥匙。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 7:01:21

数据驱动,安全先行:百盛化学的云上之旅

客户简介百盛化学百盛化学是专业从事精细化工产品生产和研发的专精特新企业&#xff0c;在环氧固化剂领域深耕26年&#xff0c;先后斩获江西省创新型中小企业、省级专精特新中小企业&#xff0c;国家级高新技术企业等重磅荣誉&#xff0c;手握30余项专利&#xff0c;产品广泛应…

作者头像 李华
网站建设 2026/7/7 6:56:59

10分钟掌握WebODM:免费开源无人机图像处理终极指南

10分钟掌握WebODM&#xff1a;免费开源无人机图像处理终极指南 【免费下载链接】WebODM User-friendly, commercial-grade software for processing aerial imagery. ✈️ Download it for free! 项目地址: https://gitcode.com/gh_mirrors/we/WebODM 想要将无人机拍摄的…

作者头像 李华
网站建设 2026/7/7 6:56:32

2026年江苏省高新技术企业认定指南

一、政策依据《高新技术企业认定管理办法》&#xff08;国科发火〔2016〕32号&#xff09;&#xff1b;《高新技术企业认定管理工作指引》&#xff08;国科发火〔2016〕195号&#xff09;&#xff1b;《关于组织申报2026年度高新技术企业的通知》&#xff08;苏高企协办〔2026〕…

作者头像 李华
网站建设 2026/7/7 6:56:24

2026国内十大企业级数字人平台推荐:适配多场景满足合规运营需求

一、引文摘要&#xff1a;企业选型先看这四个硬指标2026年企业级数字人平台已从单一视频生成工具演变为覆盖内容生产、直播运营、搜索优化的全链路基础设施。选型核心不是比谁功能多&#xff0c;而是看克隆还原度、平台适配稳定性、全链路功能完整度、成本透明度四项硬指标。结…

作者头像 李华
网站建设 2026/7/7 6:55:59

3.4 Nginx 负载均衡——动态再平衡的反人性纪律

2017年&#xff0c;一个做SRE的朋友在凌晨三点被报警电话叫醒。他负责的某个核心服务突然开始大量返回502错误。他迷迷糊糊地打开笔记本&#xff0c;登录到Nginx服务器上看了一眼日志&#xff0c;发现后端有一台应用服务器的响应时间在逐步升高&#xff0c;最终超过了Nginx设置…

作者头像 李华