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安全的主要在于服务接口的鉴权和访问控制支持。

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

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

 

推荐阅读:

一:关于PHP在企业级开发领域的访谈

二:浅谈 PHP 与手机 APP 开发(API 接口开发)

三:比RBAC更好的权限认证方式(Auth类认证)

四:2019年PHP面试题附答案(实战经验)

原文地址:https://www.cnblogs.com/993089249a/p/11749268.html

时间: 2024-11-06 07:46:47

PHP实现简单RPC的相关文章

简单RPC实现之Netty实现

所谓RPC就是远程方法调用(Remote  Process Call ),简单的来说就是通过MQ,TCP,HTTP或者自己写的网络协议来传输我要调用对方的什么接口,对方处理之后再把结果返回给我.就这么简单的一个过程. 运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:1.调用客户端句柄:执行传送参数2.调用本地系统内核发送网络消息3.消息传送到远程主机4.服务器句柄得到消息并取得参数5.执行远程过程6.执行的过程将结果返回服务器句柄7.服务器句柄返回结果,调用远程系统内核8.消息

一个简单RPC框架是如何炼成的(V)——引入传输层

开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注册 RPC消息处理    接下来处理数据传输.实际应用场景一般都是基于socket.socket代码比较多,使用起来也比较麻烦.而且具体的传输通道使用socket或者其他的方式,如更上层的http,或者android里的binder,都是可替换的,只是具体的一种实现而已.所以,这里我就偷个懒,只是引入一个很简单的Connection类,用来描述一下如何将数据传输 这一层给独立出来. 首先简单列出Conne

一个简单RPC框架是如何炼成的(VI)——引入服务注册机制

开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注册 RPC消息处理 接下来处理RPC服务的注册机制.所谓注册机制,就是Server需要声明支持哪些rpc方法,然后当客户端发送调用某个声明的rpc方法之后,服务端能自动找到执行该请求的具体方法.以实际的例子为例,这是现在server端处理RPC请求的代码 def procRequest(self): # 循环读取并处理收到的客户端请求 while True: req = self.conn.recv()

一个简单RPC框架是如何炼成的(II)——制定RPC消息

开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注册 RPC消息处理 下面,我们先看一个普通的过程调用 class Client(object): def __init__(self): self.remote = None ## # 内部是委托给远程remote对象来获取结果. def sayHello(self): if self.remote: return self.remote.sayHello() else : return None cla

简单RPC框架-基于Consul的服务注册与发现

*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* BLOCKS =============================================================================*/ p, blockquote, ul, ol, dl, table, pre { margin: 15px 0; } /* HEAD

一个简单RPC框架是如何炼成的(III)——实现带参数的RPC调用

上一篇,我们制定了一个很简单的RPC消息 的格式,但是还遗留了两个问题 我们并没有实现相应的encode和decode方法,没有基于可以跨设备的字符串传输,而是直接的内存变量传递. 现在的RPC request不支持带参数的请求命令.如add(a, b), 如何在RPC消息中描述参数a,b . 我先来实现第二个问题,即带参数的RPC调用. 其实,也没什么太大不同.既然是要带参数,那只能扩展原来的Request消息了,加个parameter成员,用于表示参数,具体的格式采用字典方式,{ 'arg1

一个简单RPC框架是如何炼成的(IV)——实现RPC消息的编解码

之前我们制定了一个很简单的RPC消息 的格式,但是还遗留了两个问题,上一篇解决掉了一个,还留下一个 我们并没有实现相应的encode和decode方法,没有基于可以跨设备的字符串传输,而是直接的内存变量传递. 现在的RPC request不支持带参数的请求命令.如add(a, b), 如何在RPC消息中描述参数a,b . 下面我们处理掉这个编解码问题. 实际的RPC应用基本都是跨机器连接,所以无法直接传递内存变量,也就是说还需要将消息编码成 诸如字符串一类的可以跨设备传输的内容.具体的RPC消息

一个简单RPC框架是怎样炼成的(II)——制定RPC消息

开局篇我们说了,RPC框架的四个核心内容 RPC数据的传输. RPC消息 协议 RPC服务注冊 RPC消息处理 以下,我们先看一个普通的过程调用 class Client(object): def __init__(self): self.remote = None ## # 内部是托付给远程remote对象来获取结果. def sayHello(self): if self.remote: return self.remote.sayHello() else : return None cla

一个简单RPC框架是如何炼成的(I)——开局篇

开场白,这是一个关于RPC的相关概念的普及篇系列,主要是通过一步步的调整,提炼出一个相对完整的RPC框架. RPC(Remote Procedure Call Protocol)--远程过程调用协议,基于C/S模型.网络上有一篇文章写得不错,可以去了解一下相关概念深入浅出RPC 这里,直接使用一下上面作者的一个示意图 总结下来就是有4块核心内容 RPC数据的传输.如上面的RPCConnector,RPCChannel.它们主要负责数据传输这一块, 具体客户端与服务器之间的连接是不是socket连