PHP实现简单RPC

1.什么是rpc

RPC全称为Remote Procedure Call,翻译过来为“远程过程调用”。目前,主流的平台中都支持各种远程调用技术,以满足分布式系统架构中不同的系统之间的远程通信和相互调用。远程调用的应用场景极其广泛,实现的方式也各式各样。

2.从通信协议的层面

基于HTTP协议的(例如基于文本的SOAP(XML)、Rest(JSON),基于二进制Hessian(Binary))
基于TCP协议的(通常会借助Mina、Netty等高性能网络框架)

3.从不同的开发语言和平台层面

单种语言或平台特定支持的通信技术(例如Java平台的RMI、.NET平台Remoting)
支持跨平台通信的技术(例如HTTP Rest、Thrift等)

4.从调用过程来看

同步通信调用(同步RPC)
异步通信调用(MQ、异步RPC)

5.常见的几种通信方式

远程数据共享(例如:共享远程文件,共享数据库等实现不同系统通信)
消息队列
RPC(远程过程调用)

6.php实现简单的rpc

目录结构


 
image.png


rpc服务端

  1 <?php
  2 /**
  3  * User: yuzhao
  4  * CreateTime: 2018/11/15 下午11:46
  5  * Description: Rpc服务端
  6  */
  7 class RpcServer {
  8 
  9     /**
 10      * User: yuzhao
 11      * CreateTime: 2018/11/15 下午11:51
 12      * @var array
 13      * Description: 此类的基本配置
 14      */
 15     private $params = [
 16         'host'  => '',  // ip地址,列出来的目的是为了友好看出来此变量中存储的信息
 17         'port'  => '', // 端口
 18         'path'  => '' // 服务目录
 19     ];
 20 
 21     /**
 22      * User: yuzhao
 23      * CreateTime: 2018/11/16 上午12:14
 24      * @var array
 25      * Description: 本类常用配置
 26      */
 27     private $config = [
 28         'real_path' => '',
 29         'max_size'  => 2048 // 最大接收数据大小
 30     ];
 31 
 32     /**
 33      * User: yuzhao
 34      * CreateTime: 2018/11/15 下午11:50
 35      * @var nul
 36      * Description:
 37      */
 38     private $server = null;
 39 
 40     /**
 41      * Rpc constructor.
 42      */
 43     public function __construct($params)
 44     {
 45         $this->check();
 46         $this->init($params);
 47     }
 48 
 49     /**
 50      * User: yuzhao
 51      * CreateTime: 2018/11/16 上午12:0
 52      * Description: 必要验证
 53      */
 54     private function check() {
 55         $this->serverPath();
 56     }
 57 
 58     /**
 59      * User: yuzhao
 60      * CreateTime: 2018/11/15 下午11:48
 61      * Description: 初始化必要参数
 62      */
 63     private function init($params) {
 64         // 将传递过来的参数初始化
 65         $this->params = $params;
 66         // 创建tcpsocket服务
 67         $this->createServer();
 68     }
 69 
 70     /**
 71      * User: yuzhao
 72      * CreateTime: 2018/11/16 上午12:0
 73      * Description: 创建tcpsocket服务
 74 
 75      */
 76     private function createServer() {
 77         $this->server = stream_socket_server("tcp://{$this->params['host']}:{$this->params['port']}", $errno,$errstr);
 78         if (!$this->server) exit([
 79             $errno,$errstr
 80         ]);
 81     }
 82 
 83     /**
 84      * User: yuzhao
 85      * CreateTime: 2018/11/15 下午11:57
 86      * Description: rpc服务目录
 87      */
 88     public function serverPath() {
 89         $path = $this->params['path'];
 90         $realPath = realpath(__DIR__ . $path);
 91         if ($realPath === false ||!file_exists($realPath)) {
 92             exit("{$path} error!");
 93         }
 94         $this->config['real_path'] = $realPath;
 95     }
 96 
 97     /**
 98      * User: yuzhao
 99      * CreateTime: 2018/11/15 下午11:51
100      * Description: 返回当前对象
101      */
102     public static function instance($params) {
103         return new RpcServer($params);
104     }
105 
106     /**
107      * User: yuzhao
108      * CreateTime: 2018/11/16 上午12:06
109      * Description: 运行
110      */
111     public function run() {
112         while (true) {
113             $client = stream_socket_accept($this->server);
114             if ($client) {
115                 echo "有新连接\n";
116                 $buf = fread($client, $this->config['max_size']);
117                 print_r('接收到的原始数据:'.$buf."\n");
118                 // 自定义协议目的是拿到类方法和参数(可改成自己定义的)
119                 $this->parseProtocol($buf,$class, $method,$params);
120                 // 执行方法
121                 $this->execMethod($client, $class, $method, $params);
122                 //关闭客户端
123                 fclose($client);
124                 echo "关闭了连接\n";
125             }
126         }
127     }
128 
129     /**
130      * User: yuzhao
131      * CreateTime: 2018/11/16 上午12:19
132      * @param $class
133      * @param $method
134      * @param $params
135      * Description: 执行方法
136      */
137     private function execMethod($client, $class, $method, $params) {
138         if($class && $method) {
139             // 首字母转为大写
140             $class = ucfirst($class);
141             $file = $this->params['path'] . '/' . $class . '.php';
142             //判断文件是否存在,如果有,则引入文件
143             if(file_exists($file)) {
144                 require_once $file;
145                 //实例化类,并调用客户端指定的方法
146                 $obj = new $class();
147                 //如果有参数,则传入指定参数
148                 if(!$params) {
149                     $data = $obj->$method();
150                 } else {
151                     $data = $obj->$method($params);
152                 }
153                 // 打包数据
154                 $this->packProtocol($data);
155                 //把运行后的结果返回给客户端
156                 fwrite($client, $data);
157             }
158         } else {
159             fwrite($client, 'class or method error');
160         }
161     }
162 
163     /**
164      * User: yuzhao
165      * CreateTime: 2018/11/16 上午12:10
166      * Description: 解析协议
167      */
168     private function parseProtocol($buf, &$class, &$method, &$params) {
169         $buf = json_decode($buf, true);
170         $class = $buf['class'];
171         $method = $buf['method'];
172         $params = $buf['params'];
173     }
174 
175     /**
176      * User: yuzhao
177      * CreateTime: 2018/11/16 上午12:30
178      * @param $data
179      * Description: 打包协议
180      */
181     private function packProtocol(&$data) {
182         $data = json_encode($data, JSON_UNESCAPED_UNICODE);
183     }
184 
185 }
186 
187 RpcServer::instance([
188     'host'  => '127.0.0.1',
189     'port'  => 8888,
190     'path'  => './api'
191 ])->run();

