带你轻松使用Hyperf玩转Grpc(三)编写服务
三、编写服务
相信大家打开这篇文章都是对grpc有一定的了解了,并且使用hyperf但官方文档中并无针对grpc的详细注册、发现等教程,官方文档中较多的是围绕 jsonrpc。对比jsonrpc grpc依赖protobuf有更小的传输压缩体积、多语言兼容等优势。
定义服务
下面就开始 编写 proto 并生成代码
在 proto 文件夹下 分别创建 product_srv.proto 、shopping_cart_srv.proto、order_srv.proto
关于 proto 文件 的内容格式以及如何定义这边不再啰嗦,大家请自行 google 学习一下。
我直接将定义好的 服务 展示,大家复制即可。
product.proto
syntax = "proto3";
package mall;
option php_generic_services = true;
option php_metadata_namespace = "App\\Grpc\\GPBMetadata";
option php_namespace = "App\\Grpc\\Mall\\ProductSrv";
service ProductSrv {
rpc create (ProductCreateRequest) returns (ProductId) {}
rpc getList (ProductListRequest) returns (ProductListReply){}
rpc getStock (ProductId) returns (ProductStockReply){}
rpc incStock (ProductStockRequest) returns (ProductStockReply){}
rpc decStock (ProductStockRequest) returns (ProductStockReply){}
}
message ProductCreateRequest {
string title = 1;
string icon = 2;
int32 stock = 3;
}
message Product {
int32 id = 1;
string title = 2;
string icon = 3;
int32 stock = 4;
string created_at = 5;
string updated_at = 6;
}
message ProductId {
int32 id = 1;
}
message ProductListRequest {
int32 page = 1;
int32 pageSize = 2;
}
message ProductListReply {
int32 total = 1;
repeated Product list = 2;
}
message ProductStockReply {
int32 stock = 1;
}
message ProductStockRequest {
int32 id = 1;
int32 stock = 2;
}
其他服务大家可以 自行实现,这边就先使用 product 服务做演示。
生成代码
进入service/mall-service 根目录,执行
protoc --php_out=./ --grpc_out=./ --plugin=protoc-gen-grpc=/usr/local/lib/grpc_php_plugin -I=/app/proto product_srv.proto
然后到我们的项目下查看,这边以phpstorm打开项目为例
这样我们就把定义好的服务成功生成代码了
编写服务
这一步就从一个刚接触 hyperf 框架的 新手角度 描述步骤。
先把需要用到的插件引入
composer require crayoon/hyperf-grpc
0、先完成配置
在宿主机中使用mysql管理工具 连接 mysql 并创建 mall 数据库,这一步就不啰嗦了。选择 utf8mb4(utf8mb4_general_ci)。
host:127.0.0.1:33306 ( 若修改了环境变量自行更换 )
username:root
password:123456
创建product表
CREATE TABLE `product` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`icon` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
`stock` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
service-mall/.env 中 mysql/redis 配置如下
DB_DRIVER=mysql
DB_HOST=mysql //对应编排的名称 非容器名
DB_PORT=3306 //内网端口 非对外端口,在宿主机中访问可以使用 127.0.0.1:33306 访问并如何建 product表 这边也不啰嗦
DB_DATABASE=mall
DB_USERNAME=root
DB_PASSWORD=123456
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_general_ci
DB_PREFIX=
REDIS_HOST=redis
REDIS_AUTH=(null)
REDIS_PORT=6379
REDIS_DB=0
1、实现接口
到我们 service-mall/app/Controller 目录下创建ProductSrvController
并实现 App\Grpc\Mall\ProductSrv\ProductSrvInterface
<?php
namespace App\Controller;
use App\Grpc\Mall\ProductSrv\ProductCreateRequest;
use App\Grpc\Mall\ProductSrv\ProductId;
use App\Grpc\Mall\ProductSrv\ProductListRequest;
use App\Grpc\Mall\ProductSrv\ProductSrvInterface;
use App\Grpc\Mall\ProductSrv\ProductStockRequest;
class ProductSrvController implements ProductSrvInterface
{
public function create(ProductCreateRequest $request)
{
// TODO: Implement create() method.
}
public function getList(ProductListRequest $request)
{
// TODO: Implement getList() method.
}
public function getStock(ProductId $request)
{
// TODO: Implement getStock() method.
}
public function incStock(ProductStockRequest $request)
{
// TODO: Implement incStock() method.
}
public function decStock(ProductStockRequest $request)
{
// TODO: Implement decStock() method.
}
}
2、创建Product Model类
在 app/Model 下 创建 Product
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Model\Model;
/**
* @property int $id
* @property string $title
* @property string $icon
* @property int $stock
* @property string $created_at
* @property string $updated_at
*/
class Product extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'Product';
protected array $guarded = [];
}
3、实现ProductSrvController方法
再编辑 service-mall/app/Controller/ProductSrvController
<?php
namespace App\Controller;
use App\Grpc\Mall\ProductSrv\ProductCreateRequest;
use App\Grpc\Mall\ProductSrv\ProductId;
use App\Grpc\Mall\ProductSrv\ProductListReply;
use App\Grpc\Mall\ProductSrv\ProductListRequest;
use App\Grpc\Mall\ProductSrv\ProductSrvInterface;
use App\Grpc\Mall\ProductSrv\ProductStockReply;
use App\Grpc\Mall\ProductSrv\ProductStockRequest;
use App\Model\Product;
use Hyperf\Grpc\StatusCode;
use Hyperf\GrpcServer\Exception\GrpcException;
class ProductSrvController implements ProductSrvInterface
{
public function create(ProductCreateRequest $request): ProductId
{
//@todo 可以将数据访问再抽一层再通过di注入,这边为了简单演示就不搞复杂了
$new = Product::create([
"title" => $request->getTitle(),
"icon" => $request->getIcon(),
"stock" => 0
]);
return new ProductId(['id' => $new->id]);
}
public function getList(ProductListRequest $request): ProductListReply
{
$query = Product::query();
//@todo 这里可以再自行实现 条件查询
$total = $query->count();
$list = [];
/**
* @var Product $item
*/
foreach ($query->skip(($request->getPage() - 1) * $request->getPageSize())
->take($request->getPageSize())
->orderByDesc("id")
->get() as $item) {
$list[] = (new \App\Grpc\Mall\ProductSrv\Product())
->setId($item->id)
->setIcon($item->icon)
->setStock($item->stock)
->setTitle($item->title)
->setCreatedAt($item->created_at)
->setUpdatedAt($item->updated_at);
}
return new ProductListReply(compact("total", "list"));
}
public function getStock(ProductId $request): ProductStockReply
{
$product = $this->getProduct($request->getId());
return (new ProductStockReply())->setStock($product->stock ?? 0);
}
public function incStock(ProductStockRequest $request): ProductStockReply
{
$product = $this->getProduct($request->getId());
$product->stock += $request->getStock();
$product->save();
return (new ProductStockReply())->setStock($product->stock);
}
public function decStock(ProductStockRequest $request): ProductStockReply
{
$product = $this->getProduct($request->getId());
$product->stock -= $request->getStock();
$product->save();
return (new ProductStockReply())->setStock($product->stock);
}
private function getProduct(int $id): ?Product {
$product = Product::where("id","=", $id)->first();
if(!$product) throw new GrpcException("product not found", StatusCode::ABORTED);
return $product;
}
}
至此,我们就把要实现的服务完成了。
本作品采用《CC 协议》,转载必须注明作者和本文链接
其他语言的grpc服务可以注册到这里,然后供 hyperf 使用吗