RPC的简单实现

什么是RPC

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

通俗点讲,RPC一般采用TCP/IP通信,按照约定的数据格式(协议),对远端服务器上的服务进行(例如:xx函数)调用, 使得程序能够像访问本地系统资源一样,去访问远端系统资源。

RPC

接下来,我们用PHP的socket来简单实现RPC功能:

Socket服务端

通过Socket来接受RPC客户端来过来的数据,约定发送的数据采用以下数据格式(也可以称为协议):

Rpc-Class:调用的类
Rpc-Method:调用的类中的方法
Rpc-Params: 调用的方法的参数值(数据形式)

RPC

socket服务端代码,模拟远程服务器上的服务:

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
<?php

class RpcServer {

private $socket = null;

function __construct($host, $port, $path) {
$this->socket = stream_socket_server("tcp://{$host}:{$port}", $errno, $errstr);
if (!$this->socket) {
exit("{$errno}:{$errstr} \n");
}
// 判断RPC程序目录是否存在
$realpath = realpath($path);
if ($realpath === false || !file_exists($realpath)) {
echo("{$path} error \n");
}

while (true) {
$client = stream_socket_accept($this->socket);
echo $client." \n";

if ($client) {
$buf = fread($client, 2048);
echo $buf." \n";

// 解析客户端发过来的协议
$classRet = preg_match('/Rpc-Class:\s(.*);/i', $buf, $class);
$methodRet = preg_match('/Rpc-Method:\s(.*);/i', $buf, $method);
$paramRet = preg_match('/Rpc-Params:\s(.*);/i', $buf, $params);
if($classRet && $methodRet) {
$class = ucfirst($class[1]);
$file = $realpath . '/' . $class . '.php';
// 判断文件是否存在,如果有,则引入文件
if (file_exists($file)) {
require_once $file;
//实例化类,并调用客户端指定的方法
$obj = new $class();
//如果有参数,则传入指定参数
if(!$paramsRet) {
$data = $obj->$method[1]();
} else {
$data = $obj->$method[1](json_decode($params[1], true));
}
//把运行后的结果返回给客户端
fwrite($client, $data);
}
} else {
fwrite($client, "class or method error");
}

//关闭客户端
fclose($client);

}
}
}

function __destruct() {
fclose($this->socket);
}
}

new RpcServer('127.0.0.1',8888, './service');

Socket客户端

Socket客户端主要是发送socket请求,按照约定数据格式,发送数据:

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
<?php

class RpcClient {

private $urlInfo = array();

function __construct($url) {
$this->urlInfo = parse_url($url);
if (!$this->urlInfo) {
echo("{$url} error \n");
}
}

function __call($method, $params) {
// 创建一个客户端
$client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}", $errno, $errstr);

if (!$client) {
exit("{$errno} : {$errstr} \n");
}
//传递调用的类名
$class = basename($this->urlInfo['path']);
$proto = "Rpc-Class: {$class};" . PHP_EOL;
//传递调用的方法名
$proto .= "Rpc-Method: {$method};" . PHP_EOL;
//传递方法的参数
$params = json_encode($params);
$proto .= "Rpc-Params: {$params};" . PHP_EOL;
//向服务端发送我们自定义的协议数据
fwrite($client, $proto);
//读取服务端传来的数据
$data = fread($client, 2048);
//关闭客户端
fclose($client);
return $data;
}
}

$cli = new RpcClient("http://127.0.0.1:8888/test");
echo $cli->say();

分别运行server和client代码,运行结果如下:

rpc rpc

这样,通过socket我们就简单实现了RPC的调用。

有用就打赏一下作者吧!