#### 实现
> 应用程序创建一个swoole_client,与swoole_server三次握手建立连接,从而进行通信,通信过程中可以使用多种协议进行加解密
#### 使用
```php
// 同步请求
ZCSwoole::$app->rpcClient->request(router, params);
// 异步请求
ZCSwoole::$app->rpcClient->asyncRequest(router, params);
```
- router:控制器所在路由,与http一样,都是由Router类解析
- params:请求参数,参考call_user_func_params()
- 举个例子,ZCSwoole::$app->rpcClient->request('/rpc/test/saveName, array($name, $type))相当于调用下面代码:
```php
$controller = new app/controllers/rpc/testController();
$controller->saveName($name, $type);
```
#### 通信协议
- rpc的通信协议由zcswoole\rpc\RpcProtocol类负责,目前有三种方式,分别为json,serialize,swoole_seralize,对应参数如下:
```php
const PHP_JSON = 1; // json类型
const PHP_SERIALIZE = 2; // serialize类型
const SWOOLE_SERIALIZE = 3; // swoole_serialize类型
```
- 整个数据包包括: 包头(pkg_len+version+command_id+encode_type) + 包体(数据根据encode_type编码,例如json_encode)
- 通信协议的包头固定为16个字节,格式如下:NpkgLen/Nversion/NcommandID/NencodeType
| 字段 | 名称 |
| :--- | :--- |
| pkgLen | 包体长度,用于检验包的完整性 |
| version | 版本号,暂无用到 |
| commandID | 命令ID,暂无用到 |
| encodeType | 编码方式,用于解码数据 |
- 编码解码使用unpack,pack函数处理,以json为例:
```php
// 编码
$header = pack('N4', strlen($body), $versionID, $commandID, self::PHP_JSON);
// 解码
$header = unpack('NpkgLen/Nversion/NcommandID/NencodeType', $header);
```
#### 代码
> 目前http服务已经内置rpc功能,开发者不需要单独启动rpc服务,参考文件vendor/wuzhc/zcswoole/src/command/HttpServerCommand.php
```php
/**
* 钩子函数,用于服务启动前设置
*/
protected function beforeStart()
{
// 增加一个rpc服务,监听端口号9504
$rpc = $this->server->addListener('127.0.0.1', 9504, SWOOLE_SOCK_TCP);
$rpc->set([]); // 需要调用 set 方法覆盖主服务器的设置
$rpc->on('receive', [$this, 'rpcReceive']);
}
/**
* rpc接受客户端事件
* @param $server
* @param int $fd
* @param int $reactorID
* @param string $data
*/
public function rpcReceive(Server $server, $fd, $reactorID, $data)
{
$res = null;
$t1 = microtime(true);
list($code, $header, $body) = RpcProtocol::decode($data);
// 解包成功后处理业务
if ($code === RpcProtocol::ERR_UNPACK_OK) {
$target = $body['router'] ?? '';
$params = $body['params'] ?? [];
$router = new Router($target);
list($controller, $action) = $router->parse();
if (class_exists($controller)) {
try {
$res = call_user_func_array([$controller, $action], $params);
if (false === $res) {
$status = Constant::STATUS_FAILED;
$msg = "call $router failed";
} else {
$status = Constant::STATUS_SUCCESS;
$msg = 'success';
}
} catch (\Exception $e) {
$msg = $e->getMessage();
$status = Constant::STATUS_FAILED;
}
} else {
$msg = "Class $controller is not exist";
$status = Constant::STATUS_FAILED;
}
} else {
$msg = RpcProtocol::codeMsg($code);
$status = Constant::STATUS_FAILED;
}
// 通知结果给客户端
$server->send($fd, RpcProtocol::encode([
'data' => $res,
'time' => microtime(true) - $t1,
'status' => $status,
'msg' => $msg
], $header['encodeType']));
}
```