历经2个多月,终于验证了崩溃修复是有效的,也可以说是独家了,写这篇文章来与大家分享下。
起因
我们从 0 开始建设了「桌面端」|Electron 崩溃自行上报实现 和「桌面端」|Electron 崩溃自行归因分析 崩溃监控之后,需要开始分析问题,解决问题。
那从崩溃占比最高的错误问题开始:
近7天崩溃 211 次,它占了 60% 以上的崩溃,所以解决了它,整年的绩效就有的说了,降低崩溃率 60% 以上[手动狗头]。
分析
官方 issues (没有解决问题)
大眼一看,日志消息崩溃,虽然说不上为什么日志还能崩溃,但这种崩溃数量级,首先我们应该去官方里寻找下 issues,看看有没有人遇到。
相关 issues: github.com/electron/el...、github.com/electron/el...
确实很好搜,看起来都是LogMessage
引起的崩溃问题,而且看起来官方在 20.0.1 版本已经修复了。
electron 修复PR:github.com/electron/el...、github.com/electron/el...
我们当时用的 electron 版本是 20.3.12 明显是包括这个修复的,为什么还会产生崩溃呢?
仔细看了一下修复记录:
啊 ... 这?只是把错误等级降级了,就叫做修复吗【手动狗头】
真的是上古掩耳盗铃之术,说实在话,Electron 官方看起来更新很积极,但真的是很少能解决问题。
再搜也没有相关记录了,Stack Overflow 上也没有类似记录,是大家都没遇到还是根本没几个人关心 Electron 崩溃情况?这一点笔者怀疑是猜测是没什么人关心,毕竟连崩溃分析都要自己搭 ...
崩溃排查
既然找不到市面上的解决方案,那就只能自己来了,首先也是想到既然是日志消息产生的崩溃,会不会是我们的日志有问题?比如我们 console 中特殊字符,过长过大之类的问题,但我们项目中各种 console 浩如烟海,想找到是哪个或者哪几个导致出现这个崩溃,简直是不可能。
那我们继续看堆栈信息,从中发现:
看起来跟 GPU 有关系,查询了下IntentionallyCrashBrowserForUnusableGpuProcess
这个方法的崩溃问题点,它是说Browser
跟 GPU 进程断开导致,继续看堆栈,也发现它确实断在进程间ipc
通信上。
那这确实属于 chromium 内核问题,所以 Electron 没人解决也是看似合理了。
解决方式
那我们如何解决呢?
简单粗暴点,不要产生 GPU 进程间通信,是不是能解决问题。
source.chromium.org/search?q=in...
arduino
app.commandLine.appendSwitch('in-process-gpu');
这个指令,Google 给的官方说明:Saves some memory by moving GPU process into a browser process thread.
意思是:通过将 GPU 进程移动到浏览器进程线程中,来节省一些内存。
但毕竟这是一个全局的改动,虽然说是节省内存,但默认不开启,也是因为会降低整体的应用的使用性能。
在无法验证这个可以修复崩溃的前提下,我们需要有巧妙的方式进行实验验证。毕竟这种疑难杂症都是不可复现,甚至就没在测试机上出现过。
那我们可用的实验方式:
【X】功能开关(桌面端未建设) 【X】AB 实验(桌面端未建设) 【X】配置下发(需单独功能单独建设)
好的,并没有趁手的实验方式。(桌面端真的是任重道远啊)
那我们只能简陋一点,通过一个白名单列表,只对发生崩溃的设备进行开启操作,来观测是否该问题会下降。尽量不影响到其他用户体验。当前没有这样的设备白名单服务端设计,所以临时先放本地,验证问题是否解决。
typescript
/**
* !!!: 尝试修复 LogMessage 崩溃问题
*/
export const tryFixLogMessageCrash = async () => {
const deviceId = await getDeviceId();
if (logMessageCrashWhiteList.includes(deviceId)) {
console.log('in-process-gpu');
app.commandLine.appendSwitch('in-process-gpu');
}
};
logMessageCrashWhiteList
存放出现问题的设备 ID 集合。
在 app 启动后注入。
typescript
await app.whenReady();
await tryFixLogMessageCrash();
观测
前文也说到怎么用了2个月呢?
最开始是用了 160 个问题设备 ID,本想说可以肉眼看到崩溃率下降的情况。
然并卵,160 个设备 ID,升级到带白名单版本的设备不到10个,样本太少,根本说明不了是未出现崩溃还是崩溃修复了 ...
下个迭代,我们放宽到出现该崩溃的 460 个设备上,经过1周的观测,升级了30多个设备并没有出现该崩溃,虽然样本还是极其的少,不能确定修复的正确性,继续这样放白名单也没什么意义 ...
但通过用户反馈或者有限的观测能力来看,这行代码并没有带来额外问题,所以我们去掉设备白名单限制,改为所有 Windows 设备都生效。
经过1个版本的观测,并没有再出现此类崩溃问题,说明我们的修复是有效的。
至此,这篇文章终于可以写了 ~
后续
解决了这个崩溃最大归因问题后,第二大问题是 OOM
OOM 问题不能通过崩溃来分析解决,毕竟你不能怪压死骆驼的最后一根稻草,而是要找哪些堆栈一直没释放,这个在 App 上也是难分析的,何况是在 Electron 做的桌面端,本来 Electron 就以内存占用大闻名于世【手动狗头】。
所以下一步我们是先规划实现进程监控,先排查出是哪些进程内存占用大,结合用户行为排查出发生内存膨胀的具体点,来锁定问题。
再感叹下,用 Electron 开发确实前期比较快,但遇到问题是很难解决的,依赖两个关键三方 chromium 和 node。出了问题,Electron 锅往下一甩就算解决了【手动狗头】。至于三方什么时候解决,不干它事 ...
感谢阅读,如果对你有用请点个赞 ❤️