Hi, would like to share an interesting approach how to simplify RPC in Nim.
So that calling remote function is as easy as if it's local.
Nim Server, exposing pi and multiply functions.
import ./rpcm, web/serverm
proc pi: float = 3.14
sfun pi
proc multiply(a, b: float): float = a * b
sfun multiply
rserver.run
Nim Client, multiplying pi by 2.
import ./rpcm
proc pi: float = cfun pi
proc multiply(a, b: float): float = cfun multiply
echo multiply(pi(), 2)
# => 6.28
Also available as REST JSON API
curl --request POST --data '{"a":4,"b":2}' http://localhost:5000/rpc/multiply?format=json
It's already working, but needs some refactoring and implementing the network transport in a more efficient way with TCP and asyncdispatch, and, I haven't published it to Nimble yet.
sfun and cfun could be macro pragmas, enabling this syntax:
#server.nim
proc pi:float{.sfun.} = 3.14
#client.nim
proc pi:float{.cfun.}
Thanks, I updated the server function, now it works with pragma as proc pi: float {.sfun.} = 3.14, nice :)
But can the client function also be used with pragma? As proc pi: float {.sfun.} without the function body?
It's working, but needs some refactoring and implementing the network transport in a more efficient way with TCP and asyncdispatch, and, I haven't published it to Nimble yet.
As a suggestion, you could try using the ZMQ library. It comes with "battery included" regarding efficiency and communication patterns. I've used it successfully in the past to implement RPC in C a few years ago.
It has the benefit of being supported in almost every language - which combined with a well known serialization protocol (such as msgpack) allows you to expand writing clients other languages as well (or as a simpler version, define a ZMQ API for clients).
As a suggestion, you could try using the ZMQ library
Yes, like it, but I would like to avoid dependencies, and this case is relatively simple.
Added simple TCP based messaging, without the need to handle connection/reconnection (I want to be able to call another hosts, so used TCP instead of UnixSockets).
Server
echo "server started"
proc handle (message: string): Option[string] =
case message
of "quit": # Hanldes `send`, without reply
echo "quitting"
quit()
of "process": # Handles `call`, with reply
echo "processing"
return "some result".some
else:
throw "unknown message" & message
receive("tcp://localhost:4000", handle)
Client
echo "client started"
let server = "tcp://localhost:4000"
echo server.call("process")
server.send "quit"