快速搭建 Go JSON-RPC Server

阅读数:146 2019 年 9 月 24 日 18:15

快速搭建Go JSON-RPC Server

jsonrpc 是无状态、轻量级的远程过程调用协议,传递数据格式为 JSON。GO 官方提供 rpc 包和 jsonrpc 包,与 rpc 包不同的是,jsonrpc 可以实现跨平台通信。本文将介绍如何用 Go 快速搭建一个 jsonrpc Server, 用 PHP 实现 jsonrpc client 进行验证,同时也记录下验证过程中出现的坑。

实现 jsonrpc_server

引入包

复制代码
1import (
2 "net/rpc"
3 "net"
4 "log"
5 "net/rpc/jsonrpc"
6 "fmt"
7)

定义 server 端要伺服的 rpc method

复制代码
1// rpc handler
2type Edwin int
3// 定义 rpc method 第一个参数是请求对象,第二参数是返回对象, 返回值是返回 rpc 内部调用过程中出现的错误信息
4func (this *Edwin) Add(args map[string]float64,res *float64) error {
5 *res = args["num1"] + args["num2"]
6 return nil
7}
8func (this *Edwin) Multi(args map[string]interface{},res *float64) error {
9 *res = args["num1"].(float64) * args["num2"].(float64)
10 return nil
11}

注册 rpc handler,开启 server connection

复制代码
1 rpc.Register(new(Edwin))
2
3 l,err := net.Listen("tcp",":11223")
4
5 if err!=nil{
6 log.Fatalln("listen error:",err)
7 }
8
9 for{
10 conn,err := l.Accept()
11
12 if err!= nil{
13 log.Fatalln("accept failed:",err)
14 }
15
16 fmt.Println("jsonrpc server start lisen on 11223...")
17
18
19 go func(conn net.Conn) {
20 fmt.Println("a new connection is coming...")
21 jsonrpc.ServeConn(conn)
22 }(conn)
23 }

php 实现 jsonrpc_client

复制代码
1class JsonRpc {
2 private $conn;
3
4 function __construct($host, $port) {
5 $this->conn = fsockopen($host, $port, $errno, $errStr, 2);
6
7 }
8
9 public function call($method, $params) {
10
11 $err = fwrite($this->conn, json_encode([
12 'method' => $method,
13 'params' => array($params),
14 'id' => 1,
15 ]));
16
17 if (empty($err)) {
18 return false;
19 }
20
21 stream_set_timeout($this->conn, 0, 300);
22
23 $line = fgets($this->conn);
24 if ($line === false) {
25 return NULL;
26 }
27
28 fclose($this->conn);
29
30 return json_decode($line, true);
31 }
32}
33
34$client = new JsonRPC("127.0.0.1", 11223);
35$client2 = new JsonRPC("127.0.0.1", 11223);
36$ret = $client->Call("Edwin2.Multi", array("num1" => 14, "num2" => 20));
37$ret2 = $client2->Call("Edwin2.Add", array("num1" => 14, "num2" => 20));
38var_export($ret);
39var_export($ret2);

验证

复制代码
1# 开启 jsonrpc server
2go run jsonrpc_server.go
3# 正常会输出监听信息
4> jsonrpc server start lisen on 11223...
5> a new connection is coming...
6# 执行 jsonrpc client
7php jsonrpc_clinent.php
8# 正常输出以下信息
9> array (
10 'id' => 1,
11 'result' => 280,
12 'error' => NULL,
13)array (
14 'id' => 1,
15 'result' => 34,
16 'error' => NULL,
17)

验证中的问题

问题代码:

复制代码
1$err = fwrite($this->conn, json_encode([
2 'method' => $method,
3 'params' => array($params),
4 // 'params' => $params // 不能这么用
5 'id' => 1,
6 ]));

params 参数必须用 array 包含,如果直接传递过去,将报出以下错误信息:

复制代码
1array (
2 'id' => 1,
3 'result' => NULL,
4 'error' => 'json: cannot unmarshal object into Go value of type [1]interface {}',
5)

根据 JSON-RPC 规范,参数 params 可以是 Json Array,也可以是 Json Object,可是为什么还报错呢?于是查看源码,发现:

复制代码
1 在 src/net/rpc/jsonrpc/server.go 第 95 行,go 官方实现指定 params 必须是数组:
2 var params [1]interface{}
3 params[0] = x
4 return json.Unmarshal(*c.req.Params, &params)
5 ...

通过这个坑发现,Go jsonrpc 并未严格按照 JSON-RPC 规范实现。

内部处理流程

快速搭建Go JSON-RPC Server

作者介绍:
作者张大辉(企业代号名),目前负责贝壳找房 PHP、GO 方向研发工作。

本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。

原文链接:

https://mp.weixin.qq.com/s/k0cQ_kIs4FhfAz_ncctRBg

评论

发布