IoTHub半个月崩了五次?都是裸rethrow惹的祸

线上服务频繁崩溃,每次栈帧都不一样,看起来像玄学问题?其实很多时候都是同一类低级错误堆出来的。


问题现象

IoTHub进程半个月内连续崩溃了五次,每次崩溃的coredump栈帧都不一样:

  • 一次崩在 getTopicPrx
  • 一次崩在 getPublisher
  • 一次崩在IceStorm内部的rethrow
  • 一次崩在LOG_RECORD宏里
  • 还有一次栈帧直接乱了,根本看不出来在干嘛

每次重启完好好的,跑个一两天就崩,特别玄学。


第一反应:是不是Ice中间件的bug?

一开始以为是Ice 3.7的bug,毕竟每次崩溃栈里都有Ice的函数。 后来把所有coredump翻出来对比,发现一个共同点:所有崩溃的栈帧,最上面一层都是 __cxa_rethrow

也就是异常被重新抛出之后,没人接住,直接把进程干崩了。


找问题:五处裸rethrow

把整个IoTHub代码搜了一遍,找出来五处写得非常"潇洒"的catch块:

cpp 复制代码
catch(...) {
    throw; // 直接裸rethrow,啥也不干
}

还有更离谱的:

cpp 复制代码
catch(const Ice::Exception& e) {
    throw e; // 重新拷贝抛出
}

这种代码写的时候爽,觉得"我不在这儿处理,交给上层处理",但问题是------上层根本就没有处理。 异常一路往上抛,最后跑到框架的事件循环里,框架看到没接住的异常,直接abort。


第二个问题:getTopicPrx里的catch不完整

还有一个坑:getTopicPrx 函数内部虽然写了try-catch,但是catch不全:

cpp 复制代码
try {
    tpcPrx = topicManager->retrieve(topic);
} catch(const Ice::NoTopic&) {
    // 处理NoTopic异常
}
// 没 catch Ice::ConnectionLost、SocketException 这些连接类异常

当IceStorm服务端重启、网络闪断的时候,抛出的连接异常根本没被catch住,直接就漏出去了,然后一路裸rethrow到进程退出。


修复:把所有漏出去的异常都接住

修复其实特别简单,就是把所有裸rethrow的地方,要么catch住打日志,要么在外面加一层兜底catch:

1. 五处裸rethrow,全部改成catch住打日志

原来的代码:

cpp 复制代码
try {
    // 一堆Ice操作
} catch(...) {
    throw; // 直接抛出去
}

改成:

cpp 复制代码
try {
    // 一堆Ice操作
} catch(const Ice::Exception& e) {
    LOG_ERROR("操作失败: " << e.what());
} catch(...) {
    LOG_ERROR("未知操作失败");
}

业务逻辑上不需要处理这个异常,那就接住打个日志就行,别让它把进程干崩了。 分布式系统里,网络闪断、服务端重启都是常态,不能因为一次连接失败就把整个进程搞挂。

2. getTopicPrx外面包一层兜底

cpp 复制代码
Ice::ObjectPrxPtr getTopicPrx(const string& topic) {
    try {
        // 原来的获取逻辑
    } catch(...) {
        LOG_ERROR("getTopicPrx failed: " << topic);
        return nullptr; // 返回空指针,调用处自己判断
    }
}

3. LOG_RECORD宏里的异常也接住

之前还有个坑:打日志的LOG_RECORD宏内部,打日志的时候抛了异常,结果日志没打成,反而把进程崩了。 这种地方更要兜底------日志系统是最后一道防线,不能因为打日志把进程搞挂。直接在宏内部catch住所有异常,啥也不做就行。


效果

改完之后部署上去,到现在跑了快一周,一次都没崩过。 之前平均两三天崩一次,现在稳得很。


总结

这种问题真的很常见:

  1. 写代码的时候觉得"这个异常我不在这处理,抛给上层",结果上层忘了处理
  2. try-catch写一半,只catch了想得到的异常,没想到的连接类、超时类异常直接漏出去
  3. 核心组件的初始化函数里没兜底,一次网络抖动就整个进程崩了

线上服务的稳定性,很多时候不是靠什么高深的架构,就是靠这些不起眼的细节:

  • 所有对外调用的地方,异常都接住,别让它炸穿进程
  • 核心组件失败了返回空指针,让业务层自己判断要不要重试
  • 宁可多打两行日志,也别图省事写个裸rethrow

毕竟,线上崩一次,半夜爬起来排障的痛苦,谁经历谁知道。

相关推荐
Wang's Blog1 小时前
Java 项目实战: 外卖平台-后台系统登录功能开发
java·服务器·redis
我不会起名字3221 小时前
一天一道力扣Hot100(36):深度优先算法---组合总和
数据结构·c++·后端·python·算法·go
纪念 2291 小时前
c++类和对象(四)
开发语言·c++
回家路上绕了弯1 小时前
AI 编程助手为什么反复读取同一份上下文?如何减少无效重复
后端
Lost of 程序猿1 小时前
用 Quartz.NET 优雅地处理 .NET 定时任务:从选型到 Docker 部署全记录
后端·c#·asp.net
汉克老师1 小时前
GESP2026年9月认证C++六级( 第三部分编程题(2、分树规划))精讲
c++·gesp·小学生·学c++编程
学生小羊1 小时前
C++ 初阶 学习博客
c语言·c++·c++与c语言的区别·c++基础学习
名字还没想好☜2 小时前
Spring Boot 优雅停机实战:等在途请求处理完再退出,配合 K8s preStop 别丢请求
java·后端·spring
第十人i2 小时前
Linux 系统初始化配置清单:新服务器快速部署
linux·服务器