前面我会说一下rpc,然后再说一下我搭建的步骤,可能文章会比较长,如果你的环境已经搭建好了,可以移动到最底部,看客户端、服务端、路由的代码。就可以了,但前提你要保证你完成了这些步骤:

1、composer下载

2、生成两个配置文件 route/rpc.php、config/hprose

3、.env 编写监听的端口和采用的通讯协议

4、在route/rpc.php中编写路由

5、编写路由对应的逻辑方法

6、服务端可以监听端口了

 

RPC

RPC(Remote Procedure Call Protocol)——远程过程调用协议,RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了近似于无限制提升的可能。在OSI网络通信模型中,RPC跨域了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC架构

一个完整的RPC架构里面包含了四个核心的组件,分别是Client,Client Stub,Server以及Server Stub,这个Stub可以理解为存根。

  • 客户端(Client),服务的调用方。
  • 客户端存根(Client Stub),存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
  • 服务端(Server),真正的服务提供者。
  • 服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。

RPC调用过程

基于C的RPC组件_php

 

(1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;

(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);

(3) 客户端通过sockets将消息发送到服务端;

(4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);

(5) 服务端存根( server stub)根据解码结果调用本地的服务;

(6) 本地服务执行并将结果返回给服务端存根( server stub);

(7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);

(8) 服务端(server)通过sockets将消息发送到客户端;

(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);

(10) 客户端(client)得到最终结果。

RPC的目标是要把2、3、4、7、8、9这些步骤都封装起来。

注意:无论是何种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流再恢复为对象。

laravel中使用RPC

具体文档:

官方:https://github.com/hprose/hprose-php/wiki/05-Hprose-%E5%AE%A2%E6%88%B7%E7%AB%AF

laravel框架的:https://github.com/zhuqipeng/laravel-hprose

要求:

php >= 5.0
laravel / lumen >= 5.2

demo

 

先切换镜像到阿里云再执行下载 命令如下:

composer config repo.packagist composer https://mirrors.aliyun.com/composer/
composer require "zhuqipeng/laravel-hprose:v1.0-alpha"  --ignore-platform-reqs   -vvv

 

遇到两个错误

第一个错误,执行下载rpc包命令时候

Fatal error: Class UpdateHelper\ComposerPlugin contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Composer\Plugin\PluginInterface::deactivate, Composer\Plugin\PluginInterface::uninstall) in /Users/twj/Documents/nginx_www/more/mhhf_act/vendor/kylekatarnls/update-helper/src/UpdateHelper/ComposerPlugin.php on line 11

 

初步怀疑是 你的laravel框架中 vendor/kylekatarnls的版本问题 (当然也可能其他包也存在版本问题也不排除,我这里是vendor/kylekatarnls的版本太低了)

第一种解决方案是删除再重新执行下载rpc包的命令,但是记得做好备份再删除!!!!!!!

第二种就是在你另一个laravel项目中把vendor/kylekatarnls这个包复制粘贴进去,当然也要做好备份哦,免得找不回来旧包而导致别的错误

基于C的RPC组件_php_02

 

基于C的RPC组件_服务端_03

 

我提升了包的版本后,开始遇到第二个错误

 

In PackageManifest.php line 120:

Undefined index: name

Script @php artisan package:discover handling the post-autoload-dump event returned with error code 1

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

 

 

这个错误的源码我找到是在这里的120行
 

mhhf_act/vendor/laravel/framework/src/Illuminate/Foundation/PackageManifest.php

基于C的RPC组件_网络_04

此时,进入这个方法 : mapWithKeys

修改他的源码,具体是什么原因,你们打印一下就知道了。

基于C的RPC组件_网络_05

 

修改后的样子:

 

/**
     * Run an associative map over each of the items.
     *
     * The callback should return an associative array with a single key/value pair.
     *
     * @param  callable  $callback
     * @return static
     */
    public function mapWithKeys(callable $callback)
    {
        $result = [];
        $item = $this->items;
        if(isset($item['packages'])){
            $item = $item['packages'];
        }

        foreach ($item as $key => $value) {
            $assoc = $callback($value, $key);

            foreach ($assoc as $mapKey => $mapValue) {
                $result[$mapKey] = $mapValue;
            }
        }

        return new static($result);
    }

