閱讀目錄
- 構(gòu)建服務(wù)消費(fèi)者
- 安裝json rpc依賴
- 安裝JSON RPC客戶端
- server配置
- 編寫業(yè)務(wù)代碼
- 編寫服務(wù)消費(fèi)者類
- consumer配置
- 配置 UserServiceInterface
- 編寫UserController
- postman測試
- 自動(dòng)配置服務(wù)消費(fèi)者
- 配置優(yōu)化
- 統(tǒng)一結(jié)果處理
我們說過,服務(wù)提供者可以提供各種服務(wù),它可以和數(shù)據(jù)庫進(jìn)行交互;服務(wù)消費(fèi)者是純消費(fèi)的服務(wù),只需要遠(yuǎn)程訪問服務(wù)提供者即可。
下面我們按步驟構(gòu)建消費(fèi)者模塊。
源碼已上傳至github,https://github.com/bailangzhan/hyperf-rpc
1、構(gòu)建服務(wù)消費(fèi)者
除了對時(shí)區(qū)進(jìn)行設(shè)置,其他的組件暫時(shí)都不安裝,選擇“n”即可。
composer create-project hyperf/hyperf-skeleton shop_consumer_user
Creating a "hyperf/hyperf-skeleton" project at "./shop_consumer_user"
Installing hyperf/hyperf-skeleton (v2.2.1)
- Installing hyperf/hyperf-skeleton (v2.2.1): Extracting archive
Created project in /data/web/test/hyperf-rpc/shop_consumer_user
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
> Installer\Script::install
Setting up optional packages
Setup data and cache dir
Removing installer development dependencies
What time zone do you want to setup ?
[n] Default time zone for php.ini
Make your selection or type a time zone name, like Asia/Shanghai (n):
Asia/Shanghai
Do you want to use Database (MySQL Client) ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (yes): n
Do you want to use Redis Client ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (yes): n
Which RPC protocol do you want to use ?
[1] JSON RPC with Service Governance
[2] JSON RPC
[3] gRPC
[n] None of the above
Make your selection or type a composer package name and version (n): n
Which config center do you want to use ?
[1] Apollo
[2] Aliyun ACM
[3] ETCD
[4] Nacos
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/constants component ?
[y] yes
[n] None of the above
Make your selection (n): n
Do you want to use hyperf/async-queue component ? (A simple redis queue component)
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/amqp component ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/model-cache component ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/elasticsearch component ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/tracer component ? (An open tracing protocol component, adapte with Zipkin etc.)
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
2、安裝json rpc依賴
cd shop_consumer_user
composer require hyperf/json-rpc
3、安裝 JSON RPC 客戶端
shop_consumer_user 不需要對外提供服務(wù),所以我們只安裝客戶端,不需要安裝hyperf/rpc-server組件
composer require hyperf/rpc-client
4、server配置
server的配置這里用默認(rèn)的就好了,9501端口提供http服務(wù),不需要改動(dòng)
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9501,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
],
],
],
5、編寫業(yè)務(wù)代碼
5-1、編寫服務(wù)消費(fèi)者類
app下新建JsonRpc目錄,編寫UserService.php和UserServiceInterface.php文件
【UserServiceInterface.php】
<?php
namespace App\JsonRpc;
interface UserServiceInterface
{
public function createUser(string $name, int $gender);
public function getUserInfo(int $id);
}
<span class="redactor-invisible-space">
</span>【UserService.php】
<?php
namespace App\JsonRpc;
use Hyperf\RpcClient\AbstractServiceClient;
class UserService extends AbstractServiceClient implements UserServiceInterface
{
/**
* 定義對應(yīng)服務(wù)提供者的服務(wù)名稱
* @var string
*/
protected $serviceName = 'UserService';
/**
* 定義對應(yīng)服務(wù)提供者的服務(wù)協(xié)議
* @var string
*/
protected $protocol = 'jsonrpc-http';
/**
* @param string $name
* @param int $gender
* @return mixed
*/
public function createUser(string $name, int $gender)
{
return $this->__request(__FUNCTION__, compact('name', 'gender'));
}
/**
* @param int $id
* @return mixed
*/
public function getUserInfo(int $id)
{
return $this->__request(__FUNCTION__, compact('id'));
}
}
hyperf 官方的hyperf/rpc-client組件已經(jīng)幫我們實(shí)現(xiàn)了rpc遠(yuǎn)程調(diào)用的實(shí)現(xiàn),所以我們只需要再配置一下服務(wù)消費(fèi)者,告訴hyperf從哪個(gè)節(jié)點(diǎn)哪個(gè)端口調(diào)用即可。
5-2、consumer配置
config/autoload/services.php內(nèi)定義consumers如下:(沒有services.php文件的可以自行創(chuàng)建)
<?php
return [
'consumers' => [
[
// 對應(yīng)消費(fèi)者類的 $serviceName
'name' => 'UserService',
// 直接對指定的節(jié)點(diǎn)進(jìn)行消費(fèi),通過下面的 nodes 參數(shù)來配置服務(wù)提供者的節(jié)點(diǎn)信息
'nodes' => [
['host' => '127.0.0.1', 'port' => 9600],
],
]
],
];
5-3、配置 UserServiceInterface
為了可以方便的注入 UserServiceInterface,我們在 config/autoload/dependencies.php 內(nèi)定義 UserServiceInterface 和 UserService 的關(guān)系如下:
App\JsonRpc\UserServiceInterface::class => App\JsonRpc\UserService::class,
5-4、編寫UserController,實(shí)現(xiàn)獲取用戶和創(chuàng)建用戶的接口調(diào)用
【app\Controller\UserController.php】
<?php
declare(strict_types=1);
namespace App\Controller;
use App\JsonRpc\UserServiceInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\AutoController;
/**
* Class UserController
* @package App\Controller
* @AutoController()
*/
class UserController extends AbstractController
{
/**
* @Inject()
* @var UserServiceInterface
*/
private $userServiceClient;
public function createUser()
{
$name = (string) $this->request->input('name', '');
$gender = (int) $this->request->input('gender', 0);
return $this->userServiceClient->createUser($name, $gender);
}
public function getUserInfo()
{
$id = (int) $this->request->input('id');
return $this->userServiceClient->getUserInfo($id);
}
}
6、postman訪問測試
啟動(dòng)shop_consumer_user項(xiàng)目的同時(shí),務(wù)必要保證 shop_provider_user 也啟動(dòng)了,不然請求會(huì)失敗。
7、自動(dòng)配置服務(wù)消費(fèi)者
你可能已經(jīng)注意到 app\JsonRpc\UserService 類的方法并沒有實(shí)際意義,只是構(gòu)建參數(shù)發(fā)起請求并返回響應(yīng)結(jié)果,千篇一律的操作著實(shí)增加了復(fù)雜度。hyperf支持自動(dòng)配置服務(wù)消費(fèi)者代理類(生產(chǎn)者暫不支持自動(dòng)配置)。
自動(dòng)配置非常簡單,只需要在 consumer 配置項(xiàng)增加service配置即可,如下:
return [
'consumers' => [
[
// 對應(yīng)消費(fèi)者類的 $serviceName
'name' => 'UserService',
// 服務(wù)接口名,可選,默認(rèn)值等于 name 配置的值,如果 name 直接定義為接口類則可忽略此行配置,
// 如 name 為字符串則需要配置 service 對應(yīng)到接口類
'service' => \App\JsonRpc\UserServiceInterface::class,
// 直接對指定的節(jié)點(diǎn)進(jìn)行消費(fèi),通過下面的 nodes 參數(shù)來配置服務(wù)提供者的節(jié)點(diǎn)信息
'nodes' => [
['host' => '127.0.0.1', 'port' => 9600],
],
]
],
];
現(xiàn)在我們做兩件事,測試consumer走的是自動(dòng)配置還是手動(dòng)創(chuàng)建的UserService
- 把 config/autoload/dependencies.php 內(nèi)定義 UserServiceInterface 和 UserService 的關(guān)系屏蔽
- 在 App\JsonRpc\UserService::getUserInfo() 方法內(nèi)打印點(diǎn)數(shù)據(jù)測試
GET請求 http://127.0.0.1:9501/user/getUserInfo?id=2
結(jié)果發(fā)現(xiàn)控制臺(tái)并沒有任何輸出,走的是自動(dòng)配置的consumer
反過來
- 我們再把 config/autoload/dependencies.php 內(nèi)定義 UserServiceInterface 和 UserService 的關(guān)系放開
- 把 config/autoload/services.php 文件內(nèi) consumers 的配置項(xiàng) service 屏蔽
GET請求 http://127.0.0.1:9501/user/getUserInfo?id=2
string(36) "App\JsonRpc\UserService::getUserInfo"
發(fā)現(xiàn)控制臺(tái)輸出了我們在 App\JsonRpc\UserService::getUserInfo() 方法內(nèi)打印的數(shù)據(jù),
走的是手動(dòng)創(chuàng)建的consumer
在沒有特殊情況下,后續(xù)consumer我們僅做配置,不在手動(dòng)創(chuàng)建,因?yàn)闆]有創(chuàng)建的必要。
8、配置優(yōu)化
我們注意到 config/autoload/services.php 文件內(nèi) consumers 的配置,一個(gè)服務(wù)是一個(gè)配置,服務(wù)消費(fèi)者需要消費(fèi)的服務(wù)可能很多,所以我們很有必要優(yōu)化下這里的寫法,下面是參考官網(wǎng)的寫法:
// 服務(wù)定義
$consumerServices = [
'UserService' => \App\JsonRpc\UserServiceInterface::class,
];
return [
'consumers' => value(function () use ($consumerServices) {
$consumers = [];
foreach ($consumerServices as $name => $interface) {
$consumers[] = [
'name' => $name,
'service' => $interface,
'nodes' => [
['host' => '127.0.0.1', 'port' => 9600],
],
];
}
return $consumers;
}),
];
這樣一來,我們每次只需要在數(shù)組 $consumerServices 內(nèi)添加需要新的服務(wù)即可。
最后,我們來看一個(gè)比較大的問題。
consumer拿到的結(jié)果,又是字符串又是對象,還動(dòng)不動(dòng)直接 Internal Server Error. 數(shù)據(jù)格式的不統(tǒng)一非常不利于前端小伙伴解析。
統(tǒng)一結(jié)果處理
為了規(guī)范,我們制定了一個(gè)簡單的標(biāo)準(zhǔn),統(tǒng)一返回帶有code,message,data的數(shù)據(jù)格式,有興趣的小伙伴可以先研究下怎么解決這個(gè)問題,我們下一節(jié)繼續(xù)。