RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
在一个完整的Rpc协议中,包含了以下对象:
1:服务端,提供Rpc服务接口的服务端,可以有多个
2:客户端,请求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
/**
* 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 3.x版本的Rpc组件,可实现一个功能完善的Rpc框架
https://github.com/easy-swoole/rpc
本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn