rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法。我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机、不同语言中的方法时就需要借助 rpc 来实现,下面我一步步实现一个简单的 rpc 调用。

server 端注册函数,运行并接收客户端请求

func main() {
srv := NewServer()
srv.Register("fn", fn)
srv.Run()
}
//为了简单,这里只需要接收到消息打印出就代表执行成功
func fn(args ...interface{}) {
fmt.println(args)
}

定义请求格式

type rpcData struct {
Name string //函数名
Args []interface{} //参数
}

server 运行起来后,接收 socket 请求,解析消息调用已注册的函数

//server结构体
type server struct {
conn net.Conn //socket连接
maps map[string]reflect.Value //函数字典
}
//构造函数
func NewServer() *server {
return &server{
maps: make(map[string]reflect.Value),
}
}
//注册函数
func (s *server) Register(fname string, fun interface{}) {
if _, ok := s.maps[fname]; !ok {
s.maps[fname] = reflect.ValueOf(fun)
}
}
//运行一个socket接收请求
func (s *server) Run() {
listen, err := net.Listen("tcp4", ":3001")
if err != nil {
panic(err)
}
for {
s.conn, err = listen.Accept()
if err != nil {
continue
}
go s.handleConnect()
}
}

处理请求时,这里为了简单我使用 json 解析,同时需要定义一个简单的协议:客户端发送时,前4个字节放置消息长度,这样服务端接收到时就能知道消息的长度,从而正常解码消息

func (s *server) handleConnect() {
for {
header := make([]byte, 4)
if _, err := s.conn.Read(header); err != nil {
continue
}
bodyLen := binary.BigEndian.Uint32(header)
body := make([]byte, int(bodyLen))
if _, err := s.conn.Read(body); err != nil {
continue
}
var req rpcData
if err := json.Unmarshal(body, &req); err != nil {
continue
}
inArgs := make([]reflect.Value, len(req.Args))
for i := range req.Args {
inArgs[i] = reflect.ValueOf(req.Args[i])
}
fn := s.maps[req.Name]
fn.Call(inArgs)
}
}

client 端只需调用函数,通过网络发送请求

func main() {
var req = rpcData{"fn", []interface{}{1, "aaa"}}
rpcCall(req)
} func rpcCall(data rpcData) {
conn, err := net.Dial("tcp4", "127.0.0.1:3001")
if err != nil {
panic(err)
}
req, err := json.Marshal(data)
if err != nil {
panic(err)
}
buf := make([]byte, 4+len(req))
binary.BigEndian.PutUint32(buf[:4], uint32(len(req)))
copy(buf[4:], req)
_, err = conn.Write(buf)
if err != nil {
panic(err)
}
}

测试时,首先运行 server,然后运行 client,只要看到正确的打印就代表调用成功,这就是一个最简单(简陋)的 rpc 了。

当我们使用 grpc 这些 rpc 框架时,就可以不用自己实现消息编码解码、socket连接这些细节,专注于业务逻辑,而且更为可靠。

参考: https://github.com/ankur-anand/simple-go-rpc

最新文章

  1. C程序(3)
  2. 二模 (7) day1
  3. POJ2299: Ultra-QuickSort-合并排序解决逆序数问题
  4. oracle 中使用触发器自动生成UUID
  5. java学习面向对象之static内存图解
  6. JS动态加载 js css
  7. Spring学习之Aop的基本概念
  8. 【剑指offer】Q38:数字在数组中出现的次数
  9. db2 查新索引 主键 sql
  10. HttpPost方式调用接口的3种方式
  11. 迁移学习 transferlearning
  12. OpenCL 矩阵乘法
  13. Executors创建的4种线程池的使用
  14. 《FPGA全程进阶---实战演练》之搞定阻抗匹配
  15. 微服务解决框架--SpringCloud
  16. Windows Remote Shell(WinRM)使用介绍
  17. html5 canvas arcTo()
  18. python模块之importlib(py3中功能有明显加强)
  19. appium 重新启动apk
  20. linux刻录iso到u盘

热门文章

  1. WinUI 3试玩报告
  2. pyhon的浅拷贝与深拷贝
  3. android-sdk-window的环境搭建以及appium简单录制脚本的使用
  4. 阻塞队列一——java中的阻塞队列
  5. virtualbox 基于nat模式搭建局域网并且和宿主机通信
  6. 关于宝塔面板ftp+sublime
  7. 网络聚合Network Teaming
  8. 代码点(code point)和代码单元(code units)
  9. mybatis 学习教程
  10. Excel随机生成批量日期,以及注意事项