skynet-gorpc
skynet cluster with golang rpc
github.com/changlongH/srpc
支持golang
和skynet cluster
通信。无侵入设计无需修改任何skyne
代码,简单的api设计上手即用。
Golang
无状态微服务(例如登陆大厅),有丰富的生态和框架可以选择。大量的第三方库。
Skynet
有状态的服务,使用lua无需编译可以高效业务开发和热更。skynet高的性能沙盒隔离。
支持特性
- 支持
payload codec
配置可选json/msgpack
(建议使用msgpack
),支持自定义
注意lua t = {}
不区分map/array
导致转Go
时候类型不匹配。建议 encode_empty_table_as_null
msgpack仓库中的源码已经修改,cjson可以参考cloudwu/cjson 中encode_table_as_array实现修改。
skynet
提供 libsrpc.lua
参考引入即可使用
golang
支持 cluster
节点动态变更和自动重连
golang
支持 client
级别和call
级别的payload codec
golang
支持 SkynetContext
上下文传递更方便做链路追踪和分析
golang
和 skynet
都支持profile
统计消耗
golang API
服务端分发skynet请求:
-
server.Open("127.0.0.1:2531")
开启集群端口监听
-
server.Shutdown(eventLoop netpoll.EventLoop, timeout time.Duration)
关闭集群监听
-
server.Register(rcvr any, name string, opts ...Option) error
注册一个服务
server.WithPayloadCodec(&payloadcodec.MsgPack{})
指定payload打包方式默认为msgpack
server.WithAccessLog(handler)
指定访问日志处理回调,如果传入nil 则使用默认输出日志。不调用则不输出
-
server.GetRegisterMethods(name string) ([]string, error)
获取成功注册的方法,可用于开发调试。
-
server.SetRecoveryHandler(handle func(string, any))
服务器消息panic 回调
-
更多用法参考 server_test
客户端请求skynet服务:
-
cluster.Register(node string, address string, opts ...client.Option)
注册一个远程skynet节点
-
cluster.Remove(node string)
移除一个节点
-
cluster.Query(node string) *client.Client
查询一个已注册节点
-
cluster.ReloadCluster(nodes map[string]string, opts ...client.Option)
批量注册或者更新节点(如何没有变化不会产生影响)
-
sprc.Call(node string, addr any, cmd string, args any, reply any) error
通过node和addr支持一个简单的rpc请求,返回error表示调用结果
-
srpc.Send(node string, addr any, cmd string, args any) error
发送消息,error表示是否失败
-
sprc.Invoke(caller *client.Caller) error
支持构建复杂的调用。WithTimeout/WithPayloadCodec等
-
更多用法参考 client_test
skynet API
skynet_example 提供 libsrpc 一个很简单的封装参考(100行代码)
sprc.set_default_codec(name)
设置payload 打包方式,默认msgpack
作为客户端请求Golang服务:
srpc.send(node, sname, cmd, req)
发送消息到golang 不阻塞
srpc.call(node, sname, cmd, req)
阻塞等待,两个返回值:ok表示调用是否成功。ret 表示返回内容
作为服务端分发Golang消息:
srpc.router(svc, cmd, callback)
为svr注册一个go cluster
消息路由
local db = {}
-- 注册一个PING方法到db
srpc.router(db, "PINGX", function(msg) return msg end)
-- 可以参考example用例做一层简单的封装
db:router("PINGX", function(msg) return msg end)
-- 不影响原生skynet dispatch
function db.PING(msg)
return msg
end
-- Golang sprc.Call 可以通过withPayloadCodec("text")调用
function db.TEXT(text)
skynet.error(text)
return text
end
设计思路
skynet cluster一次rpc请求需要携带的参数为:
addr uint32/string
目标地址Id或者name
session uint32
分包合并标记或者区分请求和推送
cmd string
调用的方法
payload string
携带的参数
我们约定payload为一个请求参数,不支持可变参数,并且payload必须被encode为字符串。这样能简化实现保证稳定,
如果支持可变类型展开,实现golang解析并不复杂。但是支持可变参数需要大量的反射和条件判断会让整体实现变得复杂,没有统一写法的约束也更容易出BUG.
lua 支持数据number string boolean nil table
等基础类型。table
转成golang struct
可通过lua tag
实现编码和解码。
我们可以选择更为通用的json/msgpack
作为codec,经过验证更可靠。
通常rpc会通过代码生成的方式和反射两种方案
这里使用反射更合适,为了提高效率我们约定只有一个请求参数和一个返回参数。同时约定Go函数原型的第一个参数必须为SkynetContext
支持无输入参数和无返回值。返回值error作为可选。必须作为最后一个返回参数
// 标准用法
func (s *Service) Set(*SkynetContext, args) *reply, error
// 同时兼容以下 缺省参数原型
func (s *Service) Set(*SkynetContext) *reply, error
func (s *Service) Set(*SkynetContext) *reply
func (s *Service) Set(*SkynetContext) error
func (s *Service) Set(*SkynetContext, args) *reply
func (s *Service) Set(*SkynetContext, args) error
func (s *Service) Set(*SkynetContext)
skynet 请求参数同时约束为一个请求值和一个返回值,使用codec序列化成字符串,codec可以自定义(默认提供json/msgpack
)
local req = {k =1, v =1}
local ok, res = pcall(cluster.call, node, sname, "Set", codec.encode(req))
if ok then
res = codec.decode(res)
end
如果使用json作为codec 则需要注意lua空table问题,建议encode 为null
, 并且json序列化必须为lua table
和golang struct/map
,不支持int/string/bool
等类型
默认skynet和golang使用msgpack作为codec,支持原子类型和复杂的类型。