Rpc的实现原理以及实现一个简单的Rpc

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。


在一个完整的Rpc协议中,包含了以下对象:

1:服务端,提供Rpc服务接口的服务端,可以有多个

2:客户端,请求Rpc服务端,可以有多个

同时,客户端也可以是服务端,服务端也可以是客户端,互相调用不同的服务


Rpc服务启动过程如下:

仙士可博客


Rpc服务调用过程如下:


仙士可博客


通过这2张图,可能你会发现:

这不就是相当于接口方法调用吗??

可以这么说,Rpc就是一种远程的接口方法调用的协议,

而且是一种跨服务器,跨平台化的通用接口调用的协议,

通过Rpc协议,我们将使用特定的字符串格式,请求其他服务器上的"方法"

同时,我们的客户端也可以不用关心服务端的服务实现了什么,我们只需要注册服务,用户通过字符串请求,就能请求到正确的服务端


服务发现

在Rpc服务启动流程图中,你可能发现了:客户端必须知道有serviceA这个服务,才能进行注册这个服务节点,才能进行调用?

在一般情况下的确是这样的,但是,我们可以做一个服务发现:

服务发现是指当服务端提供某个服务后,并不需要客户端进行注册,直接让服务端通知给客户端自己有这个服务

例如:

小明在服务端A中编写了"登录"服务,

小红在服务端B中编写了"注册"服务

而这2个服务在客户端是未知的,

在这个情况,小明让服务器A 使用udp协议,告诉了客户端以下内容:

新增"登录"服务,在服务器A,ip地址x.x.x.x,调用服务名为:"login"

新增"注册"服务,在服务器B,ip地址x.x.x.x,调用服务名为:"register"


这样的话,客户端接收到数据包,自动新增2个服务

小明则可以请求客户端,构造请求"login",客户端接收到,直接去请求服务器A获取数据


使用php实现一个简单的rpc

服务端代码:

<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 18-10-21
 * Time: 下午7:33
 */

class RpcServer
{
    protected $tcp_server;
    public function __construct($ip,$port)
    {
        //创建一个tcp服务,用于监听客户端数据
        //创建一个tcp socket服务
        $errno='';
        $errstr='';
        $this->tcp_server = stream_socket_server("tcp://{$ip}:{$port}", $errno, $errstr);
        if (!$this->tcp_server) {
            exit("{$errno} : {$errstr} \n");
        }
        echo "服务创建成功\n";
        while (true) {
            $client = stream_socket_accept($this->tcp_server);
            if ($client) {
                echo "客户端连接成功\n";
                //这里为了简单,我们一次性读取
                $buf = fread($client, 2048);
                echo "客户端请求数据为:{$buf}\n";
                //可以增加客户端数据包验证,ip验证等
                $data = json_decode($buf,1);
                //自行定义数据包格式,这里是用json
                //这儿需要验证json
                $class_name = $data['service'];
                $action_name = $data['action'];
                $parameter = $data['args'];
                //这里需要验证文件,类,方法是否存在
                include_once $class_name.".php";
                $class = new $class_name();
                $rev = $class->$action_name($parameter);
                echo "调用结果:{$rev}\n";
                //发送调用结果给客户端
                fwrite($client, $rev);
                //关闭客户端
                fclose($client);
            }
        }
    }
    public function __destruct() {
        fclose($this->tcp_server);
    }

}


客户端以及客户端请求代码:

<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 18-10-21
 * Time: 下午7:43
 */

//服务节点注册(直接模拟注册过程)
$service = [
    'Login' => [
        [
            'node' => 'node1',
            'ip'   => '127.0.0.1',
            'port' => '9501',
        ],
        [
            'node' => 'node2',
            'ip'   => '127.0.0.1',
            'port' => '9601',
        ],
    ]
];


//直接调用客户端请求服务端
$arr = [
    'service' => 'Login',
    'action'  => 'userLogin',
    'args'    => [
        'name'     => 'tioncico',
        'password' => '123456'
    ]
];
//根据请求服务名,获得服务节点(一个服务可以被多个服务器注册)
$node_array = $service[$arr['service']];
$node =$node_array[array_rand($node_array)];
echo "请求的服务节点为:{$node['node']}\n";
$fp = stream_socket_client("tcp://{$node['ip']}:{$node['port']}");

$sendStr = json_encode($arr);

fwrite($fp, $sendStr);
$data = fread($fp, 65533);

echo "服务端返回结果:{$data}\n";

启动服务端代码:

<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 18-10-21
 * Time: 下午7:37
 */

include "RpcServer.php";
$server = new RpcServer('0.0.0.0',9501);
<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 18-10-21
 * Time: 下午7:37
 */

include "RpcServer.php";
$server = new RpcServer('0.0.0.0',9601);

调用结果:

仙士可博客仙士可博客


EasySwoole Rpc组件

直接使用EasySwoole 3.x版本的Rpc组件,可实现一个功能完善的Rpc框架

https://github.com/easy-swoole/rpc




仙士可博客
请先登录后发表评论
  • 最新评论
  • 总共1条评论
仙士可博客

亘古匆匆:赞

2019-05-05 13:59:15 回复

  • 本站由白俊遥博客程序搭建
    © 2017-1-17 php20.cn 版权所有 ICP证:闽ICP备17001387号
  • 联系邮箱:1067197739@qq.com