机房温湿度监控自建方案:双协议记录仪与大屏联动实战
1. 机房温度失控的代价逼我重新盘了一遍温湿度监控的底那次的事我记得很清楚。凌晨两点多一台核心数据库服务器因为风扇故障导致机柜内局部温度飙到43摄氏度业务直接报错等我从家里赶到机房已经过去四十分钟。设备外壳摸着都烫手但墙上那套老旧的动环系统只显示“室温26度”——因为温度探头挂在机柜正面的走廊墙上离真正发热的位置还隔着一排机柜的距离。后来我花三周时间把机房的温湿度监控整个重做了一遍核心动作就是换成支持双协议的温湿度记录仪把数据实时接进自建的可视化大屏做了一套大屏数据联动方案。这套方案不涉及昂贵的商业动环平台全部用常见的开源工具和工业传感器就能落地。如果你手里也有一个传统机房或者正在被“温度计挂在走廊里”这种形同虚设的监控方式困扰这篇文章应该能帮你省不少弯路。1.1 那次宕机事故暴露出的三个监控盲区事后复盘问题不在那台风扇而在监控本身。第一温度探头位置不合理。传统动环系统的探头通常部署在天花板下、空调回风口或走廊墙上测的是“机房平均温度”而机柜内部、热通道排风口这些真正需要盯的位置反而是盲区。第二数据只在动环主机上存着值班室看不到更不要说联动分析。第三没有历史曲线和趋势预判温度是缓慢爬升还是瞬间跳变完全无感知。等阈值告警触发的时候设备往往已经在高温环境里待了很久。所以这次升级我给自己定了几个硬指标要能测到机柜进风口的真实温度要有多点部署要能看到每一分钟的变化趋势还要在异常时把告警同时推到大屏和手机上。折腾一圈之后发现“双协议温湿度记录仪 自主研发采集服务 ECharts大屏”这条路线性价比最高也最可控。1.2 项目目标写了什么监控边界、数据链路与交付形式刚开始做需求梳理的时候我把范围卡得比较死避免做着做着就变成“什么都想接”。监控对象机房内五个关键点位包括两排机柜的进风侧、热通道排风侧、空调送风口、以及机房对角线的环境参考点。采集频率Modbus TCP主动轮询每2秒一次SNMP被动查询每30秒一次保证实时性同时不把传感器和服务端拖垮。数据存储实时数据进Redis历史数据落MySQL保留30天用于趋势分析。交付形式中控室一台55寸电视作为可视化大屏Web页面实时刷新异常时大屏弹窗高亮同时推送到企业微信机器人。明确不做不做空调自动控制不做制冷联动第一阶段只做监测、告警和数据可视化。把这个边界写清楚很重要。机房改造最怕越做越复杂一旦牵扯到制冷设备的控制权安全责任和安全风险完全不是一个量级。先解决“看得见”的问题再谈“管得住”。1.3 为什么是“双协议记录仪大屏联动”而不是商业动环市面上现成的动环监控系统其实很多报价从几万到几十万都有功能也确实全。但我这次没选商业方案原因有三个一是现有系统扩展接口基本都收费想接自己的大屏还得买SDK或者走协议授权二是商业系统的数据模型是封闭的我拿不到原始寄存器级的读数不方便做二次分析三是大部分动环系统的可视化界面偏老旧交互体验和大屏展示效果都一般。自建方案的核心是“双协议记录仪”。这类设备通常带一个以太网口同时支持Modbus TCP和SNMP两种标准协议既可以被我用Python脚本高频轮询又可以被Zabbix等网管平台用标准协议纳管。传感器数据先统一进数据服务再经过Redis缓存与WebSocket推送最终落到浏览器里的可视化大屏上。整条链路全部是标准协议和开源组件后期要加点位、换设备、对接第三方系统都很轻松。2. 双协议温湿度记录仪选型逻辑Modbus TCP管采集SNMP管融合选硬件之前我先把“双协议”这三个字拆清楚。市面上有些设备标称“双协议”指的是RS485串口加以太网口双通道也就是同一个设备既能走Modbus RTU也能走Modbus TCP还有一类设备是同时支持多种以太网应用层协议比如同时监听502端口和161端口。我这次落地用的是后一种思路一台设备既能通过Modbus TCP被采集服务高频读取也能通过SNMP被传统网管平台和第三方工具查询。2.1 双协议具体指哪两个协议各扛什么活Modbus TCP是工业自动化领域最常见的以太网协议之一基于TCP 502端口报文结构紧凑读写寄存器非常直接。它特别适合做高频、确定性的数据采集你发一条“读保持寄存器”的请求设备就返回一串寄存器值温度、湿度、状态位都可以映射进来。我用它做主力采集通道2秒一次轮询延迟基本可以忽略。SNMP简单网络管理协议则是网络设备管理的标准协议走UDP 161端口采用MIB树形结构组织数据。它的优势在于生态成熟Zabbix、Prometheus通过SNMP exporter、各厂商网管平台都能直接纳管。我用它做第二通道一方面做数据核对隔几十秒用SNMP再取一次值防止Modbus通道异常时毫无察觉另一方面保留被其他网管平台接入的能力以后如果公司上了统一的监控平台不需要重新改造硬件。两个协议各司其职等于给温度数据上了双保险。实际部署中我也验证过当Modbus TCP偶发连接异常时SNMP通道的数据依然能撑起大屏曲线不断线。2.2 硬件参数与安装位置决定的不是精度而是数据可信度温湿度记录仪的选型我主要看五个参数测量范围、测量精度、分辨率、通讯接口、供电方式。以我选的设备为例温度测量范围是-20到70摄氏度精度正负0.3摄氏度湿度范围0到100%RH精度正负3%RH分辨率分别为0.1摄氏度和0.1%RH。这个精度级别做机房环境监控已经足够再高就是实验室级别价格翻几倍但实际意义不大。供电方式是容易被忽略的坑。工业温湿度记录仪常见的有DC 12V、DC 24V、PoE供电等。机房环境务必优先选PoE供电或者就近取电的版本可以少拉一路电源线。我当时特意选了一款支持IEEE 802.3af标准PoE供电的设备和网络线一根线搞定施工省了很多事。探头安装位置比设备本身更能影响数据质量。传统做法是装天花板但机房的热源在机柜后方和顶部。我的布置策略是机柜进风侧冷通道距地1.5米左右贴近机柜前门测的是服务器实际进气温度。机柜排风侧热通道安装在机柜后部排风口附近测的是热通道最高温度。空调送风口下方测的是制冷送风的实际温度用于判断空调工作状态。机房对角位置各放一台作为环境基准参考。多测几个位置后你会发现机房不同区域的温差可能超过5摄氏度只看一个点没有任何意义。2.3 设备到手先别急着接线三件事验证协议可用性新设备拿回来我一般会先在办公桌上做一轮协议连通性验证确认没问题再上架。这个步骤能省掉后面排查的很多时间。第一件事设置好静态IP并ping通。工业设备出厂默认IP五花八门有的还是DHCP先在电脑上手动配置同网段地址确认能ping通再继续。第二件事用Modbus Poll或简单的Python脚本读一遍寄存器找到温度和湿度在寄存器表里的实际位置。这一步没必要等正式代码写完再做直接跑一个十几行的脚本就能把关键信息摸出来。第三件事用snmpwalk命令走一遍SNMP的OID树确认设备型号、温度、湿度这些节点都能正常返回。snmpwalk是net-snmp工具包里的命令比如snmpwalk -v 2c -c public 192.168.10.30 .1.3.6.1.4.1如果能看到完整的OID树说明SNMP通道没问题。把MIB文件里温度、湿度对应的OID记下来后面写代码要用。这三件事做完硬件部分的底就算打好了后面纯粹是软件层面的活。3. 采集服务与数据链路搭建从寄存器读数到大屏像素整个系统的数据链路我画得很简单温湿度记录仪负责采集物理量采集服务定时读取协议数据统一清洗成标准格式写入Redis作为实时缓存再通过WebSocket推给浏览器端的大屏页面展示。历史数据单独落一份到MySQL供后续趋势分析和报表查询。3.1 架构先理顺采集、缓存、历史、推送各管一段很多人做物联网数据链路容易犯一个毛病就是全程用轮询前端每秒去拉一次后端接口。这在点位少的时候问题不大但当设备数量变多、刷新频率变高之后服务和数据库的压力会很难看。我这次用了“采集服务独立跑 Redis做缓冲 WebSocket主动推送”的架构。采集服务是Python写的一个常驻后台的运行进程里面开了两个独立的工作线程一个负责Modbus TCP轮询一个负责SNMP查询。每次采集到的数据先做合法性校验温度范围、湿度范围、时间戳是否新鲜然后写Redis。WebSocket服务端不直接访问传感器只从Redis读最新值向前端推送。前端也不用轮询只需要在WebSocket回调里更新图表数据。这样做的好处很明显前端刷新频率和后端采集频率解耦谁出问题都不影响另一方。传感器采集不到数据时Redis里的旧值还在大屏曲线不会马上变得很难看只是状态标记会变成“离线”。3.2 Modbus TCP轮询代码与采集参数怎么定Modbus TCP的采集代码本身不复杂用pymodbus库即可。以我的设备为例温度值存在保持寄存器的第0和第1个地址湿度存在第2和第3个地址单位是0.1摄氏度实际值需要除以10。核心轮询代码大致如下import time from pymodbus.client import ModbusTcpClient SENSOR_IP 192.168.10.30 client ModbusTcpClient(SENSOR_IP, port502, timeout3) def read_sensor(): if not client.connect(): return None try: # 读取保持寄存器从地址0开始连续读4个寄存器 rr client.read_holding_registers(address0, count4, slave1) if rr.isError(): return None temp_raw rr.registers[0:2] humi_raw rr.registers[2:4] # 两个16位寄存器拼成32位有符号整数再除以100或10 temp combine_32bit(temp_raw, signedTrue) / 10.0 humi combine_32bit(humi_raw, signedTrue) / 10.0 return {temp: round(temp, 1), humi: round(humi, 1)} except Exception as e: print(f读取异常: {e}) return None finally: client.close()combine_32bit这个函数看起来简单实际是踩坑重灾区后面第五章我会专门讲。轮询周期我设为2秒一次超时时间3秒。如果设备数量多建议每台设备一个独立连接或者用一个连接池避免一台设备响应超时把整个循环卡住。数据合法性校验一定要在解析后立刻做。我当时的规则是温度在-10到60摄氏度之间湿度在0到100%RH之间但凡超范围直接丢弃并记录日志。因为Modbus通信一旦发生位错乱解析出来的数值可能变成几万这种脏数据绝不能进缓存。3.3 SNMP被动查询与设备告警通道的用法SNMP这路的代码我用的是pysnmp库查询逻辑比Modbus更简单本质就是发一个GET请求到目标OID节点设备返回对应的取值。比如温度节点OID是1.3.6.1.4.1.12345.1.1.1.0湿度节点是1.3.6.1.4.1.12345.1.1.2.0那么代码如下from pysnmp.hlapi import * def snmp_get(ip, oid): iterator getCmd( SnmpEngine(), CommunityData(public, mpModel1), # SNMP v2c UdpTransportTarget((ip, 161), timeout2, retries1), ContextData(), ObjectType(ObjectIdentity(oid)) ) errorIndication, errorStatus, errorIndex, varBinds next(iterator) if errorIndication: return None return varBinds[0][1].prettyPrint()其中mpModel1表示使用SNMP v2c如果设备只支持v1改成0即可。SNMP这路除了被动查询还有一个重要能力是利用设备的主动告警功能。部分温湿度记录仪支持在设备端设置温度上限超标时主动发送SNMP Trap到指定地址。采集服务里监听UDP 162端口的Trap消息一旦收到立即触发大屏弹窗和消息推送。这等于在大屏联动机制里又多了一条“设备主动报警”的通道响应的实时性比轮询更好。3.4 Redis的数据结构怎么设计才不打架Redis在这套系统里至少承担两个职责保存实时值、缓存最近若干条历史点。结构设计不当很容易出现写覆盖和读取错乱的问题。我用的是这样一套Key设计设备实时值key为sensor:{device_id}:retain类型为Hash字段包括temp、humi、ts、status。最近历史点key为sensor:{device_id}:history类型为ZSetmember是JSON字符串score是Unix时间戳。告警状态key为alarm:{device_id}:level类型为String保存当前的告警级别。每次采集线程拿到新数据时先把实时Hash整体重写再往历史ZSet里加一条member同时用ZREMRANGEBYSCORE把30分钟之前的老数据清掉。这样既保证了长期历史存MySQL又保留了最近30分钟的高频曲线数据。前端刚打开页面时可以先从历史ZSet把最近几分钟的数据拉出来一次性渲染然后再等WebSocket增量推送。Redis里这两个结构的写入必须保证原子性我直接用流水线pipeline来实现避免并发时出现Hash更新了但ZSet没跟上、或者反过来。4. 大屏可视化联动实现ECharts动态曲线与告警弹窗的细节可视化大屏是整个项目的脸面也是值班员每天盯得最多的地方。我的目标很明确两米外能看清全局状态一米内能看清具体数值告警时半秒内能定位到具体设备位置。4.1 大屏布局的第一原则值班员一眼能看到什么大屏页面我用了左中右三栏结构。中间最显眼的区域放的是五台设备的实时温度数字和温湿度趋势曲线数字字号做得非常大用颜色编码区分状态绿色正常、黄色预警、红色告警。左侧是一张简易的机房平面示意图用SVG画了几个机柜位每个机柜位用一个色块表示当前进风温度颜色值班员一眼就能看出是哪一排哪个区域出了问题。右侧是告警滚动列表和最近事件流包括每一次告警的触发时间、恢复时间、设备名称、最高温度。大屏最忌讳的是把一堆数字堆上去。我当时做了个减法普通状态只展示中心大数字和趋势线只有告警了才弹窗、闪烁、高亮。没有告警的时候右侧列表应该是安静的不要滚动刷屏否则值班员很快会视觉疲劳。4.2 WebSocket推送ECharts实时曲线的最小可用实现大屏前端我用的是原生JavaScript加ECharts后端用FastAPI搭建WebSocket服务。WebSocket服务端逻辑很直接客户端连接上来之后服务端先推一次当前全部设备的实时状态然后周期性从Redis读取最新数据推过去。后端核心代码大致是这样from fastapi import FastAPI, WebSocket import json, redis, asyncio app FastAPI() r redis.Redis(host127.0.0.1, port6379, decode_responsesTrue) clients set() app.websocket(/ws/sensor) async def websocket_endpoint(websocket: WebSocket): await websocket.accept() clients.add(websocket) try: while True: await websocket.receive_text() # 维持连接 except Exception: pass finally: clients.remove(websocket) async def push_loop(): while True: for dev_id in [dev01, dev02, dev03, dev04, dev05]: data r.hgetall(fsensor:{dev_id}:retain) if data: payload {device: dev_id, **data} for client in list(clients): await client.send_text(json.dumps(payload, ensure_asciiFalse)) await asyncio.sleep(2) # 应用启动时执行 asyncio.create_task(push_loop())前端ECharts更新也不需要重绘整个图表拿到新的数据点后用appendData或者setOption更新series即可。我的实时曲线采用了两条线温度和湿度共用时间横轴数据点动态滑动。ECharts的动画建议关掉或者调短大屏上平滑动画看着酷但会掩盖数据本身的突跳异常时反而误判。前端重连逻辑必须写好。大屏经常会连续运行几天不刷新网络一闪断或者后台服务重启一次WebSocket就断了。我的做法是监听onclose事件做指数退避重连先隔1秒重连失败后2秒、4秒、8秒最多隔30秒重试一次同时在上次数据时间戳超过30秒时把页面状态标为“数据离线”避免用户看到一条断掉的曲线还以为是正常的。4.3 告警联动不是发条消息那么简单大屏联动的核心不只是曲线实时刷新更是告警的精准触发与通知。我设置了三个告警级别提示级温度高于28摄氏度或湿度高于65%RH大屏状态变黄不推送消息。提醒级温度高于32摄氏度持续5分钟大屏闪烁推送企业微信机器人消息。严重级温度高于35摄氏度或超过预设上限大屏弹窗、声音告警推送人工确认。告警不能一触发就疯狂刷屏一定要加持续条件判断和冷却时间。我在采集服务里维护了每个设备的告警状态机只有满足“连续N次采集均超过阈值”才真正触发恢复时也要连续N次低于恢复阈值才清除告警中间状态实时更新但不下发推送。推送用的是企业微信机器人Webhook一条消息里包含设备编号、当前温度、位置、触发时间、一条带大屏地址的跳转链接。值班主任点开手机上的链接能直接看到大屏同款实时数据。4.4 联动还可以延伸到移动端和值班工单大屏虽然做出来了但值班员不可能24小时盯在电视前面。我一并做了一个移动端适配的H5页面URL和大屏完全一致用响应式布局在手机上重排。手机端的核心动作是看告警、确认告警、查看最近一小时趋势不做过多花哨展示。更进一步我还把告警和值班工单做了对接。严重告警触发后系统自动在值班系统里生成一条处理任务负责人、时限、设备位置、历史温升曲线都带过去。这个联动一旦跑起来就不只是“数据可视化”而成了一条完整的监控闭环。整套逻辑不复杂但运维效率提升非常明显。5. 上线后最容易翻车的五个细节与排查过程方案跑通只是第一步真正让人长记性的是上线后那几天。我踩过的几个坑每个都值得单独拿出来说一遍因为它们都属于“文档不会提示你、代码跑起来才发现”的典型问题。5.1 寄存器地址偏移读到-40℃别先怀疑传感器坏了第一次用Modbus TCP读数据时温度值显示-40摄氏度湿度值显示0.0%RH。我的第一反应是传感器坏了但用厂商自带的调试工具读出来却完全正常。问题出在寄存器地址偏移上。大部分Modbus设备手册里给的地址是PLC风格的“40001”这种形式这是基于1的地址而pymodbus库的address参数是基于0的地址。换句话说手册写“温度寄存器地址40001”代码里应该填address0手册写“40003”代码里填address2。我当时按手册的数字直接填了40001库函数自然跑到了地址40001对应的物理位置读回来的数据完全是另外一块存储区的内容。排查方法很简单拿厂商调试工具读出每个寄存器原始值再在Python里用脚本把地址0到20的寄存器全部打印一遍对照数据表找到温度和湿度实际所在的位置。这件事强烈建议在选型验证阶段就做掉不要等到联调。5.2 字节序陷阱两个字节交换一下数据全乱了寄存器地址找到之后温度值又出现了第二个问题读出来的数值在500多和600多之间跳除以10之后变成50多度明显偏高。厂家文档里写的是32位浮点数但两个16位寄存器的排列顺序并不一定是“高位在前”。Modbus协议本身不规定多寄存器数据的字节序有些设备采用高字节在前大端有些采用高字节在后小端还有的32位数据是两个寄存器按“ABCD”或者“BADC”的顺序排列。我的设备实际使用的顺序是“CDAB”也就是第二个寄存器的低字节和第一个寄存器的高字节拼接。解决方式很直接先打印出原始寄存器的十六进制值再用不同字节序组合换算对比找到正确的那一种固定下来。我自己写了个小函数把两种常见的拼接顺序都试一遍选取数值落在合理范围内的结果。import struct def combine_32bit(regs, signedTrue): # regs是长度为2的list按大端组合 raw struct.pack(HH, regs[0], regs[1]) return struct.unpack(i if signed else I, raw)[0]如果发现数值不对改成struct.pack(HH, ...)再试。做协议对接时这个函数一定要优先调试好否则后面所有数据都是错的。5.3 采集线程互相阻塞Modbus和SNMP抢通道系统刚上线时有一个很诡异的现象大屏上Modbus通道的数据一切正常但SNMP那条通道在定时查询时经常出现超时两台设备轮流报离线。排查日志发现SNMP超时的时间点总是和Modbus批量轮询重叠。原因是一个很蠢的共享锁问题。我在采集服务里图省事把Modbus和SNMP复用了一个requests.Session和同一个socket两个线程并发读写时底层socket被对方占用导致请求排队阻塞。SNMP走UDP本身不是长连接但共用socket后依然会互相干扰。修正方法很简单把两条采集通道彻底分离Modbus用自己独立的TCP连接池SNMP用独立的UDP socket两边的线程用各自的锁保护。改完之后两条通道互不干扰SNMP查询响应稳定在50毫秒以内。5.4 探头安装位置导致的数据漂移上线初期有个机柜的温度曲线比别的点高6到8摄氏度而且波动非常大。我一度以为是传感器故障后来跑到现场一看那台设备的探头被装在了机柜顶部一个出风口的正对面直接被服务器排出的热风对着吹。这是个非常典型的部署问题。机房内热空气是流动的探头安装位置决定了它测的是“进风温度”还是“出风温度”还是“混合温度”。我的处理方式是把所有点位统一标准进风侧探头安装在机柜前方理线槽附近、离地1.5米、避开空调直吹热通道探头安装在机柜背板后方的排风区但距离出风口至少30厘米避免被单台服务器的局部热风影响。统一之后数据横向可比性大大增强。大屏上的色块语义也清晰了进风温度高代表冷通道送风不足或者局部热点排风温度高代表机柜负载异常或者风扇异常不同位置的数据对应不同的问题排查起来才有效率。5.5 大屏长时间运行的断线重连与时间戳对齐大屏连续运行一周后遇到两件烦心事一是WebSocket断线之后页面不再自动恢复二是ECharts时间横轴显示的曲线整体偏移了8小时。断线问题很好理解浏览器和WebSocket服务之间有长时间无数据交互会被网络设备断掉或者服务端重启后客户端没感知。处理方式就是我前面说的指数退避重连机制同时前端要避免每次重连都重新创建一个ECharts实例更常见的坑是重连回调里重复注册onmessage事件导致每条数据被渲染两三次。正确做法是把onmessage只注册一次重连时只调用socket.send发送心跳。时间戳偏移的坑在于服务端存的Unix时间戳是UTC标准时间前端new Date()转换时如果直接用字符串拼接会把UTC当成CST导致整条曲线右移8小时。我统一在后端把时间戳转成毫秒级的Unix时间戳再传给前端前端用new Date(timestamp)来解析不依赖任何时区字符串问题立刻消失。6. 这套方案的成本、边界与后续演进整套系统跑通之后我算过一笔账也复盘了这套方案的边界在哪。如果你正打算动手做类似的事这几条结论应该能帮你避开做无用功。6.1 成本拆解比商用动环省在哪双协议温湿度记录仪按型号不同单台价格大约在几百元到一千多元我部署了五台平均单价不到六七百元硬件总成本三千多元。采集服务和可视化大屏全部跑在一台已有的服务器上不增加额外硬件成本。软件方面用的是Python、FastAPI、Redis、MySQL、ECharts全是开源组件没有任何商业授权费用。对比过供应商报的商用动环方案一套覆盖相同点位数的报价通常在两万到五万元还不含后期大屏定制费用。自建方案在成本上的优势非常明显更重要的是所有数据结构和协议接口都掌握在自己手里后期加传感器、加页面、加告警渠道都只需要改代码。6.2 从环境监控到动环监控的扩展方向温湿度只是机房动环的起点我这套架构完全可以平移到更多场景烟雾传感器和漏水绳可以接入同一套采集服务UPS的SNMP状态也可以纳管进来门禁记录同样能通过标准接口对接。我接下来准备做两件事。第一把空调送回风温度、设备负载率、机房温升速率做关联分析尝试建立简单的温升预测模型比如用“过去10分钟的斜率”来判断半小时后是否可能超过温度阈值。第二把机柜功率数据也接进来通过功率和温度的联合曲线定位那些“功耗不高但温升异常”的异常设备这种设备大概率是风扇故障或散热风道堵塞的前兆。回到开头那句话机房温度失控这件事靠“走廊里挂个温度计”是防不住的但也不需要搞一套昂贵又封闭的商业系统。用双协议温湿度记录仪作为数据触点用标准协议和服务架构搭一条实时数据链路最后用大屏联动把数据变成值班员一眼能看懂的信息这条路我已经跑通了。后续无论是扩容机柜、增加点位还是升级大屏心里都有底了。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →