
早上8点15分,门诊刚开诊十分钟,收费系统突然出现异常。
第一笔报告来自3号窗口,8:17,护士小张在群里发消息:”3号窗口交易超时,病人等了五分钟。”
8:18,5号窗口。
8:19,1号、2号、4号…
8:20,整个A区收费窗口陆续报错:”交易超时”、”支付网关无响应”。
李主任的信息科办公室电话瞬间炸响。他接起第一个电话,是财务科王科长:”半小时内已经有30多笔交易失败,患者堵在收费处,情绪激动。有急救病人等着缴费用药,系统却卡住了!”
这是XX省第一人民医院HIS升级项目第139天,新系统上线后第38天。我们遇到了上线后的第一起大规模故障。
李主任的心沉了一下。他第一时间打给了老林——软佳的资深运维负责人,24小时待命的”救火队长”。
电话接通,李主任简单明了:”门诊A区收费大面积失败,大约30%的交易超时。患者开始聚集,可能要出事。”
老林正在吃早餐,他放下筷子,深吸一口气:”启动一级响应。我半小时到, you 先做三件事:第一,安抚患者,启动手工登记流程;第二,暂时关闭A区第三方支付,全部切换为院内pos机刷卡;第三,保留所有日志,不要重启任何服务。”
“明白。”
1. 第一反应:先保业务,再追根因
老林赶到医院时,信息科的小王和小刘已经在机房待命。三人围在监控大屏前,看着实时交易成功率曲线:A区从98%骤降至70%,而B区正常(98%)。
“为什么只有A区?”老林问。
“不知道,两个区用的同一套系统、同一个支付接口。”小王脸色发白,”我们已经切断了第三方支付,现在全部用手持POS机,失败率降到5%,但还没完全恢复。”
老林点头:”先这么做,确保业务不停。A区手工登记,我们同步排查。”
这是他们的铁律:先保业务,再追根因。患者缴费是刚需,不能让临床因为IT问题停摆。
2. 日志追查:从”随机失败”找规律
业务暂时稳住后,三人开始深挖日志。
老林把过去一小时内所有失败交易的日志导出,用时序排列。很快,模式浮现:
– 时间集中在 08:15-08:30(开诊高峰)
– 失败窗口清一色是A区(1-10号窗口)
– 失败码统一是 PAYMENTGATEWAYTIMEOUT
– 但从网络链路测试看,应用服务器到支付接口网关的延迟仅15ms,远低于阈值
“网关超时但网络延迟低,”小王说,”矛盾。要么是支付接口本身的问题,要么是我们的请求发出去后,得不到响应。”
老林问:”B区正常,B区和A区有什么区别?”
小刘对比配置:数据库相同、应用服务器版本相同、网络设备相同、负载均衡策略相同…唯一的不同是,A区3号窗口昨天做了一次硬件故障切换,更换了新的读卡器。
“读卡器驱动版本?”老林问。
小刘查了:”A区窗口的读卡器驱动是 v3.2,昨天刚升级。B区还是 v3.1。”
但读卡器问题怎么会导致支付网关超时?看起来八竿子打不着。
3. 关键洞察:双写与”幽灵回滚”
这时,财务科王科长跑过来,脸色焦急:”我发现一个严重问题——有病人银行卡已经扣款成功,但我们系统显示失败,导致他们重复支付!”
这句话像一道闪电,劈中了老林。
“双写问题!”老林猛地站起来。
他冲向白板,画起架构图:
患者刷卡 → 读卡器 → POS程序 → HIS应用 →
① 写本地交易表(门诊收费库)
② 调用第三方支付接口(银联)
如果第②步调用失败(超时或异常),但第①步已经提交,本地数据会显示”已支付”,实际银行没扣款或扣款成功但通知丢失,就会产生不一致。
但为什么以前没出现,偏偏今天大规模爆发?
“以前失败率低,可能低于5%,业务影响小,没被发现。”老林喃喃,”今天突然30%失败,是因为A区新驱动有bug吗?”
但B区驱动旧,为什么正常?那是否意味着,A区的新驱动触发了某种边缘场景,导致调用支付接口时的数据包异常,进而引发超时?
4. 交叉验证:驱动与超时的关联
老林决定做一次AB测试:把A区一个窗口的驱动降级回v3.1,观察故障率变化。
小王操作:10号窗口,临时降级驱动。同时保留其他窗口为新驱动。
十分钟后,数据出来了:
– A区其他窗口(新驱动):失败率 28%
– 10号窗口(旧驱动):失败率 4%
差距显著!
“驱动版本是原因。”老林有了结论。但如何解释?读卡器驱动怎么会影响支付接口?
小王调取内核日志,发现一个细节:
新驱动在读卡时,会调用一个系统API(timeBeginPeriod)来高精度计时,但该API在同一进程里被多次调用,导致系统级定时器精度异常。而HIS应用中负责调用支付接口的线程池,使用了相同的计时器来设置socket超时。
结果:在新驱动影响下,socket超时被意外缩短了80%——原设定30秒,实际只等了6秒就抛出超时,而支付接口正常响应需要8-10秒(高峰期)。
所以,B区正常(旧驱动不做手脚),A区全部中招(新驱动污染了全局定时器)。
5. 根因修复与预防机制
定位到根因,修复相对容易:
1. 紧急措施:A区所有窗口降级回v3.1驱动(半小时内完成)。
2. 长期方案:升级读卡器驱动到v3.3(厂商已修复该bug),并在应用层将socket超时长至45秒,同时增加重试机制(一次失败后自动重试一次,使用独立线程避免阻塞)。
系统逐渐恢复:A区失败率从28%下降到2%以下。
但老林知道,这次故障暴露的不仅仅是驱动bug,更是系统脆弱性:
– 为什么一个局部的硬件驱动变更,能影响核心业务流程?因为架构耦合太紧,没有隔离。
– 为什么双写不一致会导致重复支付?因为补偿机制缺失。
– 为什么故障发生30分钟后才定位到驱动问题?因为监控告警不够精细,没有”跨层关联”。
于是,他们制定了三条改进措施:
1. 引入”变更隔离”:硬件驱动升级必须先在测试环境验证其对业务链路的影响,特别是对网络、定时器、内存等共享资源的影响。
2. 双写一致性补偿:支付流程增加”对账job”,每5分钟扫描”本地已支付但银行未确认”的交易,自动发起查询/冲正。
3. 全链路监控升级:从读卡器→应用→支付接口,打上统一traceID,任何节点异常可快速回溯上下游。
6. 故障复盘会:从”救人”到”防病”
三天后,医院信息科和软佳开了故障复盘会。
老林开场:”这次故障,影响患者约200人次,重复支付5笔,客服电话被打爆。损失不小。但我们也要看到积极面:第一,响应快,半小时控制住;第二,定位准,没走弯路;第三,修复稳,没引发次生问题。”
李主任点头:”但我不想有下次。”
“所以我们改了三个机制。后续再有类似边缘场景故障,我们会更快发现、更快隔离。”
会议最后,老林说了句话:
> “故障排查的最高境界,不是’终于搞定了’,而是’同样的故障绝不会再发生第二次’——排查的终极产物不是修复,是预防机制。”
这句话后来成了信息科的座右铭。
7. 给所有技术负责人的建议:不要等出事才后悔
老周在后续的运维培训中,分享了这次事故的四个教训:
1. 故障是”礼物”,虽然包装不好看
每次故障都暴露一个或多个弱点。如果掩盖问题,下次会在更糟的时刻爆发。
2. “隔离”比”修复”更重要
故障发生后,第一要务是把影响范围圈住,防止扩散。A区出问题,快速切B区,这是隔离思维。
3. 日志要”可关联”,而非”孤岛”
如果应用日志、系统日志、网络日志、支付接口日志各管各,很难拼出全貌。必须打通traceID,实现全链路可追踪。
4. 双写必须有补偿
分布式环境下,数据一致性靠”最终一致”,不是”强一致”。必须有定时对账和自动补偿,避免人为发现太晚。
5. 不要忽视”看似无关”的变量
读卡器驱动和支付超时,八竿子打不着。但正是这种”边缘关联”,最容易被忽略。排查时要大胆假设,小心验证。
8. 患者的理解:一次危机中的温情
值得一提的是,在故障期间,收费科立即启动手工登记,并安排专人在窗口解释:”系统临时故障,需要手工处理,可能会慢一点,请谅解。”同时发放手写凭证,注明”此交易待系统确认,勿重复支付”。
一名患者家属在等待两小时后,没有抱怨,反而说:”我看到你们一直在忙,每个人都在想办法。我们理解,系统也不可能百分百不出问题。”
这句话让李主任很感动。后来他们给这位家属留了联系方式,邀请他参加医院的信息化体验座谈会。
有时候,真诚的服务态度,比技术的完美更能赢得客户理解。
互动话题
你经历过最严重的一次系统故障是什么?最终是怎么定位并解决的?有什么教训可以分享?
> 基于真实医院场景改编,人物均为化名
立即免费试用门诊系统:https://app.kmhis.com/
International Version:https://app.kmhis.com/multi/
了解软佳门诊管理系统详情:https://www.kmhis.com/outpatient-management-system.html
支持8种语言:简体中文、繁体中文、香港中文、English、藏文、泰文、老挝语、越南语
说真的。这类问题我见过太多了。每次看到医院同事为选型头疼。我就想,要是早点有人把这些经验分享出来就好了。毕竟。选择不对。后面全是麻烦。选择对了。省心省力。还能提升整个机构的运行效率。希望这篇能帮到正在纠结的你。
你如果有具体需求。也可以去 www.kmhis.com 看看。那里有更详细的技术方案和案例。
