• php 实现rpc,使用php链接jsonrpc服务


    最近在学习go时,看到微服务rpc时,在想php能不能实现呢,答案是肯定的,下面写下来记录一下。

    先看目录结构
    rpc
    ----api
    --------Test.php
    ----client
    --------RpcClient.php
    --------RpcJsonClientGo.php
    ----server
    --------RpcServer.php

    1、外面先实现php的rpc服务

    RpcServer.php

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

    Test.php

    
    
    class Test
    {
    
        public function testString()
        {
            return '测试字符串方法';
        }
    
        public function testParams($params)
        {
            return $params;
        }
    
        public function getName(){
            return 'hello word ' ;  // 返回字符串
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2、在写客户端

    RpcClient.php

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

    查看运行结果
    在这里插入图片描述
    在这里插入图片描述

    下面跨语言连接Go的jsonrpc服务

    RpcJsonClientGo.php

    
    
    
    class RpcJsonClientGo
    {
        private $conn;
    
        function __construct($host, $port)
        {
            $this->conn = fsockopen($host, $port, $errno, $errStr, 3);
            if (!$this->conn) {
                return false;
            }
        }
    
        public function Call($method, $params)
        {
            // 检验request信息
            if (!is_scalar($method)) {
                return "Method name has no scalar value";
            }
            if (!$this->conn) {
                return false;
            }
            $err = fwrite($this->conn, json_encode([
                    'method' => $method,
                    'params' => array($params),
                    'id' => 0,
                ]) . "\n");
            if ($err === false) {
                return false;
            }
            stream_set_timeout($this->conn, 0, 3000);
            $line = fgetc($this->conn);
            if($line === false){
                return null;
            }
            return json_decode($line,true);
        }
    
    }
    
    $client = new RpcJsonClientGo("127.0.0.1",8080);
    echo $client->Call(['ddd'=>22], "this is php languages");
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    执行php客户端文件,在go服务端就能看到发送来的数据
    在这里插入图片描述

  • 相关阅读:
    自学\跳槽\转行做网络安全行业的一些建议
    【代码随想录刷题记录】LeetCode34在排序数组中查找元素的第一个和最后一个位置
    基于扰动观测器的控制系统LMI控制
    python魔术方法和抽象类
    【知识点随笔分析 | 第六篇】HTTP/1.1,HTTP/2和HTTP/3的区别
    继承关系中构造函数、析构函数的调用顺序详解
    CMake日志与变量操作
    英文科技论文写作与发表-精简写法和“Chinglish“(第4章)
    自动推理的逻辑06--谓词演算
    AI概念之人工智能、机器学习和数据挖掘之间的联系与区别
  • 原文地址:https://blog.csdn.net/qq_23564667/article/details/126927309