-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathrate-limiting.html
255 lines (202 loc) · 12.4 KB
/
rate-limiting.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go by Example 中文版: 速率限制</title>
<link rel=stylesheet href="site.css">
</head>
<script>
onkeydown = (e) => {
if (e.key == "ArrowLeft") {
window.location.href = 'waitgroups';
}
if (e.key == "ArrowRight") {
window.location.href = 'atomic-counters';
}
}
</script>
<body>
<div class="example" id="rate-limiting">
<h2><a href="./">Go by Example 中文版</a>: 速率限制</h2>
<table>
<tr>
<td class="docs">
<p><a href="http://en.wikipedia.org/wiki/Rate_limiting">速率限制</a>
是控制服务资源利用和质量的重要机制。
基于协程、通道和<a href="tickers">打点器</a>,Go 优雅的支持速率限制。</p>
</td>
<td class="code empty leading">
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<a href="https://play.studygolang.com/p/5lvENXEHjI2"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<pre class="chroma"><span class="kn">package</span> <span class="nx">main</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<pre class="chroma"><span class="kn">import</span> <span class="p">(</span>
<span class="s">"fmt"</span>
<span class="s">"time"</span>
<span class="p">)</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<pre class="chroma"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>首先,我们将看一个基本的速率限制。
假设我们想限制对收到请求的处理,我们可以通过一个渠道处理这些请求。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="nx">requests</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><=</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
<span class="nx">requests</span> <span class="o"><-</span> <span class="nx">i</span>
<span class="p">}</span>
<span class="nb">close</span><span class="p">(</span><span class="nx">requests</span><span class="p">)</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p><code>limiter</code> 通道每 200ms 接收一个值。
这是我们任务速率限制的调度器。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="nx">limiter</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Tick</span><span class="p">(</span><span class="mi">200</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>通过在每次请求前阻塞 <code>limiter</code> 通道的一个接收,
可以将频率限制为,每 200ms 执行一次请求。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="k">for</span> <span class="nx">req</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">requests</span> <span class="p">{</span>
<span class="o"><-</span><span class="nx">limiter</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"request"</span><span class="p">,</span> <span class="nx">req</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">())</span>
<span class="p">}</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>有时候我们可能希望在速率限制方案中允许短暂的并发请求,并同时保留总体速率限制。
我们可以通过缓冲通道来完成此任务。
<code>burstyLimiter</code> 通道允许最多 3 个爆发(bursts)事件。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="nx">burstyLimiter</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>填充通道,表示允许的爆发(bursts)。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
<span class="nx">burstyLimiter</span> <span class="o"><-</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span>
<span class="p">}</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>每 200ms 我们将尝试添加一个新的值到 <code>burstyLimiter</code>中,
直到达到 3 个的限制。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="nx">t</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Tick</span><span class="p">(</span><span class="mi">200</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">burstyLimiter</span> <span class="o"><-</span> <span class="nx">t</span>
<span class="p">}</span>
<span class="p">}()</span>
</pre>
</td>
</tr>
<tr>
<td class="docs">
<p>现在,模拟另外 5 个传入请求。
受益于 <code>burstyLimiter</code> 的爆发(bursts)能力,前 3 个请求可以快速完成。</p>
</td>
<td class="code">
<pre class="chroma">
<span class="nx">burstyRequests</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><=</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
<span class="nx">burstyRequests</span> <span class="o"><-</span> <span class="nx">i</span>
<span class="p">}</span>
<span class="nb">close</span><span class="p">(</span><span class="nx">burstyRequests</span><span class="p">)</span>
<span class="k">for</span> <span class="nx">req</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">burstyRequests</span> <span class="p">{</span>
<span class="o"><-</span><span class="nx">burstyLimiter</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"request"</span><span class="p">,</span> <span class="nx">req</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span>
</pre>
</td>
</tr>
</table>
<table>
<tr>
<td class="docs">
<p>运行程序,我们看到第一批请求意料之中的大约每 200ms 处理一次。</p>
</td>
<td class="code leading">
<pre class="chroma">
<span class="gp">$</span> go run rate-limiting.go
<span class="go">request 1 2012-10-19 00:38:18.687438 +0000 UTC
</span><span class="go">request 2 2012-10-19 00:38:18.887471 +0000 UTC
</span><span class="go">request 3 2012-10-19 00:38:19.087238 +0000 UTC
</span><span class="go">request 4 2012-10-19 00:38:19.287338 +0000 UTC
</span><span class="go">request 5 2012-10-19 00:38:19.487331 +0000 UTC</span></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>第二批请求,由于爆发(burstable)速率控制,我们直接连续处理了 3 个请求,
然后以大约每 200ms 一次的速度,处理了剩余的 2 个请求。</p>
</td>
<td class="code">
<pre class="chroma">
<span class="go">request 1 2012-10-19 00:38:20.487578 +0000 UTC
</span><span class="go">request 2 2012-10-19 00:38:20.487645 +0000 UTC
</span><span class="go">request 3 2012-10-19 00:38:20.487676 +0000 UTC
</span><span class="go">request 4 2012-10-19 00:38:20.687483 +0000 UTC
</span><span class="go">request 5 2012-10-19 00:38:20.887542 +0000 UTC</span></pre>
</td>
</tr>
</table>
<p class="next">
下一个例子: <a href="atomic-counters">原子计数器</a>
</p>
<p class="footer">
<a href="https://twitter.com/mmcgrana">@mmcgrana</a> 和<a href="https://eli.thegreenplace.net">Eli Bendersky</a>编写 | <a href="https://github.com/gobyexample-cn">gobyexample-cn</a> 翻译 | <a href="https://github.com/gobyexample-cn/gobyexample/issues">反馈</a> | <a href="https://github.com/gobyexample-cn/gobyexample">源码</a> | <a href="https://github.com/mmcgrana/gobyexample#license">license</a> </p>
</p>
</div>
<script>
var codeLines = [];
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import (\u000A \"fmt\"\u000A \"time\"\u000A)\u000A');codeLines.push('func main() {\u000A');codeLines.push(' requests :\u003D make(chan int, 5)\u000A for i :\u003D 1; i \u003C\u003D 5; i++ {\u000A requests \u003C- i\u000A }\u000A close(requests)\u000A');codeLines.push(' limiter :\u003D time.Tick(200 * time.Millisecond)\u000A');codeLines.push(' for req :\u003D range requests {\u000A \u003C-limiter\u000A fmt.Println(\"request\", req, time.Now())\u000A }\u000A');codeLines.push(' burstyLimiter :\u003D make(chan time.Time, 3)\u000A');codeLines.push(' for i :\u003D 0; i \u003C 3; i++ {\u000A burstyLimiter \u003C- time.Now()\u000A }\u000A');codeLines.push(' go func() {\u000A for t :\u003D range time.Tick(200 * time.Millisecond) {\u000A burstyLimiter \u003C- t\u000A }\u000A }()\u000A');codeLines.push(' burstyRequests :\u003D make(chan int, 5)\u000A for i :\u003D 1; i \u003C\u003D 5; i++ {\u000A burstyRequests \u003C- i\u000A }\u000A close(burstyRequests)\u000A for req :\u003D range burstyRequests {\u000A \u003C-burstyLimiter\u000A fmt.Println(\"request\", req, time.Now())\u000A }\u000A}\u000A');codeLines.push('');codeLines.push('');
</script>
<script src="site.js" async></script>
</body>
</html>