PhpRpc 从 0 到 0.7

1.什么是RPC

RPC全称Remote Procedure Call,中文译为远程过程调用,简单理解就是 一种解决方案。

业务场景:
举一个大部分phper都接触过的商城开发,一般商城都有以下几个模块

  • 商品模块
  • 订单模块
  • 会员模块
  • XX模块

在常见架构中的体现是:

rpc11

那么在RPC架构中每个模块就是一个服务提供者,架构体现:

在这套架构中业务机的职责就是把一个请求 ,拆分成N个小请求,分发到各个服务里面,再整合各个服务的结果,返回给用户。


rpc12

例如在某次下单请求中,那么大概 发送的逻辑如下:

1. 业务机接受请求
2. 业务机提取用户参数,请求用户服务,获取用户余额等信息,等待结果
3. 业务机提取商品参数,请求商品服务,获取商品剩余库存和价格等信息,等待结果。
4. 业务机融合用户服务、商品服务的返回结果,进行下一步调用(假设满足购买条件)
5. 业务机调用用户服务进行扣款,调用商品服务进行库存扣减,调用订单服务进行下单(事务逻辑和撤回可以用请求id保证,或者自己实现其他逻辑调度)
6. 业务机根据处理响应用户

而在以上发生的行为,就称为远程过程调用。而调用过程实现的通讯协议可以有很多,比如常见的HTTP、TCP协议。

服务熔断

某个服务故障或者异常时直接熔断整个服务,而不是一直等到此服务超时

服务降级

当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回掉,返回一个缺省值 ,这样做,虽然服务水平下降,但好歹,比直接挂掉要强。 服务降级处理是在客户端实现完成的,与服务端没有关系

服务限流

例如某个服务器最多同时仅能处理100个请求, 或者是cpu负载达到百分之80的时候, 为了保护服务的稳定性,则不在希望继续收到 新的连接。那么此时就要求客户端不再对其发起请求,例如 你可以以任何的形式来监控你的服务,当触发某个条件时(CPU负载80%)下线此服务,业务机动态获取服务节点时就可以知道此服务已限流则响应用户[网络繁忙,请稍后再试] 或者此服务有多台机提供则其他机可继续提供服务,等被下线的机子恢复后又上线

2.Php Tcp通讯

源码

https://github.com/ar414-com/RpcDemo

开发环境要求

  • 保证 PHP 版本大于等于 7.2
  • 保证 Swoole 拓展版本大于等于 4.3.5
  • 使用 Linux / FreeBSD / MacOS 这三类操作系统

作者开发环境

  • PHP 7.2
  • Swoole 4.3.5
  • CentOS 7.2
rpc21

创建一个最基本的TCP服务器

<?php

//创建Server对象,监听 0.0.0.0:20001端口
$serv = new Swoole\Server("0.0.0.0", 20001);

$serv->on('Start', function ($serv) {
 echo "服务已启动,主进程PID:{$serv->master_pid}\n";
});

//监听连接进入事件
$serv->on('Connect', function ($serv, $fd) {
 echo "Client: Connect.\n";});

//监听数据接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
 echo "接收客户端数据:{$data}\n";
  $serv->send($fd, "Server: ".$data);
});

//监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
 echo "Client: Close.\n";});

//启动服务器
$serv->start();
rpc22
<?php

//建立连接
$fp = stream_socket_client('tcp://127.0.0.1:20001');

//发送数据
fwrite($fp, 'Test');

//主动获取响应
$data = fread($fp, 65533);

echo "服务端响应数据:{$data}\n";

//断开连接
fclose($fp);

客户端

rpc23

服务端

rpc24

3.客户端调用与服务端处理(提供思路)

客户端与服务器的数据传输约定

客户端请求Rpc服务(以下并非完整代码)

  • 场景:例如在一个商场系统中,我们将商品库和用户库两个服务切分开到不同的服务器当中
  • 当用户打开商场首页的时候, 我们希望App向某个网关发起请求,
  • 该网关可以自动的帮我们请求商品列表和用户信息等数据
//商品列表
$data = [
 'service' => 'Goods',  //服务名称
 'action'  => 'getList', //具体方法
 'arg'     => ['page' => 1] //请求参数
];
//用户信息
$data = [
 'service' => 'User',  //服务名称
 'action'  => 'getUserInfoForToken', //具体方法
 'arg'     => ['token' => '6aa62603ef82b70597a90d93af04b542'] //请求参数
];
//打包数据
$dataStr = serialize($data); 
$dataStr = pack('N', strlen($str)).$str;

请求API网关 API网关自动根据Service参数查询出对应服务IP、PORT并进行调用返回
本示例为了方便将Rpc服务配置写入.env文件 例:

//.env
RPC_GOODS_HOST=10.0.0.1
RPC_GOODS_PORT=8899
RPC_USER_HOST=10.0.0.2
RPC_USER_PORT=8899

服务端处理请求(完整代码

//接受请求数据并解包
$data = substr($request,'4');
$data = unserialize($data);
//TODO 检测必须参数 service action
//检测服务是否存在
//$controllerNameSpace是你的控制器命名空间
$service = ucfirst($data['service']);
$class   = "{$controllerNameSpace}\\{$service}";
if(!class_exists($class))
{
 //TODO 服务不存在
 //设置响应状态错误码(需自行封装)
 $response->setStatus(Response::STATUS_SERVICE_SERVICE_NOT_FOUND); //响应客户端(需自行封装)
 goto response;}

//检测方法是否存在
$class  = new \ReflectionClass($class);
$action = $data['action'];
if(!$class->hasMethod($action))
{
 //action不存在
 //重新组装参数
 //如果方法则调用魔术方法 比如调用一些PDO方法,如果无则调用时返回方法不存在
 $request->proxyActionAssemblyArg(); $method = $class->getMethod('__call');}
else
{
 $method = $class->getMethod($action);}

//调用
$instance = $class->newInstance($request,$response);
$ret = $method->invokeArgs($instance,$request->getArg());
$response->setMessage($ret);
//响应客户端(需自行封装)
goto response;

//作者的响应封装(仅供参考):
response:{
    if ($server->exist($fd))
    {
        $message = $response->getMessage();
        $responseData = [
            'status' => $response->getStatus(),
            'data'   => $message
        ];
        $responseData = serialize($responseData);
        $responseData = Request::pack($responseData);
        $server->send($fd,$responseData);
        //判断客户端是否需要长连接
        if(!$request->getIsKeep())
        {
            $server->close($fd);
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • 一、微服务简介 A.单体地狱 1.成功的应用有一个趋势,随着时间推移而变得越来越臃肿 2.复杂的单体应用本身就是持...
    ZyBlog阅读 2,070评论 0 43
  • 一、微服务介绍 1. 什么是微服务 在介绍微服务时,首先得先理解什么是微服务,顾名思义,微服务得从两个方面去理解,...
    阿南的生活记录阅读 439评论 0 3
  • Pomelo中游戏服务器是一个多进程相互协作的环境,各个进程之间通信采用RPC(远程过程调用)的形式完成,通过底层...
    JunChow520阅读 1,840评论 0 1
  • 引言: 近年来,大多数企业IT软件均在向微服务架构转型,由于微服务架构采用了更细粒度的分布式拆分,对于服务调用安全...
    AI云栈阅读 4,079评论 1 2
  • 昨天有个老朋友来咨询精灵卡,半年不见,自然亲切倍至。感情流动间,昔日重现,爱意流动,恍惚间回来当年初见时,...
    郗红嘉阅读 265评论 0 2