专栏首页烟草的香味PHP实现RPC(简版)

PHP实现RPC(简版)

概述

RPC这个东西是什么? 第一次听说他, 还要在它的前边加个G, 当时我以为GRPC是一项技术, 后来才知道, 并不是这样. GRPC只是RPC的谷歌实现.

谷歌搜了一下, RPC就是一种: 远程函数调用, 看到这里, 我已经等不及了, 不往下看了, 先自己实现一个. 如果只给你这样一个概念, 如何实现调用远程函数的功能呢?

自己实现

自己尝试实现一个粗糙的PHP版本. (不想看可以跳过的)

思路

远程调用, 只需要解决下面问题:

  1. 通信问题
  2. 定义传输的数据格式
  3. 如何封装后可以达到像调用本地函数一样的效果

先来解决通信问题, 直接粗暴的tcp socket

传输的数据格式, 直接用json进行传输

调用本地函数?? 这就要借助一下PHP的魔术函数了, __call() 这个函数是一个类调用不存在的方法时会跑到这里来, 所以, 我们返回一个类, 在call方法中进行远程调用, 这样, 在本地看来就只是在调用一个方法.

开始实现

PHP中进行socket连接十分简单, 直接调用系统函数. 通信问题解决了, 剩下的就是传输数据了, so easy

经过一番摸索, 看下结果

服务器内容:

<?php
class RpcServer{

    private $port = 0; // 监听端口号
    private $host = ''; // IP

    public function __construct($host, $port){
        $this->host = $host;
        $this->port = $port;
    }

    /**
     * 运行, 监听端口并处理
     */
    public function run(){
        // 创建socket
        $server = stream_socket_server("tcp://{$this->host}:{$this->port}");
        if(empty($server)) throw new Exception('创建套接字失败');
        // 监听
        while (true){
            $client = stream_socket_accept($server);
            if(empty($client)) continue;
            // 处理请求
            $this->disposeClient($client);
            fclose($client);
        }
    }

    private function disposeClient($client){
        $buf = fread($client, 4096);
        $array = json_decode($buf, true);
        // 创建对象并调用方法
        $class = $array['class'] ?? '';
        $method = $array['method'] ?? '';
        $params = $array['params'] ?? [];
        $instance = new $class();
        $result = $instance->$method(...$params);
        fwrite($client, json_encode($result));
    }
}
// 测试调用类
class Test{
    public function tt(){
        return 'return_tt';
    }

    public function add($a, $b){
        return $a + $b;
    }
}

(new RpcServer('127.0.0.1', 8888))
    ->run();

调用方:

<?php
class RpcClient{
    private $urlInfo = null;
    private $className = '';

    private function __construct($url, $className){
        $this->urlInfo = parse_url($url);
        $this->className = $className;
    }

    public static function getInstance($className){
        return new RpcClient('127.0.0.1:8888', $className);
    }

    public function __call($name, $arguments){
        // 创建客户端
        $client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}");
        if(empty($client)) return null;
        // 发送数据
        fwrite($client, json_encode([
            'class' => $this->className,
            'method' => $name,
            'params' => $arguments,
        ]));
        // 接收返回
        $data = fread($client, 4096);
        // 关闭客户端
        fclose($client);
        return json_decode($data, true);
    }
}

$test = RpcClient::getInstance('Test');
echo $test->tt(), PHP_EOL;
echo $test->add(4, 6);

结果:

嗯, 还阔以. 当然, 问题还是有很多的, 比如不能实现保存对象的修改状态等等.

其实对象可以通过序列化和反序列化来传输, 额, Java中, 不知道PHP有没有这种技术.

当然, 一个RPC中必然大量使用反射序列化动态加载代理网络请求等等, 这只是一个超级超级粗糙的示例.

继续

nice, 自己做完了, 对RPC是个什么东西有了一个基本的概念.

WHAT

RPC是什么? 简单说, 就是远程函数调用. 字面意思, 很好理解.

WHY

看到一个技术, 一定会问的一个问题就是: 为什么? 一个技术基本不会平白无故出现, 都是为了解决某些问题, 那么RPC解决了什么问题呢? 字面含义: 远程函数调用

为什么要进行远程函数调用, 把函数拿过来本地调用不就好了? 还不用走网络IO, 速度更快一些. 很好, 现在假设, 你真的这样做了, 当项目变得庞大, 你想要进行拆分, 拆分后的有: 项目A, 项目B..., 这时, 你发现这些拆分的项目部分逻辑是重叠的, 比如用户信息相关, 怎么办? 如果不抽出来, 以后的维护成本会变得很高, 一处改处处改. 如果抽出来, 跨项目如何进行调用? 哎, 走过路过不要错过, RPC推荐给你.

