zoukankan      html  css  js  c++  java
  • RPC 简单小试

     由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里

      RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
    百度中有很多种解释,但根据公司所用的,以下解释比较贴切:
            首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
            第二,要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者是从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。
            第三,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。
            第四,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
            第五,返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给A服务器上的应用

    公司所用的是客户端为php,服务端为lua。


    举例(只是两个简单的示例,实际项目比较复杂):

    服务端示例:

    <?php
    class RpcServer {
        protected $serv = null;
     
        public function __construct($host, $port, $path) {
            //创建一个tcp socket服务
            $this->serv = stream_socket_server("tcp://{$host}:{$port}", $errno, $errstr);
            if (!$this->serv) {
                exit("one {$errno} : {$errstr} 
    ");
            }
            //判断我们的RPC服务目录是否存在
            $realPath = realpath(__DIR__ . $path);
       
            if ($realPath === false || !file_exists($realPath)) {
                exit("two {$path} error 
    ");
            }
     
            while (true) {
                $client = stream_socket_accept($this->serv);
     
                if ($client) {
                    //这里为了简单,我们一次性读取
                    $buf = fread($client, 2048);
                    //解析客户端发送过来的协议
                    $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]);
                        $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' . $classRet . '-' . $methodRet);
                    }
                    //关闭客户端
                    fclose($client);
                }
            }
        }
     
        public function __destruct() {
            fclose($this->serv);
        }
    }
     
    new RpcServer('127.0.0.1', 8888, '/service');

    客户端举例:

    <?php
     
    class RpcClient {
        protected $urlInfo = array();
         
        public function __construct($url) {
            //解析URL
            $this->urlInfo = parse_url($url);
            if(!$this->urlInfo) {
                exit("{$url} error 
    ");
            }
        }
         
        public function __call($method, $params) {
            //创建一个客户端
            $client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}", $errno, $errstr);
            if (!$client) {
                exit("{$errno} : {$errstr} 
    ");
            }
            //传递调用的类名
            $class = basename($this->urlInfo['path']);
            $proto = "Rpc-Class: {$class};" . 'Say';
            //传递调用的方法名
            $proto .= "Rpc-Method: {$method};" . 'hello';
            //传递方法的参数
            $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->hehe();
    echo $cli->hehe2(array('name' => 'test', 'age' => 27));

     先启动服务端,再启动客户端

     

     

  • 相关阅读:
    牛影传说【线段树+BFS序运用】
    动态规划 :传纸条
    CQYZ-OJ P1377 危险的组合
    使用 git 管理你的配置文件
    Exponential Distribution
    初尝 C++ 类设计
    Android刷机的一般步骤
    重装 Linux 记录
    Linux 折腾记录 (非正式)
    最大熵对应的概率分布
  • 原文地址:https://www.cnblogs.com/zhenghongxin/p/7483267.html
Copyright © 2011-2022 走看看