好,现在两个错误都解决了,再次执行composer下载rpc包

composer require "zhuqipeng/laravel-hprose:v1.0-alpha"  --ignore-platform-reqs   -vvv

 

开始步骤

1、配置文件

 

 

执行命令:

php artisan vendor:publish --provider="Zhuqipeng\LaravelHprose\ServiceProvider"

会在应用目录下创建

/config/hprose.php

/routes/rpc.php

输入命令后如果提示这样,说明是成功的:

php artisan vendor:publish --provider="Zhuqipeng\LaravelHprose\ServiceProvider"
Copied File [/vendor/zhuqipeng/laravel-hprose/src/config.php] To [/config/hprose.php]
Copied File [/vendor/zhuqipeng/laravel-hprose/src/route.php] To [/routes/rpc.php]
Publishing complete.

 

基于C的RPC组件_网络_06

2、.env配置

#rpc
HPROSE_URIS=["tcp://www.mhhfact.net:1314"]//监听地址列表,字符串json格式数组
HPROSE_URL="tcp://www.mhhfact.net:1314"//远端请求地址,不是json格式数组的
HPROSE_DEMO="true"// 是否启用demo方法,true开启 false关闭,开启后将自动对外发布一个远程调用方法 demo

 服务端

在生成的文件中(/routes/rpc.php) 编写路由代码

我这里是用laravel的中间件,因为我发现好像这个包没有提供中间件,也没事,我们用laravel自带的就好。 

 

<?php

// rpc路由  -> 测试调试
\LaravelHproseRouter::add('demo', function () {
    return 'demo';
});

// rpc路由  -> 测试调试 $name 为函数的参数
\LaravelHproseRouter::add('getUserByName', function ($name) {
    return 'name: ' . $name;
});

// rpc路由 -> 测试调试 
\LaravelHproseRouter::add('userUpdate', \App\Http\Controllers\Api\v1\Rpc\RpcServiceApi::class . '@update', ['model' => \Hprose\ResultMode::Normal]);

// 开始正式请求
Route::group(['prefix' => 'v1'], function ($router) {
    $router->group(['middleware' => ['api.sign.auth']], function () {

        /**
         * Hprose\ResultMode::Normal 是默认值,表示返回正常的已被反序列化的结果。
         * Hprose\ResultMode::Serialized 表示返回的结果保持序列化的格式。
         * Hprose\ResultMode::Raw 表示返回原始数据。
         * Hprose\ResultMode::RawWithEndTag 表示返回带有结束标记的原始数据。
         */

        // rpc路由 -> 应用到我的项目代码
        \LaravelHproseRouter::add(
            'ticketWriteOffList',
            \App\Http\Controllers\Api\v1\Rpc\RpcActivityApi::class . '@getDisplayWindow',
            ['model' => \Hprose\ResultMode::Normal]
        );

        // rpc路由 -> 应用到我的项目代码
        \LaravelHproseRouter::add(
            'updateUserTake',
            \App\Http\Controllers\Api\v1\Rpc\RpcWithdrawalApi::class . '@updateUserTake',
            ['model' => \Hprose\ResultMode::Normal]
        );

    });
});

 

服务端 提供的方法 userUpdate 方法对应的路由叫userUpdate:

基于C的RPC组件_php_07

 

服务端 提供的方法getDisplayWindow。方法对应的路由也叫getDisplayWindow: 他返回的是一个商品的详情 是一个array 后面我会给大家看他的数据返回是什么样子的

<?php

namespace App\Http\Controllers\Api\v1\Rpc;

use App\Http\Controllers\Controller;
use App\Services\ActivityService;

class RpcActivityApi extends Controller
{
    /***
     * rpc 通讯 -> 获取线下活动详情
     * @param int $act_id 活动id
     * @return array
     */
    public function getDisplayWindow($act_id) : array
    {
        return ActivityService::getDisplayWindow($act_id);
    }
}

 

步骤:

1、composer下载

2、生成两个配置文件 route/rpc.php、config/hprose

3、.env 编写监听的端口和采用的通讯协议

4、在route/rpc.php中编写路由

5、编写路由对应的逻辑方法

当你完成了这五个步骤之后,就可以去终端启动监听了。

监听命令如下:

