Chuck Lua has built-in RPC support based on coroutine. All remote method calls are as simple as calling local methods. Let’s take a simple example.
rpcserver.lua
local Task = require("distri.uthread.task")
local Distri = require("distri.distri")
local Redis = require("distri.redis")
local Socket = require("distri.socket")
local chuck = require("chuck")
local RPC = require("distri.rpc")
local Packet = chuck.packet
local config = RPC.Config(
function (data) --encoder
local wpk = Packet.wpacket(512)
wpk:WriteTab(data)
return wpk
end,
function (packet) --decoder
return packet:ReadTab()
end)
local rpcServer = RPC.Server(config)
rpcServer:RegService("hello",function ()
return "world"
end)
local server = Socket.stream.Listen("127.0.0.1",8010,function (s,errno)
if s then
s:Ok(4096,Socket.stream.decoder.rpacket(4096),function (_,msg,errno)
if msg then
rpcServer:ProcessRPC(s,msg)
else
s:Close()
s = nil
end
end)
end
end)
if server then
Distri.Signal(chuck.signal.SIGINT,Distri.Stop)
Distri.Run()
end
rpcclient.lua
local Task = require("distri.uthread.task")
local Distri = require("distri.distri")
local Redis = require("distri.redis")
local Socket = require("distri.socket")
local chuck = require("chuck")
local RPC = require("distri.rpc")
local Packet = chuck.packet
local Sche = require("distri.uthread.sche")
local config = RPC.Config(
function (data) --encoder
local wpk = Packet.wpacket(512)
wpk:WriteTab(data)
return wpk
end,
function (packet) --decoder
return packet:ReadTab()
end)
local rpcClient = RPC.Client(config)
local c = 0
if Socket.stream.Connect("127.0.0.1",8010,function (s,errno)
if s then
if not s:Ok(4096,Socket.stream.decoder.rpacket(4096),
function (_,msg,errno)
if msg then
c = c + 1
RPC.OnRPCResponse(config,s,msg)
else
print("close")
s:Close()
s = nil
end
end)
then
return
end
rpcClient:Connect(s)
for i = 1,100 do
Task.New(function ()
while true do
local err,ret = rpcClient:Call("hello")
if ret ~= "world" then
print("err")
break
end
end
end)
end
end
end) then
Distri.Signal(chuck.signal.SIGINT,Distri.Stop)
Distri.Run()
end
The first thing to understand isRPC.Config
, which is the RPC configuration object:
local rpc_config = {}
function rpc_config:new(encoder,decoder,serializer,unserializer)
if not encoder or not decoder then
return nil
end
local o = {}
o.__index = rpc_config
setmetatable(o, o)
o. Decoder = decoder -- take the data out of the packet
o. Encoder = encoder -- encapsulates data into packets using a specific packet format
o. Serializer = serializer -- serializes Lua table into transmission format, can be null
o. Unserializer = unserializer -- deserialize the data in transmission format into Lua table, which can be null
return o
end
Four important methods are provided, and this object will be passed to theRPC.Client
andRPC.Server
Parameters that they will call.
In the example above, I only provideddecoder
andencoder
The following is a brief introduction to the functions of these four methods
-
Serializer: the parameters and return values of the RPC call are wrapped in luatable. This function is used to serialize the wrapped table into a public transport format, such as JSON. Its output object will be provided to the encoder
-
Deserializer: in contrast to the serializer, deserializes the public transport format into luatable. Its output object will be provided to the decoder. If these two functions are not provided, luatable will be directly provided to the encoder / decoder
-
Encoder: encapsulates the transmission data into network packets for send
-
Decoder: extract the transmitted data from the network packet
In the above example, I use a built-in packet. Write luatable directly into the packet
Another point to note in the example is that,rpcClient:Call("hello")
Must be used in the context of coroutine, otherwise an error will be returnedrpcServer:ProcessRPC(s,msg)
If you need to perform some blocking calls in the RPC request processing function, such as requesting other remote methods or requesting redis data, you mustrpcServer:ProcessRPC(s,msg)
It is executed under coroutine
If only RPC requests are involved in a link, a simple encapsulation of rpcclient can be done, which saves manual initialization steps every time, for example:
local WrapRPCClient = {}
function WrapRPCClient:new(config)
local o = {}
o.__index = actor
setmetatable(o,o)
o.rpc = RPC.Client(config)
return o
end
function WrapRPCClient:Connect(host,port,on_success)
local config = self.rpc.config
if Socket.stream.Connect("127.0.0.1",8010,function (s,errno)
if s then
if not s:Ok(4096,Socket.stream.decoder.rpacket(4096),
function (_,msg,errno)
if msg then
RPC.OnRPCResponse(config,s,msg)
else
print("close")
s:Close()
s = nil
end
end)
then
return
end
rpcClient:Connect(s)
on_success()
end
end) then
return true
end
end
function WrapRPCClient:Call(func,...)
return self.rpc:Call(func,...)
end
With this encapsulation, you just callConnect
When on_ After the success callback, you can call the remote method directly