下面给你整理一份 《skynet.rawcall 使用与应用场景分析(超全)》,重点讲解 rawcall 的作用、原理、使用方法及典型应用场景。
一、skynet.rawcall 是什么?
在 Skynet 框架中,skynet.rawcall 是 skynet.call 的底层版本。
- 区别于 call:
call是高级封装,会自动处理 coroutine 挂起、结果解包rawcall直接发送消息,不自动挂起协程,也不解包返回结果
- 特点:
- 高性能
- 可灵活控制消息处理流程
- 适合做内部 RPC 或批量消息处理
原型:
skynet.rawcall(addr, typename, session, ...)
参数说明:
| 参数 | 说明 |
|---|---|
addr | 目标服务地址(名字或 handle) |
typename | 消息类型,例如 "lua" |
session | 会话 ID(可为 0) |
... | 消息内容(需自行序列化) |
二、rawcall 与 call / send 对比
| 功能 | send | call | rawcall |
|---|---|---|---|
| 是否阻塞 | ❌ | ✔ | ❌ |
| 自动挂起协程 | ❌ | ✔ | ❌ |
| 自动解包返回 | ❌ | ✔ | ❌ |
| 使用场景 | 通知 | 同步 RPC | 高性能自定义 RPC / 内部消息 |
简单理解:
send→ 单向通知call→ 同步请求返回结果rawcall→ 底层消息发送,可自定义 session / 不挂起
三、rawcall 的底层原理
- 直接发送消息
- 将消息封装成
skynet_msg结构发送到目标服务
- 将消息封装成
- 不挂起 coroutine
- 不会阻塞调用方
- 适合在底层消息队列中处理大量请求
- 手动处理 session
- 调用方需要管理 session ID
- 目标服务返回时需用
skynet.ret或skynet.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 阻塞导致服务性能下降
六、使用注意事项
- 不会挂起协程
- rawcall 返回后,调用方不会等待结果
- 必须手动管理返回值
- session 管理
- session 唯一且对应返回消息
- 常用表存储回调函数
- 与 call 混用
- rawcall + call 可以组合
- call 用于普通 RPC,rawcall 用于批量或内部异步
- 异常处理
- 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.call | skynet.send | skynet.rawcall |
|---|---|---|---|
| 阻塞 | ✔ | ❌ | ❌ |
| 协程挂起 | ✔ | ❌ | ❌ |
| 自动解包返回 | ✔ | ❌ | ❌ |
| 适用场景 | 普通 RPC | 通知 / 单向消息 | 高性能批量 / 内部异步 / 自定义 session |
✅ 核心结论:rawcall 适合 对性能要求高、消息量大或需要自定义 session 管理的场景,而普通 RPC 使用 call 更简单方便。