本篇文章开始写利用swoole的Hyperf框架搭建一个微服务架构。
首先我们安装一个Hyperf的项目,按照官方文档来利用composer安装,最好用阿里云的源。
composer create-project hyperf/hyperf-skeleton user-srv
选择rpc的时候选择grpc。其他的看自己的需求。
进入user-srv的根目录,创建一个protoc的文件,用户书写我们的协议文件。并且在protoc文件夹下新建一个user.proto的文件。文件如下
syntax = "proto3";
package php.micro.srv.user;
service user {
rpc find (Request) returns (Response) {}
rpc create(User) returns (Response) {}
}
// 用户信息
message User {
// 用户ID
int64 userId = 1;
// 用户名称
string username = 2;
string phone = 3;
string password = 4;
string email = 5;
string nickname = 6;
string avatar = 7;
string signature = 8;
}
// 错误码
message Error {
int64 code = 1;
string message = 2;
}
// 请求
message Request {
int64 userId = 1;
string username = 2;
string email = 3;
string phone = 4;
enum Type {
ID = 0;
USERNAME = 1;
EMAIL = 2;
PHONE = 3;
}
Type type = 5;
}
// 响应
message Response {
Error error = 1;
User user = 2;
}
我们定义了两个接口,一个查询find一个create。具体关于protoc的教程可以百度和谷歌。
然后生成protocbuf文件
protoc --php_out=grpc protoc/user.proto
这时候会在根目录下的grpc文件夹中生成依赖的文件。
├── GPBMetadata
│ └── Protoc
│ └── User.php
└── Php
└── Micro
└── Srv
└── User
├── Error.php
├── Request
│ └── Type.php
├── Request.php
├── Request_Type.php
├── Response.php
└── User.php
修改composer.json文件,在psr-4下面新增依赖,加完之后如下
"psr-4": {
"App": "app/",
"GPBMetadata": "grpc/GPBMetadata",
"Php": "grpc/Php"
}
然后执行命令行
根据刚刚定义的proto创建自己的user表。我简单创建如下
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`username` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户名',
`phone` char(24) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '手机号',
`password` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '密码',
`email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '邮箱',
`nickname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '昵称',
`avatar` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '头像',
`signature` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '用户签名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
然后根据文档生成自己的model文件和Repository文件。
<?php
declare (strict_types=1);
namespace AppRepository;
/**
*/
class UserRepository
{
private $user;
/**
* User constructor.
* @param $user
*/
public function __construct(AppModelUser $user)
{
$this->user = $user;
}
//创建
public function create(PhpMicroSrvUserUser $userInfo)
{
$user = new AppModelUser();
$user->username = $userInfo->getUsername();
$user->nickname = $userInfo->getNickname();
$user->phone = $userInfo->getPhone();
$user->email = $userInfo->getEmail();
$user->password = $userInfo->getPassword();
$user->avatar = $userInfo->getAvatar();
$user->signature = $userInfo->getSignature();
$saved = $user->save();
if ($saved) {
return $user;
}
return null;
}
}
这里其实是是把请求过来的数据拿到然后写进数据库。
再然后定义我们的控制器。文件内容如下。
<?php
declare(strict_types=1);
namespace AppControllerRpcController;
use AppControllerAbstractController;
use AppRepositoryUserRepository;
use PhpMicroSrvUserError;
use PhpMicroSrvUserRequest;
use PhpMicroSrvUserResponse;
use PhpMicroSrvUserUser;
class UserController extends AbstractController
{
private $userRepository;
/**
* UserController constructor.
* @param $userRepository
*/
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* rpc 用户查询服务
* @param Request $request
* @return Response
*/
public function find (Request $request)
{
$rsp = new Response();
$user = new User();
$user->setUsername("陈偲");
$user->setUserId(1);
$user->setEmail("741001560@qq.com");
$rsp->setUser($user);
return $rsp;
}
/**
* rpc 用户创建服务
* @param User $user
* @return Response
*/
public function create (User $user)
{
$rsp = new Response();
$userEntity = $this->userRepository->create($user);
if ($userEntity) {
$userInfo = new User();
$userInfo->setUsername($userEntity->username);
$userInfo->setUserId($userEntity->id);
$userInfo->setEmail($userEntity->email);
$userInfo->setAvatar($userEntity->avatar);
$userInfo->setNickname($userEntity->nickname);
$userInfo->setSignature($userEntity->signature);
$rsp->setUser($userInfo);
return $rsp;
}
$error = new Error();
$error->setCode(10001);
$error->setMessage("创建用户失败");
$rsp->setError($error);
$rsp->setUser(null);
return $rsp;
}
}
这个文件定义了两个grpc接口一个find一个create。在然后就是定义路由了。
Router::addServer('grpc', function () {
Router::addGroup('/user.user', function () {
Router::post('/find', 'AppControllerRpcControllerUserController@find');
Router::post('/create', 'AppControllerRpcControllerUserController@create');
});
});
最后需要创建一个grpc的服务,在config/autoload/server.php目录下,在servers数组下添加一个数组。
[
'name' => 'grpc',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9503,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
SwooleEvent::ON_REQUEST => [HyperfGrpcServerServer::class, 'onRequest'],
],
],
服务层写完,开始书写服务中层。创建一个项目。
composer create-project hyperf/hyperf-skeleton user-api
定义protoc 步骤和定义服务层一致,在此不作赘述。
定义一个client端。在App目录下新建一个Grpc目录,并且创建一个UserClient的文件,文件如下。
<?php
declare(strict_types=1);
namespace AppGrpc;
use HyperfGrpcClientBaseClient;
use PhpMicroSrvUserRequest;
use PhpMicroSrvUserResponse;
use PhpMicroSrvUserUser;
class UserClient extends BaseClient
{
public function find(Request $argument)
{
return $this->simpleRequest(
'/user.user/find',
$argument,
[Response::class, 'decode']
);
}
public function create(User $argument)
{
return $this->simpleRequest(
'/user.user/create',
$argument,
[Response::class, 'decode']
);
}
}
创建一个一个控制器UserController,如下
<?php
/**
* Author: 陈偲
* Date: 2019/11/14 14:40
*/
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
*/
namespace AppController;
use AppGrpcUserClient;
use HyperfHttpServerContractRequestInterface;
use PhpMicroSrvUserRequest;
use PhpMicroSrvUserResponse;
use PhpMicroSrvUserUser;
class UserController extends AbstractController
{
public function create(RequestInterface $request)
{
$username = $request->input("username");
$password = $request->input("password");
$phone = $request->input("phone");
$nickname = $request->input("nickname");
$client = new UserClient('127.0.0.1:9503', [
'credentials' => null,
]);
$req = new User();
$req->setUsername($username);
$req->setNickname($nickname);
$req->setPassword(password_hash($password,PASSWORD_DEFAULT ));
$req->setPhone($phone);
/**
* @var $rsp PhpMicroSrvUserResponse
*/
list($rsp,$status) = $client->create($req);
if (!empty($status)) {
return [
'code' => 1000,
'msg' => "转发请求有误"
];
}
$error = $rsp->getError();
if ($error) {
return [
'code' => $error->getCode(),
'msg' => $error->getMessage(),
];
}
/**
* @var $user User
*/
$user = $rsp->getUser();
return $user->getUserId();
}
public function index()
{
$client = new UserClient('127.0.0.1:9503', [
'credentials' => null,
]);
$req = new Request();
$req->setType(0);
$req->setUserId(1);
/**
* @var $rsp PhpMicroSrvUserResponse
*/
list($rsp,$status) = $client->find($req);
if (!empty($status)) {
return [
'code' => 1000,
'msg' => "转发请求有误"
];
}
/**
* @var $user User
*/
$user = $rsp->getUser();
return $user->getUsername();
}
}
由于两个服务端口号一致,user-api这个服务中层的端口号该改成9502。
然后设置好路由。
Router::addRoute(['GET', 'POST', 'HEAD'], '/user', 'AppControllerUserController@index');
Router::addRoute(['GET', 'POST', 'HEAD'], '/create', 'AppControllerUserController@create');
启动两个服务,请求一下192.168.10.100:9502/user看看结果。
在postman执行一下,然后去数据库看看。
curl -X POST
http://192.168.10.100:9502/create
-H 'cache-control: no-cache'
-H 'content-type: application/json'
-d '{
"username":"苏格拉低",
"password":"123456",
"phone":"18026975219",
"email":"741001560@qq.com",
"nickname":"木小撇"
}'
下一篇写利用consul做服务发现和健康检查。