ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

KEPServerEX6教程:IoT Gateway与MQTT Client实现工业数据双向通信

2026/9/16 3:32:11 拓冰建站 浏览量
KEPServerEX6教程:IoT Gateway与MQTT Client实现工业数据双向通信 工业数据采集这个圈子有个绕不开的问题底层设备什么协议都有Modbus、Siemens S7、Allen-Bradley、OPC UA五花八门。上层平台呢又都想要MQTT、REST API这种轻量级接口。两边的“方言”对不上中间就得有一个翻译官。KEPServerEX6就是我用了好几年一直觉得很靠谱的那个翻译官。尤其是它的IoT Gateway和MQTT Client这两个组件一个负责把设备数据用MQTT推出去一个负责从MQTT Broker接收指令写回设备配合起来就是一个完整的工业物联网双向通道。这篇教程就把我实际配置这两个功能的全过程拆开讲从架构理解、环境准备、手动配置到踩坑记录一步步说清楚。不管你是刚接触KEPServerEX还是已经上手但卡在IoT Gateway的某个细节上这篇都值得你花十分钟看完。1. 先从架构说起为什么IoT Gateway和MQTT Client要成对出现很多人第一次打开KEPServerEX6的配置界面看到左边一堆条目什么Channel、Device、Tag再看右边还有IoT Gateway Configuration、MQTT Client Configuration直接就懵了。这里先把两个组件的职责边界理清楚后面配置就有了方向感。1.1 单向上行IoT Gateway解决的是“数据怎么出去”IoT Gateway本质上是KEPServerEX内置的一个MQTT发布端Publisher。它的工作逻辑是这样的先订阅服务器内部的实时数据Tag然后把这些数据打包成JSON或者SparkplugB格式的负载通过MQTT协议发布到你指定的Broker上。上层应用、云平台、数据库只要订阅了相应的Topic就能实时收到设备数据。这个组件的核心价值在于它不需要你额外开发数据采集程序。我在做产线数据看板项目时PLC的数十个温度、压力、运行状态点位只需要在KEPServerEX里把驱动配置好再把Tag拖进IoT项目数据流就自动从Modbus TCP跑到了MQTT Broker上前后不到半小时。IoT Gateway在配置逻辑上分成三个层级IoT Gateway网关实例定义网关名称、绑定的传输方式MQTT还是RESTIoT Item数据项目定义要发布哪些Tag以及扫描频率IoT Agent/传输Transport定义发到哪个Broker、Topic格式、QoS等级、是否启用TLS这三层各管各的数据项可以多个复用同一个传输通道调度很灵活。1.2 双向闭环MQTT Client补上了“指令怎么进去”IoT Gateway只管把数据推出去那下行控制怎么办设备总得能远程启停、改写参数。这就轮到MQTT Client登场了。MQTT Client是KEPServerEX内置的订阅端Subscriber。它连接到同一个或者另一个MQTT Broker订阅指定的Topic收到消息后解析出负载内容再把值写入到对应的Tag里。Tag一旦写入驱动就会把数据下发到PLC、仪表或传感器设备整个控制链路就通了。我把这两个组件的关系理解成“一条双向高速公路”IoT Gateway是出城方向把设备状态实时带到云端MQTT Client是进城方向把云端指令带回设备端。很多项目只需要单向数据采集但如果你要做远程控制、参数下发就必须把MQTT Client也配上。1.3 我为什么用KEPServerEX而不是自己写采集程序有人可能会问我自己写个Python脚本用pymodbus读PLC再用paho-mqtt发到Broker不也行吗确实行但有个前提——你的设备数量和协议种类别太多。我自己写脚本踩过坑Modbus寄存器地址映射搞错了、断线重连要自己处理、设备协议升级后代码要重改。KEPServerEX的优势在于驱动库非常全主流PLC、仪表、CNC、机器人基本都有现成驱动内存映射式的Tag管理采集到的数据集中存储任何客户端都能访问自带的IoT Gateway和MQTT Client免去了大量编码工作配置代替开发支持OPC UA/DA、REST、MQTT多种对外接口适配不同上层平台如果项目就一两个设备、点位数不多自研脚本没问题。但如果是一个车间、几十台设备、多种协议并存KEPServerEX能帮你省下大量开发和维护成本。至于价格确实不便宜但有试用版可以评估性能正式授权找官方或代理谈。千万别用网上的破解版来源不明、稳定性没保证生产环境一旦出事背锅的是你自己。1.4 一个典型的双向闭环架构长什么样我常用的部署方式很简单也推荐给你设备层PLC、仪表、传感器Modbus TCP / S7 / OPC UA ↓ 采集 KEPServerEX6 ├─ IoT Gateway → MQTT Broker数据上行实时状态 └─ MQTT Client ← MQTT Broker指令下行远程控制 ↓ 订阅/发布 业务层数据库、看板系统、云平台、手机App这个架构里MQTT Broker是中间的公共消息中转站KEPServerEX同时扮演生产者和消费者角色灵活度很高。2. 环境准备软件、Broker、模拟器三件套一次配齐动手配置之前先把环境搭好。我用的是KEPServerEX 6.9版本操作系统是Windows 10专业版。下面按我实际操作的顺序来。2.1 KEPServerEX6的安装与授权安装包可以去官网注册后下载安装过程一路Next就行没什么特殊选项。安装完成后默认自带IoT Gateway、MQTT Client等组件的试用授权所以不需要额外再装插件直接就能用。试用版恢复授权的方法我提一下菜单栏的Help → License Manager里能看到当前授权状态和剩余天数。试用期到了之后想继续评估要么申请正式试用序列号要么购买正式授权。网上那些“破解版”真的不建议碰工业软件不稳定是小万一被植入恶意代码整个生产网络都有风险。安装完毕第一次打开会提示创建一个管理账户用户名和密码记得保存好。这个账户是Administrator级别的后面配置IoT Gateway的一些安全选项也会用到。2.2 MQTT Broker选哪个本地部署还是公共测试IoT Gateway和MQTT Client都需要一个MQTT Broker才能运转。Broker的选择很自由只要是标准MQTT 3.1.1或5.0协议KEPServerEX6都能对接。本地测试我推荐用Mosquitto或者EMQX。Mosquitto轻量、一条命令就能起服务适合验证功能EMQX自带Dashboard调试起来更方便。如果条件允许直接用Docker起一个EMQX是效率最高的方式docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 18083:18083 emqx/emqx:5.8启动之后浏览器访问http://localhost:18083默认账号admin/public就能看到EMQX的管理界面后续能直观地看到谁连上了、消息流量如何。不过需要注意的是如果KEPServerEX在Windows里而Broker在Docker里KEPServerEX连接localhost:1883通常没问题因为Docker Desktop会自动端口映射。实际项目中Broker一般单独部署在一台服务器上连接地址填服务器的IP即可。2.3 没有真实PLC怎么办Modbus仿真器模拟设备很多读者上手KEPServerEX时手头没有真实PLC这不影响学习。我们用Modbus Slave模拟器我用的是ModRSsim2虚拟出一台Modbus TCP设备。仿真器的配置很简单打开ModRSsim2选择Connection → Connect设置监听端口为502Modbus TCP默认端口在模拟数据区手动填入一些初始值比如地址0的数值设为100地址1设为200这里要特别注意Modbus TCP的仿真器相当于一个从站设备监听502端口等待主站KEPServerEX来连接。而KEPServerEX这边配置Channel时扮演的是主站角色主动去连接仿真器的IP和端口。2.4 辅助调试工具MQTTX和网络抓包最后再准备一个MQTT客户端调试工具用来验证Broker上的数据是否正常。我个人比较喜欢用MQTTX跨平台、图形化、支持MQTT 5.0可以同时建多个连接查看多个Topic的消息。另外Wireshark也可以装上排查网络层问题时会用到。比如连接不上Broker、数据包被防火墙拦截用Wireshark一眼就能看出TCP握手是否完成。3. 手动跑通IoT Gateway把Modbus数据搬到MQTT Broker环境就绪开始正式配置。我先从数据上行链路——IoT Gateway开始完整走一遍流程。3.1 新建通道和设备Modbus TCP驱动的关键参数打开KEPServerEX6左侧树状结构里右键“Connectivity”选择“New Channel”填写通道名称比如“ModbusTCP”。驱动类型选择“Modbus TCP/IP Ethernet”这是与Modbus TCP设备通信的标准驱动。通道级别需要注意的参数是网络适配器绑定。如果服务器有多个网卡建议指定实际连接PLC网段的网卡地址避免系统走错网卡导致通信超时。另外通信超时和重试次数保持默认即可除非现场网络很糟糕一般不需要动。设备级别参数更关键。在通道下新建设备“Simulator_Device”需要填写以下内容设备的IP地址仿真器所在机器的IP如果仿真器和KEPServerEX在同一台机器上填127.0.0.1即可设备端口号Modbus TCP标准端口502单元IDUnit ID默认为0大多数Modbus TCP从站使用0或255具体看仿真器设置设置完成后右键设备选择“Properties”可以测试连接状态。如果看到“Device is online”之类的状态说明通道已经建好了。仿真器那边也会出现一个连接日志能看到KEPServerEX周期性请求数据。3.2 创建Tag寄存器地址映射最容易翻车的地方接下来在设备下新建Tag。右键设备 → New Tag我这里建了几个测试标签Tag名称: Temp_Value 数据类型: Short 地址: 40001Tag名称: Pressure_Value 数据类型: Short 地址: 40002Tag名称: Motor_Status 数据类型: Boolean 地址: 40003这里有个非常容易搞错的点Modbus协议里40001对应的是保持寄存器Holding Register的第一个地址但实际报文里的地址偏移是0。KEPServerEX的驱动在“地址”这一栏有两种写法40001这种写法表示协议地址为0即第一个保持寄存器0这种写法某些版本下会被解释成线圈或离散量语义混淆我的经验是直接遵循“4 寄存器号”的五位数字格式最稳妥。你在仿真器里看到地址0的数据对应KEPServerEX里就是40001。创建好Tag后右键Tag → Quick Client可以看到实时值。如果仿真器里把地址0的值改到567Quick Client里Temp_Value应该立刻显示567。3.3 IoT Gateway配置三步打通数据上行Tag建好之后开始配置IoT Gateway。在KEPServerEX6主界面的树状目录里找到“IoT Gateway Configuration”展开后会看到“IoT Gateway”和“IoT Item”两个分支。第一步新建IoT Gateway。右键“IoT Gateway” → New IoT Gateway填写名称比如“MQTT_Uplink”。网关类型选“MQTT”传输。接下来配置传输参数这是整个配置里最关键的一步Broker地址填入你的MQTT Broker地址比如192.168.1.100端口1883Client ID给这个连接起个唯一标识比如“Kepware_ProdLine1”Topic前缀默认是spBv1.0这是SparkplugB的标准前缀。如果你不使用SparkplugB可以改成自定义前缀比如factory/line1不过要确认下游能解析数据格式默认ISO_8601时间戳采用ISO 8601格式这个保持默认就好MQTT相关的设置还有QoS等级默认0即最多传一次。现场数据采集对实时性要求高、偶尔丢一条可接受的话QoS 0够用了是否启用TLS本地测试先不勾选生产环境一定要开后面专门讲用户名/密码Broker如果开启了认证在这里填入凭据第二步配置IoT Item数据项。右键“IoT Item” → New IoT Item这时会弹出一个对话框左边树形结构里能展开我们之前建好的通道、设备和Tag。把Temp_Value、Pressure_Value、Motor_Status全部勾选加入右侧的数据项列表。这一步要留意扫描周期Scan Rate。IoT Item支持两种模式周期性上报按固定时间间隔扫描Tag值并发布变化上报仅当Tag值变化超过死区时才发布我在“数据格式”里勾选了“ISO_8601”这样每次发布的消息里会带时间戳。如果你是周期性上报可根据业务情况把扫描周期设为1000ms或500ms。死区设置不建议设太大否则数据变化在阈值内时不会上报容易漏掉真实波动。第三步启动网关。配置完成后回到“IoT Gateway”项右键点击“MQTT_Uplink” → “Start”。日志窗口会显示“Started IoT Gateway”之类的信息。到这一步KEPServerEX就应该开始往MQTT Broker推送数据了。3.4 用MQTTX验证数据到底有没有发出去配置完别急着下结论用MQTTX连接同一个Broker来订阅验证。打开MQTTX新建连接填上Broker地址和端口连接到Broker。然后订阅一个合适的Topic比如你配置的Topic前缀是factory/line1就订阅factory/line1/#。如果一切正常MQTTX的消息列表里会滚动出现KEPServerEX发布的数据负载内容大致长这样{ timestamp: 2024-01-15T14:23:05.123Z, values: [ { id: ModbusTCP.Simulator_Device.Temp_Value, value: 567, quality: 192 }, { id: ModbusTCP.Simulator_Device.Pressure_Value, value: 200, quality: 192 }, { id: ModbusTCP.Simulator_Device.Motor_Status, value: true, quality: 192 } ] }看到这样的数据就说明IoT Gateway的上行链路已经完全打通了。sourceTag里的路径结构是“通道名.设备名.Tag名”下游系统解析时可以直接用这个路径定位数据点。这里分享一个容易忽略的细节如果Quality字段不是192而是0或64说明Tag读取质量有问题。常见原因是设备离线、地址非法或数据类型不匹配。192是Modbus驱动下的正常质量码。4. 配置MQTT Client实现下行控制远程写指令到PLC上行通了接下来解决下行。4.1 MQTT Client的配置思路和IoT Gateway正好相反MQTT Client这个功能在KEPServerEX6里位于树形结构的“MQTT Client Configuration”下。它能建立一个或多个MQTT连接订阅指定Topic然后将消息负载里的值写回KEPServerEX内存里的Tag。配置逻辑可以这么理解IoT Gateway是“把内部Tag值放进MQTT消息”MQTT Client是“把MQTT消息里带的值写到内部Tag”。一进一出正好形成闭环。具体配置步骤在“MQTT Client Configuration”下右键新建MQTT Client命名比如“MQTT_Downlink”配置Broker地址、端口、用户名、密码和Topic订阅列表创建消息映射规则Message Map指定哪个Topic里哪个字段写到哪个Tag4.2 消息映射JSON负载和Tag的对应关系MQTT Client支持多种负载格式最常用的是JSON。它会在收到消息后把JSON里的字段解析出来一一对应用户指定的Tag路径。我在项目里的做法是这样一个约定上位系统要写某台设备的值时往Topiccontrol/device01发送下面格式的JSON{ tag: ModbusTCP.Simulator_Device.Pressure_Value, value: 350 }在KEPServerEX的MQTT Client配置界面为这个Topic建立一个消息映射规则过滤器Topic Filtercontrol/device01负载格式Payload FormatJSONTag路径来源从JSON的tag字段读取Tag值来源从JSON的value字段读取这样配置之后只要MQTT Client收到符合规则的JSON消息就会自动把350写入到指定的Tag。如果这个Tag当前在驱动侧是可写的比如Modbus保持寄存器写入值会立刻下发到PLC。实际操作中我发现很多新手搞不清楚“过滤Topic”和“固定Topic”的区别。MQTT的过滤器支持通配符比如control//set可以匹配control/device01/set、control/device02/set等。如果用通配符消息映射规则就能够在同一套逻辑下处理多台设备结构会清爽很多。4.3 从Mosquitto发指令到仿真器验证闭环验证下行链路的方法很简单用MQTTX重新发布一条消息到control/device01然后再去Modbus仿真器里看地址1的数值有没有变成350。如果仿真器里数据显示350说明整条下行链路已经打通了。再回到KEPServerEX的Quick Client看Pressure_Value同样也会显示350。三条链路IoT Gateway上行、MQTT Client下行、Modbus写入PLC一起工作这就是一个完整的双向控制闭环。4.4 双向数据同步中的一个微妙问题写入冲突这里有个实际项目中一定要面对的问题如果IoT Gateway正好在把Pressure_Value发布到MQTT同时MQTT Client又收到了下行写指令把Pressure_Value改成350那到底以谁为准答案是MQTT Client的写入动作会直接更新Tag的值IoT Gateway随后扫描到新值350会再发布一次新数据到Broker。所以在极短的时间内Broker上可能会先收到旧值200然后收到新值350。这在架构上是正常的但下游系统需要注意消息的时序。如果你希望下行控制指令执行后尽量不要引发回环风暴就是下行导致上行频繁变化可以给这个Tag设置一个合理的死区或者在业务层加一个“控制回读抑制”逻辑。不过对于大多数场景MQTT消息本身是事件驱动的不会造成网络阻塞。5. 实测中的常见坑这些细节没人告诉你配置流程走完了但真正到了现场你会发现一堆问题。这里把我踩过的坑和读者常遇到的问题汇总一下按排查优先级列出。5.1 IoT Gateway启动后一直报错连不上Broker最常见的原因有三个防火墙拦截了1883端口。Windows防火墙默认会对入站连接拦截必须放行1883端口。Broker地址写错了。填了localhost但Broker在别的机器上或者IP地址写错。Broker没有开启匿名访问而KEPServerEX这边没填用户名密码。排查建议先用MQTTX在KEPServerEX同一台机器上测试连接Broker如果MQTTX能连上那问题就限定在KEPServerEX的配置里。如果MQTTX也连不上优先查防火墙和Broker本身的状态。5.2 Tag值一直不更新但驱动通信正常仿真器里的数据变了Quick Client也显示正常但MQTT Broker上收到的数据一直是旧值。这时优先检查IoT Item的扫描周期。我把扫描周期设成了10000ms10秒结果改数据后要等十秒才发布我以为是坏了。另外还有“数据变化上报”模式下的死区设置。如果现场信号本身有微小波动死区太小会导致大量重复消息死区太大又会丢失有效变化需要根据实际数据抖动范围来定。5.3 MQTT消息里的时间戳是UTC和本地差8小时IoT Gateway默认输出的时间戳是UTC格式如果你在国内下游看板显示时间会比本地实际时间慢8小时。解决方案不是去改KEPServerEX的时区设置这会影响所有日志而是让下游系统在解析时统一按UTC存储、显示时转换为本地时区。如果你的下游系统不具备时区转换能力再考虑在上行阶段把时间戳改成带时区偏移的格式。5.4 Modbus地址偏移40001和0的纠缠这是所有Modbus新手都会遇到的一个问题我再强调一次KEPServerEX里写40001实际Modbus报文里的地址偏移是0。如果你在仿真器里把地址1偏移0的值改了KEPServerEX里40001读到的就是你改后的值。有人会把Tag地址写成了30001这在Modbus协议里对应输入寄存器和保持寄存器不是同一块内存区域。如果你的设备数据在保持寄存器里地址以4开头在输入寄存器里以3开头。写错了读到的数据永远是0或者异常。5.5 数据类型不匹配导致的值异常Modbus寄存器本身只存16位整数但实际项目里经常会遇到32位浮点数比如温度、压力。这种时候如果Tag的数据类型配成了Short拿到的高16位或低16位数据就会产生负数、乱值。解决方法是在创建Tag时把数据类型选对或者在KEPServerEX里用数据转换功能Data Map把两个16位寄存器合并成32位浮点数。具体操作是地址栏写成两个寄存器的连续地址比如40001-40002上传数据类型选Float。这样驱动会自动拼接字节序省去很多麻烦。5.6 字节序的问题也容易翻车说到寄存器拼接就绕不开字节序。同一个32位浮点数寄存器高16位在前和低16位在前解析结果完全不一样。在KEPServerEX的Modbus驱动里可以通过高级设置里的“Word Order”字顺序和“Byte Order”字节顺序来切换。如果读出来的浮点数是明显不对的数量级比如应该是36.5但是读出来4660.0大概率是字节序配反了。上下行都要用同一套字节序配置否则会出现“读出来是对的、写进去是错的”这种诡异问题。我在一个传感器项目上就遇到过上行温度正常下行写设定值却写出了一个几十万的天文数字就是这个原因。5.7 重连机制和数据完整性MQTT的QoS等级决定了消息传输的可靠性。KEPServerEX的IoT Gateway可以选择QoS 0、1、2默认通常为0。实际生产环境如果是关键数据建议至少用QoS 1保证Broker收到消息后会应答如果没收到应答会自动重发。但这里有个容易被忽略的点QoS 1只保证“消息到达Broker”不保证“到达最终消费者”。如果Broker到下游系统之间断了消息仍然会积压在Broker里。数据完整性要求更高的场景可以开启MQTT持久会话Clean Session false这样客户端离线期间的消息会在重连后补齐。还有一点是生产环境中常见的周期上报模式在系统刚启动、设备还没全部就绪时会上报一批Quality为0的坏质量数据。下游系统一定要对Quality做过滤否则数据库里会混入一堆“0值”脏数据。我在设计数据接口时就要求下游只接受Quality192的数据条数才算有效。6. 进阶方向从Demo到生产环境还差这几步跑通Demo只是第一步。真正部署到生产环境还需要解决安全、性能、可靠性三方面的问题。6.1 安全加固TLS加密和账号认证工业数据往往涉及生产核心参数明文传输在公网环境是不能接受的。KEPServerEX6的IoT Gateway和MQTT Client都支持TLS加密连接但需要先在Broker端配置好证书。以EMQX为例开启TLS后需要准备服务端证书和服务端私钥同时可以开启客户端证书认证双向TLS。KEPServerEX端在IoT Gateway配置里勾选“Enable SSL/TLS”然后指定CA证书文件即可。如果只是内网环境可以暂时用简单的用户名密码认证。但任何暴露到公网的Broker必须禁用匿名访问并强制使用强密码。我在一个项目里见过生产环境Broker用了默认账号密码admin/public结果被外部扫描器盯上白白跑了好几天流量才发现。6.2 Broker承载能力连接数和消息吞吐量Broker能同时处理多少连接、每秒能转多少条消息决定了整个系统能支撑多少设备。EMQX单机支撑五万连接是很轻松的但要做到这一点需要把MQTT的Keep Alive时间和会话过期时间、最大连接数配置放在一起考虑。KEPServerEX里每个设备驱动本身就会建立一个TCP连接IoT Gateway再到Broker建立一个MQTT连接。大量数据时注意发布频率的合理性。我在一个项目中把IoT Item的扫描周期设成100ms结果Broker每秒要处理好几万条消息下游数据库根本来不及写最后调到500ms才稳定。无论如何设计阶段就要给发布频率留一点余量避免上线后被业务方要求“数据再快一点”的时候改不上来。6.3 从自定义Topic到SparkplugB标准化我之前一直用自定义的JSON上报格式后来发现设备一多Topic管理变得很乱。KEPServerEX6的IoT Gateway直接支持SparkplugB容器Payload BTopic前缀默认就是spBv1.0可以自动维护Birth/Death证书把设备状态和数据的生命周期管理起来。SparkplugB的好处在于它定义了一整套会话状态管理机制设备上线时发布Birth消息声明所有数据点设备离线时发布Death消息数据消息序列号还能检测丢包。如果你的下游平台支持SparkplugB解析强烈建议使用这个规范。如果下游平台太老解析不了再退回自定义JSON。6.4 结合数据库和云平台打通最后一公里做IoT项目最终总要把数据落到存储或可视化平台。MQTT Broker收到数据之后可以这样接通过EMQX规则引擎直接把MQTT消息写入MySQL、PostgreSQL或InfluxDB用Node-RED订阅MQTT Topic转发到REST API或Webhook将MQTT数据同步到Kafka供大数据平台统一消费我在一个小型生产看板项目里的链路就是KEPServerEX → EMQX → Node-RED → WebSocket → Vue看板设备数据实时刷新延迟不到一秒。核心业务数据同时由EMQX规则引擎直接写入InfluxDB做趋势分析全程不需要自己写一行数据采集代码。结语从采集到控制这是一套可以长期用的架构按照上面的步骤你应该已经能在自己机器上完整跑通KEPServerEX6的IoT Gateway和MQTT Client双向链路了。这套架构并不是只能在测试环境玩玩它完全可以扩展成一个车间级甚至工厂级的设备数据底座。设备多了无非是多加几个通道、设备、TagBroker换成集群加一层Kafka缓冲架构整体不需要推翻重来。我个人最深的体会是KEPServerEX这类工业网关的价值不在于它“能连Modbus”而在于把“采集-处理-转发-接收-控制”这条完整链路打包成了一个可靠的可配置系统。IO层归IO层业务层归业务层中间用MQTT这把万能钥匙打通。最后再分享一个我一直在用的工作习惯每次上线前都会用MQTTX把关键Topic完整订阅一遍观察至少一个生产周期比如一个班次的发布消息确认没有坏质量数据、没有重复消息风暴、没有时间戳漂移再正式切换到生产环境。这个习惯帮我提前规避过好几次数据事故。你配置过程中如果遇到KEPServerEX特有的报错或奇怪现象也欢迎对照这篇文章的排查思路逐条验证。