twj@tongwenjiedeMacBook-Pro mhhf_act % php artisan hprose:socket_server

基于C的RPC组件_rpc_08

 

客户端请求开始

 

 这里采用 getDisplayWindow 这个远端方法来进行做例子

客户端的代码如下:

/**
     * 线下活动商品
     * @param $act_id
     * @return array
     */
    public static function getAct($act_id)
    {
        try {
            // http://www.mhhfact.net 本地
            // http://qa.act.meilife365.com 测试
//            $url = env('APP_DEBUG')
//                ? 'http://qa.act.meilife365.com'  // 我本地的测试环境
//                : 'http://pc-act.meilife365.com'; // 正式环境
//            $withData = ['act_id' => $act_id];
//            $res = Curl::to($url . '/api/v1/get/display/window')
//                ->withHeaders(CommonService::headerSignArr())
//                ->withData($withData)
//                ->get();
//            $data = json_decode($res, true);
//            if (!isset($data['data']['act_id']) || !$data['data']['act_id']) {
//                throw new \Exception('没有该线上营,或没有绑定sku', -1);
//            }
//            $insert = [
//                'value' => $data['data']['act_id'] ?: 0,
//                'type' => 3,
//                'name' => $data['data']['title'] ?: '',
//                'price' => $data['data']['price'] ?: 0,
//                'cover_img' => $data['data']['cover'] ?: '',
//                'status' => 1,
//                'course_platform' => 2,
//            ];
//            return $insert;

            // rpc 通讯
            $clinet = new Client(env('HPROSE_URL'), false);
            $insert = $clinet->getDisplayWindow($act_id);
            if (!$insert) {
                throw new \Exception('暂无线下活动', -1);
            }
            return $insert;
        } catch (\Exception $e) {
            Log::info('获取线下活动失败', [
                '信息' => $e->getMessage(),
                '文件' => $e->getFile(),
                '行数' => $e->getLine()
            ]);
            return [];
        }
    }

 

给大家看看传统的http请求别的项目是多么的繁琐

代码上的区别

基于C的RPC组件_rpc_09

 

数据上的区别

基于C的RPC组件_rpc_10

 

基于C的RPC组件_php_11

 

总结:

1、rpc就像调用本地函数一样,不像http还要封装请求头,请求方法,数据包自然就小,适合大项目,小项目就体验不到什么速度了

2、rpc可以不用json_decode 、 json_encode 之类的交互了

3、rpc还能异步调用

4、rpc 适合微服务、项目之间关联使用。

5、rpc启动监听之后,是在内存常驻的,会消耗内存cpu,但是速度会快。

6、远端都不需要定义返回规则了,比如code、data、msg之类的,因为rpc的调用就像调用本地函数一样。

 

 

后续你肯定要守护进程的

systemd、nohup、supervisor  都可以做守护 php artisan hprose:socket_server 这一条命令

 

 

如果你想要自己手动测试,那你复制我下面的代码就可以了,上面只是讲解我的心得

首先是你环境、配置都弄好了之后,就复制这些代码

路由代码:

<?php

\LaravelHproseRouter::add('demo', function () {
    return 'demo';
});

\LaravelHproseRouter::add('getUserByName', function ($name) {
    return 'name: ' . $name;
});

\LaravelHproseRouter::add('userUpdate', \App\Http\Controllers\Api\v1\Rpc\RpcServiceApi::class . '@update', ['model' => \Hprose\ResultMode::Normal]);

客户端代码

<?php

namespace App\Http\Controllers\Api\v1\Rpc;

use App\Http\Controllers\Controller;

class RpcClientApi extends Controller
{
    /**
     * demo 客户端
     * @return mixed
     */
    public function index()
    {
        $client = new \Hprose\Socket\Client(env('HPROSE_URL'), false);
        $data = $client->userUpdate(15);
        return $data;
    }
}

服务端代码

<?php

namespace App\Http\Controllers\Api\v1\Rpc;

use App\Http\Controllers\Controller;

class RpcServiceApi extends Controller
{
    /**
     * demo 服务端
     * @param $name
     * @return string
     */
    public function update($name)
    {
        return 'update name: ' . $name;
    }
}

服务端监听端口

php artisan hprose:socket_server

客户端请求即可