目录概述主要职责死循环检测原理代码实现工作流程监控器数据结构与 Worker 线程的交互概述Monitor 线程是 Skynet 的监控线程负责监控所有 Worker 线程的运行状态检测服务是否陷入死循环。Monitor 线程通过定时检查每个 Worker 的执行状态发现异常时记录日志帮助开发者定位问题。特点:独立线程运行不参与消息处理每隔约 5 秒检查一次所有 Worker通过版本号机制检测死循环记录死循环服务的详细信息文件位置:skynet/skynet-src/skynet_start.c线程数量: 1 个主要职责1. 监控 Worker 线程功能: 检查每个 Worker 线程是否正常工作检查内容:Worker 是否在处理消息Worker 是否长时间未更新状态Worker 处理的消息是否完成2. 死循环检测功能: 检测服务是否陷入死循环检测机制:Worker 每处理一条消息版本号递增Monitor 定期检查版本号是否变化如果 5 秒内版本号未变化判定为死循环记录死循环服务的日志信息3. 运行时间统计功能: 统计服务的运行时间统计内容:服务处理消息的总时间服务最后活跃时间服务是否超时死循环检测原理版本号机制┌─────────────────────────────────────────────────────────────┐ │ 死循环检测原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Worker 线程处理消息时: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 从消息队列取出消息 │ │ │ │ 2. 更新监控器: version │ │ │ │ 3. 记录开始时间: start_time now │ │ │ │ 4. 调用服务回调函数 │ │ │ │ 5. 更新监控器: version │ │ │ │ 6. 记录结束时间: end_time now │ │ │ │ 7. 清空监控器 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Monitor 线程检查时: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 遍历所有 Worker 的监控器 │ │ │ │ 2. 检查 version 是否变化 │ │ │ │ 3. 检查 start_time 是否超时 │ │ │ │ 4. 如果 5 秒内 version 未变化 → 死循环 │ │ │ │ 5. 如果 5 秒内 start_time 未结束 → 超时 │ │ │ │ 6. 记录日志信息 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘检测流程时间轴: T0s T1s T2s T3s T4s T5s │ │ │ │ │ │ ├───────┼───────┼───────┼───────┼───────┤ │ │ │ │ │ │ Monitor │ │ │ │ │ 检查 │ │ │ │ │ version1 │ │ │ │ │ │ │ │ Worker │ │ │ 处理消息1 │ │ │ version2 │ │ │ │ │ │ Worker │ │ 处理消息2 │ │ version3 │ │ │ │ Worker │ 处理消息3 │ version4 │ │ Monitor 检查 version4 正常 ✓死循环场景T0s T1s T2s T3s T4s T5s │ │ │ │ │ │ ├───────┼───────┼───────┼───────┼───────┤ │ │ │ │ │ │ Monitor │ │ │ │ │ 检查 │ │ │ │ │ version1 │ │ │ │ │ │ │ │ Worker │ │ │ 处理消息1 │ │ │ version2 │ │ │ │ │ │ Worker │ │ 死循环! │ │ version2 │ │ (未变化) │ (未变化) │ (未变化) │ │ Monitor 检查 version2 死循环 ✗代码实现Monitor 线程主函数文件:skynet/skynet-src/skynet_start.c位置:skynet_start.c:202-226staticvoid*thread_monitor(void*p){structmonitor*mp;inti;intnm-count;// Worker 线程数量skynet_initthread(THREAD_MONITOR);pthread_setname_np(pthread_self(),monitor);// 设置线程名称for(;;){CHECK_ABORT// 检查是否应该退出// 遍历所有 worker 的监控器for(i0;in;i){skynet_monitor_check(m-m[i]);// 检查监控器}// 休眠 5 秒分成 5 次每次 1 秒以便快速响应退出for(i0;i5;i){CHECK_ABORT// 检查是否应该退出sleep(1);// 休眠 1 秒}}returnNULL;}监控器检查函数文件:skynet/skynet-src/skynet_monitor.cvoidskynet_monitor_check(structskynet_monitor*sm){inti;for(i0;i5;i){// 检查每个 workeruint32_tvsm-version[i];// 读取版本号uint32_thandlesm-source[i];// 读取服务句柄if(v!sm-check_version[i]){// 版本号变化了更新检查版本号sm-check_version[i]v;sm-check_source[i]handle;continue;}if(handle){// 版本号未变化但 handle 不为空// 说明服务还在处理消息uint32_thandlesm-source[i];uint32_thandle_ssm-check_source[i];if(handle_s!0handlehandle_s){// 同一个服务 5 秒内版本号未变化// 判定为死循环fprintf(stderr,Deadloop detected in service %d\n,handle);}}}}工作流程┌─────────────────────────────────────────────────────────────┐ │ Monitor 线程工作流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 线程启动 │ │ │ │ │ ▼ │ │ 初始化线程局部存储 │ │ skynet_initthread(THREAD_MONITOR) │ │ │ │ │ ▼ │ │ 主循环 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ while (!quit) { │ │ │ │ │ │ │ │ │ ├─ CHECK_ABORT │ │ │ │ │ (检查是否应该退出) │ │ │ │ │ │ │ │ │ ├─ 遍历所有 Worker 的监控器 │ │ │ │ │ for (i0; iworker_count; i) { │ │ │ │ │ │ │ │ │ │ │ └─ skynet_monitor_check(m-m[i]) │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ 读取监控器状态 │ │ │ │ │ - version[i] │ │ │ │ │ - source[i] │ │ │ │ │ - check_version[i] │ │ │ │ │ - check_source[i] │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ 比较版本号 │ │ │ │ │ if (version ! check_version) │ │ │ │ │ 正常更新检查版本号 │ │ │ │ │ else │ │ │ │ │ 死循环记录日志 │ │ │ │ │ } │ │ │ │ │ │ │ │ │ ├─ 休眠 5 秒 │ │ │ │ │ for (i0; i5; i) { │ │ │ │ │ CHECK_ABORT │ │ │ │ │ sleep(1); │ │ │ │ │ } │ │ │ │ │ │ │ │ │ └─ 继续循环 │ │ │ │ } │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ▼ │ │ 线程退出 │ │ │ │ │ ▼ │ │ return NULL │ │ │ └─────────────────────────────────────────────────────────────┘监控器数据结构skynet_monitor 结构文件:skynet/skynet-src/skynet_monitor.cstructskynet_monitor{intversion;// Worker 版本号intcheck_version;// Monitor 检查的版本号uint32_tsource;// 当前处理的服务句柄uint32_tcheck_source;// 检查时的服务句柄intwork_version;// 工作版本号intwork_source;// 工作源};字段说明字段类型说明versionintWorker 每处理一条消息递增初始为 0check_versionintMonitor 上次检查的版本号sourceuint32_tWorker 当前处理的服务句柄check_sourceuint32_tMonitor 上次检查的服务句柄work_versionint工作版本号用于调试work_sourceint工作源用于调试与 Worker 线程的交互Worker 线程更新监控器文件:skynet/skynet-src/skynet_monitor.cvoidskynet_monitor_trigger(structskynet_monitor*sm,intsource){sm-versionsm-version1;// 版本号递增sm-sourcesource;// 设置当前服务句柄}Worker 线程清空监控器文件:skynet/skynet-src/skynet_monitor.cvoidskynet_monitor_delete(structskynet_monitor*sm){sm-source0;// 清空服务句柄}交互时序图Worker 线程 Monitor 线程 │ │ │ 1. 取出消息 │ │ │ │ 2. skynet_monitor_trigger() │ │ ├─ version │ │ └─ source handle │ │ │ │ 3. 处理消息 │ │ (可能需要 0.1-5 秒) │ │ │ │ 4. skynet_monitor_delete() │ │ └─ source 0 │ │ │ │─────────────────────────────▶│ │ │ 5. skynet_monitor_check() │ │ ├─ version check_version? │ │ ├─ 是 → 正常 │ │ └─ 否 → 死循环 │ │ │ 6. 取出下一条消息 │ │ │ │ 7. skynet_monitor_trigger() │ │ ├─ version │ │ └─ source handle │ │ │ │─────────────────────────────▶ │ │ │ 8. skynet_monitor_check() │ │ ├─ 更新 check_version │ │ └─ 正常检测超时除了死循环检测Monitor 还可以检测服务处理超时。超时检测逻辑if(handlecheck_sourcehandle){// 同一个服务 5 秒内版本号未变化// 判定为死循环fprintf(stderr,Deadloop detected in service %d\n,handle);// 检查是否超时uint32_ttime_costcurrent_time-start_time;if(time_cost5000){// 超过 5 秒fprintf(stderr,Service %d timeout (%dms)\n,handle,time_cost);}}配置和调优默认配置配置项默认值说明检查间隔5 秒Monitor 每隔 5 秒检查一次超时阈值5 秒超过 5 秒未完成视为死循环调整检查间隔如果需要更频繁的检查可以修改代码// 原代码休眠 5 秒for(i0;i5;i){CHECK_ABORTsleep(1);}// 修改为休眠 2 秒for(i0;i2;i){CHECK_ABORTsleep(1);}注意: 更频繁的检查会增加 CPU 开销。日志示例正常情况[时间戳] Service 0x00010002: Processing message... [时间戳] Service 0x00010002: Message processed (2ms)死循环检测[时间戳] Deadloop detected in service 0x00010002 [时间戳] Service 0x00010002: Last action: skynet.call [时间戳] Service 0x00010002: Current function: handle_message总结Monitor 线程的核心作用:监控: 监控所有 Worker 线程的运行状态检测: 检测服务死循环和超时记录: 记录异常服务的详细信息帮助: 帮助开发者定位问题关键机制:版本号机制Worker 更新版本号Monitor 检查版本号定时检查每隔 5 秒检查一次非侵入式不影响 Worker 的正常工作与其他线程的关系:Worker 线程被监控对象主线程创建 Monitor 线程独立运行不参与消息处理
skynet Monitor 线程详解
目录概述主要职责死循环检测原理代码实现工作流程监控器数据结构与 Worker 线程的交互概述Monitor 线程是 Skynet 的监控线程负责监控所有 Worker 线程的运行状态检测服务是否陷入死循环。Monitor 线程通过定时检查每个 Worker 的执行状态发现异常时记录日志帮助开发者定位问题。特点:独立线程运行不参与消息处理每隔约 5 秒检查一次所有 Worker通过版本号机制检测死循环记录死循环服务的详细信息文件位置:skynet/skynet-src/skynet_start.c线程数量: 1 个主要职责1. 监控 Worker 线程功能: 检查每个 Worker 线程是否正常工作检查内容:Worker 是否在处理消息Worker 是否长时间未更新状态Worker 处理的消息是否完成2. 死循环检测功能: 检测服务是否陷入死循环检测机制:Worker 每处理一条消息版本号递增Monitor 定期检查版本号是否变化如果 5 秒内版本号未变化判定为死循环记录死循环服务的日志信息3. 运行时间统计功能: 统计服务的运行时间统计内容:服务处理消息的总时间服务最后活跃时间服务是否超时死循环检测原理版本号机制┌─────────────────────────────────────────────────────────────┐ │ 死循环检测原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Worker 线程处理消息时: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 从消息队列取出消息 │ │ │ │ 2. 更新监控器: version │ │ │ │ 3. 记录开始时间: start_time now │ │ │ │ 4. 调用服务回调函数 │ │ │ │ 5. 更新监控器: version │ │ │ │ 6. 记录结束时间: end_time now │ │ │ │ 7. 清空监控器 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Monitor 线程检查时: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 遍历所有 Worker 的监控器 │ │ │ │ 2. 检查 version 是否变化 │ │ │ │ 3. 检查 start_time 是否超时 │ │ │ │ 4. 如果 5 秒内 version 未变化 → 死循环 │ │ │ │ 5. 如果 5 秒内 start_time 未结束 → 超时 │ │ │ │ 6. 记录日志信息 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘检测流程时间轴: T0s T1s T2s T3s T4s T5s │ │ │ │ │ │ ├───────┼───────┼───────┼───────┼───────┤ │ │ │ │ │ │ Monitor │ │ │ │ │ 检查 │ │ │ │ │ version1 │ │ │ │ │ │ │ │ Worker │ │ │ 处理消息1 │ │ │ version2 │ │ │ │ │ │ Worker │ │ 处理消息2 │ │ version3 │ │ │ │ Worker │ 处理消息3 │ version4 │ │ Monitor 检查 version4 正常 ✓死循环场景T0s T1s T2s T3s T4s T5s │ │ │ │ │ │ ├───────┼───────┼───────┼───────┼───────┤ │ │ │ │ │ │ Monitor │ │ │ │ │ 检查 │ │ │ │ │ version1 │ │ │ │ │ │ │ │ Worker │ │ │ 处理消息1 │ │ │ version2 │ │ │ │ │ │ Worker │ │ 死循环! │ │ version2 │ │ (未变化) │ (未变化) │ (未变化) │ │ Monitor 检查 version2 死循环 ✗代码实现Monitor 线程主函数文件:skynet/skynet-src/skynet_start.c位置:skynet_start.c:202-226staticvoid*thread_monitor(void*p){structmonitor*mp;inti;intnm-count;// Worker 线程数量skynet_initthread(THREAD_MONITOR);pthread_setname_np(pthread_self(),monitor);// 设置线程名称for(;;){CHECK_ABORT// 检查是否应该退出// 遍历所有 worker 的监控器for(i0;in;i){skynet_monitor_check(m-m[i]);// 检查监控器}// 休眠 5 秒分成 5 次每次 1 秒以便快速响应退出for(i0;i5;i){CHECK_ABORT// 检查是否应该退出sleep(1);// 休眠 1 秒}}returnNULL;}监控器检查函数文件:skynet/skynet-src/skynet_monitor.cvoidskynet_monitor_check(structskynet_monitor*sm){inti;for(i0;i5;i){// 检查每个 workeruint32_tvsm-version[i];// 读取版本号uint32_thandlesm-source[i];// 读取服务句柄if(v!sm-check_version[i]){// 版本号变化了更新检查版本号sm-check_version[i]v;sm-check_source[i]handle;continue;}if(handle){// 版本号未变化但 handle 不为空// 说明服务还在处理消息uint32_thandlesm-source[i];uint32_thandle_ssm-check_source[i];if(handle_s!0handlehandle_s){// 同一个服务 5 秒内版本号未变化// 判定为死循环fprintf(stderr,Deadloop detected in service %d\n,handle);}}}}工作流程┌─────────────────────────────────────────────────────────────┐ │ Monitor 线程工作流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 线程启动 │ │ │ │ │ ▼ │ │ 初始化线程局部存储 │ │ skynet_initthread(THREAD_MONITOR) │ │ │ │ │ ▼ │ │ 主循环 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ while (!quit) { │ │ │ │ │ │ │ │ │ ├─ CHECK_ABORT │ │ │ │ │ (检查是否应该退出) │ │ │ │ │ │ │ │ │ ├─ 遍历所有 Worker 的监控器 │ │ │ │ │ for (i0; iworker_count; i) { │ │ │ │ │ │ │ │ │ │ │ └─ skynet_monitor_check(m-m[i]) │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ 读取监控器状态 │ │ │ │ │ - version[i] │ │ │ │ │ - source[i] │ │ │ │ │ - check_version[i] │ │ │ │ │ - check_source[i] │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ 比较版本号 │ │ │ │ │ if (version ! check_version) │ │ │ │ │ 正常更新检查版本号 │ │ │ │ │ else │ │ │ │ │ 死循环记录日志 │ │ │ │ │ } │ │ │ │ │ │ │ │ │ ├─ 休眠 5 秒 │ │ │ │ │ for (i0; i5; i) { │ │ │ │ │ CHECK_ABORT │ │ │ │ │ sleep(1); │ │ │ │ │ } │ │ │ │ │ │ │ │ │ └─ 继续循环 │ │ │ │ } │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ▼ │ │ 线程退出 │ │ │ │ │ ▼ │ │ return NULL │ │ │ └─────────────────────────────────────────────────────────────┘监控器数据结构skynet_monitor 结构文件:skynet/skynet-src/skynet_monitor.cstructskynet_monitor{intversion;// Worker 版本号intcheck_version;// Monitor 检查的版本号uint32_tsource;// 当前处理的服务句柄uint32_tcheck_source;// 检查时的服务句柄intwork_version;// 工作版本号intwork_source;// 工作源};字段说明字段类型说明versionintWorker 每处理一条消息递增初始为 0check_versionintMonitor 上次检查的版本号sourceuint32_tWorker 当前处理的服务句柄check_sourceuint32_tMonitor 上次检查的服务句柄work_versionint工作版本号用于调试work_sourceint工作源用于调试与 Worker 线程的交互Worker 线程更新监控器文件:skynet/skynet-src/skynet_monitor.cvoidskynet_monitor_trigger(structskynet_monitor*sm,intsource){sm-versionsm-version1;// 版本号递增sm-sourcesource;// 设置当前服务句柄}Worker 线程清空监控器文件:skynet/skynet-src/skynet_monitor.cvoidskynet_monitor_delete(structskynet_monitor*sm){sm-source0;// 清空服务句柄}交互时序图Worker 线程 Monitor 线程 │ │ │ 1. 取出消息 │ │ │ │ 2. skynet_monitor_trigger() │ │ ├─ version │ │ └─ source handle │ │ │ │ 3. 处理消息 │ │ (可能需要 0.1-5 秒) │ │ │ │ 4. skynet_monitor_delete() │ │ └─ source 0 │ │ │ │─────────────────────────────▶│ │ │ 5. skynet_monitor_check() │ │ ├─ version check_version? │ │ ├─ 是 → 正常 │ │ └─ 否 → 死循环 │ │ │ 6. 取出下一条消息 │ │ │ │ 7. skynet_monitor_trigger() │ │ ├─ version │ │ └─ source handle │ │ │ │─────────────────────────────▶ │ │ │ 8. skynet_monitor_check() │ │ ├─ 更新 check_version │ │ └─ 正常检测超时除了死循环检测Monitor 还可以检测服务处理超时。超时检测逻辑if(handlecheck_sourcehandle){// 同一个服务 5 秒内版本号未变化// 判定为死循环fprintf(stderr,Deadloop detected in service %d\n,handle);// 检查是否超时uint32_ttime_costcurrent_time-start_time;if(time_cost5000){// 超过 5 秒fprintf(stderr,Service %d timeout (%dms)\n,handle,time_cost);}}配置和调优默认配置配置项默认值说明检查间隔5 秒Monitor 每隔 5 秒检查一次超时阈值5 秒超过 5 秒未完成视为死循环调整检查间隔如果需要更频繁的检查可以修改代码// 原代码休眠 5 秒for(i0;i5;i){CHECK_ABORTsleep(1);}// 修改为休眠 2 秒for(i0;i2;i){CHECK_ABORTsleep(1);}注意: 更频繁的检查会增加 CPU 开销。日志示例正常情况[时间戳] Service 0x00010002: Processing message... [时间戳] Service 0x00010002: Message processed (2ms)死循环检测[时间戳] Deadloop detected in service 0x00010002 [时间戳] Service 0x00010002: Last action: skynet.call [时间戳] Service 0x00010002: Current function: handle_message总结Monitor 线程的核心作用:监控: 监控所有 Worker 线程的运行状态检测: 检测服务死循环和超时记录: 记录异常服务的详细信息帮助: 帮助开发者定位问题关键机制:版本号机制Worker 更新版本号Monitor 检查版本号定时检查每隔 5 秒检查一次非侵入式不影响 Worker 的正常工作与其他线程的关系:Worker 线程被监控对象主线程创建 Monitor 线程独立运行不参与消息处理