zoukankan      html  css  js  c++  java
  • PHP创建简单RPC服务

    RPC 定义

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

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

    1. 首先A与B之间建立一个TCP连接;
    2. 然后A把需要调用的方法名(这里是remoteAdd)以及方法参数(10, 20)序列化成字节流发送出去;
    3. B接受A发送过来的字节流,然后反序列化得到目标方法名,方法参数,接着执行相应的方法调用(可能是localAdd)并把结果30返回;
    4. A接受远程调用结果

    有些远程调用选择比较底层的 socket 协议,有些远程调用选择比较上层的 HTTP 协议。

    远程调用的好处:

    • 解耦:当方法提供者需要对方法内实现修改时,调用者完全感知不到,不用做任何变更;这种方式在跨部门,跨公司合作的时候经常用到,并且方法的提供者我们通常称为:服务的暴露方

    这里使用 PHP Socket 来创建一个服务端和客户端,目录结构如下:

    服务端

    <?php
    class RpcServer {
        protected $server = null;
    
        public function __construct($host, $port, $path)
        {
            // 创建一个 Socket 服务
            if(($this->server = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) {
                exit("socket_create() 失败的原因是:".socket_strerror($this->server)."
    ");
            }
            if(($ret = socket_bind($this->server,$host,$port)) < 0) {
                exit("socket_bind() 失败的原因是:".socket_strerror($ret)."
    ");
            }
            if(($ret = socket_listen($this->server,3)) < 0) {
                exit("socket_listen() 失败的原因是:".socket_strerror($ret)."
    ");
            }
    
            // 判断 RPC 服务目录是否存在
            $realPath = realpath(__DIR__ . $path);
            if ($realPath === false || !file_exists($realPath)) {
                exit("{$path} error 
    ");
            }
    
            do {
                $client = socket_accept($this->server);
                if($client) {
                    // 一次性读取
                    $buf = socket_read($client, 8024);
                    echo $buf;
    
                    //解析客户端发送过来的协议
                    $classRet = preg_match('/Rpc-Class:s(.*);
    /i', $buf, $class);
                    $methodRet = preg_match('/Rpc-Method:s(.*);
    /i', $buf, $method);
                    $paramsRet = preg_match('/Rpc-Params:s(.*);
    /i', $buf, $params);
    
                    if($classRet && $methodRet) {
                        $class = ucfirst($class[1]);
                        $method = $method[1];
                        $params = json_decode($params[1], true);
                        $file = $realPath . '/' . $class . '.php';  // 类文件需要和类名一致
                        $data = ''; // 执行结果
                        // 判断类文件是否存在
                        if(file_exists($file)) {
                            // 引入类文件
                            require_once $file;
                            // 实例化类
                            $rfc_obj = new ReflectionClass($class);
                            // 判断该类指定方法是否存在
                            if($rfc_obj->hasMethod($method)) {
                                // 执行类方法
                                $rfc_method = $rfc_obj->getMethod($method);
                                $data = $rfc_method->invokeArgs($rfc_obj->newInstance(), [$params]);
                            } else {
                                socket_write($client, 'method error');
                            }
                            //把运行后的结果返回给客户端
                            socket_write($client, $data);
                        }
                    } else {
                        socket_write($client, 'class or method error');
                    }
    
                    // 关闭客户端
                    socket_close($client);
                }
    
            }while(true);
        }
    
        public function __destruct()
        {
            socket_close($this->server);
        }
    }
    
    new RpcServer('127.0.0.1',8080,'./service');

    客户端

    <?php
    class RpcClient {
        protected $client = null;
        protected $url_info = [];   // 远程调用 URL 组成部分
    
        public function __construct($url)
        {
            // 解析 URL
            $this->url_info = parse_url($url);
        }
    
        public function __call($name, $arguments)
        {
            // 创建一个客户端
            $this->client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            if(!$this->client) {
                exit('socket_create() 失败');
            }
            socket_connect($this->client, $this->url_info['host'], $this->url_info['port']);
    
            // 传递调用的类名
            $class = basename($this->url_info['path']);
            // 传递调用的参数
            $args = '';
            if(isset($arguments[0])) {
                $args = json_encode($arguments[0]);
            }
            // 向服务端发送我们自定义的协议数据
            $proto = "Rpc-Class: {$class};".PHP_EOL
                ."Rpc-Method: {$name};".PHP_EOL
                ."Rpc-Params: {$args};".PHP_EOL;
            socket_write($this->client, $proto);
            // 读取服务端传来的数据
            $buf = socket_read($this->client, 8024);
            socket_close($this->client);
            return $buf;
        }
    }
    
    $rpcClient = new RpcClient('http://127.0.0.1:8080/news');
    echo $rpcClient->display(['title'=>'txl']);
    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

  • 相关阅读:
    java+opencv实现图像灰度化
    java实现高斯平滑
    hdu 3415 单调队列
    POJ 3368 Frequent values 线段树区间合并
    UVA 11795 Mega Man's Mission 状态DP
    UVA 11552 Fewest Flops DP
    UVA 10534 Wavio Sequence DP LIS
    UVA 1424 uvalive 4256 Salesmen 简单DP
    UVA 1099 uvalive 4794 Sharing Chocolate 状态DP
    UVA 1169uvalive 3983 Robotruck 单调队列优化DP
  • 原文地址:https://www.cnblogs.com/tangxuliang/p/9718447.html
Copyright © 2011-2022 走看看