skynet中使用skynet.call需要注意挂起的场景
在 skynet 框架中,skynet.call是一个同步调用函数,它会阻塞当前服务直到目标服务返回结果。这意味着如果目标服务执行时间很长或者没有返回结果,当前服务就会处于挂起状态,这可能会导致整个系统的性能下降或甚至崩溃,或因挂起导致逻辑重入,引发错误
1. 高延迟的外部调用
如果 skynet.call 用于调用外部服务(如数据库查询、HTTP 请求等),这些调用可能需要较长的时间。在这种情况下,使用 skynet.call 会导致当前服务长时间挂起
local httpc = require "skynet.http.client"
local result = skynet.call("HTTP_SERVICE", "lua", "get", "http://example.com/api/data")
解决方案:
使用 skynet.send 进行异步调用,并在回调中处理结果
local function on_response(result)
-- 处理结果
end
skynet.send("HTTP_SERVICE", "lua", "get", "http://example.com/api/data", on_response)
2. 依赖多个服务的复杂逻辑
如果当前服务需要依赖多个其他服务的返回结果才能继续执行,使用 skynet.call 会导致多个服务同时挂起
local result1 = skynet.call("SERVICE1", "lua", "get_data")
local result2 = skynet.call("SERVICE2", "lua", "get_data")
local result3 = skynet.call("SERVICE3", "lua", "get_data")
-- 处理结果
解决方案:
使用异步方式并结合 skynet.wait 来处理多个异步调用
local results = {}
local count = 3
local function on_response(service, result)
results[service] = result
if #results == count then
-- 处理结果
end
end
skynet.send("SERVICE1", "lua", "get_data", on_response)
skynet.send("SERVICE2", "lua", "get_data", on_response)
skynet.send("SERVICE3", "lua", "get_data", on_response)
skynet.wait()
3. 循环调用
如果在循环中使用 skynet.call,每次调用都会导致当前服务挂起,可能导致被循环的table变化
local M = {}
M.services = {"SERVICE1", "SERVICE2", "SERVICE3"} -- 其他地方可能会insert或者remove这个table
for _, service in ipairs(M.services) do
local result = skynet.call(service, "lua", "get_data")
-- 处理结果
end
解决方案:
使用tmp缓存table进行循环调用
M.services = {"SERVICE1", "SERVICE2", "SERVICE3"} -- 其他地方可能会insert或者remove这个table
local tmp_services = {}
for _, service in ipairs(M.services) do
table.insert(tmp_services, service)
end
for _, service in ipairs(tmp_services) do
local result = skynet.call(service, "lua", "get_data")
-- 处理结果
end
4. 无限循环或死锁
如果 skynet.call 调用的目标服务没有返回结果,或两个服务之间互相调用导致死锁,当前服务会永久挂起
-- Service A
local result = skynet.call("Service B", "lua", "get_data")
-- Service B
local result = skynet.call("Service A", "lua", "get_data")
解决方案:
避免服务之间的直接循环调用,使用中间服务或异步调用来打破死锁
-- Service A
skynet.send("Service B", "lua", "get_data")
-- Service B
local function on_request(source, ...)
-- 处理请求并返回结果
end
skynet.dispatch("lua", on_request)