rpc 客户端

 1 <?php
 2 /**
 3  * User: yuzhao
 4  * CreateTime: 2018/11/16 上午12:2
 5  * Description: Rpc客户端
 6  */
 7 class RpcClient {
 8 
 9     /**
10      * User: yuzhao
11      * CreateTime: 2018/11/16 上午12:21
12      * @var array
13      * Description: 调用的地址
14      */
15     private $urlInfo = array();
16 
17     /**
18      * RpcClient constructor.
19      */
20     public function __construct($url)
21     {
22         $this->urlInfo = parse_url($url);
23     }
24 
25     /**
26      * User: yuzhao
27      * CreateTime: 2018/11/16 上午12:2
28      * Description: 返回当前对象
29      */
30     public static function instance($url) {
31         return new RpcClient($url);
32     }
33 
34     public function __call($name, $arguments)
35     {
36         // TODO: Implement __call() method.
37         //创建一个客户端
38         $client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}", $errno, $errstr);
39         if (!$client) {
40             exit("{$errno} : {$errstr} \n");
41         }
42         $data = [
43             'class'  => basename($this->urlInfo['path']),
44             'method' => $name,
45             'params' => $arguments
46         ];
47         //向服务端发送我们自定义的协议数据
48         fwrite($client, json_encode($data));
49         //读取服务端传来的数据
50         $data = fread($client, 2048);
51         //关闭客户端
52         fclose($client);
53         return $data;
54     }
55 }
56 $cli = new RpcClient('http://127.0.0.1:8888/test');
57 echo $cli->tuzisir1()."\n";
58 echo $cli->tuzisir2(array('name' => 'tuzisir', 'age' => 23));

提供服务的文件

 1 <?php
 2 /**
 3  * User: yuzhao
 4  * CreateTime: 2018/11/16 上午12:28
 5  * Description:
 6  */
 7 
 8 class Test {
 9 
10     public function tuzisir1() {
11         return '我是无参方法';
12     }
13     public function tuzisir2($params) {
14         return $params;
15     }
16 }

效果

你是否有这样的烦恼,想学习高级技术,缺乏好的高级学习资料,11年架构师授课的TP5、laravel、swoole、swoft、高并发、分布式等资料,官方群:677079770 ,大牛带你装比带你飞

 

image.png

7.RPC的注意事项

性能:影响RPC性能的主要在几个方面:
1.序列化/反序列化的框架
2.网络协议,网络模型,线程模型等

安全
RPC安全的主要在于服务接口的鉴权和访问控制支持。

跨平台
跨不同的操作系统,不同的编程语言和平台。

推荐阅读:
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/993089249a/p/11749268.html