0%

PHP之RPC示例

RPC是一个完整的远程调用方案,它包括了接口规范+序列化反序列化规范+通信协议等,HTTP只是一个工作在OSI第七层通信协议,HTTP+Restful规范+序列化与反序列化整体构成了一个完整的远程调用方案。

一、概念

      RPC是一个完整的远程调用方案,它包括了接口规范+序列化反序列化规范+通信协议等,HTTP只是一个工作在OSI第七层通信协议,HTTP+Restful规范+序列化与反序列化整体构成了一个完整的远程调用方案。

RPC

二、示例

  1. server.php
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
<?php
class RpcServer
{
private $params = [
'host' => '', // ip地址,列出来的目的是为了友好看出来此变量中存储的信息
'port' => '', // 端口
'path' => '' // 服务目录
];

private $config = [
'real_path' => '',
'max_size' => 2048 // 最大接收数据大小
];

private $server = null;

public function __construct($params) {
$this->check();
$this->init($params);
}

private function check() {
$this->serverPath();
}

private function init($params) {
$this->params = $params;
$this->createServer();
}

private function createServer() {
$this->server = stream_socket_server("tcp://{$this->params['host']}:{$this->params['port']}", $errno, $errstr);
if (!$this->server) exit([
$errno, $errstr
]);
}

/**
* Description: rpc服务目录 通过客户端传值传过来
*/
public function serverPath() {
$path = $this->params['path'];
$realPath = realpath(__DIR__ . $path);
if ($realPath === false || !file_exists($realPath)) {
exit("{$path} error!");
}
$this->config['real_path'] = $realPath;
}

public function run() {
while (true) {
$client = stream_socket_accept($this->server, 100000); //这里的100000设置链接的超时时间
if ($client) {
echo "have a new connection\n";
$buf = fread($client, $this->config['max_size']);
print_r('receive data:' . $buf . "\n");
// 自定义协议目的是拿到类方法和参数(可改成自己定义的)
$infos = $this->parseProtocol($buf);
$this->execMethod($client, $infos["class"], $infos["method"], $infos["params"]);
fclose($client);
echo "close connection\n";
}
}
}

private function execMethod($client, $class, $method, $params) {
if ($class && $method) {
$class = ucfirst($class);
$file = $this->params['path'] . '/' . $class . '.php';
if (file_exists($file)) {
require_once $file;
$obj = new $class();
if (!$params) {
$data = $obj->$method();
} else {
$data = $obj->$method($params);
}
$data = $this->packProtocol($data);
//把运行后的结果返回给客户端
fwrite($client, $data);
}
} else {
fwrite($client, 'class or method error');
}
}

private function parseProtocol($buf) {
$buf = json_decode($buf, true);
return $buf;
}

private function packProtocol($data) {
$data = json_encode($data, JSON_UNESCAPED_UNICODE);
return $data;
}
}

(new RpcServer([
'host' => '127.0.0.1',
'port' => 9999,
'path' => './classes' #指定被调用的程序目录
]))->run();
  1. classes/test.php
1
2
3
4
5
6
7
8
9
10
<?php
class Test {

public function noparams() {
return 'no params';
}
public function myparams($params) {
return $params;
}
}
  1. client.php
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
<?php
/*
* RPC测试--RPC客户端
*/

class RpcClient {

private $urlInfo = array();

public function __construct($url) {
$this->urlInfo = parse_url($url);
print_r($this->urlInfo);
}

public static function instance($url) {
return new self($url);
}

/*
* 关键的魔术方法
*/
public function __call($name, $arguments) {
// TODO: Implement __call() method.
//创建一个客户端
$client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}", $errno, $errstr);
if (!$client) {
exit("{$errno} : {$errstr} \n");
}
$data = [
'class' => basename($this->urlInfo['path']),
'method' => $name,
'params' => $arguments
];
//向服务端发送我们自定义的协议数据
fwrite($client, json_encode($data));
//读取服务端传来的数据
$data = fread($client, 2048);
//关闭客户端
fclose($client);
return $data;
}
}
$cli = new RpcClient('tcp://127.0.0.1:9999/test');
echo $cli->noparams()."\n";
echo $cli->myparams(array("school"=>"hahah", "grade"=>3));
  1. 运行server.php
  2. 运行client.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server端:
have a new connection
receive data:{"class":"test","method":"noparams","params":[]}
close connection
have a new connection
receive data:{"class":"test","method":"myparams","params":[{"school":"hahah","grade":3}]}
close connection

client端:
Array
(
[scheme] => tcp
[host] => 127.0.0.1
[port] => 9999
[path] => /test
)
"no params"
[{"school":"hahah","grade":3}]

三、参考

  1. 参考一
  2. 参考二