13、Hyperf 3 微服务系列 - Hyperf 3 服务限流
hi,我是温新,一名 PHPer
Hyperf 3 微服务代码已上传至 Github:https://github.com/ziruchu/hyperf3-microservice-code
什么是服务限流
服务限流指在高并发情况下,为了保护系统正常运行,从而对象访问服务的请求进行限制,从而保证服务的高可用。
为什么需要服务限流
把系统拆分为微服务之后,每个微服务可能会存在相互调用的关系,若其中某个服务被突如其来的大流量击垮,可能会引发雪崩,导致相关的微服务都不可用,从而影响业务。
令牌桶限流算法
Hyperf 使用令牌桶对请求进行限流,图如下:
令牌桶算法,是添加一个固定大小的容器(令牌桶),系统以恒定速率的速度向令牌桶中添加令牌,若有请求过来,需要先从令牌桶中获取一个令牌,拿到令牌的请求才有资格访问资源。当令牌桶满时,后面生成的令牌会被丢弃。
令牌桶算法有如下几种情况:
1、请求速度大于令牌生成速度:令牌桶中令牌会被取完,后续再进来的请求由于拿不令牌而被限制访问资源;
2、请求速度等于令牌生成速度:此时系统处于平稳状态;
3、请求速度小于令牌生成速度:此时系统访问量低于系统并发能力,请求正常处理。
Hyperf 服务限流实现
案例一:服务提供者中实现
我们可以轻松的使用 Hyperf 提供的组件对服务进行限流,它是针对请求接口的 QPS 进行限流。
本案例将在 192.168.31.92 服务器的 shop_provider_user_9605 应用上进行。(当前也可以在消费者中进行)。
第一步:安装限流组件
# 安装限流组件
composer require hyperf/rate-limit
# 该限流组件默认使用 redis 作为存储,也以把 redis 装上
composer require hyperf/redis
第二步:生成配置文件
# 限流组件
# 该组件会在 config/autoload 目录下生成 rate_limit.php 文件
php bin/hyperf.php vendor:publish hyperf/rate-limit
# redis 组件
# 该组件会在 config/autoload 目录下生成 redis.php 文件
php bin/hyperf.php vendor:publish hyperf/redis
rate_limit.php
内容如下:
<?php
declare(strict_types=1);
return [
// 每秒生成令牌数
'create' => 1,
// 每次请求消耗令牌数
'consume' => 1,
// 令牌桶最大容量
'capacity' => 2,
// 触发限流时回调方法
'limitCallback' => [],
// 排队超时时间,至少是1秒
'waitTimeout' => 1,
];
配置说明
配置 | 默认值 | 类型 | 备注 |
---|---|---|---|
create | 1 | int | 每秒生成令牌数 |
consume | 1 | int | 每次请求消耗令牌数 |
capacity | 2 | int | 令牌桶最大容量 |
limitCallback | [] |
null|callable | 触发限流时回调方法 |
waitTimeout | 1 | int | 排队超时时间 |
key | 当前请求 url 地址 | callable|string | 限流的 key |
第三步:限流代码编写
下面对 UserService.php
文件进行更改:
<?php
// app/JsonRpc/Service/UserService.php;
use Hyperf\RateLimit\Exception\RateLimitException;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\RateLimit\Annotation\RateLimit;
#[RateLimit(create=1,limitCallback: [UserService::class, "limitCallback"])]
public function getUserInfo(int $id)
{
$user = User::query()->find($id);
if (empty($user)) {
throw new \RuntimeException('没有该用户');
}
return ResponseTool::success($user->toArray());
}
// 被限流后调用
public static function limitCallback(float $seconds, ProceedingJoinPoint $proceedingJoinPoint)
{
throw new RateLimitException('Frequent requests, try again later',500);
}
需要注意:该限流是针对请求进行的,而不是针对具体用户。如最大支持 1000 个请求,假如说某一个用户一瞬间请求了 1000 次,后面后续的用户都将触发限流机制。
如果要针对用户进行限流,达到 A 用户被限流,B 用户正常请求,可以根据用户 ID 进行。
第四步:测试
我里使用了 jmeter 进行测试,被限流后输出如下:
{
"jsonrpc": "2.0",
"id": "",
"error": {
"code": -32000,
"message": "node_provider_user_9605-0.0.0.0:9605-Frequent requests, try again later",
"data": {
"class": "Hyperf\\RateLimit\\Exception\\RateLimitException",
"code": 500,
"message": "Frequent requests, try again later"
}
},
"context": []
}
注意看 message
字段的消息。
案例二:控制器中实现对用户限流
步骤基本和案例一,因此这里就快速实现了
本篇案例将在 192.168.31.90 服务器中的 note_consumer_user_9502 项目进行。
第一步:安装限流组件
composer require hyperf/rate-limit
composer require hyperf/redis
第二步:生成配置文件
php bin/hyperf.php vendor:publish hyperf/rate-limit
php bin/hyperf.php vendor:publish hyperf/redis
第三步:限流代码编写
<?php
// app/Controller/UserController.php
use Hyperf\Utils\ApplicationContext;
use Hyperf\RateLimit\Annotation\RateLimit;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Di\Aop\ProceedingJoinPoint;
#[GetMapping('/users/test')]
#[RateLimit(create: 1, consume: 1, waitTimeout: 1, limitCallback: [UserController::class, 'limitCallback'], key: [UserController::class, 'getUserId'])]
public function test()
{
return ResponseTool::success($this->userService->test());
}
public static function limitCallback(float $seconds, ProceedingJoinPoint $proceedingJoinPoint)
{
throw new RateLimitException('请求过于频繁,请稍后再试!!!', 500);
}
// 针对用户进行显示
public static function getUserId(ProceedingJoinPoint $proceedingJoinPoint)
{
$request = ApplicationContext::getContainer()->get(RequestInterface::class);
echo $request->input('user_id') . PHP_EOL;
// 业务逻辑处理
}
第四步:测试
1、基础正常测试
$ curl http://192.168.31.90:9502/users/test?user_id=1
输出结果
{
"code": 200,
"message": "success",
"data": {
"code": 200,
"message": "success",
"data": {
"app_name": "node_provider_user_9605",
"host": "0.0.0.0"
}
}
}
请求发送后,192.168.31.90:9502
这台服务服务器终端可以看到输出了用户 ID。
2、使用 jmeter 测试
1 秒并发 10 个请求,触发限流,信息如下:
{
"code": 500,
"message": "请求过于频繁,请稍后再试!!!"
}
贴一个 jmeter 测试的结果
关于 jmeter 大家自己安装了,挺容易出现问题的,我 2 台电脑安装 jmeter 都出了问题了。对于 java,我个人...
我是温新,本篇文章结束。