Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.0.0 发布出现崩溃弹窗的复盘 #1

Open
dogodo-cc opened this issue Aug 15, 2023 · 1 comment
Open

2.0.0 发布出现崩溃弹窗的复盘 #1

dogodo-cc opened this issue Aug 15, 2023 · 1 comment

Comments

@dogodo-cc
Copy link
Collaborator

dogodo-cc commented Aug 15, 2023

问题

发布 2.0.0 之后,用户反馈打开编辑器并在3分钟内关闭它,偶现错误弹窗,如图:

截屏2023-08-09 11 08 09

定位问题:

先看 Dashboard 打开编辑器的逻辑,分别有2个地方会导致该窗口弹出

  • 在通过 child_proccess 启动 creator 时候有监听 error 事件
  • 子进程如果主动往 dashboard 发 editor-error 的 ipc 也会弹出
image image

因为非必现,只能加了 console ,打包 asar,拿到容易复现的 window 电脑去操作,得到如下 log ,而且明确是 error 事件发出来的,不是 编辑器主动发的。

============> onChildProcessError: 1 {"code":"ERR_IPC_CHANNEL_CLOSED"} 
"Error" "Channel closed" 
"Error [ERR_IPC_CHANNEL_CLOSED]: Channel closed
\n    at ChildProcess.target.send (internal[表情]ild_process.js:705:16)
\n    at F:\\creator3d\\CocosDashboard\\resources\\cocos.asar\\node_modules\\@editor\\user\\dist\\index.js:1:4303
\n    at Array.forEach (<anonymous>)
\n    at EventEmitter.<anonymous> (F:\\creator3d\\CocosDashboard\\resources\\cocos.asar\\node_modules\\@editor\\user\\dist\\index.js:1:4289)
\n    at EventEmitter.emit (events.js:327:22)
\n    at isLoggedIn (F:\\creator3d\\CocosDashboard\\resources\\cocos.asar\\node_modules\\@editor\\user\\dist\\index.js:1:1618)
\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)
\n    at async BaseIpc.<anonymous> (F:\\creator3d\\CocosDashboard\\resources\\cocos.asar\\node_modules\\@editor\\user\\dist\\index.js:1:4518)" 
"ERR_IPC_CHANNEL_CLOSED"

通过 ERR_IPC_CHANNEL_CLOSED ,这个错误信息进行百度,得知是 往子进程 send 一个消息,但是子进程不存在了。

查看 node 文档 https://nodejs.org/docs/latest-v14.x/api/child_process.html#child_process_event_error 明确标出导致进程报错的原因包含:

  • The process could not be spawned, or
  • The process could not be killed, or
  • Sending a message to the child process failed.

刚好和错误信息里面的 ChildProcess.target.send 对应上了。

于是将问题定位在 @editor/user 包,拿到该包的源码发现:

只要业务上调用了一次 isLoggedIn ,都会触发 info-update 进而向所有子进程进行 send 操作,而且哪怕是渲染进程调用了 isLoggedIn 也是转发到主进程执行

export async function isLoggedIn() {
    return new Promise((resolve, reject) => {
        ipc
            .send('cocos-user', 'isLoggedIn', [])
            .callback((error: Error, result: any) => {
                if (error) {
                    return reject(error);
                }
                resolve(result);
            });
    });
}
export async function isLoggedIn() {
    // ......
    info.session_id = result.data.session_id;
    info.session_key = result.data.session_key;
    info.cocos_uid = result.data.cocos_uid;
    info.email = result.data.email;
    info.nickname = result.data.nickname;
    EventManager.emit('info-update'); // 这里
    return true;
}
// 转发 info-update 事件给每个子进程
EventManager.on('info-update', () => {
    children.forEach((child) => {
        child.send({
            channel: 'cocos-user',
            emit: 'info-update',
            result: [],

        });
    });
});

原因

@editor/user 内部维护了一份 childrens,通过 addChildProcess 和 removeChildProcess 来更新队列,所以只要被 add 的子进程关闭后,没有被及时 remove,是会导致在 send 的时候报错的。

页面上的一些状态或者数据依赖了登陆状态,那么只要页面触发更新都是会请求一次 isLoggedIn , 比如 2.0.0 中加入的 persona 模块,在请求列表的时候 都会预先请求一次登录状态。
企业微信截图_7f990605-ea67-43a0-a8e9-87166548cc7a

如果这次的调用时间刚好命中如下时序:

关闭creator > 调用isLoggedIn > removeChildProcess

那么就会在 @editor/user 里面的 child.send 里报错,主要还是这边没有进行安全检查。

EventManager.on('info-update', () => {
    children.forEach((child) => {
        child.send({ // 直接 send 了,但是其实 child 可能不存在了
            channel: 'cocos-user',
            emit: 'info-update',
            result: [],

        });
    });
});

由于 2.0.0 增加了对 isLoggedIn 的调用,导致出现这种情况的概率增大了。

方案

@editor/user 应该进行防御性编程改为如下:

// 转发 info-update 事件给每个子进程
EventManager.on('info-update', () => {
    children.forEach((child) => {
        child.connected && child.send({
            channel: 'cocos-user',
            emit: 'info-update',
            result: [],

        });
    });
});

优化

dashboard 已经监听了 editor-error ipc, 进程的意外错误其实不应该直接弹窗给用户,后续可以改成生成日志文件等形式。

@jareguo
Copy link

jareguo commented Aug 17, 2023

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants