公司多个项目之前用的TP3.2,去年部份项目升级为TP6,并使用了JSONRPC服务,使用了TP6官方插件 think-swoole中rpc服务,实现了rpc客户端和服务端,但从便携性上讲,需要项目都是TP6才能快速接入rpc服务。
之前部份老项目由于升级成本过大,迟迟没有升级到TP6。由于某些模块需要在新老项目中都需要用到,所以会容易造成新老项目中相同模块中的代码逻辑不一致的问题。
为了解决这个问题,去看了下think-swoole中关于rpc服务的代码,按照里面的逻辑,仿照写了一个调用rpc服务的客户端类。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?php
$orderRpc = new CoreRpc('order');
$data = [
'serviceType' => 0,
];
$res = $orderRpc->estimate($data);
var_dump($res);

class CoreRpc
{
protected $host = '127.0.0.1'; //rpc服务IP

protected $port = 10001; //rpc服务端口

protected $timeout = 5;

protected $client;

protected $interface;

protected $rpcFunc = [
'OrderRpcInterface' => ['estimate', 'order'],
'UserRpcInterface' => ['create'],
];

protected $interfaces = [
'order' => 'OrderRpcInterface',
'user' => 'UserRpcInterface',
];

public function __construct($interface)
{
$this->interface = $interface;
}

protected function isConnected(): bool
{
return $this->client && $this->client->isConnected();
}

protected function getClient()
{
if (!$this->isConnected()) {
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect($this->host, $this->port, $this->timeout)) {
throw new Exception(sprintf('Connect failed host=%s port=%d', $this->host, $this->port));
}
$this->client = $client;
}
return $this->client;
}

public function packData($interface, $func, $arguments)
{
$method = $interface . '@' . $func;
$data = [
'jsonrpc' => '2.0',
'method' => $method,
'params' => $arguments,
'context' => [],
'id' => '',
];
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
return pack('NN', strlen($json), 0) . $json;
}

public function __call($method, array $arguments)
{
$interface = $this->interface;
if(!isset($this->interfaces[$interface])){
throw new Exception('非法的interface');
}
$rpcFunc = $this->rpcFunc[$this->interfaces[$interface]];
if(!in_array($method, $rpcFunc)){
throw new Exception('非法的method');
}
$pack = $this->packData($this->interfaces[$interface], $method, $arguments);
return $this->sendAndRecv($pack);
}

protected function sendAndRecv($data)
{
return $this->runWithClient(function (Swoole\Client $client) use ($data) {
try {
if ($client->send($data) === false) {
throw new Exception('Send data failed.');
}
$response = $client->recv();
// 处理响应数据
if ($response === false) {
throw new Exception('Recv data failed.');
}
$res = substr($response,8);
$data = json_decode($res, true);
return $data['result'];
} catch (Exception $e) {
$client->close();
throw $e;
}
});
}

protected function runWithClient($callback)
{
return $callback($this->getClient());
}
}

效果如下:

实现接入tp6 think-swoole rpc服务
完美。
老项目也可以愉快的接进来了