当前位置:首页 > 话题广场 > 攻略专题 > 游戏问答

怎么防止服务被kill看这里!skynet服务的缺陷 lua死循环

服务器端高级架构-云风的天网这边有云风天网的视频,观看就可以看到了!

Skynet是使用C Lua开发的多人在线游戏的轻量级服务器框架。

skynet的显著优点是,使用这套框架,大多数时候只是用lua写代码,很少用到c,这在一定程度上提高了开发效率。lua虽然没有C高效,但开发复杂业务却是非常敏捷。不过,skynet文档相对较少,所以这里利用一点时间学习和总结skynet相关内容,文章就讲解下skynet服务出现lua死循环如何处理。

在前面的一篇文章[1]介绍到,skynet服务运行可以霸占调度器,如果lua代码有死循环,那这个服务可以霸占一个skynet调度线程。从cpu层面看到,skynet进程独占了一个cpu核心。

在写上篇文章的时候,没想到生产环境也出现以上的情况,以为代码规范化可以避免这个问题。然而随着项目越来越庞大了,果真出现这样的情况。

这个问题最致命的是,一旦skynet服务lua代码进入了死循环,这个服务无法被kill掉。就是说服务被kill了没效果,实际上服务还在跑,还占用了一个skynet调度线程。

回到文章,skynet服务出现lua死循环后如何处理?

其实,skynet作者博客[2]也说明了这个问题,他也提供了办法来解决,让skynet服务跳出死循环。做法是,在 lua vm 在处理 JMP CALL TAILCALL FORLOOP 这几条 opcode 时,去检查一个全局变量,如果全局变量被设置成和自己的 lua state 相同的指针,就立刻抛出一个异常。

结束lua死循环

说了这么多,到底要怎么做才能结束lua死循环?

方法就是,在skyent控制台输入 signal命令,为 signal <service addr>,例子如下:

signal :0100000e

以上,:0100000e 为某个skynet服务的地址。

如果不了解skynet控制台,可以参考我的这篇文章[3]。

示例lua死循环及处理

现在以一个例子测试这个问题。

# vi example local skynet = require "skynet" local sprotoloader = require "sprotoloader" local max_client = 64 (function() ("Server start") ("protoloader") if not "daemon" then local console = ("console") end ("debug_console",8000) ("simpledb") ("test") -- 新加了这个服务 local watchdog = ("watchdog") (watchdog, "lua", "start", { port = 8888, maxclient = max_client, nodelay = true, }) ("Watchdog listen on", 8888) () end)

看下 test 服务的代码

# vi examples/ local skynet = require "skynet" (function() (function() while true do local t={} end end) end)

启动skynet进程,跑下这个例子。

# ./skynet examples/config [:01000001] LAUNCH logger [:01000002] LAUNCH snlua bootstrap [:01000003] LAUNCH snlua launcher [:01000004] LAUNCH snlua cmaster [:01000005] LAUNCH snlua cslave [:01000007] LAUNCH snlua datacenterd [:01000008] LAUNCH snlua service_mgr [:01000009] LAUNCH snlua main [:0100000a] LAUNCH snlua protoloader [:0100000b] LAUNCH snlua console [:0100000c] LAUNCH snlua debug_console 8000 [:0100000d] LAUNCH snlua simpledb [:0100000e] LAUNCH snlua test [:0100000f] LAUNCH snlua watchdog [:01000010] LAUNCH snlua gate [:01000010] Listen on 0.0.0.0:8888 [:01000009] Watchdog listen on 8888 [:01000009] KILL self [:01000002] KILL self [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 33) [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 33) [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 33)

以上日志看出,skynet有服务陷入了死循环。趁现在试下 signal指令。

# nc 127.0.0.1 8000 Welcome to skynet console signal :0100000e OK

看下skynet的运行日志,skynet服务已跳出了死循环,cpu使用恢复了正常。

[:0100000e] recv a signal 0 [:0100000e] lua call [0 to :100000e : 1 msgsz = 0] error : ./lualib: ./lualib: nil stack traceback: ./examples/:6: in upvalue 'func' ./lualib: in upvalue 'f' ./lualib: in function <./lualib; stack traceback: [C]: in function 'assert' ./lualib: in function ';

然而,实际线上遇到的复杂环境没有这么简单。

复杂多变的线上问题

假如你的代码是这样,靠上面的方法就无法解决问题了

local skynet = require "skynet" (function() (function() while true do pcall(function() while true do local t={} end end) end end) end)

以上的例子中,死循环嵌套死循环,中间还有pcall的处理。当然,真正的代码不可能这样写,但由于项目函数调用层次过深,就可能出现这样的问题。

