下面给你整理一份 《skynet.rawcall 使用与应用场景分析(超全)》,重点讲解 rawcall 的作用、原理、使用方法及典型应用场景。


一、skynet.rawcall 是什么?

在 Skynet 框架中,skynet.rawcallskynet.call 的底层版本。

  • 区别于 call:
    • call 是高级封装,会自动处理 coroutine 挂起、结果解包
    • rawcall 直接发送消息,不自动挂起协程,也不解包返回结果
  • 特点:
    • 高性能
    • 可灵活控制消息处理流程
    • 适合做内部 RPC 或批量消息处理

原型:

skynet.rawcall(addr, typename, session, ...)

参数说明:

参数说明
addr目标服务地址(名字或 handle)
typename消息类型,例如 "lua"
session会话 ID(可为 0)
...消息内容(需自行序列化)

二、rawcall 与 call / send 对比

功能sendcallrawcall
是否阻塞
自动挂起协程
自动解包返回
使用场景通知同步 RPC高性能自定义 RPC / 内部消息

简单理解:

  • send → 单向通知
  • call → 同步请求返回结果
  • rawcall → 底层消息发送,可自定义 session / 不挂起

三、rawcall 的底层原理

  1. 直接发送消息
    • 将消息封装成 skynet_msg 结构发送到目标服务
  2. 不挂起 coroutine
    • 不会阻塞调用方
    • 适合在底层消息队列中处理大量请求
  3. 手动处理 session
    • 调用方需要管理 session ID
    • 目标服务返回时需用 skynet.retskynet.response

四、rawcall 的典型使用方法

1. 客户端调用 rawcall

local skynet = require "skynet"

-- 获取下一个 session
local session = skynet.context()
-- 发送 rawcall
skynet.rawcall(".user", "lua", session, "getName", 1001)

特点:

  • 不挂起调用协程
  • 消息发送成功即可继续执行后续逻辑

2. 服务端响应

skynet.dispatch("lua", function(session, source, cmd, ...)
    if cmd == "getName" then
        local name = "User_" .. ...
        skynet.ret(skynet.pack(name))  -- 将结果返回给 session
    end
end)

注意:rawcall 的返回值需要 手动处理,不会自动解包。


五、rawcall 的应用场景分析

1. 高性能批量消息

  • 当需要一次性发送大量 RPC 消息
  • 不希望挂起调用协程
  • 可以使用 rawcall 批量发送,然后统一处理返回结果

示例:

for i = 1, 1000 do
    skynet.rawcall(".log", "lua", 0, "writeLog", "msg"..i)
end

  • 发送完即返回,不阻塞
  • 日志服务异步处理消息

2. 内部低级 RPC

  • skynet.call 太重或不需要挂起协程的内部消息
  • 例如服务之间的心跳、状态同步
  • 避免 call 的 coroutine 开销,提高性能

3. 自定义 session 管理

  • 需要自定义 session 逻辑时
  • 例如实现 消息 ID 对应回调多服务聚合调用

示例:

local session = math.random(1, 100000)
pending[session] = callback
skynet.rawcall(".user", "lua", session, "getUser", uid)

  • 目标服务返回时使用 session 定向回调处理
  • 适合高并发请求管理

4. 与 skynet.queue / coroutine 结合

  • 在高并发环境下,rawcall + skynet.queue 可实现 顺序异步处理
  • 保证不会因 call 阻塞导致服务性能下降

六、使用注意事项

  1. 不会挂起协程
    • rawcall 返回后,调用方不会等待结果
    • 必须手动管理返回值
  2. session 管理
    • session 唯一且对应返回消息
    • 常用表存储回调函数
  3. 与 call 混用
    • rawcall + call 可以组合
    • call 用于普通 RPC,rawcall 用于批量或内部异步
  4. 异常处理
    • rawcall 不自动捕获错误,需要手动 pcall

七、rawcall 实战示例

1. 批量异步请求

local skynet = require "skynet"

local pending = {}

function asyncGetName(uid, callback)
    local session = math.random(1,100000)
    pending[session] = callback
    skynet.rawcall(".user", "lua", session, "getName", uid)
end

-- 假设用户列表
local users = {1001,1002,1003}
for _, uid in ipairs(users) do
    asyncGetName(uid, function(name)
        print("UserName:", name)
    end)
end

2. 服务端统一返回

skynet.dispatch("lua", function(session, source, cmd, ...)
    if cmd == "getName" then
        local name = "User_" .. ...
        skynet.ret(skynet.pack(name))
    end
end)


八、总结

特性skynet.callskynet.sendskynet.rawcall
阻塞
协程挂起
自动解包返回
适用场景普通 RPC通知 / 单向消息高性能批量 / 内部异步 / 自定义 session

核心结论
rawcall 适合 对性能要求高、消息量大或需要自定义 session 管理的场景,而普通 RPC 使用 call 更简单方便。