HOW

那么如何实现RPC呢?

在刚才使用PHP简单实现中, 已经发现了. 需要解决的问题如下:

  1. 网络通信
  2. 信息格式
  3. 对象状态保存

1.网络通信

说到底, 网络通信不过两种: tcp udp.

有没有使用udp实现的RPC呢? 貌似也有.

使用tcp协议实现的RPC也有, 当然, 不光传输层协议, 也有直接通过应用层协议: httpwebsocket等等建立连接的. 当然, 如果需要频繁调用, 可以不断开tcp连接, 在一段时间内一直保持连接, 避免频繁握手.

2.信息格式

信息格式就有很多选择了, jsonxml等等, 也可以自己定制, 只要发送端和接收端统一信息格式就行了.

3.对象状态保存

对于一个类的调用, 通常都会有类状态修改的操作, 比如调用setName方法, 如何保存对象的信息呢? 当然, 可以服务端将对象在内存中的信息直接序列化发回去, 当客户端下次调用时携带序列化信息, 服务端接收后反序列化还原对象继续操作.

过程

个人理解的RPC调用过程:

  1. 客户端创建RPC对象
  2. 客户端调用方法
  3. RPC解析方法并将对象及参数做序列化
  4. RPC通过网络连接发送方法调用
  5. 服务端接收到方法调用, 解析对象及参数反序列化
  6. 服务端执行方法并将结果序列化返回
  7. 客户端接收到结果并进行解析, 返回给本地调用者
  8. 拿到最终结果

RPC适用于内部网络不同项目之间的通信, 如果是对外暴露的, 个人感觉还是通过接口的形式吧.

使用RPC显然会丧失一部分性能, 毕竟调用要走网络IO, 尽管是内网, 仍然要比本地调用慢上一些, 但带来了更好的可扩展性和可维护性, 感觉还是不错的.

之后如果用到的话, 拉个框架看看源码.

个人理解, 以上...

本文分享自微信公众号 - 烟草的香味(hujing-bc),作者:胡靖哥哥

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式之对象池模式

    对象池模式, 或者称为对象池服务, 其意图为: 通过循环使用对象, 减少资源在初始化和释放时的昂贵损耗(这里的"昂贵"可能是时间效益(如性能), 也可能是空间效...

    烟草的香味
  • GC算法-复制算法

    复制算法就是将内存空间二等分, 每次只使用其中一块. 当执行GC时, 将A部分的所有活动对象集体移到B中, 就可以将A全部释放.

    烟草的香味
  • Java中的深克隆和浅克隆

    克隆的对象可能包含一些已经修改过的属性, 而new出来的对象的属性都还是初始化时候的值, 所以当需要一个新的对象来保存当前对象的"状态"时就要靠克隆了.

    烟草的香味
  • 刷题外传之深入浅出 RPC

    无论是 Leetcode 和层出不穷的 Online Judge, 核心是帮助大家锻炼 Coding 和解算法题的能力。 刷题非常有用,如刷题时代的困局所述,未...

    包子面试培训
  • 【读书笔记】1.1-基于TCP协议的RPC

    1.1.1RPC名词解释 概念 全称Remote Process Call,即远程过程调用 rpc的实现包括服务的调用方和服务的提供方 过程 服务调用方发送RP...

    java思维导图
  • 网络协议 19 - RPC协议综述:远在天边近在眼前

        这几年微服务很火,想必各位博友或多或少的都接触过。微服务概念中, 各服务间的相互调用是不可或缺的一环。你知道微服务之间是通过什么方式相互调用的吗?

    北国风光
  • RPC 架构初探

    RPC的全称是Remote Procedure Call,它是一种进程间的通信方式。允许像调用本地服务一样调用远程服务,它的具体的实现方式可以不同,例如Spri...

    IMWeb前端团队
  • 架构-RPC传输之道

    RPC(Remote ProcedureCall),即远程过程调用,通过网络实现两个服务器之前的调用,RPC是跨越传输层和应用层。

    凹谷
  • rpc思维导图,让rpc不再难懂

    解析 RPC(Remote Procedure Call),远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 ? 在O...

    java思维导图
  • 阿里首席架构师科普RPC框架

    RPC概念及分类 RPC全称为Remote Procedure Call,翻译过来为“远程过程调用”。目前,主流的平台中都支持各种远程调用技术,以满足分布式系...

    Java架构

扫码关注云+社区

领取腾讯云代金券