作者张大辉(企业代号名),目前负责贝壳找房PHP、GO方向研发工作。
1前言
2实现jsonrpc_server
2.1 引入包
1import (
2 "net/rpc"
3 "net"
4 "log"
5 "net/rpc/jsonrpc"
6 "fmt"
7)
2.2 定义 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}
2.3 注册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 }
3php实现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);
4验证
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)
5验证中的问题
问题代码:
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, ¶ms)
5 ...
通过这个坑发现,Go jsonrpc并未严格按照JSON-RPC规范实现。
6内部处理流程