照前面的方法,调用signal指令后,结果却大相径庭:

# ./skynet examples/config [:01000001] LAUNCH logger [:01000002] LAUNCH snlua bootstrap [:01000003] LAUNCH snlua launcher [:01000004] LAUNCH snlua cmaster [:01000005] LAUNCH snlua cslave [:01000006] LAUNCH harbor 1 [:01000007] LAUNCH snlua datacenterd [:01000008] LAUNCH snlua service_mgr [:01000009] LAUNCH snlua main [:0100000a] LAUNCH snlua protoloader [:0100000b] LAUNCH snlua console [:0100000c] LAUNCH snlua debug_console 8000 [:0100000d] LAUNCH snlua simpledb [:0100000e] LAUNCH snlua test [:0100000f] LAUNCH snlua watchdog [:01000010] LAUNCH snlua gate [:01000009] KILL self [:01000002] KILL self [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 33) [:0100000e] recv a signal 0 [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 33) [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 33)

skynet服务还健在,收到了signal指令,但是还是没跳出死循环

那么,要怎么处理这个问题?skynet还有方法解决?

答案是否定的,没有了。现在,只能去改skynet的代码了

要怎么修改skynet的代码。方法不复杂,只需要改一处内容。

vi ./3rd/lua /* Add by skynet */ lua_State * skynet_sig_L = NULL; LUA_API void lua_checksig_(lua_State *L) { if (skynet_sig_L == G(L)->mainthread) { //skynet_sig_L = NULL; 注释掉这行代码 lua_pushnil(L); lua_error(L); } } 保存后,重新编译skynet # make clean -C 3rd/lua # make clean # make linux

现在,再启动下skynet,然后在控制台执行一下命令。

# nc 127.0.0.1 8000 Welcome to skynet console signal :0100000e OK kill :0100000e :0100000e snlua test OK

再看下skynet的运行日志,这个异常的服务已经被kill掉了

# ./skynet examples/config [:01000001] LAUNCH logger [:01000002] LAUNCH snlua bootstrap [:01000003] LAUNCH snlua launcher [:01000004] LAUNCH snlua cmaster [:01000005] LAUNCH snlua cslave [:01000006] LAUNCH harbor 1 16777221 [:01000007] LAUNCH snlua datacenterd [:01000008] LAUNCH snlua service_mgr [:01000009] LAUNCH snlua main [:01000009] Server start [:0100000a] LAUNCH snlua protoloader [:0100000b] LAUNCH snlua console [:0100000c] LAUNCH snlua debug_console 8000 [:0100000d] LAUNCH snlua simpledb [:0100000e] LAUNCH snlua test [:0100000f] LAUNCH snlua watchdog [:01000010] LAUNCH snlua gate [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 35) [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 35) [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 35) [:00000000] A message from [ :00000000 ] to [ :0100000e ] maybe in an endless loop (version = 35) [:0100000e] recv a signal 0 [:0100000e] lua call [0 to :100000e : 1 msgsz = 0] error : (no error message) [:01000003] KILL :100000e

好了,文章到这里就结束了。原理是既然跳出一层死循环无法解决问题,那么就跳出所有的死循环吧。因为标记是全局变量的关系,所以同一时间只能处理一个skynet服务。

  • 另外还有一些关于c++ Linux后台服务器开发的一些知识点分享:Linux,Nginx,MySQL,Redis,P2P,K8S,Docker,TCP/IP,协程,DPDK,webrtc,音视频等等视频。
  • 需要的朋友可以后台私信【1】获取学习视频

    1.《怎么防止服务被kill看这里!skynet服务的缺陷 lua死循环》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

    2.《怎么防止服务被kill看这里!skynet服务的缺陷 lua死循环》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

    3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/gl/3311360.html

    上一篇

    天下三孩子装备怎么三条属性?我来告诉你答案热点预告:魔兽10.0本周正式公布 天刀OL首推双生新门派

    怎么防止服务被kill?总结很全面速看!服务器被挖矿了怎么办?

    怎么防止服务被kill?总结很全面速看!服务器被挖矿了怎么办?

    怎么防止服务被kill相关介绍,前几天,我的服务器PS命令和sh等多个命令不起作用,阿里云继续告诉我CPU过载,继续警告6379端口遭到攻击,我一直不以为然,PS、top命令好像被篡改了。显然CPU很高,但这是找不到高CPU的过程。...

    怎么防止服务被kill看这里!记多个微服务同时被kill分析

    • 怎么防止服务被kill看这里!记多个微服务同时被kill分析
    • 怎么防止服务被kill看这里!记多个微服务同时被kill分析
    • 怎么防止服务被kill看这里!记多个微服务同时被kill分析