13、Hyperf 3 微服务系列 - Hyperf 3 服务限流

作者: 温新

分类: 【Hyperf 3 微服务系列】

阅读: 1248

时间: 2023-05-29 15:44:58

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 应用上进行。(当前也可以在消费者中进行)。

第一步:安装限流组件
<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important"># 安装限流组件</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">composer</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">require</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">rate</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">limit</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important"># 该限流组件默认使用 redis 作为存储,也以把 redis 装上</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">composer</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">require</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">redis</span></span>
第二步:生成配置文件
<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important"># 限流组件</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important"># 该组件会在 config/autoload 目录下生成 rate_limit.php 文件</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">bin</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span>.<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">vendor</span>:<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">publish</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">rate</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">limit</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important"># redis 组件</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important"># 该组件会在 config/autoload 目录下生成 redis.php 文件</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">bin</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span>.<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">vendor</span>:<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">publish</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">redis</span></span>

rate_limit.php 内容如下:

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important"><?</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">declare</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">strict_types</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span><span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">return</span> [</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 每秒生成令牌数</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'create'</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=></span> <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 每次请求消耗令牌数</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'consume'</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=></span> <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 令牌桶最大容量</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'capacity'</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=></span> <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">2</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 触发限流时回调方法</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'limitCallback'</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=></span> [],</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 排队超时时间,至少是1秒</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'waitTimeout'</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=></span> <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">1</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">];</span>

配置说明

第三步:限流代码编写

下面对 UserService.php 文件进行更改:

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important"><?</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// app/JsonRpc/Service/UserService.php;</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\RateLimit\Exception\RateLimitException</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\Di\Aop\ProceedingJoinPoint</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\RateLimit\Annotation\RateLimit</span>;    </span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">#[RateLimit(create=1,limitCallback: [UserService::class, "limitCallback"])]</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">public</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">function</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">getUserInfo</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">int</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$id</span>)</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$user</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">User</span>::<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">query</span>()<span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">find</span>(<span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$id</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">if</span> (<span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">empty</span>(<span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$user</span>)) {</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">        <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">throw</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">new</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">\RuntimeException</span>(<span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'没有该用户'</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    }</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">return</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">ResponseTool</span>::<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">success</span>(<span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$user</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">toArray</span>());</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 被限流后调用</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">public</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">static</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">function</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">limitCallback</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">float</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$seconds</span>, <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">ProceedingJoinPoint</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$proceedingJoinPoint</span>)</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">throw</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">new</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">RateLimitException</span>(<span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'Frequent requests, try again later'</span>,<span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">500</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span>

需要注意:该限流是针对请求进行的,而不是针对具体用户。如最大支持 1000 个请求,假如说某一个用户一瞬间请求了 1000 次,后面后续的用户都将触发限流机制。

如果要针对用户进行限流,达到 A 用户被限流,B 用户正常请求,可以根据用户 ID 进行。

第四步:测试

我里使用了 jmeter 进行测试,被限流后输出如下:

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"jsonrpc"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"2.0"</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"id"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">""</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"error"</span>: {</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">  <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"code"</span>: <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-</span><span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">32000</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">  <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"message"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"node_provider_user_9605-0.0.0.0:9605-Frequent requests, try again later"</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">  <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"data"</span>: {</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">   <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"class"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"Hyperf\\RateLimit\\Exception\\RateLimitException"</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">   <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"code"</span>: <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">500</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">   <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"message"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"Frequent requests, try again later"</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">  }</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> },</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"context"</span>: []</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span>

注意看 message 字段的消息。

案例二:控制器中实现对用户限流

步骤基本和案例一,因此这里就快速实现了

本篇案例将在 192.168.31.90 服务器中的 note_consumer_user_9502 项目进行。

第一步:安装限流组件
<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">composer</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">require</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">rate</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">limit</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">composer</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">require</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">redis</span></span>
第二步:生成配置文件
<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">bin</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span>.<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">vendor</span>:<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">publish</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">rate</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">limit</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">bin</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span>.<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">vendor</span>:<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">publish</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">hyperf</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">/</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">redis</span></span>
第三步:限流代码编写
<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important"><?</span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">php</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// app/Controller/UserController.php</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    </span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\Utils\ApplicationContext</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\RateLimit\Annotation\RateLimit</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\HttpServer\Contract\RequestInterface</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">use</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">Hyperf\Di\Aop\ProceedingJoinPoint</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">#[GetMapping('/users/test')]</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">#[RateLimit(create: 1, consume: 1, waitTimeout: 1, limitCallback: [UserController::class, 'limitCallback'], key: [UserController::class, 'getUserId'])]</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">public</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">function</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">test</span>()</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">return</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">ResponseTool</span>::<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">success</span>(<span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$this</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">userService</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">test</span>());</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">public</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">static</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">function</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">limitCallback</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">float</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$seconds</span>, <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">ProceedingJoinPoint</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$proceedingJoinPoint</span>)</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">throw</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">new</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">RateLimitException</span>(<span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'请求过于频繁,请稍后再试!!!'</span>, <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">500</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 针对用户进行显示</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">public</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">static</span> <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">function</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">getUserId</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">ProceedingJoinPoint</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$proceedingJoinPoint</span>)</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$request</span> <span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">=</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">ApplicationContext</span>::<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">getContainer</span>()<span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">get</span>(<span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">RequestInterface</span>::<span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">class</span>);</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(198, 120, 221) !important">echo</span> <span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$request</span><span style="box-sizing: border-box;color: rgb(86, 182, 194) !important">-></span><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">input</span>(<span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">'user_id'</span>) . <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">PHP_EOL</span>;</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">// 业务逻辑处理</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span>
第四步:测试

1、基础正常测试

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(224, 108, 117) !important">$</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">curl</span> <span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">http</span>:<span style="box-sizing: border-box;color: rgb(98, 151, 85) !important">//192.168.31.90:9502/users/test?user_id=1</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box"></span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(18, 170, 228) !important">输出结果</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"code"</span>: <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">200</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"message"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"success"</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"data"</span>: {</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">        <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"code"</span>: <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">200</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">        <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"message"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"success"</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">        <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"data"</span>: {</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">            <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"app_name"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"node_provider_user_9605"</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">            <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"host"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"0.0.0.0"</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">        }</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">    }</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span>

请求发送后,192.168.31.90:9502 这台服务服务器终端可以看到输出了用户 ID。

2、使用 jmeter 测试

1 秒并发 10 个请求,触发限流,信息如下:

<span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"code"</span>: <span style="box-sizing: border-box;color: rgb(209, 154, 102) !important">500</span>,</span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px"> <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"message"</span>: <span style="box-sizing: border-box;color: rgb(152, 195, 121) !important">"请求过于频繁,请稍后再试!!!"</span></span><br></br><span style="box-sizing: border-box;color: rgb(171, 178, 191);padding-right: 0.1px">}</span>

贴一个 jmeter 测试的结果

13 并发请求测试.jpg

关于 jmeter 大家自己安装了,挺容易出现问题的,我 2 台电脑安装 jmeter 都出了问题了。对于 java,我个人...

我是温新,本篇文章结束。

请登录后再评论