#### 實現
> 應用程序創建一個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']));
}
```