RPC 定义
RPC(Remote Procedure Call)即远程过程调用,指被调用方法的具体实现不在程序运行本地,而是在别的某个地方。主要应用于不同的系统之间的远程通信和相互调用。

如 A 调用 B 提供的 remoteAdd 方法:

首先A与B之间建立一个TCP连接;
然后A把需要调用的方法名(这里是remoteAdd)以及方法参数(10, 20)序列化成字节流发送出去;
B接受A发送过来的字节流,然后反序列化得到目标方法名,方法参数,接着执行相应的方法调用(可能是localAdd)并把结果30返回;
A接受远程调用结果
有些远程调用选择比较底层的 socket 协议,有些远程调用选择比较上层的 HTTP 协议。

远程调用的好处:

解耦:当方法提供者需要对方法内实现修改时,调用者完全感知不到,不用做任何变更;这种方式在跨部门,跨公司合作的时候经常用到,并且方法的提供者我们通常称为:服务的暴露方
这里使用 PHP Socket 来创建一个服务端和客户端,目录结构如下:

服务端
<?php
class RpcServer {
protected $server = null;

  1. public function __construct($host, $port, $path)
  2. {
  3. // 创建一个 Socket 服务
  4. if(($this->server = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) {
  5. exit("socket_create() 失败的原因是:".socket_strerror($this->server)."\n");
  6. }
  7. if(($ret = socket_bind($this->server,$host,$port)) < 0) {
  8. exit("socket_bind() 失败的原因是:".socket_strerror($ret)."\n");
  9. }
  10. if(($ret = socket_listen($this->server,3)) < 0) {
  11. exit("socket_listen() 失败的原因是:".socket_strerror($ret)."\n");
  12. }
  13. // 判断 RPC 服务目录是否存在
  14. $realPath = realpath(__DIR__ . $path);
  15. if ($realPath === false || !file_exists($realPath)) {
  16. exit("{$path} error \n");
  17. }
  18. do {
  19. $client = socket_accept($this->server);
  20. if($client) {
  21. // 一次性读取
  22. $buf = socket_read($client, 8024);
  23. echo $buf;
  24. //解析客户端发送过来的协议
  25. $classRet = preg_match('/Rpc-Class:\s(.*);\r\n/i', $buf, $class);
  26. $methodRet = preg_match('/Rpc-Method:\s(.*);\r\n/i', $buf, $method);
  27. $paramsRet = preg_match('/Rpc-Params:\s(.*);\r\n/i', $buf, $params);
  28. if($classRet && $methodRet) {
  29. $class = ucfirst($class[1]);
  30. $method = $method[1];
  31. $params = json_decode($params[1], true);
  32. $file = $realPath . '/' . $class . '.php'; // 类文件需要和类名一致
  33. $data = ''; // 执行结果
  34. // 判断类文件是否存在
  35. if(file_exists($file)) {
  36. // 引入类文件
  37. require_once $file;
  38. // 实例化类
  39. $rfc_obj = new ReflectionClass($class);
  40. // 判断该类指定方法是否存在
  41. if($rfc_obj->hasMethod($method)) {
  42. // 执行类方法
  43. $rfc_method = $rfc_obj->getMethod($method);
  44. $data = $rfc_method->invokeArgs($rfc_obj->newInstance(), [$params]);
  45. } else {
  46. socket_write($client, 'method error');
  47. }
  48. //把运行后的结果返回给客户端
  49. socket_write($client, $data);
  50. }
  51. } else {
  52. socket_write($client, 'class or method error');
  53. }
  54. // 关闭客户端
  55. socket_close($client);
  56. }
  57. }while(true);
  58. }
  59. public function __destruct()
  60. {
  61. socket_close($this->server);
  62. }
  63. }
  64. new RpcServer('127.0.0.1',8080,'./service');

客户端
<?php
class RpcClient {
protected $client = null;
protected $url_info = []; // 远程调用 URL 组成部分

  1. public function __construct($url)
  2. {
  3. // 解析 URL
  4. $this->url_info = parse_url($url);
  5. }
  6. public function __call($name, $arguments)
  7. {
  8. // 创建一个客户端
  9. $this->client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  10. if(!$this->client) {
  11. exit('socket_create() 失败');
  12. }
  13. socket_connect($this->client, $this->url_info['host'], $this->url_info['port']);
  14. // 传递调用的类名
  15. $class = basename($this->url_info['path']);
  16. // 传递调用的参数
  17. $args = '';
  18. if(isset($arguments[0])) {
  19. $args = json_encode($arguments[0]);
  20. }
  21. // 向服务端发送我们自定义的协议数据
  22. $proto = "Rpc-Class: {$class};".PHP_EOL
  23. ."Rpc-Method: {$name};".PHP_EOL
  24. ."Rpc-Params: {$args};".PHP_EOL;
  25. socket_write($this->client, $proto);
  26. // 读取服务端传来的数据
  27. $buf = socket_read($this->client, 8024);
  28. socket_close($this->client);
  29. return $buf;
  30. }
  31. }
  32. $rpcClient = new RpcClient('http://127.0.0.1:8080/news');
  33. echo $rpcClient->display(['title'=>'txl']);
  34. echo $rpcClient->display(['title'=>'hello world']);

服务类 News
<?php
class News {
public function display($data)
{
return json_encode([‘result’=>”News display(), title is {$data[‘title’]}”]);
}
}
运行测试:

Client

Server

更多相关文章

  1. Python将CSV文件转化为HTML文件的操作方法
  2. PHP获取数组中单列值的方法
  3. PHP获取文件属性的最简单方法
  4. 常见的关键词选取方法有哪些?
  5. 如何让网站快速收录?网站提高收录的10种方法
  6. 不安全的HTTP方法
  7. http请求不带referer的解决方法
  8. Java如何调用Matlab程序
  9. 0812静态成员方法

随机推荐

  1. Internal error. Please report to https
  2. Android多点触摸缩放图片-android学习之
  3. 对View DrawingCache的理解
  4. Android ListView+image的使用
  5. [Android] 代码实现按钮/图片自旋转(中心
  6. Andorid在布局文件中中文加粗
  7. (20120808)(01)android菜单与对话框--之日期
  8. android 获得屏幕宽度 高度
  9. 在AndroidManifest.xml文件中的android:w
  10. Widget动态换背景图片 android