RPC在实际应用中,通常会被多种编程语言所调用,但SW-X官方只提供了PHP相关的RPC-Client封装,若需要使用PHP外的其他编程语言,调用SW-X的RPC服务,则需要自行封装RPC-Client服务。
以下将进入详细的RPC-Client介绍。
一个完整的Client服务需要支持以下功能:
1、读取RPC服务节点配置(存储在Redis)
2、根据服务路由+目标函数,读取出该服务的全部节点配置
3、在节点配置中,取出评分最高,调用人数最低,延迟最低的节点
4、根据服务配置,按照SW-X规定的RPC交互格式,通过TCP协议发起请求
5、Client与RPC-Server的数据交互需支持AES加密,可自由开启关闭
6、若Server异常或Client发送失败,则需支持最大重复发送次数
7、Client需要支持异步投递任务到RPC-Server端
RPC服务节点,被存储在select 0
redis库中,存储数据格式如下:
类型:SETS 键前缀:_sets_ + md5(class + function),存储某项服务下的全部节点Redis_hash键名
类型:HMSET 键前缀:_hash_ + md5(class + function + ip + port),存储单个服务节点信息
类型:SET 键前缀:_score_ + md5(class + function + ip + port),存储单个服务的当前评分值
类型:SET 键前缀:_peaks_ + md5(class + function + ip + port),存储单个服务的当前延迟ms
类型:SET 键前缀:_num_ + md5(class + function + ip + port),存储单个服务的当前请求占用数
以下为获取某个服务下全部节点信息的PHP示例代码:
/**
* 读取配置
* @todo 无
* @author 小黄牛
* @version v1.2.24 + 2021.1.9
* @deprecated 暂不启用
* @global 无
* @return void
*/
public static function get($class, $function) {
$redis_key = \x\Config::get('rpc.redis_key');
$set_key = '_sets_'.md5($class.$function);
$redis = new \x\Redis();
$array = $redis->SMEMBERS($redis_key.$set_key);
$list = [];
// 读取全部节点
foreach ($array as $key) {
$val = $redis->hGetAll($redis_key.$key);
if ($val) {
$md5 = md5($val['class'].$val['function'].$val['ip'].$val['port']);
$score_key = '_score_'.$md5;
$peaks_key = '_peaks_'.$md5;
$num_key = '_num_'.$md5;
$val['ping_ms'] = $redis->get($redis_key.$peaks_key);
$val['score'] = $redis->get($redis_key.$score_key);
$val['request_num'] = $redis->get($redis_key.$num_key);
$list[] = $val;
}
}
$redis->return();
return $list;
}
获取到的节点配置为数据内容如下(这里用json演示说明):
{
title:"服务标题" // 服务标题
ip:"127.0.0.1" // TCP地址
port:"9501" // TCP端口
score:"100" // TCP地址
max_ms:"200" // 最大延迟MS
status:"0" // 手动关闭状态 0.开启 1.关闭
is_fault:0 // 故障状态 0.正常 1.故障
ping_ms:"0.008" // 延迟检测MS
request_num:23 // 当前使用人数
class:"driver/get_near_user" // 路由地址
function:"run" // 目标函数
}
Client是通过TCP连接,请求的RPC-Server服务,所以有严格的数据交互规范,Client的请求数据包必须是JSON
数据格式,结构如下:
{
"class": "zabbix/client_report", // 路由地址
"function": "run", // 目标函数
"headers": [], // 业务请求头
"param": { // 业务请求参数
"time": 1621498661,
"task_time": 0.005142,
"cpu": "1012.34KB"
},
"task": true, // 是否异步调用
"callback": false, // 是否启用异步回调通知,若启用则该参数填入API回调网址
"callback_type": "post" // 异步回调请求类型
}
由于SW-X的RPC通讯是走的TCP协议,所以在数据传输前后均支持AES加密与解密,若Server开启了数据加密,Client端则都要支持加解密。
使用的AES加密协议,具体可参考PHP的AES加密案例:
PHP数据加解密范例如下:
class Currency
{
/**
* AES加密方法
* @todo 无
* @author 小黄牛
* @version v1.2.24 + 2021.1.9
* @deprecated 暂不启用
* @global 无
* @param string $data 要加密的数据
* @return void
*/
public function aes_encrypt($data) {
$config = \x\Config::run()->get('rpc');
return openssl_encrypt($data, $config['aes_method'], $config['aes_key'], 0, $config['aes_iv']);
}
/**
* AES解密方法
* @todo 无
* @author 小黄牛
* @version v1.2.24 + 2021.1.9
* @deprecated 暂不启用
* @global 无
* @param string $data 要解密的数据
* @return void
*/
public function aes_decrypt($data) {
$config = \x\Config::run()->get('rpc');
return openssl_decrypt($data, $config['aes_method'], $config['aes_key'], 0, $config['aes_iv']);
}
}
若开启AES加密,Server的返回值则需要进行解密,最终得到明文数据为一个JSON
字符串,范例如下:
{
"status":"200" // 状态码
"msg":"SUCCESS" // 说明
"data":true // 服务返回值
}
若请求成功,则status
状态码为200
,其余状态码均表示请求失败。
具体的Client自行封装,可以参考SW-X提供的FPM-Client类包。点击前往
$Rpc = new \x\RpcClient();
$body = $Rpc->route(路由地址)
->func(目标函数)
->header(业务请求头)
->param(业务请求参数)
->max(异常最大递归次数)
->task(true) // 启用异步传输
->callback('http://www.baidu.com', 'GET'); // 异步回调通知
->send();
// send()的结果,只取Server返回的data部分
if ($Rpc->isSuccess()) {
var_dump($body);
} else {
var_dump('no~!');
}