-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
502 lines (235 loc) · 681 KB
/
atom.xml
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Deng的博客</title>
<link href="http://coderedeng.github.io/atom.xml" rel="self"/>
<link href="http://coderedeng.github.io/"/>
<updated>2024-05-26T03:15:39.159Z</updated>
<id>http://coderedeng.github.io/</id>
<author>
<name>Evan Deng</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>使用 net/http 实现并发爬取多个 url 标题</title>
<link href="http://coderedeng.github.io/2024/04/30/Go%E7%88%AC%E8%99%AB%20-%20%E6%89%8B%E5%8A%A8%E5%AE%9E%E7%8E%B0%E5%B9%B6%E5%8F%91%E7%88%AC%E5%8F%96%E5%A4%9A%E4%B8%AAurl%E6%A0%87%E9%A2%98/"/>
<id>http://coderedeng.github.io/2024/04/30/Go%E7%88%AC%E8%99%AB%20-%20%E6%89%8B%E5%8A%A8%E5%AE%9E%E7%8E%B0%E5%B9%B6%E5%8F%91%E7%88%AC%E5%8F%96%E5%A4%9A%E4%B8%AAurl%E6%A0%87%E9%A2%98/</id>
<published>2024-04-30T13:26:38.000Z</published>
<updated>2024-05-26T03:15:39.159Z</updated>
<content type="html"><![CDATA[<h1 id="1-net-x2F-http-包相关方法"><a href="#1-net-x2F-http-包相关方法" class="headerlink" title="1. net/http 包相关方法"></a>1. net/http 包相关方法</h1><h2 id="1-1-http-NewRequestWithContext"><a href="#1-1-http-NewRequestWithContext" class="headerlink" title="1.1 http.NewRequestWithContext"></a>1.1 <code>http.NewRequestWithContext</code></h2><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">req<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">NewRequestWithContext</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> <span class="token string">"GET"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>这个方法用于创建一个新的 HTTP 请求。</li><li>它接受一个 <code>context.Context</code> 对象,可以用来设置请求的超时、取消等操作。</li><li>第一个参数是 HTTP 方法,这里是 “GET”。</li><li>第二个参数是要请求的 URL。</li><li>第三个参数是请求体,这里传入 <code>nil</code> 表示没有请求体。</li><li>返回一个 <code>*http.Request</code> 对象和错误对象。</li></ul><h2 id="1-2-Request-结构体类型"><a href="#1-2-Request-结构体类型" class="headerlink" title="1.2 Request 结构体类型"></a>1.2 <code>Request</code> 结构体类型</h2><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> Request <span class="token keyword">struct</span> <span class="token punctuation">{</span> Method <span class="token builtin">string</span> <span class="token comment">// 指定HTTP方法(GET,POST,PUT等)。</span> URL <span class="token operator">*</span>url<span class="token punctuation">.</span>URL <span class="token operator">...</span><span class="token operator">...</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="1-3-http-DefaultClient-Do"><a href="#1-3-http-DefaultClient-Do" class="headerlink" title="1.3 http.DefaultClient.Do"></a>1.3 <code>http.DefaultClient.Do</code></h2><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span>DefaultClient<span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li><code>http.DefaultClient</code> 是一个全局的 <code>*http.Client</code> 对象,它提供了默认的 HTTP 客户端实现。</li><li><code>Do</code> 方法用于发送 HTTP 请求并返回响应。</li><li>它接受一个 <code>*http.Request</code> 对象作为参数,表示要发送的请求。</li><li>返回一个 <code>*http.Response</code> 对象和一个错误对象。</li></ul><h2 id="1-4-Response-结构体类型"><a href="#1-4-Response-结构体类型" class="headerlink" title="1.4 Response 结构体类型"></a>1.4 <code>Response</code> 结构体类型</h2><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> Response <span class="token keyword">struct</span> <span class="token punctuation">{</span> Status <span class="token builtin">string</span> <span class="token comment">// e.g. "200 OK"</span> StatusCode <span class="token builtin">int</span> <span class="token comment">// e.g. 200</span> Proto <span class="token builtin">string</span> <span class="token comment">// e.g. "HTTP/1.0"</span> ProtoMajor <span class="token builtin">int</span> <span class="token comment">// e.g. 1</span> ProtoMinor <span class="token builtin">int</span> <span class="token comment">// e.g. 0</span> <span class="token operator">...</span><span class="token operator">...</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="1-5-http-Response"><a href="#1-5-http-Response" class="headerlink" title="1.5 http.Response"></a>1.5 <code>http.Response</code></h2><ul><li><code>http.Response</code> 结构表示 HTTP 响应。</li><li>它包含响应状态码、响应头和响应体等信息。</li><li>在代码中,我们使用 <code>resp.StatusCode</code> 来检查响应的状态码是否为200,以确定请求是否成功。</li></ul><h2 id="1-6-http-Response-Body"><a href="#1-6-http-Response-Body" class="headerlink" title="1.6 http.Response.Body"></a>1.6 <code>http.Response.Body</code></h2><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li><code>Body</code> 字段是一个 <code>io.ReadCloser</code> 接口,代表响应体。</li><li>在读取完响应体后,我们应该关闭响应体以释放资源。通常使用 <code>defer</code> 关键字来确保在函数退出时关闭响应体。</li></ul><h1 id="2-golang-org-x-net-html-包相关方法"><a href="#2-golang-org-x-net-html-包相关方法" class="headerlink" title="2. golang.org/x/net/html 包相关方法"></a>2. <code> golang.org/x/net/html</code> 包相关方法</h1><h2 id="2-1-html-Parse"><a href="#2-1-html-Parse" class="headerlink" title="2.1 html.Parse"></a>2.1 <code>html.Parse</code></h2><ul><li><code>func Parse(r io.Reader) (*Node, error)</code></li><li>此函数接受一个实现了 <code>io.Reader</code> 接口的对象作为参数,通常是一个 <code>http.Response.Body</code> 或文件等。</li><li>返回一个 <code>*html.Node</code> 对象和一个 <code>error</code>,表示解析的根节点以及可能发生的错误。</li></ul><h2 id="2-2-html-Render"><a href="#2-2-html-Render" class="headerlink" title="2.2 html.Render"></a>2.2 <code>html.Render</code></h2><ul><li><code>func Render(w io.Writer, n *Node) error</code></li><li>此函数接受一个实现了 <code>io.Writer</code> 接口的对象以及一个 <code>*html.Node</code> 对象作为参数,将HTML节点 n 以HTML格式写入 w。</li><li>返回一个 <code>error</code>,表示可能发生的写入错误。</li></ul><h2 id="2-3-html-ParseFragment"><a href="#2-3-html-ParseFragment" class="headerlink" title="2.3 html.ParseFragment"></a>2.3 <code>html.ParseFragment</code></h2><ul><li><code>func ParseFragment(r io.Reader, context *Node) ([]*Node, error)</code></li><li>此函数接受一个实现了 <code>io.Reader</code> 接口的对象以及一个上下文节点 <code>*html.Node</code> 对象作为参数。</li><li>返回解析的HTML片段中的节点切片和一个 <code>error</code>。</li></ul><h2 id="2-4-html-EscapeString"><a href="#2-4-html-EscapeString" class="headerlink" title="2.4 html.EscapeString"></a>2.4 <code>html.EscapeString</code></h2><ul><li><code>func EscapeString(s string) string</code></li><li>此函数接受一个HTML字符串作为参数,返回其在HTML中的转义形式。</li></ul><h2 id="2-5-html-UnescapeString"><a href="#2-5-html-UnescapeString" class="headerlink" title="2.5 html.UnescapeString"></a>2.5 <code>html.UnescapeString</code></h2><ul><li><code>func UnescapeString(s string) string</code></li><li>此函数接受一个转义过的HTML字符串作为参数,返回其原始形式。</li></ul><h2 id="2-6-html-Node"><a href="#2-6-html-Node" class="headerlink" title="2.6 html.Node"></a>2.6 <code>html.Node</code></h2><ul><li>HTML文档中的节点表示。</li><li>每个节点都有一个类型、一系列属性和子节点。</li><li>可以通过 <code>Type</code> 字段来判断节点的类型,如 <code>ElementNode</code>、<code>TextNode</code> 等。</li><li>可以通过 <code>Data</code> 字段获取节点的数据,如元素节点的标签名或文本节点的内容。</li><li>可以通过 <code>Attr</code> 字段获取节点的属性。</li></ul><h1 id="3-具体实现代码"><a href="#3-具体实现代码" class="headerlink" title="3. 具体实现代码"></a>3. 具体实现代码</h1><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token string">"net/http"</span><span class="token string">"os"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token string">"golang.org/x/net/html"</span><span class="token punctuation">)</span><span class="token comment">// fetchTitle 使用给定的URL获取网站的标题。</span><span class="token keyword">func</span> <span class="token function">fetchTitle</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> url <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>startTime <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>req<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">NewRequestWithContext</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> <span class="token string">"GET"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> err<span class="token punctuation">}</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span>DefaultClient<span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> err<span class="token punctuation">}</span><span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> resp<span class="token punctuation">.</span>StatusCode <span class="token operator">!=</span> http<span class="token punctuation">.</span>StatusOK <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"bad status: %s"</span><span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Status<span class="token punctuation">)</span><span class="token punctuation">}</span>doc<span class="token punctuation">,</span> err <span class="token operator">:=</span> html<span class="token punctuation">.</span><span class="token function">Parse</span><span class="token punctuation">(</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> err<span class="token punctuation">}</span><span class="token keyword">var</span> title <span class="token builtin">string</span><span class="token keyword">var</span> f <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token operator">*</span>html<span class="token punctuation">.</span>Node<span class="token punctuation">)</span>f <span class="token operator">=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>n <span class="token operator">*</span>html<span class="token punctuation">.</span>Node<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">if</span> n<span class="token punctuation">.</span>Type <span class="token operator">==</span> html<span class="token punctuation">.</span>ElementNode <span class="token operator">&&</span> n<span class="token punctuation">.</span>Data <span class="token operator">==</span> <span class="token string">"title"</span> <span class="token operator">&&</span> n<span class="token punctuation">.</span>FirstChild <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>title <span class="token operator">=</span> n<span class="token punctuation">.</span>FirstChild<span class="token punctuation">.</span>Data<span class="token punctuation">}</span><span class="token keyword">for</span> c <span class="token operator">:=</span> n<span class="token punctuation">.</span>FirstChild<span class="token punctuation">;</span> c <span class="token operator">!=</span> <span class="token boolean">nil</span><span class="token punctuation">;</span> c <span class="token operator">=</span> c<span class="token punctuation">.</span>NextSibling <span class="token punctuation">{</span><span class="token function">f</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token function">f</span><span class="token punctuation">(</span>doc<span class="token punctuation">)</span>elapsedTime <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>startTime<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Round</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond<span class="token punctuation">)</span><span class="token keyword">return</span> title <span class="token operator">+</span> <span class="token string">" ("</span> <span class="token operator">+</span> elapsedTime<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">")"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token comment">// crawlURLs 并发地爬取一系列URL的标题。</span><span class="token keyword">func</span> <span class="token function">crawlURLs</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> urls <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 使用WaitGroup等待所有的goroutine完成</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroupwg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token function">len</span><span class="token punctuation">(</span>urls<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 使用通道来收集结果(防止结果竟态)</span>titles <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>urls<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> url <span class="token operator">:=</span> <span class="token keyword">range</span> urls <span class="token punctuation">{</span> <span class="token comment">// 每一个url创建一个goroutine</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span>url <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span>title<span class="token punctuation">,</span> err <span class="token operator">:=</span> <span class="token function">fetchTitle</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> url<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Error fetching %s: %v\n"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> err<span class="token punctuation">)</span>titles <span class="token operator"><-</span> <span class="token string">"Error: "</span> <span class="token operator">+</span> err<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 将标题发送到通道</span>titles <span class="token operator"><-</span> title<span class="token punctuation">}</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 等待所有的goroutine完成</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">close</span><span class="token punctuation">(</span>titles<span class="token punctuation">)</span><span class="token comment">// 将通道的结果收集到数组中</span>resultTitles <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>urls<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">for</span> title <span class="token operator">:=</span> <span class="token keyword">range</span> titles <span class="token punctuation">{</span>resultTitles <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>resultTitles<span class="token punctuation">,</span> title<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">return</span> resultTitles<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>urls <span class="token operator">:=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"https://www.baidu.com"</span><span class="token punctuation">,</span><span class="token string">"https://www.36kr.com"</span><span class="token punctuation">,</span><span class="token string">"https://www.sina.com.cn"</span><span class="token punctuation">,</span><span class="token string">"https://www.jd.com"</span><span class="token punctuation">,</span><span class="token string">"https://www.taobao.com"</span><span class="token punctuation">,</span><span class="token string">"https://www.pinduoduo.com"</span><span class="token punctuation">,</span><span class="token string">"https://www.tmall.com"</span><span class="token punctuation">,</span><span class="token string">"https://www.zhihu.com"</span><span class="token punctuation">,</span><span class="token string">"http://www.juejin.cn"</span><span class="token punctuation">,</span><span class="token string">"https://www.aliyun.com"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 创建一个上下文,例如,用于设置超时</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithTimeout</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token operator">*</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token keyword">defer</span> <span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span>titles<span class="token punctuation">,</span> err <span class="token operator">:=</span> <span class="token function">crawlURLs</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> urls<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Error:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>os<span class="token punctuation">.</span><span class="token function">Exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">for</span> i<span class="token punctuation">,</span> title <span class="token operator">:=</span> <span class="token keyword">range</span> titles <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%d: %s\n"</span><span class="token punctuation">,</span> i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="1-net-x2F-http-包相关方法"><a href="#1-net-x2F-http-包相关方法" class="headerlink" title="1. net&#x2F;http 包相关方法"></a>1. net&#x2F;http 包相关方法</</summary>
<category term="Go爬虫" scheme="http://coderedeng.github.io/categories/Go%E7%88%AC%E8%99%AB/"/>
<category term="Go爬虫" scheme="http://coderedeng.github.io/tags/Go%E7%88%AC%E8%99%AB/"/>
</entry>
<entry>
<title>colly</title>
<link href="http://coderedeng.github.io/2024/04/30/Go%E7%88%AC%E8%99%AB%20-%20colly/"/>
<id>http://coderedeng.github.io/2024/04/30/Go%E7%88%AC%E8%99%AB%20-%20colly/</id>
<published>2024-04-30T12:12:11.000Z</published>
<updated>2024-05-21T12:49:51.595Z</updated>
<content type="html"><![CDATA[<p>Colly 是 Go 语言中一个功能强大的爬虫库,它被设计用于简化 Web 页面的抓取和数据提取过程。下面是关于 Colly 的一些主要特点和用法:</p><ol><li><strong>简单易用</strong>:Colly 提供了一个简洁的 API,使得编写爬虫变得非常容易。你可以很容易地定义需要爬取的网站的规则,并提取感兴趣的数据。</li><li><strong>灵活的规则定义</strong>:你可以定义多个规则来匹配不同类型的网页,并在每个规则中指定需要采取的操作,例如提取数据或者跟踪链接。</li><li><strong>并发支持</strong>:Colly 内置了对并发的支持,可以同时爬取多个页面,从而提高爬取效率。</li><li><strong>中间件</strong>:Colly 提供了中间件机制,允许你在请求发送、响应接收等各个阶段添加自定义逻辑,从而灵活地扩展爬虫的功能。</li><li><strong>内置的数据提取工具</strong>:Colly 提供了一些方便的工具函数,用于从 HTML 页面中提取数据,例如使用 CSS 选择器或者 XPath。</li><li><strong>可扩展性</strong>:Colly 的设计非常灵活,你可以根据自己的需求轻松地扩展和定制功能。</li></ol><p>以下是一个爬取微博热搜的示例代码:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"log"</span><span class="token string">"strings"</span><span class="token string">"github.com/gocolly/colly/v2"</span><span class="token string">"github.com/gocolly/colly/v2/extensions"</span><span class="token punctuation">)</span><span class="token comment">// 获取微博热搜榜colly</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>c <span class="token operator">:=</span> colly<span class="token punctuation">.</span><span class="token function">NewCollector</span><span class="token punctuation">(</span><span class="token punctuation">)</span>extensions<span class="token punctuation">.</span><span class="token function">RandomUserAgent</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">OnRequest</span><span class="token punctuation">(</span><span class="token keyword">func</span><span class="token punctuation">(</span>r <span class="token operator">*</span>colly<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 向请求头添加 cookie 防止进入访客模式</span>r<span class="token punctuation">.</span>Headers<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token string">"cookie"</span><span class="token punctuation">,</span> <span class="token string">"SUB=_2AkMRY4uTf8NxqwFRmfsdxWLna410ygHEieKnP3pIJRMxHRl-yT9kqkAvtRB6OuOlexrgRHkeY_A2VqgX2CcV_p0455qS; SUBP=0033WrSXqPxfM72-Ws9jqgMF55529P9D9WhOnHVoAxngUau7LDxX9KO_; _s_tentry=passport.weibo.com; Apache=6010347679488.02.1715406001552; SINAGLOBAL=6010347679488.02.1715406001552; ULV=17154060015601116010347679488.02.1715406001552"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 解析热搜页面HTML结构,获取相应热搜内容</span>c<span class="token punctuation">.</span><span class="token function">OnHTML</span><span class="token punctuation">(</span><span class="token string">".data table tbody"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>e <span class="token operator">*</span>colly<span class="token punctuation">.</span>HTMLElement<span class="token punctuation">)</span> <span class="token punctuation">{</span> startTime <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>e<span class="token punctuation">.</span><span class="token function">ForEach</span><span class="token punctuation">(</span><span class="token string">"tr"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>i <span class="token builtin">int</span><span class="token punctuation">,</span> tr <span class="token operator">*</span>colly<span class="token punctuation">.</span>HTMLElement<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">if</span> i <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token comment">// 去除第一个官方宣传内容</span>str <span class="token operator">:=</span> tr<span class="token punctuation">.</span>Textstr1 <span class="token operator">:=</span> strings<span class="token punctuation">.</span><span class="token function">ReplaceAll</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> <span class="token string">" "</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span>str2 <span class="token operator">:=</span> strings<span class="token punctuation">.</span><span class="token function">ReplaceAll</span><span class="token punctuation">(</span>str1<span class="token punctuation">,</span> <span class="token string">"\n"</span><span class="token punctuation">,</span> <span class="token string">" "</span><span class="token punctuation">)</span><span class="token keyword">if</span> <span class="token operator">!</span>strings<span class="token punctuation">.</span><span class="token function">Contains</span><span class="token punctuation">(</span>str2<span class="token punctuation">,</span> <span class="token string">"•"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 根据•标记去除相应的广告</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%+v\n"</span><span class="token punctuation">,</span> str2<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span> timeConsum <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>startTime<span class="token punctuation">)</span> <span class="token comment">// 计算时间耗时</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"总耗时:"</span><span class="token punctuation">,</span> timeConsum<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>err <span class="token operator">:=</span> c<span class="token punctuation">.</span><span class="token function">Visit</span><span class="token punctuation">(</span><span class="token string">"https://s.weibo.com/top/summary/"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Fatalln</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><p>Colly 是 Go 语言中一个功能强大的爬虫库,它被设计用于简化 Web 页面的抓取和数据提取过程。下面是关于 Colly 的一些主要特点和用法:</p>
<ol>
<li><strong>简单易用</strong>:Colly 提供了一个简洁的 API,使得编写爬虫变得</summary>
<category term="Go爬虫" scheme="http://coderedeng.github.io/categories/Go%E7%88%AC%E8%99%AB/"/>
<category term="Go爬虫" scheme="http://coderedeng.github.io/tags/Go%E7%88%AC%E8%99%AB/"/>
</entry>
<entry>
<title>Go语言历史版本演进和新特性[持续更新]</title>
<link href="http://coderedeng.github.io/2024/04/10/Go%E8%AF%AD%E8%A8%80%E5%8E%86%E5%8F%B2%E7%89%88%E6%9C%AC%E6%BC%94%E8%BF%9B%E5%92%8C%E6%96%B0%E7%89%B9%E6%80%A7[%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0]/"/>
<id>http://coderedeng.github.io/2024/04/10/Go%E8%AF%AD%E8%A8%80%E5%8E%86%E5%8F%B2%E7%89%88%E6%9C%AC%E6%BC%94%E8%BF%9B%E5%92%8C%E6%96%B0%E7%89%B9%E6%80%A7[%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0]/</id>
<published>2024-04-10T13:34:11.000Z</published>
<updated>2024-05-21T13:58:14.408Z</updated>
<content type="html"><![CDATA[<p>发布总览:<a href="https://go.dev/doc/devel/release">Release History - The Go Programming Language</a></p><h2 id="GO-1-22-新特性"><a href="#GO-1-22-新特性" class="headerlink" title="GO 1.22 新特性"></a>GO 1.22 新特性</h2><p>发布时间:2024-02-06</p><p>官方说明:<a href="https://go.dev/doc/go1.22">Go 1.22 Release Notes - The Go Programming Language</a></p><ul><li><p>循环变量改进:Go 1.22解决了for循环中循环变量在迭代之间意外共享的问题。在新的版本中,for循环中的循环变量(如for range语句中的变量)将不再在整个循环中共享,而是在每次迭代中都有自己的变量。这意味着在goroutine中使用循环变量时,每个goroutine将捕获其迭代的变量,而不是共享同一个变量。这一变化可能会对现有代码的行为产生影响,因此Go团队提供了一个工具来检测代码中可能因此特性变化而产生问题的地方。</p></li><li><p>range支持整型表达式:在Go 1.22中,for range循环的range表达式除了支持传统的数组、切片、map、channel等类型外,还支持整型表达式。这意味着你可以在for range循环中使用整型值,循环将基于该整型值进行迭代。</p></li><li><p>性能提升:Go 1.22在运行时进行了内存优化,提高了CPU性能(约1-3%),并减少了大多数Go程序的内存开销(约1%)。此外,Go 1.21引入的profile-guided optimization(PGO)功能在1.22版本中得到了进一步改进,包括改进的devirtualization,允许更多的接口方法调用进行静态调度,从而提高了程序性能。</p></li><li><p>标准库新增内容:</p><ul><li>引入了一个新的<code>math/rand/v2</code>包,提供更清晰、更一致的API,并使用更高质量、更快的伪随机生成算法。</li><li><code>net/http.ServeMux</code>的 patterns 现在接受方法和通配符,例如可以匹配仅限GET请求的<code>GET /task/{id}/</code>模式。</li><li><code>database/sql</code>包中新增了一个<code>Null[T]</code>类型,用于扫描可为空的列。</li><li>在<code>slices</code>包中添加了一个<code>Concat</code>函数,用于连接任意类型的多个切片。</li></ul></li><li><p>工具链:</p><p>在Go工具链改善方面,首当其冲的要数go module相关工具了。</p><p>在Go 1.22中,go work增加了一个与go mod一致的特性:支持vendor。通过go work vendor,可以将workspace中的依赖放到vendor目录下,同时在构建时,如果module root下有vendor目录,那么默认的构建是go build -mod=vendor,即基于vendor的构建。</p><p>go mod init在Go 1.22中将不再考虑GOPATH时代的包依赖工具的配置文件了,比如Gopkg.lock。在Go 1.22版本之前,如果go module之前使用的是类似<a href="https://tonybai.com/2017/06/08/first-glimpse-of-dep/">dep这样的工具来管理包依赖</a>,go mod init会尝试读取dep配置文件来生成go.mod。</p><p>go vet工具取消了对loop变量引用的警告,增加了对空append的行为的警告(比如:slice = append(slice))、增加了deferring time.Since的警告以及在log/slog包的方法调用时key-value pair不匹配的警告。</p></li></ul><h2 id="GO-1-21-新特性"><a href="#GO-1-21-新特性" class="headerlink" title="GO 1.21 新特性"></a>GO 1.21 新特性</h2><p>发布时间:2023.08.08</p><p>官方说明:<a href="https://go.dev/doc/go1.21">Go 1.21 Release Notes - The Go Programming Language</a></p><p>特性:</p><p>go1.21.1(发布于 2023 年 9 月 6 日)包括对<code>cmd/go</code>、<code>crypto/tls</code>和<code>html/template</code>包的四个安全修复,以及对编译器、<code>go</code>命令、链接器、运行时和<code>context</code>、<code>crypto/tls</code>、<code>encoding/gob</code>、<code>encoding/xml</code>、<code>go/types</code>、<code>net/http</code>、<code>os</code>和 的错误修复<code>path/filepath</code>包。</p><p>go1.21.2(2023 年 10 月 5 日发布)包括对包的一项安全修复<code>cmd/go</code>,以及对编译器、<code>go</code>命令、链接器、运行时和包的错误修复<code>runtime/metrics</code>。 </p><p>go1.21.3(2023 年 10 月 10 日发布)包含对该<code>net/http</code>软件包的安全修复。</p><h2 id="GO-1-20-新特性"><a href="#GO-1-20-新特性" class="headerlink" title="GO 1.20 新特性"></a>GO 1.20 新特性</h2><p>发布时间:2023.02.01</p><p>官方说明:<a href="https://go.dev/doc/go1.20">Go 1.20 Release Notes - The Go Programming Language</a></p><p>特性:</p><ul><li>支持将slice直接转为数组</li><li>Comparable类型可比较</li><li>unsafe包添加<code>Slice</code>,<code>SliceData</code>,<code>String</code>,<code>StringData 4个函数</code></li><li>可移植性:Go 1.20将会成为支持macOS 10.13 High Sierra和10.14 Mojave的最后一个版本。</li><li>Go 1.20增加了对于RISC-V架构在FreeBSD操作系统的实验性支持</li><li>PGO引入</li><li>标准库加强<ul><li>新增了几个 时间转换格式常量</li><li>新包 crypto/ecdh 支持通过 NIST 曲线和 Curve25519 椭圆曲线 Diffie-Hellman 密钥交换</li><li>新类型 http.ResponseController 访问 http.ResponseWriter 接口未处理的扩展请求</li><li>httputil.ReverseProxy 包含一个新的 Rewrite 钩子函数,取代了之前的 Director 钩子</li><li>新方法 context.WithCancelCause 提供了一种方法来取消具有给定错误的上下文</li><li>os/exec.Cmd 结构体中的新字段 Cancel 和 WaitDelay, 指定 Cmd 在其关联的 Context 被取消或其进程退出时的回调</li></ul></li><li>工具链<ul><li>cover 工具可以收集整个程序的覆盖率,不仅仅是单元测试</li><li>go build、go install 和其他与构建相关的命令可以接收一个 -pgo 标志,启用配置文件引导优化,以及一个 -cover 标志,用于整个程序覆盖率分析</li><li>go test -json 的实现已得到改进,可以处理复杂多样的 Stdout 输出</li><li>vet 在并行运行的测试中可能会发生更多循环变量引用错误</li><li>在没有 C 工具链 的系统上默认禁用 CGO</li></ul></li><li>性能提升<ul><li><a href="https://so.csdn.net/so/search?q=%E7%BC%96%E8%AF%91%E5%99%A8&spm=1001.2101.3001.7020">编译器</a>和 GC 的优化减少了内存开销,并将 CPU 性能整体提高了 2%</li><li>针对编译时间进行了优化,提升了 10%。使得构建速度与 Go 1.17 保持一致 (恢复到了泛型之前的速度)</li><li>Go 发行版瘦身,新版本起,Go 的 $GOROOT/pkg 目录将不再存储标准库的预编译包存档,Go 发行版的将迎来一轮瘦身</li></ul></li></ul><h2 id="GO-1-19-新特性"><a href="#GO-1-19-新特性" class="headerlink" title="GO 1.19 新特性"></a>GO 1.19 新特性</h2><p>时间:2022.05</p><p>官方说明:<a href="https://go.dev/doc/go1.19">Go 1.19 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>泛型问题fix</li><li>修订Go memory model:对Go memory model做了更正式的整体描述,增加了对multiword竞态、runtime.SetFinalizer、更多sync类型、atomic操作以及编译器优化方面的描述。</li><li>修订go doc comment格式:增加了对超链、列表、标题、标准库API引用等格式支持</li><li>新增runtime.SetMemoryLimit和GOMEMLIMIT环境变量:避免Go程序因分配heap过多,超出系统内存资源限制而被kill,默认memory limit是math.MaxInt64,limit限制的是go runtime掌控的内存总量,对于开发者自行从os申请的内存(比如通过mmap)则不予考虑。</li><li>启动时将默认提高打开文件的限值:对于导入os包的go程序,Go将在1.19中默认提高这些限制值到hard limit。</li><li>race detector将升级到v3版thread sanitizer:race detector性能相对于上一版将提升1.5倍-2倍,内存开销减半,并且没有对goroutine的数量的上限限制</li><li>增加”unix” build tag://go:build unix</li><li>标准库net包使用EDNS</li><li>标准库flag包增加TextVar函数</li><li>正式支持64位龙芯cpu架构 (GOOS=linux, GOARCH=loong64)</li><li>当Go程序空闲时,Go GC进入到周期性的GC循环的情况下(2分钟一次),Go运行时现在会在idle的操作系统线程上安排更少的GC worker goroutine,减少空闲时Go应用对os资源的占用。</li><li>Go行时将根据goroutine的历史平均栈使用率来分配初始goroutine栈,避免了一些goroutine的最多2倍的goroutine栈空间浪费。</li><li>sync/atomic包增加了新的高级原子类型Bool, Int32, Int64, Uint32, Uint64, Uintptr和Pointer,提升了使用体验。</li><li>Go编译器使用jump table重新实现了针对大整型数和string类型的switch语句,平均性能提升20%左右。</li><li>等</li></ul><h2 id="Go-1-18-新特性"><a href="#Go-1-18-新特性" class="headerlink" title="Go 1.18 新特性"></a>Go 1.18 新特性</h2><p>时间:2022.03</p><p>官方说明:<a href="https://go.dev/doc/go1.18">Go 1.18 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>泛型支持</li><li>Workspaces 工作区</li><li>Go编译器与Go module变化:修正的语法bug,在AMD64平台上引入architectural level,为ARM64架构带来高达 20% 的 CPU 性能改进:但由于编译器中与支持泛型有关的变化,Go 1.18 的编译速度可能比Go 1.17的编译速度大约慢15%。编译后的代码的执行时间不受影响。打算在Go 1.19中提高编译器的速度。Go 1.18明确了能修改go.mod、go.sum的命令只有三个:go get、go mod tidy和go mod download。</li><li>go fuzzing test:将fuzz testing纳入了go test工具链,与单元测试、性能基准测试等一起成为了Go原生测试工具链中的重要成员,单元测试函数名样式:FuzzXxx</li><li>go get 不再执行编译和安装工作</li><li>gofmt支持并发</li><li>内置函数Append对切片的扩容算法发生变化:和Go 1.17以1024作为大小分界不同,Go 1.18使用256作为threshold</li><li>新增net/netip包</li><li>tls client默认将使用TLS 1.2版本</li><li>crypto/x509包默认将拒绝使用SHA-1哈希函数签名的证书(自签发的除外)</li><li>strings包和bytes包新增Cut函数</li><li>runtime/pprof精确性提升</li><li>sync包新增Mutex.TryLock、RWMutex.TryLock和RWMutex.TryRLock</li><li>等</li></ul><h2 id="Go-1-17-新特性"><a href="#Go-1-17-新特性" class="headerlink" title="Go 1.17 新特性"></a>Go 1.17 新特性</h2><p>时间:2021.08</p><p>官方说明:<a href="https://go.dev/doc/go1.17">Go 1.17 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>从切片到数组指针的转换: []T 类型的表达式 s 现在可以转换为数组指针类型 *[N]T</li><li>go modules 支持“修剪模块图”(Pruned module graphs):go mod tidy -go=1.17</li><li>编译器带来了额外的改进:即一种传递函数参数和结果的新方法,程序性能提高了约 5%,amd64 平台的二进制大小减少了约 2%。</li><li>unsafe包新增了<code>unsafe.Add</code>和<code>unsafe.Slice</code></li><li><code>go.mod 中添加 // Deprecated: 注释来弃用模块</code></li><li>net包:</li></ul><ol><li>url参数解析增加对“;”的支持变化(原先 example?a=1;b=2&c=3 会解析成 <code>map[a:[1] b:[2] c:[3]]</code>, 现在解析成<code>map[c:[3]]</code>)</li><li>增加 IP.IsPrivate 判断私有 IP</li><li>a.b.c.d 格式的 ip v4 地址不允许每段有前缀 0(因为某些系统会认为前缀 0 表示 8进制)</li><li>等</li></ol><h2 id="Go-1-16-新特性"><a href="#Go-1-16-新特性" class="headerlink" title="Go 1.16 新特性"></a>Go 1.16 新特性</h2><p>时间:2021.02</p><p>官方说明:<a href="https://go.dev/doc/go1.17">Go 1.16 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>GO111MODULE 默认为 on</li><li>支持编译阶段将静态资源文件打包进编译好的程序中,并提供访问这些文件的能力://go:embed</li></ul><h2 id="Go-1-15-新特性"><a href="#Go-1-15-新特性" class="headerlink" title="Go 1.15 新特性"></a>Go 1.15 新特性</h2><p>时间:2020.08</p><p>官方说明:<a href="https://go.dev/doc/go1.16">Go 1.15 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>改进了对高核心数的小对象的分配</li><li>编译器/汇编器/链接器的优化:二进制大小减少了约 5%,减少了链接器资源的使用(时间和内存)并提高了代码的稳健性/可维护性。</li><li>内置了time/tzdata包:允许将时区数据库嵌入到程序中</li><li>等</li></ul><h2 id="Go-1-14-新特性"><a href="#Go-1-14-新特性" class="headerlink" title="Go 1.14 新特性"></a>Go 1.14 新特性</h2><p>时间:2020.02</p><p>官方说明:<a href="https://go.dev/doc/go1.15">Go 1.14 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>Go Module已可用于生产使用</li><li>嵌入具有重叠方法集的接口</li><li>改进了defer的性能</li><li>goroutines 异步可抢占</li><li>页面分配器更高效</li><li>内部定时器更高效</li><li>等</li></ul><h2 id="Go-1-13-新特性"><a href="#Go-1-13-新特性" class="headerlink" title="Go 1.13 新特性"></a>Go 1.13 新特性</h2><p>时间:2019.09</p><p>官方说明:<a href="https://go.dev/doc/go1.14">Go 1.13 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>优化sync.Pool</li></ul><p>sync 包的 Pool 组件得到改进,得其中的资源不会在垃圾回收时被清除(通过新机制里引入的缓存,两次垃圾回收之间没有被使用过的实例才会被清除)</p><ul><li>重了逃逸分析逻辑,使得 Go 程序减少了堆上的分配次数</li><li>go 命令默认使用 Go module mirror and Go checksum database下载和验证模块</li><li>对数字文字的改进</li><li>错误换行</li><li>默认开启 TLS 1.3</li><li>等</li></ul><h2 id="Go-1-12-新特性"><a href="#Go-1-12-新特性" class="headerlink" title="Go 1.12 新特性"></a>Go 1.12 新特性</h2><p>时间:2019.02</p><p>官方说明:<a href="https://go.dev/doc/go1.13">Go 1.12 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>改进了Go modules</li><li>在<a href="https://link.juejin.cn/?target=https://pkg.go.dev/golang.org/x/tools/go/analysis">analysis包</a>基础上重写了 go vet 命令</li><li>等</li></ul><h2 id="Go-1-11-新特性"><a href="#Go-1-11-新特性" class="headerlink" title="Go 1.11 新特性"></a>Go 1.11 新特性</h2><p>时间:2018.08</p><p>官方说明:<a href="https://go.dev/doc/go1.12">Go 1.11 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>Go modules</li></ul><h2 id="Go-1-10-新特性"><a href="#Go-1-10-新特性" class="headerlink" title="Go 1.10 新特性"></a>Go 1.10 新特性</h2><p>时间:2018.02</p><p>官方说明:<a href="https://go.dev/doc/go1.12">Go 1.10 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>go test with cache:go test命令可以缓存测试结果</li><li>go build 命令会缓存最近构建过的包,从而加快了构建过程</li><li>明确预声明类型(predeclared type)是defined type还是alias type</li><li>移除spec中对method expression: T.m中T的类型的限制</li><li>默认的GOROOT</li><li>增加GOTMPDIR变量</li><li>通过cache实现增量构建,提高go tools性能</li><li>go tool pprof做了一个较大的改变:增加了Web UI</li><li>标准库新增strings.Builder</li><li>标准库bytes包的几个方法Fields, FieldsFunc, Split和SplitAfter在底层实现上有变化,使得外部展现的行为有所变化</li><li>等</li></ul><h2 id="Go-1-9-新特性"><a href="#Go-1-9-新特性" class="headerlink" title="Go 1.9 新特性"></a>Go 1.9 新特性</h2><p>时间:2017.08</p><p>官方说明:<a href="https://go.dev/doc/go1.10">Go 1.9 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>提升了垃圾收集器和编译器</li><li>增加了类型别名</li><li>新增了sync.Map</li><li>time包更加安全</li><li>testing包新增helper方法</li><li>支持渐进式代码重构</li><li>引入了类型别名并提升了运行时和工具支持</li></ul><h2 id="Go-1-8-新特性"><a href="#Go-1-8-新特性" class="headerlink" title="Go 1.8 新特性"></a>Go 1.8 新特性</h2><p>时间:2017.02</p><p>官方说明:<a href="https://go.dev/doc/go1.9">Go 1.8 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>优化编译</li></ul><p>CPU 时间在 32 位 ARM 系统上减少了 20-30%, 还针对 64 位 x86 系统进行了一些适度的性能改进。编译器和<a href="https://so.csdn.net/so/search?q=%E9%93%BE%E6%8E%A5%E5%99%A8&spm=1001.2101.3001.7020">链接器</a>变得更快。</p><p>编译时间应该比 Go 1.7 改进了大约 15%</p><p>Go 1.7中进入标准库的context,提供了取消和超时机制。</p><p>Go 1.8 让标准库中更多package使用(支持)context,包括 database/sql,net 包, net/http 包中的 Server.Shutdown等</p><ul><li>对垃圾回收器改进,使两次垃圾回收的暂停时间减小到了毫秒级</li><li>同时识别了剩余仍未解决的暂停模式,并在下一个版本中得到修复。修复后,通常情况下暂停时间能控制在 100 微秒左右,甚至能低至 10 微秒。</li><li>改进了 defer 函数</li><li>部分标准库使用context包来改造</li><li>sort 包中新添加的 Slice 函数,对切片进行排序变得比之前简单得多</li></ul><h2 id="Go-1-7-新特性"><a href="#Go-1-7-新特性" class="headerlink" title="Go 1.7 新特性"></a>Go 1.7 新特性</h2><p>时间:2016.08</p><p>官方说明:<a href="https://go.dev/doc/go1.8">Go 1.7 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>context包转正</li><li>编译时间显着加快:二进制文件大小减少了 20-30%, CPU 时间减少了 5-35%</li><li>垃圾收集器的加速和标准库的优化</li><li>go tool trace改进</li></ul><h2 id="Go-1-6-新特性"><a href="#Go-1-6-新特性" class="headerlink" title="Go 1.6 新特性"></a>Go 1.6 新特性</h2><p>时间:2016.02</p><p>官方说明:<a href="https://go.dev/doc/go1.7">Go 1.6 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>增加对于 HTTP/2 协议的默认支持</li><li>再一次降低了垃圾回收器的延迟</li><li>runtime改变了打印程序结束恐慌的方式。现在只打印发生panic的 goroutine 的堆栈,而不是所有现有的 goroutine</li><li>默认启用vendor目录</li><li>sort.Sort 内部的算法进行了改进,运行速度提高了约 10%</li></ul><h2 id="Go-1-5-新特性"><a href="#Go-1-5-新特性" class="headerlink" title="Go 1.5 新特性"></a>Go 1.5 新特性</h2><p>时间:2015.08</p><p>官方说明:<a href="https://go.dev/doc/go1.6">Go 1.5 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li><a href="https://link.juejin.cn/?target=https://go.dev/doc/go1.5%23gc">垃圾回收器</a>被完全重新设计实现: 基于并发的回收期,GC延迟显著降低,来自Twitter生产案例从300ms下降到30ms</li><li>调度程序的相关改进允许将默认的 GOMAXPROCS 值(并发执行的 goroutine 的数量)从 1 更改为逻辑 CPU 的数量。在以前的版本中,默认值为 1</li><li>go tool trace:可以在运行时可视化跟踪程序,追踪信息可在测试或运行期间生成,展示在浏览器窗口中</li><li>map语法的更改:由于疏忽,允许从slice literals中省略元素类型的规则未应用于map。在1.5版本得到了修正,以下两种定义map的方式从1.5及之后都可以(即可以省略Point的类型)</li></ul><h2 id="Go-1-4-新特性"><a href="#Go-1-4-新特性" class="headerlink" title="Go 1.4 新特性"></a>Go 1.4 新特性</h2><p>时间:2014.02</p><p>官方说明:<a href="https://go.dev/doc/go1.5">Go 1.4 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li><p>For-range loops支持新语法</p><p>1234567891011121314151617</p><p>package mainimport “fmt”func main() { sli := []string{“shandong”, “zhejiang”, “guangdong”, “jiangsu”} for k, v := range sli { fmt.Println(“k-v:”, k, v) //go 1.3及之前的For-range loops } for range sli { fmt.Println(“从1.4开始这种写法是可以通过编译的”) }}</p></li><li><p>Android 的官方支持包<a href="https://link.juejin.cn/?target=https://github.com/golang/mobile">golang.org/x/mobile</a>随该版本一同发布,使开发者可以仅用 Go 代码编写简单的 Android 应用。</p></li><li><p>之前用 C 和汇编语言编写的大多数运行时已转换为用 Go 语言实现 && 使用了更精准的垃圾收集器,堆栈大小减少了 10~30%</p></li><li><p>发布 go generate 命令,此命令会扫描//go:generate 指令提供的信息生成代码,简化了代码生成的方式。 <a href="https://link.juejin.cn/?target=https://go.dev/blog/generate">Generating code</a></p></li><li><p>引入了Internal包</p></li></ul><p>Go 的项目代码管理工具从 Mercurial 切换为 Git,与此同时,项目也从 Google Code 迁移到了 Github 上</p><h2 id="Go-1-3-新特性"><a href="#Go-1-3-新特性" class="headerlink" title="Go 1.3 新特性"></a>Go 1.3 新特性</h2><p>时间:2014.06</p><p>官方说明:<a href="https://go.dev/doc/go1.4">Go 1.3 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>堆栈管理得到了重要改善</li><li>发布了 sync 包的 Pool 组件</li><li>改进了<a href="https://link.juejin.cn/?target=https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub">channel的实现</a>,提升了性能</li></ul><h2 id="Go-1-2-新特性"><a href="#Go-1-2-新特性" class="headerlink" title="Go 1.2 新特性"></a>Go 1.2 新特性</h2><p>时间:2013.12</p><p>官方说明:<a href="https://go.dev/doc/go1.3">Go 1.2 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li><a href="https://link.juejin.cn/?target=https://go.dev/doc/go1.2%23three_index">Three-index slices</a></li><li>go test 命令支持代码覆盖率报告,并提供新的 <code>go tool cover</code> 命令输出代码测试覆盖率的统计信息. <a href="https://link.juejin.cn/?target=https://go.dev/blog/cover">The cover story</a></li></ul><h2 id="Go-1-1-新特性"><a href="#Go-1-1-新特性" class="headerlink" title="Go 1.1 新特性"></a>Go 1.1 新特性</h2><p>时间:2013.05</p><p>官方说明:<a href="https://go.dev/doc/go1.2">Go 1.1 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>增强语言特性(编译器、垃圾回收机制、映射、goroutine 调度器)与性能。</li></ul><h2 id="Go-1-0-新特性"><a href="#Go-1-0-新特性" class="headerlink" title="Go 1.0 新特性"></a>Go 1.0 新特性</h2><p>时间:2012.03</p><p>官方说明:<a href="https://go.dev/doc/go1">Go 1 Release Notes - The Go Programming Language</a></p><p>主要特性:</p><ul><li>承诺兼容性,确保向后兼容 <a href="https://go.dev/doc/go1compat">Go 1 and the Future of Go Programs - The Go Programming Language</a></li></ul><p>本文转自 <a href="https://blog.csdn.net/mdpets/article/details/127663206%EF%BC%8C%E5%A6%82%E6%9C%89%E4%BE%B5%E6%9D%83%EF%BC%8C%E8%AF%B7%E8%81%94%E7%B3%BB%E5%88%A0%E9%99%A4%E3%80%82">https://blog.csdn.net/mdpets/article/details/127663206,如有侵权,请联系删除。</a></p>]]></content>
<summary type="html"><p>发布总览:<a href="https://go.dev/doc/devel/release">Release History - The Go Programming Language</a></p>
<h2 id="GO-1-22-新特性"><a href="#GO-1</summary>
<category term="Go基础" scheme="http://coderedeng.github.io/categories/Go%E5%9F%BA%E7%A1%80/"/>
<category term="Go基础" scheme="http://coderedeng.github.io/tags/Go%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>并发</title>
<link href="http://coderedeng.github.io/2023/10/20/Go%E8%BF%9B%E9%98%B6%20-%20%E5%B9%B6%E5%8F%91/"/>
<id>http://coderedeng.github.io/2023/10/20/Go%E8%BF%9B%E9%98%B6%20-%20%E5%B9%B6%E5%8F%91/</id>
<published>2023-10-20T12:50:13.000Z</published>
<updated>2024-07-18T14:21:45.410Z</updated>
<content type="html"><![CDATA[<h1 id="1-并发"><a href="#1-并发" class="headerlink" title="1. 并发"></a>1. 并发</h1><h2 id="1-1-并发和并行的区别"><a href="#1-1-并发和并行的区别" class="headerlink" title="1.1 并发和并行的区别"></a>1.1 并发和并行的区别</h2><p>并发和并行是两个不同的概念:</p><ul><li>并行意味着程序在<strong>任意时刻</strong>都是同时运行的;</li><li>并发意味着程序在<strong>单位时间内</strong>是同时运行的</li></ul><h3 id="1-1-1-并行"><a href="#1-1-1-并行" class="headerlink" title="1.1.1 并行"></a>1.1.1 并行</h3><p><strong>并行</strong>就是在任一粒度时间内都具备同时执行的能力:简单来说并行就是多机或多台机器并行处理; SMP(SMP 是对称多处理器(Symmetric MultiProcessing)的简称。在这样的系统中包含多个处理器,同时,处理器间共享了内存和 I/O 总线。”对称”是指所有的处理器在功能和位置上地位相同,不存在主处理器或者被处理器较多的 “主机”) 表面上看是并行的,但由于是共享内存,以及线程间的同步等,不可能完全做到并行。</p><h3 id="1-1-2-并发"><a href="#1-1-2-并发" class="headerlink" title="1.1.2 并发"></a>1.1.2 并发</h3><p><strong>并发</strong>是在规定的时间内多个请求都得到执行和处理,强调的是给外界的感觉,实际上内部可能是分时操作的。并发重在避免阻塞,使程序不会因为一个阻塞而停止处理。并发典型的应用场景:分时操作系统就是一种并发设计(忽略多核 CPU)。</p><h2 id="1-2-goroutine"><a href="#1-2-goroutine" class="headerlink" title="1.2 goroutine"></a>1.2 goroutine</h2><p><code>goroutine</code>是 Go 语言中处理并发执行的一个主要工具,是 Go 运行时层面的轻量级线程,与 OS 线程相比,它的开销更小。操作系统可以进行线程和进程的调度,本身具备并发处理能力,但进程切换代价还是过高,当操作系统在系统进程之间切换时,它需要保存当前正在运行进程的状态,以便在再次切换回该进程时恢复执行。这通常涉及保存进程的 “上下文”,即使该进程能够从中断点继续执行的所有信息(<strong>处理器的寄存器</strong>、<strong>内存管理信息</strong>、<strong>进程状态</strong>、<strong>输入和输出状态</strong>、<strong>资源使用情况</strong>等)。如果应用可以在用户态进行调度,应该可以更大限度地提升程序运行效率,goroutine就是基于这个思想实现的。</p><ul><li>goroutine 示例,代码如下:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup <span class="token comment">// 第一步:定义一个计数器</span><span class="token keyword">func</span> <span class="token function">routine1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"routine1 你好golang-"</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span> <span class="token comment">// routine1 你好golang-0, ...9</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//协程计数器-1 // 第三步:协程执行完毕,计数器-1</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">routine2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">2</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"routine2 你好golang-"</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span> <span class="token comment">// routine2 你好golang-0, routine2 你好golang-1</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//协程计数器-1</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">//协程计数器+1 第二步:开启一个协程计数器+1</span> <span class="token keyword">go</span> <span class="token function">routine1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//表示开启一个协程</span> wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">//协程计数器+1</span> <span class="token keyword">go</span> <span class="token function">routine2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//表示开启一个协程</span> wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//等待协程执行完毕... 第四步:计数器为0时推出</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"主线程退出..."</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>goroutine 有如下特性:</p><ul><li>go 的执行是非阻塞的,不会等待;</li><li>go 后面函数的返回值会被忽略;</li><li>调度器不能保证多个 goroutine 的执行次序;</li><li>没有父子 goroutine 的概念,所有 goroutine 是平等地被调度和执行的;</li><li>go 程序运行时会在 main 函数先创建一个 goroutine,其他 go 关键字创建的 goroutine 会另外创建;</li><li>go 没有暴露 goroutine id 给用户,所以不能在一个 goroutine 里面显式地操作另一个 goroutine ,不过 runtime 包提供了一些函数访问和设置 goroutine 的相关信息;</li></ul><h3 id="1-2-1-GOMAXPROCS"><a href="#1-2-1-GOMAXPROCS" class="headerlink" title="1.2.1 GOMAXPROCS"></a>1.2.1 GOMAXPROCS</h3><p>GOMAXPROCS( n int ) 用来设置或查询可以并发执行的 goroutine 数目,n 大于 1 表示设置 GOMAXPROCS 值,否则表示查询当前 GOMAXPROCS 的值。</p><h3 id="1-2-2-Goexit"><a href="#1-2-2-Goexit" class="headerlink" title="1.2.2 Goexit"></a>1.2.2 Goexit</h3><p>Goexit() 是结束当前 goroutine 的运行, Goexit 在结束当前 goroutine 运行之前会调用当前 goroutine 已经注册的 defer 。 Goexit 并不会产生 panic ,所以该 goroutine defer 里面的 recover 调用都返回 nil 。</p><h3 id="1-2-3-Gosched"><a href="#1-2-3-Gosched" class="headerlink" title="1.2.3 Gosched"></a>1.2.3 Gosched</h3><p>Gosched() 是放弃当前调度执行机会,将当前 goroutine 放到队列中等待下次被调度。只有 goroutine 还是不够的,多个 goroutine 之间还需要通信、同步、协同等。</p><h2 id="1-3-Chan"><a href="#1-3-Chan" class="headerlink" title="1.3 Chan"></a>1.3 Chan</h2><p>chan 是 Go 语言里面的一个关键宇,是 channel 的简写,翻译为中文就是通道。 goroutine 是 Go 语言里面的并发执行体,通道是 goroutine 之间通信和同步的重要组件。 Go 的哲学是“不要通过共享内存来通信,而是通过通信来共享内存”(CSP(Communicating Sequential Processes)是一种用于设计并发系统的模型,它强调通过在独立的并发实体或“进程”之间传递消息来进行通信),通道是 Go 通过通信来共享内存的载体。例如:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">//创建一个无缓冲的通道,通道存放元素的类型为 datatype <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> datatype <span class="token punctuation">)</span> //创建一个有 <span class="token number">10</span> 个缓冲的通道,通道存放元素的类型为 datatype <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> datatype<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>通道分为无缓冲的通道和有缓冲的通道, Go 提供内置函数 len 和 cap ,<strong>无缓冲的通道的 len</strong> <strong>和 cap 都是 0</strong>,<strong>有缓冲的通道的 len 代表没有被读取的元素数, cap 代表整个通道的容量</strong>。无缓冲的通道既可以用于通信,也可以用于两个 goroutine 的同步,有缓冲的通道主要用于通信。有缓冲通道示例:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">var</span> m sync<span class="token punctuation">.</span>Mutex<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>m<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 互斥锁</span>c <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">,</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">defer</span> m<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 解锁</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">100</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">{</span>c <span class="token operator"><-</span> i <span class="token comment">// 向 c 通道传递数据</span><span class="token punctuation">}</span><span class="token function">close</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>m<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等到互斥锁解锁,然后再次锁定用来阻塞主程序。</span><span class="token keyword">for</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> c <span class="token punctuation">{</span> <span class="token comment">// 向已关闭的通道遍历读取数据</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>写到缓冲通道中的数据不会消失,它还可以缓冲和适配两个 goroutine 处理速率不一致的情况,缓冲通道和消息队列类似,有削峰和增大吞吐量的功能。</p><p>操作不同状态的 chan 会引发三种行为:</p><ol><li>panic<ul><li>向已经关闭的通道写数据会导致 panic ;最佳实践是由写入者关闭通道,能最大程度地避免向已经关闭的通道写数据而导致的 panic;</li><li>重复关闭的通道会导致 panic;</li></ul></li><li>阻塞<ul><li>向未初始化的通道写数据或读数据都会导致当前 goroutine 的永久阻塞;</li><li>向缓冲区己满的通道写入数据会导致 goroutine 阻塞;</li><li>通道中没有数据,读取该通道会导致 goroutine 阻塞;</li></ul></li><li>非阻塞<ul><li>读取己经关闭的通道不会引发阻塞,而是立即返回通道元素类型的零值,可以使用 comrna , ok 语法判断通道是否己经关闭;</li><li>向有缓冲且没有满的通道读/写不会引发阻塞;</li></ul></li></ol><h2 id="1-4-WaitGroup"><a href="#1-4-WaitGroup" class="headerlink" title="1.4 WaitGroup"></a>1.4 WaitGroup</h2><p>goroutine 和 chan 一个用于并发,另一个用于通信。没有缓冲的通道具有同步的功能,除此之外, sync 包也提供了多个 goroutine 同步的机制,主要是通过 WaitGroup 实现的。</p><p>主要数据结构和操作如下:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> WaitGroup <span class="token keyword">struct</span> <span class="token punctuation">{</span> <span class="token comment">// contains filtered or unexported fields</span><span class="token punctuation">}</span><span class="token comment">// 添加等待信号</span><span class="token keyword">func</span> <span class="token punctuation">(</span>wg <span class="token operator">*</span>WaitGroup<span class="token punctuation">)</span> <span class="token function">Add</span><span class="token punctuation">(</span>delta <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token comment">// 释放等待信号</span><span class="token keyword">func</span> <span class="token punctuation">(</span>wg <span class="token operator">*</span>WaitGroup<span class="token punctuation">)</span> <span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等待</span><span class="token keyword">func</span> <span class="token punctuation">(</span>wg <span class="token operator">*</span>WaitGroup<span class="token punctuation">)</span> <span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>WaitGroup 用来等待多个 goroutine 完成, main goroutine 调用 Add 设置需要等待 goroutine 的数目,每一个 goroutine 结束时调用 Done(), Wait() 被 main 用来等待所有的 goroutine 完成。</p><h2 id="1-5-select"><a href="#1-5-select" class="headerlink" title="1.5 select"></a>1.5 select</h2><p>select 是类 UNIX 系统提供的一个多路复用系统 API, Go 语言借用多路复用的概念,提供了 select 关键字,用于多路监听多个通道。当监听的通道没有状态是可读或可写的, select 是阻塞的;只要监听的通道中有一个状态是可读或可写的,则 select 就不会阻塞,而是进入处理就绪通道的分支流程。如果监听的通道有多个可读或可写的状态, 则 select 随机选取一个处理。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> ch <span class="token punctuation">:</span> <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">{</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token comment">//0 或 1 的写入是随机的</span> <span class="token keyword">case</span> ch <span class="token operator"><</span> <span class="token operator">-</span> <span class="token number">0</span> <span class="token punctuation">:</span> <span class="token keyword">case</span> ch <span class="token operator"><-</span> <span class="token number">1</span> <span class="token punctuation">:</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token punctuation">(</span>ch<span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token punctuation">:</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">;</span>i<span class="token operator">++</span> <span class="token punctuation">{</span> <span class="token function">println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 运行结果</span><span class="token number">0</span> <span class="token number">0</span> <span class="token number">1</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token number">1</span> <span class="token number">0</span> <span class="token number">1</span> <span class="token number">1</span> <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="1-6-扇入(-Fan-in-)和扇出(-Fan-out"><a href="#1-6-扇入(-Fan-in-)和扇出(-Fan-out" class="headerlink" title="1.6 扇入( Fan in )和扇出( Fan out )"></a>1.6 扇入( Fan in )和扇出( Fan out )</h2><p>编程中经常遇到 “扇入和扇出” 两个概念,所谓的扇入是指将多路通道聚合到一条通道中处理,Go 语言最简单的扇入就是使用 select 聚合多条通道服务;所谓的扇出是指将一条通道发散到多条通道中处理,在 Go 语言里面具体实现就是使用 go 关键字启动多个 goroutine 并发处理。</p><p>中国有句经典的哲学名句叫 “分久必合,合久必分” 软件的设计和开发也遵循同样的哲学思想,扇入就是合,扇出就是分。当生产者的速度很慢时,需要使用扇入技术聚合多个生产者满足消费者, 比如很耗时的加密/解密服务;当消费者的速度很慢时,需要使用扇出技术,比如Web 服务器并发请求处理。扇入和扇出是 Go 并发编程中常用的技术。</p><h3 id="1-6-1-扇入(Fan-In):"><a href="#1-6-1-扇入(Fan-In):" class="headerlink" title="1.6.1 扇入(Fan-In):"></a>1.6.1 扇入(Fan-In):</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">fanIn</span><span class="token punctuation">(</span>input1<span class="token punctuation">,</span> input2 <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span> <span class="token punctuation">{</span> c <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">{</span> <span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> s <span class="token operator">:=</span> <span class="token operator"><-</span>input1<span class="token punctuation">:</span> c <span class="token operator"><-</span> s <span class="token keyword">case</span> s <span class="token operator">:=</span> <span class="token operator"><-</span>input2<span class="token punctuation">:</span> c <span class="token operator"><-</span> s <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> c<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>扇入指的是将多个输入 channel 合并到一个 channel 中,扇出是将一个输入 channel 分散给多个 worker 进行处理。</p><h3 id="1-6-2-扇出(Fan-Out):"><a href="#1-6-2-扇出(Fan-Out):" class="headerlink" title="1.6.2 扇出(Fan-Out):"></a>1.6.2 扇出(Fan-Out):</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">fanOut</span><span class="token punctuation">(</span>input <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">,</span> workerCount <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> outputs <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> workerCount<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> outputs <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>outputs<span class="token punctuation">,</span> <span class="token function">createWorker</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> outputs<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">createWorker</span><span class="token punctuation">(</span>input <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">string</span> <span class="token punctuation">{</span> c <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> n <span class="token operator">:=</span> <span class="token keyword">range</span> input <span class="token punctuation">{</span> c <span class="token operator"><-</span> <span class="token function">doWork</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">close</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> c<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">doWork</span><span class="token punctuation">(</span>n <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">string</span> <span class="token punctuation">{</span> <span class="token comment">//...执行一些操作...</span> <span class="token keyword">return</span> n<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>在以上扇出的例子中,<code>input</code>是输入channel,在<code>fanOut</code>函数中,我们根据<code>workerCount</code>创建相同数量的Worker来处理输入信息。每个Worker处理的任务是从输入channel读取信息,然后进行一些工作(在<code>doWork</code>函数中定义),然后将信息写入自己的输出channel中。Workers的输出channel会被添加到<code>outputs</code>切片中,并从<code>fanOut</code>函数返回。</p><h3 id="1-6-3-扇入扇出分别对应的应用场景"><a href="#1-6-3-扇入扇出分别对应的应用场景" class="headerlink" title="1.6.3 扇入扇出分别对应的应用场景"></a>1.6.3 扇入扇出分别对应的应用场景</h3><p>扇入和扇出的概念常用在处理并发和流处理系统中,它们各自有一些常见的应用场景:</p><p><strong>(1)扇入(Fan-In)</strong><br>扇入是将来自多个源的数据聚合到一个通道中,这种方式常用于多个并行或异步任务完成时集中处理结果,如:</p><ol><li>对来自多个源的日志或状态更新聚合到一个处理者,以实现统一的日志记录、分析或监控。</li><li>在分布式计算的上下文中,多个节点可能正在并行处理任务,并在完成时将结果发送回中央节点以进行聚合和处理。</li></ol><p><strong>(2)扇出(Fan-Out)</strong><br>扇出是将数据从一个源分发到多个接收者的过程,每个接收者都会得到完整的数据拷贝,扇出可以提高处理或任务的吞吐量。具体应用可能包括:</p><ol><li>在负载均衡的上下文中,扇出通常用作一种将任务分发到多个工作节点的手段以提高整体处理速度,每个节点处理部分工作负载。</li><li>在自然语言处理或图像处理等领域,可以使用扇出来并行训练或运行多个模型,然后比较各自的输出以确定最优解。</li><li>扇出模式还可以用于数据备份和冗余存储的场景。比如,我们可以将一个流量的数据同时发送到多个存储节点,以此达到数据的备份和冗余保障。</li></ol><h2 id="1-7-通知退出机制"><a href="#1-7-通知退出机制" class="headerlink" title="1.7 通知退出机制"></a>1.7 通知退出机制</h2><p>读取己经关闭的通道不会引起阻塞,也不会导致 panic ,而是立即返回该通道存储类型的零值。关闭 select 监听的某个通道能使 select 立即感知这种通知,然后进行相应的处理,这就是所谓的退出通知机制(close channel to broadcast )。下面通过一个随机数生成器的示例演示退出通知机制,下游的消费者不需要随机数时,显式地通知生产者停止生产。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// GenerateintA 是一个随机数生成器</span><span class="token keyword">func</span> <span class="token function">GenerateintA</span><span class="token punctuation">(</span>done <span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>Label<span class="token punctuation">:</span><span class="token keyword">for</span> <span class="token punctuation">{</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> ch <span class="token operator"><-</span> rand<span class="token punctuation">.</span><span class="token function">Int</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">case</span> <span class="token operator"><-</span>done<span class="token punctuation">:</span><span class="token keyword">break</span> Label<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token function">close</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> ch<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>done <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>ch <span class="token operator">:=</span> <span class="token function">GenerateintA</span><span class="token punctuation">(</span>done<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span><span class="token function">close</span><span class="token punctuation">(</span>done<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"NumGoroutine="</span><span class="token punctuation">,</span> runtime<span class="token punctuation">.</span><span class="token function">NumGoroutine</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 输出结果</span><span class="token number">146870834388028874</span><span class="token number">7216694335601338127</span><span class="token number">0</span><span class="token number">0</span>NumGoroutine<span class="token operator">=</span> <span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="1-8-并发范式"><a href="#1-8-并发范式" class="headerlink" title="1.8 并发范式"></a>1.8 并发范式</h2><p>通过具体的程序示例来演示 Go 语言强大的并发处理能力,每个示例代表一个并发处理范式,这些范式具有典型的特征,在真实的程序中稍加改造就能使用。</p><h3 id="1-8-1-生成器"><a href="#1-8-1-生成器" class="headerlink" title="1.8.1 生成器"></a>1.8.1 生成器</h3><p>在应用系统编程中,常见的应用场景就是调用一个统一的全局的生成器服务, 用于生成全局事务号、订单号、序列号和随机数等。 Go 对这种场景的支持非常简单,下面以一个随机数生成器为例来说明。</p><ol><li>最简单的带缓冲的生成器。 例如:</li></ol><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// RandomNumber 是一个随机数生成器</span><span class="token keyword">func</span> <span class="token function">RandomNumber</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// 启动一个 go routine 用于生成随机数,函数返回一个通道用于获取随机数</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span>ch <span class="token operator"><-</span> rand<span class="token punctuation">.</span><span class="token function">Int</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> ch<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">RandomNumber</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 输出结果</span><span class="token number">8442295699646266936</span><span class="token number">6343099628820528177</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ol start="2"><li>多个 goroutine 增强型生成器。 例如:</li></ol><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// RandomNumber1 是一个随机数生成器</span><span class="token keyword">func</span> <span class="token function">RandomNumber1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token comment">// 启动一个 go routine 用于生成随机数,函数返回一个通道用于获取随机数</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span>ch <span class="token operator"><-</span> rand<span class="token punctuation">.</span><span class="token function">Int</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> ch<span class="token punctuation">}</span><span class="token comment">// RandomNumber2 是一个随机数生成器</span><span class="token keyword">func</span> <span class="token function">RandomNumber2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token comment">// 启动一个 go routine 用于生成随机数,函数返回一个通道用于获取随机数</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span>ch <span class="token operator"><-</span> rand<span class="token punctuation">.</span><span class="token function">Int</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> ch<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">GenerateInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> ch <span class="token operator"><-</span> <span class="token operator"><-</span><span class="token function">RandomNumber1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">case</span> ch <span class="token operator"><-</span> <span class="token operator"><-</span><span class="token function">RandomNumber2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> ch<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>ch <span class="token operator">:=</span> <span class="token function">GenerateInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">100</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>ch<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 输出结果</span><span class="token number">4732711589376798349</span><span class="token number">5980361011433472918</span><span class="token number">8507484322095864034</span><span class="token operator">...</span><span class="token operator">...</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-8-2-管道"><a href="#1-8-2-管道" class="headerlink" title="1.8.2 管道"></a>1.8.2 管道</h3><p>通道可以分为两个方向,一个是读,另一个是写,假如一个函数的输入参数和输出参数都是相同的 chan 类型, 则该函数可以调用自己,最终形成一个调用链。当然多个具有相同参数类型的函数也能组成一个调用链,这很像 UNIX 系统的管道,是一个有类型的管道。</p><p>下面通过具体的示例演示 Go 程序这种链式处理能力:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span><span class="token punctuation">)</span><span class="token comment">// chain 函数的输入参数和输出参数类型相同,都是 chan int 类型</span><span class="token comment">// chain 函数的功能是将 chan 内的数据统一加1</span><span class="token keyword">func</span> <span class="token function">chain</span><span class="token punctuation">(</span>in <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span> out <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">for</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> in<span class="token punctuation">{</span> out <span class="token operator"><-</span> <span class="token number">1</span> <span class="token operator">+</span> v <span class="token punctuation">}</span> <span class="token function">close</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> out<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> in <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> in <span class="token operator"><-</span> i <span class="token punctuation">}</span> <span class="token function">close</span><span class="token punctuation">(</span>in<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 连续调用 3 次 chan,相当于把 in 中的每个元素都加 3</span> out <span class="token operator">:=</span> <span class="token function">chain</span><span class="token punctuation">(</span><span class="token function">chain</span><span class="token punctuation">(</span><span class="token function">chain</span><span class="token punctuation">(</span>in<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> out <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-8-3-每个请求一个-goroutine"><a href="#1-8-3-每个请求一个-goroutine" class="headerlink" title="1.8.3 每个请求一个 goroutine"></a>1.8.3 每个请求一个 goroutine</h3><p>下面以计算 100 个自然数的和来举例,将计算任务拆分为多个 task,每个 task 启动一个 goroutine 进行处理,程序示例代码如下:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"sync"</span><span class="token punctuation">)</span><span class="token comment">// 工作任务</span><span class="token keyword">type</span> task <span class="token keyword">struct</span> <span class="token punctuation">{</span>begin <span class="token builtin">int</span>end <span class="token builtin">int</span>result <span class="token keyword">chan</span><span class="token operator"><-</span> <span class="token builtin">int</span><span class="token punctuation">}</span><span class="token comment">// 任务执行:计算 begin 到 end 的和</span><span class="token comment">// 执行结果写入到结果 chan result 中</span><span class="token keyword">func</span> <span class="token punctuation">(</span>t <span class="token operator">*</span>task<span class="token punctuation">)</span> <span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>sum <span class="token operator">:=</span> <span class="token number">0</span><span class="token keyword">for</span> i <span class="token operator">:=</span> t<span class="token punctuation">.</span>begin<span class="token punctuation">;</span> i <span class="token operator"><=</span> t<span class="token punctuation">.</span>end<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>sum <span class="token operator">+=</span> i<span class="token punctuation">}</span>t<span class="token punctuation">.</span>result <span class="token operator"><-</span> sum<span class="token punctuation">}</span><span class="token comment">// 构建 task 并写入到 task 通道</span><span class="token keyword">func</span> <span class="token function">InitTask</span><span class="token punctuation">(</span>taskchan <span class="token keyword">chan</span><span class="token operator"><-</span> task<span class="token punctuation">,</span> r <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> p <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>qu <span class="token operator">:=</span> p <span class="token operator">/</span> <span class="token number">10</span>mod <span class="token operator">:=</span> p <span class="token operator">%</span> <span class="token number">10</span>high <span class="token operator">:=</span> qu <span class="token operator">*</span> <span class="token number">10</span><span class="token keyword">for</span> j <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator"><</span> qu<span class="token punctuation">;</span> j<span class="token operator">++</span> <span class="token punctuation">{</span>b <span class="token operator">:=</span> <span class="token number">10</span><span class="token operator">*</span>j <span class="token operator">+</span> <span class="token number">1</span>e <span class="token operator">:=</span> <span class="token number">10</span> <span class="token operator">*</span> <span class="token punctuation">(</span>j <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>tsk <span class="token operator">:=</span> task<span class="token punctuation">{</span>begin<span class="token punctuation">:</span> b<span class="token punctuation">,</span>end<span class="token punctuation">:</span> e<span class="token punctuation">,</span>result<span class="token punctuation">:</span> r<span class="token punctuation">,</span><span class="token punctuation">}</span>taskchan <span class="token operator"><-</span> tsk<span class="token punctuation">}</span><span class="token keyword">if</span> mod <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span>tsk <span class="token operator">:=</span> task<span class="token punctuation">{</span>begin<span class="token punctuation">:</span> high <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span>end<span class="token punctuation">:</span> p<span class="token punctuation">,</span>result<span class="token punctuation">:</span> r<span class="token punctuation">,</span><span class="token punctuation">}</span>taskchan <span class="token operator"><-</span> tsk<span class="token punctuation">}</span><span class="token function">close</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 读取 task chan ,每个 task 一个 worker goroutine 处理</span><span class="token comment">// 并等待每个 task 运行完,关闭结果通道</span><span class="token keyword">func</span> <span class="token function">DistributeTask</span><span class="token punctuation">(</span>taskchan <span class="token operator"><-</span><span class="token keyword">chan</span> task<span class="token punctuation">,</span> wait <span class="token operator">*</span>sync<span class="token punctuation">.</span>WaitGroup<span class="token punctuation">,</span> result <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> taskchan <span class="token punctuation">{</span>wait<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">ProcessTask</span><span class="token punctuation">(</span>v<span class="token punctuation">,</span> wait<span class="token punctuation">)</span><span class="token punctuation">}</span>wait<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">close</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 工作 goroutine 处理具体工作,并将处理结构发送到结果通道</span><span class="token keyword">func</span> <span class="token function">ProcessTask</span><span class="token punctuation">(</span>t task<span class="token punctuation">,</span> wait <span class="token operator">*</span>sync<span class="token punctuation">.</span>WaitGroup<span class="token punctuation">)</span> <span class="token punctuation">{</span>t<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span>wait<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 读取结果通道,汇总结果</span><span class="token keyword">func</span> <span class="token function">ProcessResult</span><span class="token punctuation">(</span>resultchan <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>sum <span class="token operator">:=</span> <span class="token number">0</span><span class="token keyword">for</span> r <span class="token operator">:=</span> <span class="token keyword">range</span> resultchan <span class="token punctuation">{</span>sum <span class="token operator">+=</span> r<span class="token punctuation">}</span><span class="token keyword">return</span> sum<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 创建任务通道</span>taskchan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> task<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// 创建结果通道</span>resultchan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// wait 用于同步等待任务的执行</span>wait <span class="token operator">:=</span> <span class="token operator">&</span>sync<span class="token punctuation">.</span>WaitGroup<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token comment">// 初始化 task 的 goroutine,计算 100 个自然数之和</span><span class="token keyword">go</span> <span class="token function">InitTask</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">,</span> resultchan<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token comment">//每个 task 启动一个 goroutine 处理,</span><span class="token keyword">go</span> <span class="token function">DistributeTask</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">,</span> wait<span class="token punctuation">,</span> resultchan<span class="token punctuation">)</span><span class="token comment">// 通过结果通道获取结果并汇总</span>sum <span class="token operator">:=</span> <span class="token function">ProcessResult</span><span class="token punctuation">(</span>resultchan<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"sum="</span><span class="token punctuation">,</span> sum<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 结果</span>sum<span class="token operator">=</span> <span class="token number">5050</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>程序的逻辑分析:<br>(1)InitTask 函数构建 task 并发送到 task 通道中;<br>(2)分发任务函数 DistributeTask 为每个 task 启动一个 goroutine 处理任务, 等待其处理完成, 然后关闭结果通道;<br>(3)ProcessResult 函数读取并统计所有的结果。这几个函数分别在不同的 goroutine 中运行, 它们通过通道和sync.WaitGroup 进行通信和同步;</p><h3 id="1-8-4-固定-worker-工作池"><a href="#1-8-4-固定-worker-工作池" class="headerlink" title="1.8.4 固定 worker 工作池"></a>1.8.4 固定 worker 工作池</h3><p>服务器编程中使用最多的就是通过线程池来提升服务的井发处理能力。在 Go 语言编程中,<br>一样可以轻松地构建固定数目的 goroutines 作为工作线程池。下面还是以计算多个整数的和为例来说明这种并发范式。程序中除了主要的 main goroutine ,还开启了如下几类 goroutine:<br>(1)初始化任务的 goroutme;<br>(2)分发任务的 goroutine;<br>(3)等待所有 worker 结束通知,然后关闭结果通道的 goroutine;<br>main 函数负责拉起上述 goroutine ,并从结果通道获取最终的结果;<br>程序采用三个通道,分别是:<br>(1)传递 task 任务的通道;<br>(2)传递 task 结果的通道;<br>(3)接收 worker 处理完任务后所发送通知的通道;<br>相关的代码如下:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token punctuation">)</span><span class="token comment">// 工作池的 goroutine 数目</span><span class="token keyword">const</span> <span class="token punctuation">(</span>NUMBER <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// 工作任务</span><span class="token keyword">type</span> task <span class="token keyword">struct</span> <span class="token punctuation">{</span>begin <span class="token builtin">int</span>end <span class="token builtin">int</span>result <span class="token keyword">chan</span><span class="token operator"><-</span> <span class="token builtin">int</span><span class="token punctuation">}</span><span class="token comment">// 任务处理:计算 begin 到 end 的和</span><span class="token comment">// 执行结果写入到结果 chan result 中</span><span class="token keyword">func</span> <span class="token punctuation">(</span>t <span class="token operator">*</span>task<span class="token punctuation">)</span> <span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>sum <span class="token operator">:=</span> <span class="token number">0</span><span class="token keyword">for</span> i <span class="token operator">:=</span> t<span class="token punctuation">.</span>begin<span class="token punctuation">;</span> i <span class="token operator"><=</span> t<span class="token punctuation">.</span>end<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>sum <span class="token operator">+=</span> i<span class="token punctuation">}</span>t<span class="token punctuation">.</span>result <span class="token operator"><-</span> sum<span class="token punctuation">}</span><span class="token comment">// 初始化待处理 task chan</span><span class="token keyword">func</span> <span class="token function">InitTask</span><span class="token punctuation">(</span>taskchan <span class="token keyword">chan</span><span class="token operator"><-</span> task<span class="token punctuation">,</span> r <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> p <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>qu <span class="token operator">:=</span> p <span class="token operator">/</span> <span class="token number">10</span>mod <span class="token operator">:=</span> p <span class="token operator">%</span> <span class="token number">10</span>high <span class="token operator">:=</span> qu <span class="token operator">*</span> <span class="token number">10</span><span class="token keyword">for</span> j <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator"><</span> qu<span class="token punctuation">;</span> j<span class="token operator">++</span> <span class="token punctuation">{</span>b <span class="token operator">:=</span> <span class="token number">10</span><span class="token operator">*</span>j <span class="token operator">+</span> <span class="token number">1</span>e <span class="token operator">:=</span> <span class="token number">10</span> <span class="token operator">*</span> <span class="token punctuation">(</span>j <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>tsk <span class="token operator">:=</span> task<span class="token punctuation">{</span>begin<span class="token punctuation">:</span> b<span class="token punctuation">,</span>end<span class="token punctuation">:</span> e<span class="token punctuation">,</span>result<span class="token punctuation">:</span> r<span class="token punctuation">,</span><span class="token punctuation">}</span>taskchan <span class="token operator"><-</span> tsk<span class="token punctuation">}</span><span class="token keyword">if</span> mod <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span>tsk <span class="token operator">:=</span> task<span class="token punctuation">{</span>begin<span class="token punctuation">:</span> high <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span>end<span class="token punctuation">:</span> p<span class="token punctuation">,</span>result<span class="token punctuation">:</span> r<span class="token punctuation">,</span><span class="token punctuation">}</span>taskchan <span class="token operator"><-</span> tsk<span class="token punctuation">}</span><span class="token function">close</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 读取 task chan 分发到 worker goroutine 处理,workers 的总的数量是 workers</span><span class="token keyword">func</span> <span class="token function">DistributeTask</span><span class="token punctuation">(</span>taskchan <span class="token operator"><-</span><span class="token keyword">chan</span> task<span class="token punctuation">,</span> workers <span class="token builtin">int</span><span class="token punctuation">,</span> done <span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> workers<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span><span class="token keyword">go</span> <span class="token function">ProcessTask</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">,</span> done<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 工作 goroutine 处理具体工作,并将处理结构发送到结果 chan</span><span class="token keyword">func</span> <span class="token function">ProcessTask</span><span class="token punctuation">(</span>taskchan <span class="token operator"><-</span><span class="token keyword">chan</span> task<span class="token punctuation">,</span> done <span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> t <span class="token operator">:=</span> <span class="token keyword">range</span> taskchan <span class="token punctuation">{</span>t<span class="token punctuation">.</span><span class="token function">do</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>done <span class="token operator"><-</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 通过 done channel 来同步等待所有工作 goroutine 的结束,然后关闭结果 chan</span><span class="token keyword">func</span> <span class="token function">CloseResult</span><span class="token punctuation">(</span>done <span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> resultchan <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> workers <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> workers<span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span><span class="token operator"><-</span>done<span class="token punctuation">}</span><span class="token function">close</span><span class="token punctuation">(</span>done<span class="token punctuation">)</span><span class="token function">close</span><span class="token punctuation">(</span>resultchan<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 读取结果通道,汇总结果</span><span class="token keyword">func</span> <span class="token function">ProcessResult</span><span class="token punctuation">(</span>resultchan <span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>sum <span class="token operator">:=</span> <span class="token number">0</span><span class="token keyword">for</span> r <span class="token operator">:=</span> <span class="token keyword">range</span> resultchan <span class="token punctuation">{</span>sum <span class="token operator">+=</span> r<span class="token punctuation">}</span><span class="token keyword">return</span> sum<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>workers <span class="token operator">:=</span> NUMBER<span class="token comment">// 工作通道</span>taskchan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> task<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// 结果通道</span>resultchan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// worker 信号通道</span>done <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token comment">// 初始化 task 的 goroutine,计算 1000 个自然数之和</span><span class="token keyword">go</span> <span class="token function">InitTask</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">,</span> resultchan<span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token comment">// 分发任务在 NUMBER 个 goroutine 池</span><span class="token function">DistributeTask</span><span class="token punctuation">(</span>taskchan<span class="token punctuation">,</span> workers<span class="token punctuation">,</span> done<span class="token punctuation">)</span><span class="token comment">// 获取各个 goroutine 处理完任务的通知,并关闭结果通道</span><span class="token keyword">go</span> <span class="token function">CloseResult</span><span class="token punctuation">(</span>done<span class="token punctuation">,</span> resultchan<span class="token punctuation">,</span> workers<span class="token punctuation">)</span><span class="token comment">// 通过结果通道处理结果</span>sum <span class="token operator">:=</span> <span class="token function">ProcessResult</span><span class="token punctuation">(</span>resultchan<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"sum="</span><span class="token punctuation">,</span> sum<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 结果</span>sum<span class="token operator">=</span> <span class="token number">5050</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>程序的逻辑分析:<br>(1)构建 task 并发送到 task 通道中;<br>(2)分别启动 n 个工作线程,不停地从 task 通道中获取任务,然后将结果写入结果通道。如果任务通道被关闭,则负责向收敛结果的 goroutine 发送通知,告诉其当前 worker 已经完成工作;<br>(3)收敛结果的 goroutine 接收到所有 task 己经处理完毕的信号后,主动关闭结果通道;<br>(4)main 中的函数 ProcessResult 读取并统计所有的结果;</p><h3 id="1-8-5-future-模式"><a href="#1-8-5-future-模式" class="headerlink" title="1.8.5 future 模式"></a>1.8.5 future 模式</h3><p>编程中经常遇到在一个流程中需要调用多个子调用的情况,这些子调用相互之间没有依赖,如果串行地调用,则耗时会很长,此时可以使用 Go 并发编程中的 future 模式。<br>future 模式的基本工作原理:<br>(1)使用 chan 作为函数参数;<br>(2)启动 goroutine 调用函数;<br>(3)通过 chan 传入参数;<br>(4)做其他可以并行处理的事情;<br>(5)通过 chan 异步获取结果;<br>下面通过一段抽象的代码来学习该模式:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// 一个查询结构体</span><span class="token comment">// 这里的 sql 和 result 是一个简单的抽象,具体的应用,可能是更复杂的数据类型</span><span class="token keyword">type</span> query <span class="token keyword">struct</span> <span class="token punctuation">{</span><span class="token comment">// 参数 Channel</span>sql <span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token comment">// 结果 Channel</span>result <span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">}</span><span class="token comment">// 执行 Query</span><span class="token keyword">func</span> <span class="token function">execQuery</span><span class="token punctuation">(</span>q query<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 启动协程</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 获取输入</span>sql <span class="token operator">:=</span> <span class="token operator"><-</span>q<span class="token punctuation">.</span>sql<span class="token comment">// 访问数据库</span><span class="token comment">// 输出结果通道</span>q<span class="token punctuation">.</span>result <span class="token operator"><-</span> <span class="token string">"result from "</span> <span class="token operator">+</span> sql<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 初始化 Query</span>q <span class="token operator">:=</span> query<span class="token punctuation">{</span><span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 执行 Query,注意执行的时候无需准备参数</span><span class="token keyword">go</span> <span class="token function">execQuery</span><span class="token punctuation">(</span>q<span class="token punctuation">)</span><span class="token comment">//准备参数</span>q<span class="token punctuation">.</span>sql <span class="token operator"><-</span> <span class="token string">"select * from table;"</span><span class="token comment">// do otherthings</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token comment">//获取结果</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token operator"><-</span>q<span class="token punctuation">.</span>result<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p>future 最大的好处是将函数的同步调用转换为异步调用, 适用于一个交易需要多个子调用且这些子调用没有依赖的场景。 实际情况可能比上面示例复杂得多,要考虑错误和异常的处理。</p>]]></content>
<summary type="html"><h1 id="1-并发"><a href="#1-并发" class="headerlink" title="1. 并发"></a>1. 并发</h1><h2 id="1-1-并发和并行的区别"><a href="#1-1-并发和并行的区别" class="headerli</summary>
<category term="Go进阶" scheme="http://coderedeng.github.io/categories/Go%E8%BF%9B%E9%98%B6/"/>
<category term="Go进阶" scheme="http://coderedeng.github.io/tags/Go%E8%BF%9B%E9%98%B6/"/>
</entry>
<entry>
<title>regexp2</title>
<link href="http://coderedeng.github.io/2022/06/23/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20regexp2/"/>
<id>http://coderedeng.github.io/2022/06/23/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20regexp2/</id>
<published>2022-06-23T14:02:41.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="21-regexp2"><a href="#21-regexp2" class="headerlink" title="21.regexp2"></a>21.regexp2</h1><h2 id="01-regexp2"><a href="#01-regexp2" class="headerlink" title="01.regexp2"></a>01.regexp2</h2><ul><li>Regexp2:<a href="https://blog.csdn.net/dianxin113/article/details/118769094">https://blog.csdn.net/dianxin113/article/details/118769094</a></li><li>GitHub:<a href="https://github.com/dlclark/regexp2">https://github.com/dlclark/regexp2</a></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/dlclark/regexp2"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">Regexp2GroupMatch</span><span class="token punctuation">(</span>m <span class="token operator">*</span>regexp2<span class="token punctuation">.</span>Match<span class="token punctuation">,</span> re <span class="token operator">*</span>regexp2<span class="token punctuation">.</span>Regexp<span class="token punctuation">)</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span> <span class="token punctuation">{</span><span class="token keyword">var</span> matches <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token keyword">for</span> m <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">var</span> ret <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span>gps <span class="token operator">:=</span> m<span class="token punctuation">.</span><span class="token function">Groups</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">for</span> index<span class="token punctuation">,</span> g <span class="token operator">:=</span> <span class="token keyword">range</span> gps <span class="token punctuation">{</span><span class="token keyword">if</span> index <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span><span class="token keyword">continue</span><span class="token punctuation">}</span>ret <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>ret<span class="token punctuation">,</span> g<span class="token punctuation">.</span>Captures<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span>matches <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>matches<span class="token punctuation">,</span> ret<span class="token punctuation">)</span>m<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> re<span class="token punctuation">.</span><span class="token function">FindNextMatch</span><span class="token punctuation">(</span>m<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">return</span> matches<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">CompileRegexp</span><span class="token punctuation">(</span>regex <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>regexp2<span class="token punctuation">.</span>Regexp<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>msgRegexp<span class="token punctuation">,</span> e <span class="token operator">:=</span> regexp2<span class="token punctuation">.</span><span class="token function">Compile</span><span class="token punctuation">(</span>regex<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">if</span> e <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">return</span> msgRegexp<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>str <span class="token operator">:=</span> <span class="token string">"2022-8-12 2023-8-11"</span>expr <span class="token operator">:=</span> <span class="token string">"(\\d{4})-(\\d{1,2})-(\\d{1,2})"</span> <span class="token comment">// [[2022 8 12]]</span>reg<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> <span class="token function">CompileRegexp</span><span class="token punctuation">(</span>expr<span class="token punctuation">)</span>m<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> reg<span class="token punctuation">.</span><span class="token function">FindStringMatch</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span>ret <span class="token operator">:=</span> <span class="token function">Regexp2GroupMatch</span><span class="token punctuation">(</span>m<span class="token punctuation">,</span> reg<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token comment">// [[2022 8 12] [2023 8 11]]</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="21-regexp2"><a href="#21-regexp2" class="headerlink" title="21.regexp2"></a>21.regexp2</h1><h2 id="01-regexp2"><a href="#01-regexp2"</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>machinery</title>
<link href="http://coderedeng.github.io/2022/06/22/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20machinery/"/>
<id>http://coderedeng.github.io/2022/06/22/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20machinery/</id>
<published>2022-06-22T14:52:21.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="20-machinery"><a href="#20-machinery" class="headerlink" title="20.machinery"></a>20.machinery</h1><h2 id="01-异步框架machinery"><a href="#01-异步框架machinery" class="headerlink" title="01.异步框架machinery"></a>01.异步框架machinery</h2><ul><li><a href="https://github.com/RichardKnop/machinery">github地址(opens new window)</a></li></ul><h3 id="1-1-machinery介绍"><a href="#1-1-machinery介绍" class="headerlink" title="1.1 machinery介绍"></a>1.1 machinery介绍</h3><ul><li>go machinery框架类似python中常用celery框架,主要用于 异步任务和定时任务,有一下特性<ul><li>任务重试机制</li><li>延迟任务支持</li><li>任务回调机制</li><li>任务结果记录</li><li>支持Workflow模式:Chain,Group,Chord</li><li>多Brokers支持:Redis, AMQP, <a href="https://link.segmentfault.com/?enc=DHe3inPV2HBq6uxnZ2CEOg==.XhC+OAXOeJgqueAZ3HrM4nnCWizYNVCgbJcSKS7+sPE=">AWS SQS(opens new window)</a></li><li>多Backends支持:Redis, Memcache, AMQP, <a href="https://link.segmentfault.com/?enc=s1hIwqlq+qzLK6uutFwrfg==.LOQSm2d600WNtAuHuxnSX++/1hm1iNUspqC1IcqZPCrXnAQ23jRJTDMUunMDFawu2mQXughrPBtdSeR7f1l45w==">MongoDB(opens new window)</a></li></ul></li></ul><h3 id="1-2-架构"><a href="#1-2-架构" class="headerlink" title="1.2 架构"></a>1.2 架构</h3><ul><li>任务队列,简而言之就是一个放大的生产者消费者模型</li><li>用户请求会生成任务,队列的处理器程序充当消费者不断的消费任务。</li><li>基于这种框架设计思想,我们来看下machinery的简单设计结构图例<ul><li>Sender:业务推送模块,生成具体任务,可根据业务逻辑中,按交互进行拆分;</li><li>Broker:存储具体序列化后的任务,machinery中目前支持到Redis, AMQP,和SQS;</li><li>Worker:工作进程,负责消费者功能,处理具体的任务;</li><li>Backend:后端存储,用于存储任务执行状态的数据;</li></ul></li></ul><p><img src="/img/image-20220206111846252.6cdeba77.png"></p><h2 id="02-machinery使用"><a href="#02-machinery使用" class="headerlink" title="02.machinery使用"></a>02.machinery使用</h2><h3 id="2-1-异步和定时任务"><a href="#2-1-异步和定时任务" class="headerlink" title="2.1 异步和定时任务"></a>2.1 异步和定时任务</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span>redisbackend <span class="token string">"github.com/RichardKnop/machinery/v2/backends/redis"</span>redisbroker <span class="token string">"github.com/RichardKnop/machinery/v2/brokers/redis"</span>eagerlock <span class="token string">"github.com/RichardKnop/machinery/v2/locks/eager"</span><span class="token string">"github.com/RichardKnop/machinery/v2"</span><span class="token string">"github.com/RichardKnop/machinery/v2/config"</span><span class="token string">"github.com/RichardKnop/machinery/v2/tasks"</span><span class="token string">"os"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">if</span> <span class="token function">len</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Args<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span> <span class="token operator">&&</span> os<span class="token punctuation">.</span>Args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"worker"</span> <span class="token punctuation">{</span> <span class="token comment">// 启动worker</span><span class="token keyword">if</span> err <span class="token operator">:=</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token function">TestPeriodicTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 触发一个定时任务(定时任务由客户端控制,客户端退出定时就会结束)</span><span class="token function">TestAdd</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 触发一个异步任务</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/* 触发执行Add异步任务 */</span><span class="token keyword">func</span> <span class="token function">TestAdd</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>server<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 调用异步任务 Add 函数,执行 1+4=5这个逻辑</span>signature <span class="token operator">:=</span> <span class="token operator">&</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">{</span>Name<span class="token punctuation">:</span> <span class="token string">"add"</span><span class="token punctuation">,</span>Args<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>tasks<span class="token punctuation">.</span>Arg<span class="token punctuation">{</span><span class="token punctuation">{</span>Type<span class="token punctuation">:</span> <span class="token string">"int64"</span><span class="token punctuation">,</span>Value<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span>Type<span class="token punctuation">:</span> <span class="token string">"int64"</span><span class="token punctuation">,</span>Value<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span>asyncResult<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">SendTask</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span> <span class="token comment">// 任务可以通过将Signature的实例传递给Server实例来调用</span>results<span class="token punctuation">,</span><span class="token boolean">_</span> <span class="token operator">:=</span> asyncResult<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond <span class="token operator">*</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 您还可以执行同步阻塞调用来等待任务结果</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> result <span class="token operator">:=</span> <span class="token keyword">range</span> results <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">Interface</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">/* 触发执行periodicTask异步任务 */</span><span class="token keyword">func</span> <span class="token function">TestPeriodicTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>server<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span>signature <span class="token operator">:=</span> <span class="token operator">&</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">{</span>Name<span class="token punctuation">:</span> <span class="token string">"periodicTask"</span><span class="token punctuation">,</span>Args<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>tasks<span class="token punctuation">.</span>Arg<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 每分钟执行一次periodicTask函数,验证发现不支持秒级别定时任务</span>err <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">RegisterPeriodicTask</span><span class="token punctuation">(</span><span class="token string">"*/1 * * * ?"</span><span class="token punctuation">,</span> <span class="token string">"periodic-task"</span><span class="token punctuation">,</span> signature<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span>asyncResult<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">SendTask</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>asyncResult<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第一:配置Server并注册任务</span><span class="token keyword">func</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>machinery<span class="token punctuation">.</span>Server<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>cnf <span class="token operator">:=</span> <span class="token operator">&</span>config<span class="token punctuation">.</span>Config<span class="token punctuation">{</span>DefaultQueue<span class="token punctuation">:</span> <span class="token string">"machinery_tasks"</span><span class="token punctuation">,</span>ResultsExpireIn<span class="token punctuation">:</span> <span class="token number">3600</span><span class="token punctuation">,</span>Redis<span class="token punctuation">:</span> <span class="token operator">&</span>config<span class="token punctuation">.</span>RedisConfig<span class="token punctuation">{</span>MaxIdle<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>IdleTimeout<span class="token punctuation">:</span> <span class="token number">240</span><span class="token punctuation">,</span>ReadTimeout<span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>WriteTimeout<span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>ConnectTimeout<span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>NormalTasksPollPeriod<span class="token punctuation">:</span> <span class="token number">1000</span><span class="token punctuation">,</span>DelayedTasksPollPeriod<span class="token punctuation">:</span> <span class="token number">500</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 创建服务器实例</span>broker <span class="token operator">:=</span> redisbroker<span class="token punctuation">.</span><span class="token function">NewGR</span><span class="token punctuation">(</span>cnf<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"localhost:6379"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>backend <span class="token operator">:=</span> redisbackend<span class="token punctuation">.</span><span class="token function">NewGR</span><span class="token punctuation">(</span>cnf<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"localhost:6379"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>lock <span class="token operator">:=</span> eagerlock<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>server <span class="token operator">:=</span> machinery<span class="token punctuation">.</span><span class="token function">NewServer</span><span class="token punctuation">(</span>cnf<span class="token punctuation">,</span> broker<span class="token punctuation">,</span> backend<span class="token punctuation">,</span> lock<span class="token punctuation">)</span><span class="token comment">// 注册异步任务</span>tasksMap <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"add"</span><span class="token punctuation">:</span> Add<span class="token punctuation">,</span><span class="token string">"periodicTask"</span><span class="token punctuation">:</span> PeriodicTask<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token keyword">return</span> server<span class="token punctuation">,</span> server<span class="token punctuation">.</span><span class="token function">RegisterTasks</span><span class="token punctuation">(</span>tasksMap<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二步:启动Worker</span><span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span><span class="token comment">//消费者的标记</span>consumerTag <span class="token operator">:=</span> <span class="token string">"machinery_worker"</span>server<span class="token punctuation">,</span> err <span class="token operator">:=</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> err<span class="token punctuation">}</span><span class="token comment">//第二个参数并发数, 0表示不限制</span>worker <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">NewWorker</span><span class="token punctuation">(</span>consumerTag<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token comment">//钩子函数</span>errorhandler <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>pretaskhandler <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>signature <span class="token operator">*</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>posttaskhandler <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>signature <span class="token operator">*</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>worker<span class="token punctuation">.</span><span class="token function">SetPostTaskHandler</span><span class="token punctuation">(</span>posttaskhandler<span class="token punctuation">)</span>worker<span class="token punctuation">.</span><span class="token function">SetErrorHandler</span><span class="token punctuation">(</span>errorhandler<span class="token punctuation">)</span>worker<span class="token punctuation">.</span><span class="token function">SetPreTaskHandler</span><span class="token punctuation">(</span>pretaskhandler<span class="token punctuation">)</span><span class="token keyword">return</span> worker<span class="token punctuation">.</span><span class="token function">Launch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第三步:添加异步执行函数</span><span class="token keyword">func</span> <span class="token function">Add</span><span class="token punctuation">(</span>args <span class="token operator">...</span><span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int64</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"############# 执行Add方法 #############"</span><span class="token punctuation">)</span>sum <span class="token operator">:=</span> <span class="token function">int64</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> arg <span class="token operator">:=</span> <span class="token keyword">range</span> args <span class="token punctuation">{</span>sum <span class="token operator">+=</span> arg<span class="token punctuation">}</span><span class="token keyword">return</span> sum<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token comment">// 第四步:添加一个周期性任务</span><span class="token keyword">func</span> <span class="token function">PeriodicTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"################ 执行周期任务PeriodicTask #################"</span><span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-启动服务并发送任务"><a href="#2-2-启动服务并发送任务" class="headerlink" title="2.2 启动服务并发送任务"></a>2.2 启动服务并发送任务</h3><blockquote><ul><li>go run main.go worker // 启动worker服务</li><li>go run main.go // 发送任务到worker</li></ul></blockquote><p><img src="/img/image-20220206111935400.8a7a4509.png"></p><h2 id="03-gin-machinery"><a href="#03-gin-machinery" class="headerlink" title="03.gin+machinery"></a>03.gin+machinery</h2><h3 id="3-0-项目结构"><a href="#3-0-项目结构" class="headerlink" title="3.0 项目结构"></a>3.0 项目结构</h3><blockquote><p>go run main.go // 直接执行即可测试</p></blockquote><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">xiaonaiqiang1@ZBMac<span class="token operator">-</span>C02CW08SM work <span class="token operator">%</span> tree ginWorker ginWorker├── main<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 项目入库</span>└── pkg └── task ├── server<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// machinery服务初始化</span> ├── start<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 启动异步任务入口</span> ├── cronJobs<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 触发周期性任务</span> ├── sendJobs<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 触发异任务</span> └── workers └── tasks<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 定义执行任务函数</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-1-main-go"><a href="#3-1-main-go" class="headerlink" title="3.1 main.go"></a>3.1 main.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"ginWorker/pkg/task"</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">go</span> task<span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 启动异步任务worker</span><span class="token keyword">go</span> task<span class="token punctuation">.</span><span class="token function">StartCron</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 启动定时任务</span>r <span class="token operator">:=</span> gin<span class="token punctuation">.</span><span class="token function">Default</span><span class="token punctuation">(</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/add"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>task<span class="token punctuation">.</span><span class="token function">TaskAdd</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 测试执行异步任务</span>c<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> <span class="token string">"hello word"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"http://127.0.0.1:8000"</span><span class="token punctuation">)</span><span class="token comment">//监听端口默认为8080</span>r<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token string">":8000"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-2-pkg-x2F-task-x2F-server-go"><a href="#3-2-pkg-x2F-task-x2F-server-go" class="headerlink" title="3.2 pkg/task/server.go"></a>3.2 pkg/task/server.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> task<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"ginWorker/pkg/task/workers"</span><span class="token string">"github.com/RichardKnop/machinery/v2"</span>redisbackend <span class="token string">"github.com/RichardKnop/machinery/v2/backends/redis"</span>redisbroker <span class="token string">"github.com/RichardKnop/machinery/v2/brokers/redis"</span><span class="token string">"github.com/RichardKnop/machinery/v2/config"</span>eagerlock <span class="token string">"github.com/RichardKnop/machinery/v2/locks/eager"</span><span class="token string">"github.com/RichardKnop/machinery/v2/tasks"</span><span class="token punctuation">)</span><span class="token keyword">var</span> AsyncTaskCenter <span class="token operator">*</span>machinery<span class="token punctuation">.</span>Server<span class="token comment">// 第一:配置Server并注册任务</span><span class="token keyword">func</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>machinery<span class="token punctuation">.</span>Server<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>cnf <span class="token operator">:=</span> <span class="token operator">&</span>config<span class="token punctuation">.</span>Config<span class="token punctuation">{</span>DefaultQueue<span class="token punctuation">:</span> <span class="token string">"machinery_tasks"</span><span class="token punctuation">,</span>ResultsExpireIn<span class="token punctuation">:</span> <span class="token number">3600</span><span class="token punctuation">,</span>Redis<span class="token punctuation">:</span> <span class="token operator">&</span>config<span class="token punctuation">.</span>RedisConfig<span class="token punctuation">{</span>MaxIdle<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>IdleTimeout<span class="token punctuation">:</span> <span class="token number">240</span><span class="token punctuation">,</span>ReadTimeout<span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>WriteTimeout<span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>ConnectTimeout<span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>NormalTasksPollPeriod<span class="token punctuation">:</span> <span class="token number">1000</span><span class="token punctuation">,</span>DelayedTasksPollPeriod<span class="token punctuation">:</span> <span class="token number">500</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 创建服务器实例</span>broker <span class="token operator">:=</span> redisbroker<span class="token punctuation">.</span><span class="token function">NewGR</span><span class="token punctuation">(</span>cnf<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"localhost:6379"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>backend <span class="token operator">:=</span> redisbackend<span class="token punctuation">.</span><span class="token function">NewGR</span><span class="token punctuation">(</span>cnf<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"localhost:6379"</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>lock <span class="token operator">:=</span> eagerlock<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>server <span class="token operator">:=</span> machinery<span class="token punctuation">.</span><span class="token function">NewServer</span><span class="token punctuation">(</span>cnf<span class="token punctuation">,</span> broker<span class="token punctuation">,</span> backend<span class="token punctuation">,</span> lock<span class="token punctuation">)</span>tasksMap <span class="token operator">:=</span> <span class="token function">initAsyncTaskMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span>AsyncTaskCenter <span class="token operator">=</span> server<span class="token keyword">return</span> server<span class="token punctuation">,</span> server<span class="token punctuation">.</span><span class="token function">RegisterTasks</span><span class="token punctuation">(</span>tasksMap<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二步:启动Worker</span><span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>consumerTag <span class="token operator">:=</span> <span class="token string">"machinery_worker"</span><span class="token comment">//消费者的标记</span>server<span class="token punctuation">,</span> err <span class="token operator">:=</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> err<span class="token punctuation">}</span>worker <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">NewWorker</span><span class="token punctuation">(</span>consumerTag<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token comment">//第二个参数并发数, 0表示不限制</span><span class="token comment">//钩子函数</span>errorhandler <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>pretaskhandler <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>signature <span class="token operator">*</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>posttaskhandler <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>signature <span class="token operator">*</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>worker<span class="token punctuation">.</span><span class="token function">SetPostTaskHandler</span><span class="token punctuation">(</span>posttaskhandler<span class="token punctuation">)</span>worker<span class="token punctuation">.</span><span class="token function">SetErrorHandler</span><span class="token punctuation">(</span>errorhandler<span class="token punctuation">)</span>worker<span class="token punctuation">.</span><span class="token function">SetPreTaskHandler</span><span class="token punctuation">(</span>pretaskhandler<span class="token punctuation">)</span><span class="token keyword">return</span> worker<span class="token punctuation">.</span><span class="token function">Launch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第三步:注册函数</span><span class="token keyword">func</span> <span class="token function">initAsyncTaskMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">{</span>tasksMap <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"add"</span><span class="token punctuation">:</span> workers<span class="token punctuation">.</span>Add<span class="token punctuation">,</span><span class="token string">"periodicTask"</span><span class="token punctuation">:</span> workers<span class="token punctuation">.</span>PeriodicTask<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token keyword">return</span> tasksMap<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-3-pkg-x2F-task-x2F-start-go"><a href="#3-3-pkg-x2F-task-x2F-start-go" class="headerlink" title="3.3 pkg/task/start.go"></a>3.3 pkg/task/start.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> task<span class="token keyword">func</span> <span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 启动worker</span><span class="token keyword">if</span> err <span class="token operator">:=</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 启动周期性任务</span><span class="token keyword">func</span> <span class="token function">StartCron</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">TestPeriodicTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-4-pkg-x2F-task-x2F-cronJobs-go"><a href="#3-4-pkg-x2F-task-x2F-cronJobs-go" class="headerlink" title="3.4 pkg/task/cronJobs.go"></a>3.4 pkg/task/cronJobs.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> task<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/RichardKnop/machinery/v2/tasks"</span><span class="token punctuation">)</span><span class="token comment">/* 触发执行periodicTask异步任务 */</span><span class="token keyword">func</span> <span class="token function">TestPeriodicTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>server<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> <span class="token function">startServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span>signature <span class="token operator">:=</span> <span class="token operator">&</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">{</span>Name<span class="token punctuation">:</span> <span class="token string">"periodicTask"</span><span class="token punctuation">,</span>Args<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>tasks<span class="token punctuation">.</span>Arg<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 每分钟执行一次periodicTask函数,验证发现不支持秒级别定时任务</span>err <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">RegisterPeriodicTask</span><span class="token punctuation">(</span><span class="token string">"*/1 * * * ?"</span><span class="token punctuation">,</span> <span class="token string">"periodic-task"</span><span class="token punctuation">,</span> signature<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span>asyncResult<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">SendTask</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>asyncResult<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-5-pkg-x2F-task-x2F-sendJobs-go"><a href="#3-5-pkg-x2F-task-x2F-sendJobs-go" class="headerlink" title="3.5 pkg/task/sendJobs.go"></a>3.5 pkg/task/sendJobs.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> task<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/RichardKnop/machinery/v2/tasks"</span><span class="token punctuation">)</span><span class="token comment">/* 触发执行Add异步任务 */</span><span class="token keyword">func</span> <span class="token function">TaskAdd</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span>b <span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>signature <span class="token operator">:=</span> <span class="token operator">&</span>tasks<span class="token punctuation">.</span>Signature<span class="token punctuation">{</span>Name<span class="token punctuation">:</span> <span class="token string">"add"</span><span class="token punctuation">,</span>Args<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>tasks<span class="token punctuation">.</span>Arg<span class="token punctuation">{</span><span class="token punctuation">{</span>Type<span class="token punctuation">:</span> <span class="token string">"int64"</span><span class="token punctuation">,</span>Value<span class="token punctuation">:</span> a<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span>Type<span class="token punctuation">:</span> <span class="token string">"int64"</span><span class="token punctuation">,</span>Value<span class="token punctuation">:</span> b<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> AsyncTaskCenter<span class="token punctuation">.</span><span class="token function">SendTask</span><span class="token punctuation">(</span>signature<span class="token punctuation">)</span> <span class="token comment">// 任务可以通过将Signature的实例传递给Server实例来调用</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-6-pkg-x2F-task-x2F-workers-x2F-tasks-go"><a href="#3-6-pkg-x2F-task-x2F-workers-x2F-tasks-go" class="headerlink" title="3.6 pkg/task/workers/tasks.go"></a>3.6 pkg/task/workers/tasks.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> workers<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// 添加异步执行函数</span><span class="token keyword">func</span> <span class="token function">Add</span><span class="token punctuation">(</span>args <span class="token operator">...</span><span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int64</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"############# 执行Add方法 #############"</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">10</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token comment">// 模拟执行耗时任务</span>sum <span class="token operator">:=</span> <span class="token function">int64</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> arg <span class="token operator">:=</span> <span class="token keyword">range</span> args <span class="token punctuation">{</span>sum <span class="token operator">+=</span> arg<span class="token punctuation">}</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"############# Add方法Done #############"</span><span class="token punctuation">)</span><span class="token keyword">return</span> sum<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token comment">// 添加一个周期性任务</span><span class="token keyword">func</span> <span class="token function">PeriodicTask</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"################ 执行周期任务PeriodicTask #################"</span><span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-7-运行结果"><a href="#3-7-运行结果" class="headerlink" title="3.7 运行结果"></a>3.7 运行结果</h3><ul><li><code>执行周期任务:每秒执行一次</code></li></ul><p><img src="/img/image-20220206112020324.6a282832.png"></p><ul><li><figure><div class="code-wrapper"><pre class="language-none"><code class="language-none">通过接口触发异步任务</code></pre></div></figure><ul><li><a href="http://127.0.0.1:8000/add">http://127.0.0.1:8000/add</a></li></ul></li></ul><p><img src="/img/image-20220206112110293.6b3d47fc.png"></p>]]></content>
<summary type="html"><h1 id="20-machinery"><a href="#20-machinery" class="headerlink" title="20.machinery"></a>20.machinery</h1><h2 id="01-异步框架machinery"><a href</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>cron</title>
<link href="http://coderedeng.github.io/2022/06/21/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20cron/"/>
<id>http://coderedeng.github.io/2022/06/21/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20cron/</id>
<published>2022-06-21T13:24:57.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="19-cron定时"><a href="#19-cron定时" class="headerlink" title="19.cron定时"></a>19.cron定时</h1><h2 id="01-cron基本使用"><a href="#01-cron基本使用" class="headerlink" title="01.cron基本使用"></a>01.cron基本使用</h2><h3 id="1-1-使用举例"><a href="#1-1-使用举例" class="headerlink" title="1.1 使用举例"></a>1.1 使用举例</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/robfig/cron"</span><span class="token punctuation">)</span><span class="token comment">//主函数</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>cron2 <span class="token operator">:=</span> cron<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//创建一个cron实例</span><span class="token comment">//执行定时任务(每5秒执行一次)</span>err<span class="token operator">:=</span> cron2<span class="token punctuation">.</span><span class="token function">AddFunc</span><span class="token punctuation">(</span><span class="token string">"*/5 * * * * *"</span><span class="token punctuation">,</span> print5<span class="token punctuation">)</span><span class="token keyword">if</span> err<span class="token operator">!=</span><span class="token boolean">nil</span><span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">//启动/关闭</span>cron2<span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">defer</span> cron2<span class="token punctuation">.</span><span class="token function">Stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{</span> <span class="token comment">//查询语句,保持程序运行,在这里等同于for{}</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">//执行函数</span><span class="token keyword">func</span> <span class="token function">print5</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"每5s执行一次cron"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-2-配置"><a href="#1-2-配置" class="headerlink" title="1.2 配置"></a>1.2 配置</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">┌─────────────second 范围 <span class="token punctuation">(</span><span class="token number">0</span> <span class="token operator">-</span> <span class="token number">60</span><span class="token punctuation">)</span>│ ┌───────────── min <span class="token punctuation">(</span><span class="token number">0</span> <span class="token operator">-</span> <span class="token number">59</span><span class="token punctuation">)</span>│ │ ┌────────────── hour <span class="token punctuation">(</span><span class="token number">0</span> <span class="token operator">-</span> <span class="token number">23</span><span class="token punctuation">)</span>│ │ │ ┌─────────────── day of month <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> <span class="token number">31</span><span class="token punctuation">)</span>│ │ │ │ ┌──────────────── month <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> <span class="token number">12</span><span class="token punctuation">)</span>│ │ │ │ │ ┌───────────────── day of week <span class="token punctuation">(</span><span class="token number">0</span> <span class="token operator">-</span> <span class="token number">6</span><span class="token punctuation">)</span>│ │ │ │ │ ││ │ │ │ │ │<span class="token operator">*</span> <span class="token operator">*</span> <span class="token operator">*</span> <span class="token operator">*</span> <span class="token operator">*</span> <span class="token operator">*</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-3-多个crontab任务"><a href="#1-3-多个crontab任务" class="headerlink" title="1.3 多个crontab任务"></a>1.3 多个crontab任务</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/robfig/cron"</span><span class="token punctuation">)</span><span class="token keyword">type</span> TestJob <span class="token keyword">struct</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>this TestJob<span class="token punctuation">)</span> <span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"testJob1..."</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">type</span> Test2Job <span class="token keyword">struct</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>this Test2Job<span class="token punctuation">)</span> <span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"testJob2..."</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">//启动多个任务</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>c <span class="token operator">:=</span> cron<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>spec <span class="token operator">:=</span> <span class="token string">"*/5 * * * * ?"</span><span class="token comment">//AddJob方法</span>c<span class="token punctuation">.</span><span class="token function">AddJob</span><span class="token punctuation">(</span>spec<span class="token punctuation">,</span> TestJob<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">AddJob</span><span class="token punctuation">(</span>spec<span class="token punctuation">,</span> Test2Job<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token comment">//启动计划任务</span>c<span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//关闭着计划任务, 但是不能关闭已经在执行中的任务.</span><span class="token keyword">defer</span> c<span class="token punctuation">.</span><span class="token function">Stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">/*testJob1...testJob2...testJob1...testJob2...*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="02-gin框架cron应用"><a href="#02-gin框架cron应用" class="headerlink" title="02.gin框架cron应用"></a>02.gin框架cron应用</h2><ul><li>目录结构</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token punctuation">.</span>├── main<span class="token punctuation">.</span><span class="token keyword">go</span>└── pkg └── jobs ├── job_cron<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 分布式任务配置</span> └── test_task<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token comment">// 具体任务实例</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-1-main-go"><a href="#2-1-main-go" class="headerlink" title="2.1 main.go"></a>2.1 main.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"go_cron_demo/pkg/jobs"</span><span class="token string">"net/http"</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>jobs<span class="token punctuation">.</span><span class="token function">InitJobs</span><span class="token punctuation">(</span><span class="token punctuation">)</span>r <span class="token operator">:=</span> gin<span class="token punctuation">.</span><span class="token function">Default</span><span class="token punctuation">(</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>c<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> <span class="token string">"hello World!"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token string">":8000"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-pkg-x2F-jobs-x2F-job-cron-go"><a href="#2-2-pkg-x2F-jobs-x2F-job-cron-go" class="headerlink" title="2.2 pkg/jobs/job_cron.go"></a>2.2 pkg/jobs/job_cron.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> jobs<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/robfig/cron"</span><span class="token punctuation">)</span><span class="token keyword">var</span> mainCron <span class="token operator">*</span>cron<span class="token punctuation">.</span>Cron<span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>mainCron <span class="token operator">=</span> cron<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>mainCron<span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">InitJobs</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 每5s钟调度一次,并传参</span>mainCron<span class="token punctuation">.</span><span class="token function">AddJob</span><span class="token punctuation">(</span><span class="token string">"*/5 * * * * ?"</span><span class="token punctuation">,</span>TestJob<span class="token punctuation">{</span>Id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> Name<span class="token punctuation">:</span> <span class="token string">"zhangsan"</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/* 运行结果1 zhangsantestJob1...1 zhangsantestJob1...*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-pkg-x2F-jobs-x2F-test-task-go"><a href="#2-3-pkg-x2F-jobs-x2F-test-task-go" class="headerlink" title="2.3 pkg/jobs/test_task.go"></a>2.3 pkg/jobs/test_task.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> jobs<span class="token keyword">import</span> <span class="token string">"fmt"</span><span class="token keyword">type</span> TestJob <span class="token keyword">struct</span> <span class="token punctuation">{</span>Id <span class="token builtin">int</span>Name <span class="token builtin">string</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>this TestJob<span class="token punctuation">)</span> <span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>this<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> this<span class="token punctuation">.</span>Name<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"testJob1..."</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="19-cron定时"><a href="#19-cron定时" class="headerlink" title="19.cron定时"></a>19.cron定时</h1><h2 id="01-cron基本使用"><a href="#01-cron基本使用" c</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>logrus</title>
<link href="http://coderedeng.github.io/2022/06/20/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20logrus/"/>
<id>http://coderedeng.github.io/2022/06/20/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20logrus/</id>
<published>2022-06-20T13:54:53.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="18-logrus"><a href="#18-logrus" class="headerlink" title="18.logrus"></a>18.logrus</h1><h2 id="01-logrus基础"><a href="#01-logrus基础" class="headerlink" title="01.logrus基础"></a>01.logrus基础</h2><ul><li><a href="https://github.com/sirupsen/logrus">参考GitHub(opens new window)</a></li><li><a href="https://blog.51cto.com/u_15183360/2737283">参考博客1(opens new window)</a></li><li><a href="https://www.liwenzhou.com/posts/Go/go_logrus/">参考博客2(opens new window)</a></li><li>安装</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go get github.com/sirupsen/logrus<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="1-1-简介"><a href="#1-1-简介" class="headerlink" title="1.1 简介"></a>1.1 简介</h3><blockquote><p>Logrus是Go(golang)的结构化logger,与标准库logger完全API兼容,它有以下特点</p></blockquote><ul><li>完全兼容标准日志库,拥有七种日志级别:<code>Trace</code>, <code>Debug</code>, <code>Info</code>, <code>Warning</code>, <code>Error</code>, <code>Fatal</code>and <code>Panic</code>。</li><li>可扩展的Hook机制,允许使用者通过Hook的方式将日志分发到任意地方<ul><li>如本地文件系统,logstash,elasticsearch或者mq等,或者通过Hook定义日志内容和格式等</li></ul></li><li>可选的日志输出格式,内置了两种日志格式JSONFormater和TextFormatter,还可以自定义日志格式</li><li>Field机制,通过Filed机制进行结构化的日志记录</li><li>线程安全</li></ul><h3 id="1-2-简单导报使用"><a href="#1-2-简单导报使用" class="headerlink" title="1.2 简单导报使用"></a>1.2 简单导报使用</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span>log <span class="token string">"github.com/sirupsen/logrus"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>log<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"animal"</span><span class="token punctuation">:</span> <span class="token string">"dog"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"测试info日志"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// INFO[0000] 测试info日志 animal=dog</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-3-日志级别"><a href="#1-3-日志级别" class="headerlink" title="1.3 日志级别"></a>1.3 日志级别</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/sirupsen/logrus"</span><span class="token punctuation">)</span><span class="token comment">// 创建一个新的logger实例。可以创建任意多个。</span><span class="token keyword">var</span> log <span class="token operator">=</span> logrus<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Trace</span><span class="token punctuation">(</span><span class="token string">"Something very low level."</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Debug</span><span class="token punctuation">(</span><span class="token string">"Useful debugging information."</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"Something noteworthy happened!"</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Warn</span><span class="token punctuation">(</span><span class="token string">"You should probably take a look at this."</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token string">"Something failed but I'm not quitting."</span><span class="token punctuation">)</span><span class="token comment">// 记完日志后会调用os.Exit(1)</span>log<span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span><span class="token string">"Bye."</span><span class="token punctuation">)</span><span class="token comment">// 记完日志后会调用 panic()</span>log<span class="token punctuation">.</span><span class="token function">Panic</span><span class="token punctuation">(</span><span class="token string">"I'm bailing."</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*INFO[0000] Something noteworthy happened!WARN[0000] You should probably take a look at this.ERRO[0000] Something failed but I'm not quitting.FATA[0000] Bye.*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-4-设置日志级别"><a href="#1-4-设置日志级别" class="headerlink" title="1.4 设置日志级别"></a>1.4 设置日志级别</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// 会记录info及以上级别 (warn, error, fatal, panic)</span>log<span class="token punctuation">.</span><span class="token function">SetLevel</span><span class="token punctuation">(</span>log<span class="token punctuation">.</span>InfoLevel<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><h3 id="1-5-字段"><a href="#1-5-字段" class="headerlink" title="1.5 字段"></a>1.5 字段</h3><ul><li>Logrus鼓励通过日志字段进行谨慎的结构化日志记录,而不是冗长的、不可解析的错误消息。</li><li>例如,区别于使用<code>log.Fatalf("Failed to send event %s to topic %s with key %d")</code></li><li>你应该使用如下方式记录更容易发现的内容</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span>log <span class="token string">"github.com/sirupsen/logrus"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>log<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"event"</span><span class="token punctuation">:</span> <span class="token string">"event"</span><span class="token punctuation">,</span><span class="token string">"topic"</span><span class="token punctuation">:</span> <span class="token string">"topic"</span><span class="token punctuation">,</span><span class="token string">"key"</span><span class="token punctuation">:</span> <span class="token string">"key"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span><span class="token string">"Failed to send event"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// FATA[0000] Failed to send event event=event key=key topic=topic</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-6-默认字段"><a href="#1-6-默认字段" class="headerlink" title="1.6 默认字段"></a>1.6 默认字段</h3><ul><li>通常,将一些字段始终附加到应用程序的全部或部分的日志语句中会很有帮助。</li><li>例如,你可能希望始终在请求的上下文中记录<code>request_id</code>和<code>user_ip</code>。</li><li>区别于在每一行日志中写上<code>log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})</code></li><li>你可以向下面的示例代码一样创建一个<code>logrus.Entry</code>去传递这些字段。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> log <span class="token string">"github.com/sirupsen/logrus"</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>requestLogger <span class="token operator">:=</span> log<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>log<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"request_id"</span><span class="token punctuation">:</span> <span class="token string">"request_id"</span><span class="token punctuation">,</span> <span class="token string">"user_ip"</span><span class="token punctuation">:</span> <span class="token string">"user_ip"</span><span class="token punctuation">}</span><span class="token punctuation">)</span>requestLogger<span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"something happened on that request"</span><span class="token punctuation">)</span> <span class="token comment">// will log request_id and user_ip</span>requestLogger<span class="token punctuation">.</span><span class="token function">Warn</span><span class="token punctuation">(</span><span class="token string">"something not great happened"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*INFO[0000] something happened on that request request_id=request_id user_ip=user_ipWARN[0000] something not great happened request_id=request_id user_ip=user_ip */</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-7-Hooks"><a href="#1-7-Hooks" class="headerlink" title="1.7 Hooks"></a>1.7 Hooks</h3><ul><li>你可以添加日志级别的钩子(Hook)。</li><li>例如,向异常跟踪服务发送<code>Error</code>、<code>Fatal</code>和<code>Panic</code>、信息到StatsD或同时将日志发送到多个位置,例如syslog。</li><li>Logrus配有内置钩子,在<code>init</code>中添加这些内置钩子或你自定义的钩子</li><li><a href="https://github.com/sirupsen/logrus/blob/master/hooks/syslog/README.md">GitHub参考(opens new window)</a></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span>log <span class="token string">"github.com/sirupsen/logrus"</span><span class="token string">"gopkg.in/gemnasium/logrus-airbrake-hook.v2"</span> <span class="token comment">// the package is named "airbrake"</span>logrus_syslog <span class="token string">"github.com/sirupsen/logrus/hooks/syslog"</span><span class="token string">"log/syslog"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// Use the Airbrake hook to report errors that have Error severity or above to</span><span class="token comment">// an exception tracker. You can create custom hooks, see the Hooks section.</span>log<span class="token punctuation">.</span><span class="token function">AddHook</span><span class="token punctuation">(</span>airbrake<span class="token punctuation">.</span><span class="token function">NewHook</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">,</span> <span class="token string">"xyz"</span><span class="token punctuation">,</span> <span class="token string">"production"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>hook<span class="token punctuation">,</span> err <span class="token operator">:=</span> logrus_syslog<span class="token punctuation">.</span><span class="token function">NewSyslogHook</span><span class="token punctuation">(</span><span class="token string">"udp"</span><span class="token punctuation">,</span> <span class="token string">"localhost:514"</span><span class="token punctuation">,</span> syslog<span class="token punctuation">.</span>LOG_INFO<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token string">"Unable to connect to local syslog daemon"</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">AddHook</span><span class="token punctuation">(</span>hook<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-8-格式化"><a href="#1-8-格式化" class="headerlink" title="1.8 格式化"></a>1.8 格式化</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/sirupsen/logrus"</span><span class="token punctuation">)</span><span class="token keyword">var</span> log <span class="token operator">=</span> logrus<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span>Formatter <span class="token operator">=</span> <span class="token operator">&</span>logrus<span class="token punctuation">.</span>JSONFormatter<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">//log.SetReportCaller(true) // 可以开启记录函数名,但是会消耗性能</span>log<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>logrus<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"event"</span><span class="token punctuation">:</span> <span class="token string">"event"</span><span class="token punctuation">,</span><span class="token string">"topic"</span><span class="token punctuation">:</span> <span class="token string">"topic"</span><span class="token punctuation">,</span><span class="token string">"key"</span><span class="token punctuation">:</span> <span class="token string">"key"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"Failed to send event"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*{ "event":"event", "key":"key", "level":"info", "msg":"Failed to send event", "time":"2021-12-23T12:21:55+08:00", "topic":"topic"} */</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-9-gin中使用logrus"><a href="#1-9-gin中使用logrus" class="headerlink" title="1.9 gin中使用logrus"></a>1.9 gin中使用logrus</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/sirupsen/logrus"</span><span class="token string">"os"</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token punctuation">)</span><span class="token keyword">var</span> log <span class="token operator">=</span> logrus<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// Log as JSON instead of the default ASCII formatter.</span>log<span class="token punctuation">.</span>Formatter <span class="token operator">=</span> <span class="token operator">&</span>logrus<span class="token punctuation">.</span>JSONFormatter<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token comment">// Output to stdout instead of the default stderr</span><span class="token comment">// Can be any io.Writer, see below for File example</span>f<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span><span class="token string">"./gin.log"</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>Out <span class="token operator">=</span> fgin<span class="token punctuation">.</span><span class="token function">SetMode</span><span class="token punctuation">(</span>gin<span class="token punctuation">.</span>ReleaseMode<span class="token punctuation">)</span>gin<span class="token punctuation">.</span>DefaultWriter <span class="token operator">=</span> log<span class="token punctuation">.</span>Out<span class="token comment">// Only log the warning severity or above.</span>log<span class="token punctuation">.</span>Level <span class="token operator">=</span> logrus<span class="token punctuation">.</span>InfoLevel<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 创建一个默认的路由引擎</span>r <span class="token operator">:=</span> gin<span class="token punctuation">.</span><span class="token function">Default</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// GET:请求方式;/hello:请求的路径</span><span class="token comment">// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数</span>r<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/hello"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>logrus<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"animal"</span><span class="token punctuation">:</span> <span class="token string">"walrus"</span><span class="token punctuation">,</span><span class="token string">"size"</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Warn</span><span class="token punctuation">(</span><span class="token string">"A group of walrus emerges from the ocean"</span><span class="token punctuation">)</span><span class="token comment">// c.JSON:返回JSON格式的数据</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"message"</span><span class="token punctuation">:</span> <span class="token string">"Hello world!"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token comment">// 启动HTTP服务,默认在0.0.0.0:8080启动服务</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">`http://127.0.0.1:8080/hello`</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token string">":8080"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>记录日志</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-text" data-language="text"><code class="language-text">{"animal":"walrus","level":"warning","msg":"A group of walrus emerges from the ocean","size":10,"time":"2021-12-23T12:37:21+08:00"}[GIN] 2021/12/23 - 12:37:21 | 200 | 705.823µs | 127.0.0.1 | GET "/hello"<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><h2 id="02-在gin中封装使用"><a href="#02-在gin中封装使用" class="headerlink" title="02.在gin中封装使用"></a>02.在gin中封装使用</h2><h3 id="2-0-目录结构"><a href="#2-0-目录结构" class="headerlink" title="2.0 目录结构"></a>2.0 目录结构</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">logrus<span class="token operator">-</span>demo├── main<span class="token punctuation">.</span><span class="token keyword">go</span>└── middleware └── logger<span class="token punctuation">.</span><span class="token keyword">go</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-1-main-go"><a href="#2-1-main-go" class="headerlink" title="2.1 main.go"></a>2.1 main.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"cobra-demo/middleware"</span><span class="token string">"fmt"</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token string">"github.com/sirupsen/logrus"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">helloWorld</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 测试写入日志</span>middleware<span class="token punctuation">.</span>Logger<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>logrus<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"data"</span> <span class="token punctuation">:</span> <span class="token string">"访问/hello"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"测试写入info"</span><span class="token punctuation">)</span><span class="token comment">// c.JSON:返回JSON格式的数据</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"message"</span><span class="token punctuation">:</span> <span class="token string">"Hello world!"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>r <span class="token operator">:=</span> gin<span class="token punctuation">.</span><span class="token function">Default</span><span class="token punctuation">(</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">Use</span><span class="token punctuation">(</span>middleware<span class="token punctuation">.</span><span class="token function">LoggerMiddleware</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/hello"</span><span class="token punctuation">,</span> helloWorld<span class="token punctuation">)</span><span class="token comment">// 启动HTTP服务,默认在0.0.0.0:8080启动服务</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">`http://127.0.0.1:8080/hello`</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token string">":8080"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-middleware-x2F-logger-ge"><a href="#2-2-middleware-x2F-logger-ge" class="headerlink" title="2.2 middleware/logger.ge"></a>2.2 middleware/logger.ge</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> middleware<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/gin-gonic/gin"</span>rotatelogs <span class="token string">"github.com/lestrrat-go/file-rotatelogs"</span><span class="token string">"github.com/rifflock/lfshook"</span><span class="token string">"github.com/sirupsen/logrus"</span><span class="token string">"os"</span><span class="token string">"path"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token keyword">var</span> <span class="token punctuation">(</span>logFilePath <span class="token operator">=</span> <span class="token string">"./"</span>logFileName <span class="token operator">=</span> <span class="token string">"system.log"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">LoggerMiddleware</span><span class="token punctuation">(</span><span class="token punctuation">)</span> gin<span class="token punctuation">.</span>HandlerFunc <span class="token punctuation">{</span><span class="token comment">// 日志文件</span>fileName <span class="token operator">:=</span> path<span class="token punctuation">.</span><span class="token function">Join</span><span class="token punctuation">(</span>logFilePath<span class="token punctuation">,</span> logFileName<span class="token punctuation">)</span><span class="token comment">// 写入文件</span>src<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span>fileName<span class="token punctuation">,</span> os<span class="token punctuation">.</span>O_APPEND<span class="token operator">|</span>os<span class="token punctuation">.</span>O_WRONLY<span class="token punctuation">,</span> os<span class="token punctuation">.</span>ModeAppend<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"err"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 实例化</span>logger <span class="token operator">:=</span> logrus<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//设置日志级别</span>logger<span class="token punctuation">.</span><span class="token function">SetLevel</span><span class="token punctuation">(</span>logrus<span class="token punctuation">.</span>DebugLevel<span class="token punctuation">)</span><span class="token comment">//设置输出</span>logger<span class="token punctuation">.</span>Out <span class="token operator">=</span> src<span class="token comment">// 设置 rotatelogs</span>logWriter<span class="token punctuation">,</span> err <span class="token operator">:=</span> rotatelogs<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token comment">// 分割后的文件名称</span>fileName<span class="token operator">+</span><span class="token string">".%Y%m%d.log"</span><span class="token punctuation">,</span><span class="token comment">// 生成软链,指向最新日志文件</span>rotatelogs<span class="token punctuation">.</span><span class="token function">WithLinkName</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token comment">// 设置最大保存时间(7天)</span>rotatelogs<span class="token punctuation">.</span><span class="token function">WithMaxAge</span><span class="token punctuation">(</span><span class="token number">7</span><span class="token operator">*</span><span class="token number">24</span><span class="token operator">*</span>time<span class="token punctuation">.</span>Hour<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token comment">// 设置日志切割时间间隔(1天)</span>rotatelogs<span class="token punctuation">.</span><span class="token function">WithRotationTime</span><span class="token punctuation">(</span><span class="token number">24</span><span class="token operator">*</span>time<span class="token punctuation">.</span>Hour<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">)</span>writeMap <span class="token operator">:=</span> lfshook<span class="token punctuation">.</span>WriterMap<span class="token punctuation">{</span>logrus<span class="token punctuation">.</span>InfoLevel<span class="token punctuation">:</span> logWriter<span class="token punctuation">,</span>logrus<span class="token punctuation">.</span>FatalLevel<span class="token punctuation">:</span> logWriter<span class="token punctuation">,</span>logrus<span class="token punctuation">.</span>DebugLevel<span class="token punctuation">:</span> logWriter<span class="token punctuation">,</span>logrus<span class="token punctuation">.</span>WarnLevel<span class="token punctuation">:</span> logWriter<span class="token punctuation">,</span>logrus<span class="token punctuation">.</span>ErrorLevel<span class="token punctuation">:</span> logWriter<span class="token punctuation">,</span>logrus<span class="token punctuation">.</span>PanicLevel<span class="token punctuation">:</span> logWriter<span class="token punctuation">,</span><span class="token punctuation">}</span>logger<span class="token punctuation">.</span><span class="token function">AddHook</span><span class="token punctuation">(</span>lfshook<span class="token punctuation">.</span><span class="token function">NewHook</span><span class="token punctuation">(</span>writeMap<span class="token punctuation">,</span> <span class="token operator">&</span>logrus<span class="token punctuation">.</span>JSONFormatter<span class="token punctuation">{</span>TimestampFormat<span class="token punctuation">:</span> <span class="token string">"2006-01-02 15:04:05"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">//开始时间</span>startTime <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//处理请求</span>c<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//结束时间</span>endTime <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 执行时间</span>latencyTime <span class="token operator">:=</span> endTime<span class="token punctuation">.</span><span class="token function">Sub</span><span class="token punctuation">(</span>startTime<span class="token punctuation">)</span><span class="token comment">//请求方式</span>reqMethod <span class="token operator">:=</span> c<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Method<span class="token comment">//请求路由</span>reqUrl <span class="token operator">:=</span> c<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>RequestURI<span class="token comment">//状态码</span>statusCode <span class="token operator">:=</span> c<span class="token punctuation">.</span>Writer<span class="token punctuation">.</span><span class="token function">Status</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//请求ip</span>clientIP <span class="token operator">:=</span> c<span class="token punctuation">.</span><span class="token function">ClientIP</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 日志格式</span>logger<span class="token punctuation">.</span><span class="token function">WithFields</span><span class="token punctuation">(</span>logrus<span class="token punctuation">.</span>Fields<span class="token punctuation">{</span><span class="token string">"status_code"</span><span class="token punctuation">:</span> statusCode<span class="token punctuation">,</span><span class="token string">"latency_time"</span><span class="token punctuation">:</span> latencyTime<span class="token punctuation">,</span><span class="token string">"client_ip"</span><span class="token punctuation">:</span> clientIP<span class="token punctuation">,</span><span class="token string">"req_method"</span><span class="token punctuation">:</span> reqMethod<span class="token punctuation">,</span><span class="token string">"req_uri"</span><span class="token punctuation">:</span> reqUrl<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-logging-x2F-logger-go"><a href="#2-3-logging-x2F-logger-go" class="headerlink" title="2.3 logging/logger.go"></a>2.3 logging/logger.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> logging<span class="token keyword">import</span> <span class="token punctuation">(</span>setting <span class="token string">"bamboo.com/pipeline/Go-assault-squad/config"</span><span class="token string">"fmt"</span><span class="token string">"github.com/sirupsen/logrus"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token keyword">var</span> WebLog <span class="token operator">*</span>logrus<span class="token punctuation">.</span>Logger<span class="token keyword">func</span> <span class="token function">Init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">initWebLog</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">initWebLog</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>WebLog <span class="token operator">=</span> <span class="token function">initLog</span><span class="token punctuation">(</span>setting<span class="token punctuation">.</span>Conf<span class="token punctuation">.</span>LogConfig<span class="token punctuation">.</span>WebLogName<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 初始化日志句柄</span><span class="token keyword">func</span> <span class="token function">initLog</span><span class="token punctuation">(</span>logFileName <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">*</span>logrus<span class="token punctuation">.</span>Logger<span class="token punctuation">{</span>log <span class="token operator">:=</span> logrus<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span>Formatter <span class="token operator">=</span> <span class="token operator">&</span>logrus<span class="token punctuation">.</span>JSONFormatter<span class="token punctuation">{</span>TimestampFormat<span class="token punctuation">:</span> <span class="token string">"2006-01-02 15:04:05"</span><span class="token punctuation">,</span><span class="token punctuation">}</span>logFilePath <span class="token operator">:=</span> setting<span class="token punctuation">.</span>Conf<span class="token punctuation">.</span>LogFilePathlogName <span class="token operator">:=</span> logFilePath <span class="token operator">+</span> logFileName<span class="token keyword">var</span> f <span class="token operator">*</span>os<span class="token punctuation">.</span>File<span class="token keyword">var</span> err <span class="token builtin">error</span><span class="token comment">//判断日志文件夹是否存在,不存在则创建</span><span class="token keyword">if</span> <span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Stat</span><span class="token punctuation">(</span>logFilePath<span class="token punctuation">)</span><span class="token punctuation">;</span> os<span class="token punctuation">.</span><span class="token function">IsNotExist</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>os<span class="token punctuation">.</span><span class="token function">MkdirAll</span><span class="token punctuation">(</span>logFilePath<span class="token punctuation">,</span> os<span class="token punctuation">.</span>ModePerm<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">//判断日志文件是否存在,不存在则创建,否则就直接打开</span><span class="token keyword">if</span> <span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Stat</span><span class="token punctuation">(</span>logName<span class="token punctuation">)</span><span class="token punctuation">;</span> os<span class="token punctuation">.</span><span class="token function">IsNotExist</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>f<span class="token punctuation">,</span> err <span class="token operator">=</span> os<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span>logName<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>f<span class="token punctuation">,</span> err <span class="token operator">=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span>logName<span class="token punctuation">,</span>os<span class="token punctuation">.</span>O_APPEND<span class="token operator">|</span>os<span class="token punctuation">.</span>O_WRONLY<span class="token punctuation">,</span> os<span class="token punctuation">.</span>ModeAppend<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"open log file failed"</span><span class="token punctuation">)</span><span class="token punctuation">}</span>log<span class="token punctuation">.</span>Out <span class="token operator">=</span> flog<span class="token punctuation">.</span>Level <span class="token operator">=</span> logrus<span class="token punctuation">.</span>InfoLevel<span class="token keyword">return</span> log<span class="token punctuation">}</span><span class="token comment">/*---- 日志写入测试 ----WebLog.WithFields(logrus.Fields{"data" : "访问/hello",}).Info("测试写入info")---- 写入结构如下 ----{"data":"访问/hello","level":"info","msg":"测试写入info","time":"2021-12-29 18:15:54"} */</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-访问测试"><a href="#2-4-访问测试" class="headerlink" title="2.4 访问测试"></a>2.4 访问测试</h3><blockquote><p>go run main.go</p></blockquote><ul><li><a href="http://127.0.0.1:8080/hello">http://127.0.0.1:8080/hello</a></li><li>写入日志格式</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span><span class="token property">"data"</span><span class="token operator">:</span><span class="token string">"访问/hello"</span><span class="token punctuation">,</span><span class="token property">"level"</span><span class="token operator">:</span><span class="token string">"info"</span><span class="token punctuation">,</span><span class="token property">"msg"</span><span class="token operator">:</span><span class="token string">"测试写入info"</span><span class="token punctuation">,</span><span class="token property">"time"</span><span class="token operator">:</span><span class="token string">"2021-12-23 15:27:37"</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token property">"client_ip"</span><span class="token operator">:</span><span class="token string">"127.0.0.1"</span><span class="token punctuation">,</span><span class="token property">"latency_time"</span><span class="token operator">:</span><span class="token number">418116</span><span class="token punctuation">,</span><span class="token property">"level"</span><span class="token operator">:</span><span class="token string">"info"</span><span class="token punctuation">,</span><span class="token property">"msg"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"req_method"</span><span class="token operator">:</span><span class="token string">"GET"</span><span class="token punctuation">,</span><span class="token property">"req_uri"</span><span class="token operator">:</span><span class="token string">"/hello"</span><span class="token punctuation">,</span><span class="token property">"status_code"</span><span class="token operator">:</span><span class="token number">200</span><span class="token punctuation">,</span><span class="token property">"time"</span><span class="token operator">:</span><span class="token string">"2021-12-23 15:27:37"</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="18-logrus"><a href="#18-logrus" class="headerlink" title="18.logrus"></a>18.logrus</h1><h2 id="01-logrus基础"><a href="#01-logrus基础" c</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>Cobor</title>
<link href="http://coderedeng.github.io/2022/06/19/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20cobor/"/>
<id>http://coderedeng.github.io/2022/06/19/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20cobor/</id>
<published>2022-06-19T14:26:53.000Z</published>
<updated>2024-03-27T12:42:47.444Z</updated>
<content type="html"><![CDATA[<h1 id="17-cobor"><a href="#17-cobor" class="headerlink" title="17.cobor"></a>17.cobor</h1><h2 id="01-cobra使用"><a href="#01-cobra使用" class="headerlink" title="01.cobra使用"></a>01.cobra使用</h2><ul><li>GitHub地址: <a href="https://github.com/spf13/cobra/blob/master/user_guide.md">https://github.com/spf13/cobra/blob/master/user_guide.md</a></li><li>参考博客:<a href="https://www.qikqiak.com/post/create-cli-app-with-cobra/">https://www.qikqiak.com/post/create-cli-app-with-cobra/</a></li><li>安装</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go get <span class="token parameter variable">-u</span> github.com/spf13/cobra <span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="1-1-基本使用"><a href="#1-1-基本使用" class="headerlink" title="1.1 基本使用"></a>1.1 基本使用</h3><ul><li>初始项目</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">mkdir</span> cobra-demo <span class="token operator">&&</span> <span class="token builtin class-name">cd</span> cobra-demo$ go mod init cobra-demo<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><ul><li>2)下载cobra</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 强烈推荐配置该环境变量</span>$ <span class="token builtin class-name">export</span> <span class="token assign-left variable">GOPROXY</span><span class="token operator">=</span>https://goproxy.cn$ go get <span class="token parameter variable">-u</span> github.com/spf13/cobra/cobra<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>3)<code>cobra init</code> 命令来初始化 CLI 应用的脚手架</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ cobra init<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="1-2-初始化结构说明"><a href="#1-2-初始化结构说明" class="headerlink" title="1.2 初始化结构说明"></a>1.2 初始化结构说明</h3><ul><li>目录结构</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-text" data-language="text"><code class="language-text">├── cmd│ └── root.go└── main.go<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>main.go</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token string">"cobra-demo/cmd"</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>cmd<span class="token punctuation">.</span><span class="token function">Execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>cmd/root.go</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> cmd<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"os"</span><span class="token string">"github.com/spf13/cobra"</span><span class="token punctuation">)</span><span class="token keyword">var</span> rootCmd <span class="token operator">=</span> <span class="token operator">&</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">{</span>Use<span class="token punctuation">:</span> <span class="token string">"cobra-demo"</span><span class="token punctuation">,</span>Short<span class="token punctuation">:</span> <span class="token string">"A brief description of your application"</span><span class="token punctuation">,</span>Long<span class="token punctuation">:</span> Run<span class="token punctuation">:</span> <span class="token keyword">func</span><span class="token punctuation">(</span>cmd <span class="token operator">*</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">,</span> args <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Hello Cobra CLI"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 然后再执行 execute 方法</span><span class="token keyword">func</span> <span class="token function">Execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>err <span class="token operator">:=</span> rootCmd<span class="token punctuation">.</span><span class="token function">Execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>os<span class="token punctuation">.</span><span class="token function">Exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 每当执行或者调用命令的时候,它都会先执行 init 函数中的所有函数</span><span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>rootCmd<span class="token punctuation">.</span><span class="token function">Flags</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">BoolP</span><span class="token punctuation">(</span><span class="token string">"toggle"</span><span class="token punctuation">,</span> <span class="token string">"t"</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token string">"Help message for toggle"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li><code>rootCmd</code> 根命令就会首先运行 <code>initConfig</code> 函数,当所有的初始化函数执行完成后,才会执行 <code>rootCmd</code> 的 <code>RUN: func</code> 执行函数</li><li>我们可以在 <code>initConfig</code> 函数里面添加一些 Debug 信息</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">initConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"I'm inside initConfig function in cmd/root.go"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="02-cobra项目使用"><a href="#02-cobra项目使用" class="headerlink" title="02.cobra项目使用"></a>02.cobra项目使用</h2><h3 id="2-0-目录结构"><a href="#2-0-目录结构" class="headerlink" title="2.0 目录结构"></a>2.0 目录结构</h3><ul><li>目录结构</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">cobra-demo├── cmd│ ├── root.go│ └── serve.go└── main.go<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-1-main-go"><a href="#2-1-main-go" class="headerlink" title="2.1 main.go"></a>2.1 main.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token string">"cobra-demo/cmd"</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>cmd<span class="token punctuation">.</span><span class="token function">Execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-cmd-x2F-root-go"><a href="#2-2-cmd-x2F-root-go" class="headerlink" title="2.2 cmd/root.go"></a>2.2 cmd/root.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> cmd<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"errors"</span><span class="token string">"github.com/spf13/cobra"</span><span class="token string">"log"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token keyword">var</span> rootCmd <span class="token operator">=</span> <span class="token operator">&</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">{</span>Use<span class="token punctuation">:</span> <span class="token string">"demo"</span><span class="token punctuation">,</span> <span class="token comment">// 命令行时关键字</span>Short<span class="token punctuation">:</span> <span class="token string">"cobra demo example"</span><span class="token punctuation">,</span> <span class="token comment">// 命令简单描述</span>Long<span class="token punctuation">:</span> <span class="token string">`cobra demo example ....`</span><span class="token punctuation">,</span> <span class="token comment">// 命令详细描述</span>Args<span class="token punctuation">:</span> <span class="token keyword">func</span><span class="token punctuation">(</span>cmd <span class="token operator">*</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">,</span> args <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span><span class="token keyword">if</span> <span class="token function">len</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span> <span class="token operator"><</span> <span class="token number">1</span> <span class="token punctuation">{</span><span class="token keyword">return</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"requires at least one arg"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token punctuation">,</span>PersistentPreRunE<span class="token punctuation">:</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token operator">*</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>Run<span class="token punctuation">:</span> <span class="token keyword">func</span><span class="token punctuation">(</span>cmd <span class="token operator">*</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">,</span> args <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 钩子函数</span>usageStr <span class="token operator">:=</span> <span class="token string">`可以使用 -h 查看命令`</span>log<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%s\n"</span><span class="token punctuation">,</span> usageStr<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 第二步:然后再执行 execute 方法</span><span class="token keyword">func</span> <span class="token function">Execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>err <span class="token operator">:=</span> rootCmd<span class="token punctuation">.</span><span class="token function">Execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>os<span class="token punctuation">.</span><span class="token function">Exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 第一步:每当执行或者调用命令的时候,它都会先执行 init 函数中的所有函数</span><span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>rootCmd<span class="token punctuation">.</span><span class="token function">AddCommand</span><span class="token punctuation">(</span>StartCmd<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-cmd-x2F-serve-go"><a href="#2-3-cmd-x2F-serve-go" class="headerlink" title="2.3 cmd/serve.go"></a>2.3 cmd/serve.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> cmd<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/spf13/cobra"</span><span class="token string">"log"</span><span class="token punctuation">)</span><span class="token keyword">var</span> <span class="token punctuation">(</span>config <span class="token builtin">string</span> <span class="token comment">// 启动配置文件位置</span>port <span class="token builtin">string</span> <span class="token comment">// 启动端口号</span>mode <span class="token builtin">string</span> <span class="token comment">// 启动模式</span>StartCmd <span class="token operator">=</span> <span class="token operator">&</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">{</span><span class="token comment">// go run main.go server -c=config/settings.dev.yml</span>Use<span class="token punctuation">:</span> <span class="token string">"server"</span><span class="token punctuation">,</span> <span class="token comment">// 启动时要添加 server关键字</span>Short<span class="token punctuation">:</span> <span class="token string">"Start API server"</span><span class="token punctuation">,</span> <span class="token comment">// 对命令简单描述</span>Example<span class="token punctuation">:</span> <span class="token string">"ferry server config/settings.yml"</span><span class="token punctuation">,</span> <span class="token comment">// 运行命令例子</span>PreRun<span class="token punctuation">:</span> <span class="token keyword">func</span><span class="token punctuation">(</span>cmd <span class="token operator">*</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">,</span> args <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 钩子函数,在RunE前执行</span><span class="token function">usage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span>RunE<span class="token punctuation">:</span> <span class="token keyword">func</span><span class="token punctuation">(</span>cmd <span class="token operator">*</span>cobra<span class="token punctuation">.</span>Command<span class="token punctuation">,</span> args <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span> <span class="token comment">// 钩子函数</span><span class="token keyword">return</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 为 Command 添加选项(flags)</span>StartCmd<span class="token punctuation">.</span><span class="token function">PersistentFlags</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">StringVarP</span><span class="token punctuation">(</span><span class="token operator">&</span>config<span class="token punctuation">,</span> <span class="token string">"config"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">,</span> <span class="token string">"config/settings.yml"</span><span class="token punctuation">,</span> <span class="token string">"Start server with provided configuration file"</span><span class="token punctuation">)</span>StartCmd<span class="token punctuation">.</span><span class="token function">PersistentFlags</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">StringVarP</span><span class="token punctuation">(</span><span class="token operator">&</span>port<span class="token punctuation">,</span> <span class="token string">"port"</span><span class="token punctuation">,</span> <span class="token string">"p"</span><span class="token punctuation">,</span> <span class="token string">"8002"</span><span class="token punctuation">,</span> <span class="token string">"Tcp port server listening on"</span><span class="token punctuation">)</span>StartCmd<span class="token punctuation">.</span><span class="token function">PersistentFlags</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">StringVarP</span><span class="token punctuation">(</span><span class="token operator">&</span>mode<span class="token punctuation">,</span> <span class="token string">"mode"</span><span class="token punctuation">,</span> <span class="token string">"m"</span><span class="token punctuation">,</span> <span class="token string">"dev"</span><span class="token punctuation">,</span> <span class="token string">"server mode ; eg:dev,test,prod"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 记录日志</span><span class="token keyword">func</span> <span class="token function">usage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>usageStr <span class="token operator">:=</span> <span class="token string">`starting api server`</span>log<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%s\n"</span><span class="token punctuation">,</span> usageStr<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 初始化项目</span><span class="token keyword">func</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 1. 读取配置</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"启动命令配置文件:"</span><span class="token punctuation">,</span>config<span class="token punctuation">)</span><span class="token comment">// 2. 初始化数据库链接</span><span class="token comment">// 3. 启动异步任务队列</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span><span class="token comment">// 1.获取当前启动模式</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"启动命令当前模式:"</span><span class="token punctuation">,</span> mode<span class="token punctuation">)</span><span class="token comment">// 2.获取当前启动端口</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"启动命令当前端口"</span><span class="token punctuation">,</span> port<span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-运行测试"><a href="#2-4-运行测试" class="headerlink" title="2.4 运行测试"></a>2.4 运行测试</h3><ul><li>我们可以根据当前命令行传入的 <code>配置文件位置、端口号、启动模式</code> 来启动项目</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">xiaonaiqiang1@ZBMac-C02CW08SM cobra-demo % go run main.go server <span class="token parameter variable">-c</span><span class="token operator">=</span>config/settings.dev.yml <span class="token parameter variable">-p</span><span class="token operator">=</span><span class="token number">8888</span> <span class="token parameter variable">-m</span><span class="token operator">=</span>release<span class="token number">2021</span>/12/23 <span class="token number">11</span>:09:12 starting api server启动命令配置文件: config/settings.dev.yml启动命令当前模式: release启动命令当前端口 <span class="token number">8888</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="17-cobor"><a href="#17-cobor" class="headerlink" title="17.cobor"></a>17.cobor</h1><h2 id="01-cobra使用"><a href="#01-cobra使用" class="</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>Makefile</title>
<link href="http://coderedeng.github.io/2022/06/17/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20Makefile/"/>
<id>http://coderedeng.github.io/2022/06/17/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20Makefile/</id>
<published>2022-06-17T14:21:58.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="16-Makefile"><a href="#16-Makefile" class="headerlink" title="16.Makefile"></a>16.Makefile</h1><h2 id="01-介绍"><a href="#01-介绍" class="headerlink" title="01.介绍"></a>01.介绍</h2><h3 id="1-1-make介绍"><a href="#1-1-make介绍" class="headerlink" title="1.1 make介绍"></a>1.1 make介绍</h3><ul><li><code>make</code>是一个构建自动化工具,会在当前目录下寻找<code>Makefile</code>或<code>makefile</code>文件</li><li>如果存在相应的文件,它就会依据其中定义好的规则完成构建任务。</li></ul><h3 id="1-2-Makefile介绍"><a href="#1-2-Makefile介绍" class="headerlink" title="1.2 Makefile介绍"></a>1.2 Makefile介绍</h3><ul><li>借助<code>Makefile</code>我们在编译过程中不再需要每次手动输入编译的命令和编译的参数,可以极大简化项目编译过程。</li><li>我们可以把<code>Makefile</code>简单理解为它定义了一个项目文件的编译规则。</li><li>借助<code>Makefile</code>我们在编译过程中不再需要每次手动输入编译的命令和编译的参数,可以极大简化项目编译过程。</li><li>同时使用<code>Makefile</code>也可以在项目中确定具体的编译规则和流程,很多开源项目中都会定义<code>Makefile</code>文件。</li></ul><h3 id="1-3-win10安装make"><a href="#1-3-win10安装make" class="headerlink" title="1.3 win10安装make"></a>1.3 win10安装make</h3><ul><li>MinGW下载网页:<a href="http://sourceforge.net/projects/mingw/files/latest/download?source=files">http://sourceforge.net/projects/mingw/files/latest/download?source=files</a></li><li>右击计算机->属性->高级系统设置->环境变量,在系统变量中找到PATH</li><li>将MinGW安装目录里的bin文件夹的地址添加到PATH里面。</li><li>打开MinGW的安装目录,打开bin文件夹,将mingw32-make.exe重命名为make.exe。</li><li>经过以上步骤后,控制台可以输入make。</li></ul><h3 id="1-4-规则介绍"><a href="#1-4-规则介绍" class="headerlink" title="1.4 规则介绍"></a>1.4 规则介绍</h3><ul><li><code>Makefile</code>由多条规则组成,每条规则主要由两个部分组成,分别是依赖的关系和执行的命令。</li><li>其结构如下所示:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-makefile" data-language="makefile"><code class="language-makefile"><span class="token target symbol">[target] ...</span> <span class="token punctuation">:</span> [prerequisites] ...<tab>[command] ... ...<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>其中:<ul><li>targets:规则的目标</li><li>prerequisites:可选的要生成 targets 需要的文件或者是目标。</li><li>command:make 需要执行的命令(任意的 shell 命令)。可以有多条命令,每一条命令占一行。</li></ul></li><li>举个例子:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-makefile" data-language="makefile"><code class="language-makefile"><span class="token target symbol">build</span><span class="token punctuation">:</span>CGO_ENABLED<span class="token operator">=</span>0 GOOS<span class="token operator">=</span>linux GOARCH<span class="token operator">=</span>amd64 go build -o xx<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><h2 id="02-makefile基本使用"><a href="#02-makefile基本使用" class="headerlink" title="02.makefile基本使用"></a>02.makefile基本使用</h2><h3 id="2-1-main-go"><a href="#2-1-main-go" class="headerlink" title="2.1 main.go"></a>2.1 main.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>http<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> hello<span class="token punctuation">)</span>server <span class="token operator">:=</span> <span class="token operator">&</span>http<span class="token punctuation">.</span>Server<span class="token punctuation">{</span>Addr<span class="token punctuation">:</span> <span class="token string">":8888"</span><span class="token punctuation">,</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"server startup..."</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">:=</span> server<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"server startup failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">hello</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>w<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span><span class="token string">"hello v5blog.cn!"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-示例"><a href="#2-2-示例" class="headerlink" title="2.2 示例"></a>2.2 示例</h3><ul><li><code>BINARY="xxx"</code>是定义变量</li><li><code>.PHONY</code>用来定义伪目标,不创建目标文件,而是去执行这个目标下面的命令</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-makefile" data-language="makefile"><code class="language-makefile"><span class="token builtin-target builtin">.PHONY</span><span class="token punctuation">:</span> all build run gotool clean help<span class="token comment"># 编译后的项目名</span>BINARY<span class="token operator">=</span><span class="token string">"xxx"</span><span class="token comment"># 如果make后面不加任何参数,默认执行all</span><span class="token target symbol">all</span><span class="token punctuation">:</span> gotool build<span class="token target symbol">build</span><span class="token punctuation">:</span>CGO_ENABLED<span class="token operator">=</span>0 GOOS<span class="token operator">=</span>linux GOARCH<span class="token operator">=</span>amd64 go build -o <span class="token variable">$</span><span class="token punctuation">{</span>BINARY<span class="token punctuation">}</span><span class="token target symbol">run</span><span class="token punctuation">:</span><span class="token operator">@</span>go run ./main.go<span class="token comment">#@go run ./main.go conf/config.yaml</span><span class="token target symbol">gotool</span><span class="token punctuation">:</span>go fmt ./go vet ./<span class="token target symbol">clean</span><span class="token punctuation">:</span><span class="token operator">@</span>if [ -f <span class="token variable">$</span><span class="token punctuation">{</span>BINARY<span class="token punctuation">}</span> ] <span class="token punctuation">;</span> then rm <span class="token variable">$</span><span class="token punctuation">{</span>BINARY<span class="token punctuation">}</span> <span class="token punctuation">;</span> fi<span class="token target symbol">help</span><span class="token punctuation">:</span><span class="token operator">@</span>echo <span class="token string">"make - 格式化 Go 代码, 并编译生成二进制文件"</span><span class="token operator">@</span>echo <span class="token string">"make build - 编译 Go 代码, 生成二进制文件"</span><span class="token operator">@</span>echo <span class="token string">"make run - 直接运行 Go 代码"</span><span class="token operator">@</span>echo <span class="token string">"make clean - 移除二进制文件和 vim swap files"</span><span class="token operator">@</span>echo <span class="token string">"make gotool - 运行 Go 工具 'fmt' and 'vet'"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-使用"><a href="#2-3-使用" class="headerlink" title="2.3 使用"></a>2.3 使用</h3><p><img src="/img/image-20210615203624174.c01717f8.png"></p><h2 id="03-完整"><a href="#03-完整" class="headerlink" title="03.完整"></a>03.完整</h2><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token punctuation">.</span>PHONY<span class="token punctuation">:</span> all build run gotool clean helpBINARY<span class="token operator">=</span><span class="token string">"bluebell"</span>all<span class="token punctuation">:</span> gotool buildbuild<span class="token punctuation">:</span>CGO_ENABLED<span class="token operator">=</span><span class="token number">0</span> GOOS<span class="token operator">=</span>linux GOARCH<span class="token operator">=</span>amd64 <span class="token keyword">go</span> build <span class="token operator">-</span>ldflags <span class="token string">"-s -w"</span> <span class="token operator">-</span>o <span class="token punctuation">.</span><span class="token operator">/</span>bin<span class="token operator">/</span>$<span class="token punctuation">{</span>BINARY<span class="token punctuation">}</span>run<span class="token punctuation">:</span>@<span class="token keyword">go</span> run <span class="token punctuation">.</span><span class="token operator">/</span>main<span class="token punctuation">.</span><span class="token keyword">go</span> conf<span class="token operator">/</span>config<span class="token punctuation">.</span>yamlgotool<span class="token punctuation">:</span><span class="token keyword">go</span> fmt <span class="token punctuation">.</span><span class="token operator">/</span><span class="token keyword">go</span> vet <span class="token punctuation">.</span><span class="token operator">/</span>clean<span class="token punctuation">:</span>@<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token operator">-</span>f $<span class="token punctuation">{</span>BINARY<span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> then rm $<span class="token punctuation">{</span>BINARY<span class="token punctuation">}</span> <span class="token punctuation">;</span> fihelp<span class="token punctuation">:</span>@echo <span class="token string">"make - 格式化 Go 代码, 并编译生成二进制文件"</span>@echo <span class="token string">"make build - 编译 Go 代码, 生成二进制文件"</span>@echo <span class="token string">"make run - 直接运行 Go 代码"</span>@echo <span class="token string">"make clean - 移除二进制文件和 vim swap files"</span>@echo <span class="token string">"make gotool - 运行 Go 工具 'fmt' and 'vet'"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="16-Makefile"><a href="#16-Makefile" class="headerlink" title="16.Makefile"></a>16.Makefile</h1><h2 id="01-介绍"><a href="#01-介绍" class</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>go-wrk</title>
<link href="http://coderedeng.github.io/2022/06/15/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20go-wrk/"/>
<id>http://coderedeng.github.io/2022/06/15/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20go-wrk/</id>
<published>2022-06-15T13:52:47.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="15-go-wrk压测"><a href="#15-go-wrk压测" class="headerlink" title="15.go-wrk压测"></a>15.go-wrk压测</h1><h2 id="01-压测介绍"><a href="#01-压测介绍" class="headerlink" title="01.压测介绍"></a>01.压测介绍</h2><h3 id="1-1-压测作用"><a href="#1-1-压测作用" class="headerlink" title="1.1 压测作用"></a>1.1 压测作用</h3><ul><li>在项目正式上线之前,我们通常需要通过压测来评估当前系统能够支撑的请求量、排查可能存在的隐藏bug</li><li>同时了解了程序的实际处理能力能够帮我们更好的匹配项目的实际需求,节约资源成本。</li></ul><h3 id="1-2-压测相关术语"><a href="#1-2-压测相关术语" class="headerlink" title="1.2 压测相关术语"></a>1.2 压测相关术语</h3><ul><li>响应时间(RT) :指系统对请求作出响应的时间.</li><li>吞吐量(Throughput) :指系统在单位时间内处理请求的数量</li><li>QPS每秒查询率(Query Per Second) :“每秒查询率”,是一台服务器每秒能够响应的查询次数<ul><li>是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。</li></ul></li><li>TPS(TransactionPerSecond):每秒钟系统能够处理的交易或事务的数量</li><li>并发连接数:某个时刻服务器所接受的请求总数</li></ul><h2 id="02-压力测试工具"><a href="#02-压力测试工具" class="headerlink" title="02.压力测试工具"></a>02.压力测试工具</h2><h3 id="2-1-ab"><a href="#2-1-ab" class="headerlink" title="2.1 ab"></a>2.1 ab</h3><ul><li>ab全称Apache Bench,是Apache自带的性能测试工具。</li><li>使用这个工具,只须指定同时连接数、请求数以及URL,即可测试网站或网站程序的性能。</li><li>通过ab发送请求模拟多个访问者同时对某一URL地址进行访问,可以得到每秒传送字节数、每秒处理请求数、每请求处理时间等统计数据。</li><li>命令格式:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">ab <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>http://<span class="token punctuation">]</span>hostname<span class="token punctuation">[</span>:port<span class="token punctuation">]</span>/path<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>常用参数如下:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token parameter variable">-n</span> requests 总请求数<span class="token parameter variable">-c</span> concurrency 一次产生的请求数,可以理解为并发数<span class="token parameter variable">-t</span> timelimit 测试所进行的最大秒数, 可以当做请求的超时时间<span class="token parameter variable">-p</span> postfile 包含了需要POST的数据的文件<span class="token parameter variable">-T</span> content-type POST数据所使用的Content-type头信息<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>更多参数请查看<a href="http://httpd.apache.org/docs/2.2/programs/ab.html">官方文档 (opens new window)</a>。</li><li>例如测试某个GET请求接口:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">ab <span class="token parameter variable">-n</span> <span class="token number">10000</span> <span class="token parameter variable">-c</span> <span class="token number">100</span> <span class="token parameter variable">-t</span> <span class="token number">10</span> <span class="token string">"http://127.0.0.1:8080/api/v1/posts?size=10"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>测试POST请求接口:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">ab <span class="token parameter variable">-n</span> <span class="token number">10000</span> <span class="token parameter variable">-c</span> <span class="token number">100</span> <span class="token parameter variable">-t</span> <span class="token number">10</span> <span class="token parameter variable">-p</span> post.json <span class="token parameter variable">-T</span> <span class="token string">"application/json"</span> <span class="token string">"http://127.0.0.1:8080/api/v1/post"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="2-2-wrk"><a href="#2-2-wrk" class="headerlink" title="2.2 wrk"></a>2.2 wrk</h3><ul><li>是一款开源的HTTP性能测试工具,它和上面提到的<code>ab</code>同属于HTTP性能测试工具</li><li>它比<code>ab</code>功能更加强大,可以通过编写lua脚本来支持更加复杂的测试场景。</li><li>Mac下安装</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">brew <span class="token function">install</span> wrk<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>常用命令参数:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token parameter variable">-c</span> --conections:保持的连接数<span class="token parameter variable">-d</span> --duration:压测持续时间<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token parameter variable">-t</span> --threads:使用的线程总数<span class="token parameter variable">-s</span> --script:加载lua脚本<span class="token parameter variable">-H</span> --header:在请求头部添加一些参数<span class="token parameter variable">--latency</span> 打印详细的延迟统计信息<span class="token parameter variable">--timeout</span> 请求的最大超时时间<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>使用示例:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">wrk <span class="token parameter variable">-t8</span> <span class="token parameter variable">-c100</span> <span class="token parameter variable">-d30s</span> <span class="token parameter variable">--latency</span> http://127.0.0.1:8080/api/v1/posts?size<span class="token operator">=</span><span class="token number">10</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>输出结果:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Running 30s <span class="token builtin class-name">test</span> @ http://127.0.0.1:8080/api/v1/posts?size<span class="token operator">=</span><span class="token number">10</span> <span class="token number">8</span> threads and <span class="token number">100</span> connections Thread Stats Avg Stdev Max +/- Stdev Latency <span class="token number">14</span>.55ms <span class="token number">2</span>.02ms <span class="token number">31</span>.59ms <span class="token number">76.70</span>% Req/Sec <span class="token number">828.16</span> <span class="token number">85.69</span> <span class="token number">0</span>.97k <span class="token number">60.46</span>% Latency Distribution <span class="token number">50</span>% <span class="token number">14</span>.44ms <span class="token number">75</span>% <span class="token number">15</span>.76ms <span class="token number">90</span>% <span class="token number">16</span>.63ms <span class="token number">99</span>% <span class="token number">21</span>.07ms <span class="token number">198091</span> requests <span class="token keyword">in</span> <span class="token number">30</span>.05s, <span class="token number">29</span>.66MB <span class="token builtin class-name">read</span>Requests/sec: <span class="token number">6592.29</span>Transfer/sec: <span class="token number">0</span>.99MB<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-go-wrk"><a href="#2-3-go-wrk" class="headerlink" title="2.3 go-wrk"></a>2.3 go-wrk</h3><ul><li>是Go语言版本的<code>wrk</code></li><li>Windows同学可以使用它来测试,使用如下命令来安装<code>go-wrk</code></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go get github.com/adeven/go-wrk<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>使用方法同<code>wrk</code>类似,基本格式如下:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go-wrk <span class="token punctuation">[</span>flags<span class="token punctuation">]</span> url<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>常用的参数:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token parameter variable">-H</span><span class="token operator">=</span><span class="token string">"User-Agent: go-wrk 0.1 bechmark<span class="token entity" title="\n">\n</span>Content-Type: text/html;"</span><span class="token builtin class-name">:</span> 由<span class="token string">'\n'</span>分隔的请求头<span class="token parameter variable">-c</span><span class="token operator">=</span><span class="token number">100</span>: 使用的最大连接数<span class="token parameter variable">-k</span><span class="token operator">=</span>true: 是否禁用keep-alives<span class="token parameter variable">-i</span><span class="token operator">=</span>false: <span class="token keyword">if</span> TLS security checks are disabled<span class="token parameter variable">-m</span><span class="token operator">=</span><span class="token string">"GET"</span><span class="token builtin class-name">:</span> HTTP请求方法<span class="token parameter variable">-n</span><span class="token operator">=</span><span class="token number">1000</span>: 请求总数<span class="token parameter variable">-t</span><span class="token operator">=</span><span class="token number">1</span>: 使用的线程数<span class="token parameter variable">-b</span><span class="token operator">=</span><span class="token string">""</span> HTTP请求体<span class="token parameter variable">-s</span><span class="token operator">=</span><span class="token string">""</span> 如果指定,它将计算响应中包含搜索到的字符串s的频率<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>执行测试:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go-wrk <span class="token parameter variable">-t</span><span class="token operator">=</span><span class="token number">8</span> <span class="token parameter variable">-c</span><span class="token operator">=</span><span class="token number">100</span> <span class="token parameter variable">-n</span><span class="token operator">=</span><span class="token number">10000</span> <span class="token string">"http://127.0.0.1:8080/api/v1/posts?size=10"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>输出结果:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span>BENCHMARK<span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span>URL: http://127.0.0.1:8080/api/v1/posts?size<span class="token operator">=</span><span class="token number">10</span>Used Connections: <span class="token number">100</span>Used Threads: <span class="token number">8</span>Total number of calls: <span class="token number">10000</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span>TIMINGS<span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span>Total <span class="token function">time</span> passed: <span class="token number">2</span>.74sAvg <span class="token function">time</span> per request: <span class="token number">27</span>.11msRequests per second: <span class="token number">3644.53</span>Median <span class="token function">time</span> per request: <span class="token number">26</span>.88ms99th percentile time: <span class="token number">39</span>.16msSlowest <span class="token function">time</span> <span class="token keyword">for</span> request: <span class="token number">45</span>.00ms<span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span>DATA<span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">=</span>Total response body sizes: <span class="token number">340000</span>Avg response body per request: <span class="token number">34.00</span> ByteTransfer rate per second: <span class="token number">123914.11</span> Byte/s <span class="token punctuation">(</span><span class="token number">0.12</span> MByte/s<span class="token punctuation">)</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span>RESPONSES<span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span>20X Responses: <span class="token number">10000</span> <span class="token punctuation">(</span><span class="token number">100.00</span>%<span class="token punctuation">)</span>30X Responses: <span class="token number">0</span> <span class="token punctuation">(</span><span class="token number">0.00</span>%<span class="token punctuation">)</span>40X Responses: <span class="token number">0</span> <span class="token punctuation">(</span><span class="token number">0.00</span>%<span class="token punctuation">)</span>50X Responses: <span class="token number">0</span> <span class="token punctuation">(</span><span class="token number">0.00</span>%<span class="token punctuation">)</span>Errors: <span class="token number">0</span> <span class="token punctuation">(</span><span class="token number">0.00</span>%<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="15-go-wrk压测"><a href="#15-go-wrk压测" class="headerlink" title="15.go-wrk压测"></a>15.go-wrk压测</h1><h2 id="01-压测介绍"><a href="#01-压测介绍" c</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>jwt-go</title>
<link href="http://coderedeng.github.io/2022/06/13/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20jwt-go/"/>
<id>http://coderedeng.github.io/2022/06/13/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20jwt-go/</id>
<published>2022-06-13T13:01:24.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="14-jwt-go"><a href="#14-jwt-go" class="headerlink" title="14.jwt-go"></a>14.jwt-go</h1><h2 id="01-JWT介绍"><a href="#01-JWT介绍" class="headerlink" title="01.JWT介绍"></a>01.JWT介绍</h2><h3 id="1-1-什么是JWT?"><a href="#1-1-什么是JWT?" class="headerlink" title="1.1 什么是JWT?"></a>1.1 什么是JWT?</h3><ul><li>JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式</li><li>目前多用于前后端分离项目和OAuth2.0业务场景下。</li></ul><h3 id="1-2-jwt三部分"><a href="#1-2-jwt三部分" class="headerlink" title="1.2 jwt三部分"></a>1.2 jwt三部分</h3><ul><li>基于JWT技术及RSA非对称加密实现真正无状态的单点登录</li></ul><p><img src="/img/image-20210614161501680.e267e765.png"></p><h2 id="02-JWT基本用法"><a href="#02-JWT基本用法" class="headerlink" title="02.JWT基本用法"></a>02.JWT基本用法</h2><h3 id="2-1-定义需求"><a href="#2-1-定义需求" class="headerlink" title="2.1 定义需求"></a>2.1 定义需求</h3><ul><li>我们需要定制自己的需求来决定JWT中保存哪些数据</li><li>比如我们规定在JWT中要存储<code>username</code>信息</li><li>那么我们就定义一个<code>MyClaims</code>结构体如下</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims</span><span class="token comment">// jwt包自带的jwt.StandardClaims只包含了官方字段</span><span class="token comment">// 我们这里需要额外记录一个username字段,所以要自定义结构体</span><span class="token comment">// 如果想要保存更多信息,都可以添加到这个结构体中</span><span class="token keyword">type</span> MyClaims <span class="token keyword">struct</span> <span class="token punctuation">{</span>Username <span class="token builtin">string</span> <span class="token string">`json:"username"`</span>jwt<span class="token punctuation">.</span>StandardClaims<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>然后我们定义JWT的过期时间,这里以2小时为例:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">const</span> TokenExpireDuration <span class="token operator">=</span> time<span class="token punctuation">.</span>Hour <span class="token operator">*</span> <span class="token number">2</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>接下来还需要定义Secret:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">var</span> MySecret <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span><span class="token string">"夏天夏天悄悄过去"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="2-2-生成JWT"><a href="#2-2-生成JWT" class="headerlink" title="2.2 生成JWT"></a>2.2 生成JWT</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// GenToken 生成JWT</span><span class="token keyword">func</span> <span class="token function">GenToken</span><span class="token punctuation">(</span>username <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 创建一个我们自己的声明</span>c <span class="token operator">:=</span> MyClaims<span class="token punctuation">{</span>username<span class="token punctuation">,</span> <span class="token comment">// 自定义字段</span>jwt<span class="token punctuation">.</span>StandardClaims<span class="token punctuation">{</span>ExpiresAt<span class="token punctuation">:</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>TokenExpireDuration<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Unix</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 过期时间</span>Issuer<span class="token punctuation">:</span> <span class="token string">"my-project"</span><span class="token punctuation">,</span> <span class="token comment">// 签发人</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 使用指定的签名方法创建签名对象</span>token <span class="token operator">:=</span> jwt<span class="token punctuation">.</span><span class="token function">NewWithClaims</span><span class="token punctuation">(</span>jwt<span class="token punctuation">.</span>SigningMethodHS256<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token comment">// 使用指定的secret签名并获得完整的编码后的字符串token</span><span class="token keyword">return</span> token<span class="token punctuation">.</span><span class="token function">SignedString</span><span class="token punctuation">(</span>MySecret<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-解析JWT"><a href="#2-3-解析JWT" class="headerlink" title="2.3 解析JWT"></a>2.3 解析JWT</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// ParseToken 解析JWT</span><span class="token keyword">func</span> <span class="token function">ParseToken</span><span class="token punctuation">(</span>tokenString <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>MyClaims<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 解析token</span>token<span class="token punctuation">,</span> err <span class="token operator">:=</span> jwt<span class="token punctuation">.</span><span class="token function">ParseWithClaims</span><span class="token punctuation">(</span>tokenString<span class="token punctuation">,</span> <span class="token operator">&</span>MyClaims<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>token <span class="token operator">*</span>jwt<span class="token punctuation">.</span>Token<span class="token punctuation">)</span> <span class="token punctuation">(</span>i <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">return</span> MySecret<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> err<span class="token punctuation">}</span><span class="token keyword">if</span> claims<span class="token punctuation">,</span> ok <span class="token operator">:=</span> token<span class="token punctuation">.</span>Claims<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token operator">*</span>MyClaims<span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token operator">&&</span> token<span class="token punctuation">.</span>Valid <span class="token punctuation">{</span> <span class="token comment">// 校验token</span><span class="token keyword">return</span> claims<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"invalid token"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="03-gin使用JWT"><a href="#03-gin使用JWT" class="headerlink" title="03.gin使用JWT"></a>03.gin使用JWT</h2><h3 id="3-0-demo结构"><a href="#3-0-demo结构" class="headerlink" title="3.0 demo结构"></a>3.0 demo结构</h3><p><img src="/img/image-20210614170143996.08fc999d.png"></p><h3 id="3-1-main-go"><a href="#3-1-main-go" class="headerlink" title="3.1 main.go"></a>3.1 main.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token string">"jwt-test/controllers"</span><span class="token string">"jwt-test/middlewares"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>r <span class="token operator">:=</span> gin<span class="token punctuation">.</span><span class="token function">Default</span><span class="token punctuation">(</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">POST</span><span class="token punctuation">(</span><span class="token string">"/auth"</span><span class="token punctuation">,</span> controllers<span class="token punctuation">.</span>AuthHandler<span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/home"</span><span class="token punctuation">,</span> middlewares<span class="token punctuation">.</span><span class="token function">JWTAuthMiddleware</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> controllers<span class="token punctuation">.</span>HomeHandler<span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token string">":8000"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-2-controllers-x2F-user-go"><a href="#3-2-controllers-x2F-user-go" class="headerlink" title="3.2 controllers/user.go"></a>3.2 controllers/user.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> controllers<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token string">"jwt-test/pkg/jwt"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token comment">// ParamSignUp 注册请求参数</span><span class="token keyword">type</span> UserInfo <span class="token keyword">struct</span> <span class="token punctuation">{</span>Username <span class="token builtin">string</span> <span class="token string">`json:"username" binding:"required"`</span>Password <span class="token builtin">string</span> <span class="token string">`json:"password" binding:"required"`</span>RePassword <span class="token builtin">string</span> <span class="token string">`json:"confirm_password" binding:"required,eqfield=Password"`</span><span class="token comment">//RePassword string `json:"re_password" binding:"required,eqfield=Password"`</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">AuthHandler</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 用户发送用户名和密码过来</span><span class="token keyword">var</span> user UserInfoerr <span class="token operator">:=</span> c<span class="token punctuation">.</span><span class="token function">ShouldBind</span><span class="token punctuation">(</span><span class="token operator">&</span>user<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2001</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"无效的参数"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 校验用户名和密码是否正确</span><span class="token keyword">if</span> user<span class="token punctuation">.</span>Username <span class="token operator">==</span> <span class="token string">"zhangsan"</span> <span class="token operator">&&</span> user<span class="token punctuation">.</span>Password <span class="token operator">==</span> <span class="token string">"123456"</span> <span class="token punctuation">{</span><span class="token comment">// 生成Token</span>tokenString<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> jwt<span class="token punctuation">.</span><span class="token function">GenToken</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>Username<span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2000</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"success"</span><span class="token punctuation">,</span><span class="token string">"data"</span><span class="token punctuation">:</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"token"</span><span class="token punctuation">:</span> tokenString<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2002</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"鉴权失败"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">HomeHandler</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>username <span class="token operator">:=</span> c<span class="token punctuation">.</span><span class="token function">MustGet</span><span class="token punctuation">(</span><span class="token string">"username"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2000</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"success"</span><span class="token punctuation">,</span><span class="token string">"data"</span><span class="token punctuation">:</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"username"</span><span class="token punctuation">:</span> username<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-3-pkg-x2F-jwt-x2F-jwt-go"><a href="#3-3-pkg-x2F-jwt-x2F-jwt-go" class="headerlink" title="3.3 pkg/jwt/jwt.go"></a>3.3 pkg/jwt/jwt.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> jwt<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"errors"</span><span class="token string">"github.com/dgrijalva/jwt-go"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims</span><span class="token comment">// jwt包自带的jwt.StandardClaims只包含了官方字段</span><span class="token comment">// 我们这里需要额外记录一个username字段,所以要自定义结构体</span><span class="token comment">// 如果想要保存更多信息,都可以添加到这个结构体中</span><span class="token keyword">type</span> MyClaims <span class="token keyword">struct</span> <span class="token punctuation">{</span>Username <span class="token builtin">string</span> <span class="token string">`json:"username"`</span>jwt<span class="token punctuation">.</span>StandardClaims<span class="token punctuation">}</span><span class="token keyword">const</span> TokenExpireDuration <span class="token operator">=</span> time<span class="token punctuation">.</span>Hour <span class="token operator">*</span> <span class="token number">2</span><span class="token keyword">var</span> MySecret <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span><span class="token string">"夏天夏天悄悄过去"</span><span class="token punctuation">)</span><span class="token comment">// GenToken 生成JWT</span><span class="token keyword">func</span> <span class="token function">GenToken</span><span class="token punctuation">(</span>username <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 创建一个我们自己的声明</span>c <span class="token operator">:=</span> MyClaims<span class="token punctuation">{</span>username<span class="token punctuation">,</span> <span class="token comment">// 自定义字段</span>jwt<span class="token punctuation">.</span>StandardClaims<span class="token punctuation">{</span>ExpiresAt<span class="token punctuation">:</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>TokenExpireDuration<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Unix</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 过期时间</span>Issuer<span class="token punctuation">:</span> <span class="token string">"my-project"</span><span class="token punctuation">,</span> <span class="token comment">// 签发人</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 使用指定的签名方法创建签名对象</span>token <span class="token operator">:=</span> jwt<span class="token punctuation">.</span><span class="token function">NewWithClaims</span><span class="token punctuation">(</span>jwt<span class="token punctuation">.</span>SigningMethodHS256<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token comment">// 使用指定的secret签名并获得完整的编码后的字符串token</span><span class="token keyword">return</span> token<span class="token punctuation">.</span><span class="token function">SignedString</span><span class="token punctuation">(</span>MySecret<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// ParseToken 解析JWT</span><span class="token keyword">func</span> <span class="token function">ParseToken</span><span class="token punctuation">(</span>tokenString <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>MyClaims<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 解析token</span>token<span class="token punctuation">,</span> err <span class="token operator">:=</span> jwt<span class="token punctuation">.</span><span class="token function">ParseWithClaims</span><span class="token punctuation">(</span>tokenString<span class="token punctuation">,</span> <span class="token operator">&</span>MyClaims<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>token <span class="token operator">*</span>jwt<span class="token punctuation">.</span>Token<span class="token punctuation">)</span> <span class="token punctuation">(</span>i <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">return</span> MySecret<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> err<span class="token punctuation">}</span><span class="token keyword">if</span> claims<span class="token punctuation">,</span> ok <span class="token operator">:=</span> token<span class="token punctuation">.</span>Claims<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token operator">*</span>MyClaims<span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token operator">&&</span> token<span class="token punctuation">.</span>Valid <span class="token punctuation">{</span> <span class="token comment">// 校验token</span><span class="token keyword">return</span> claims<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"invalid token"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-4-middlewares-x2F-auth-go"><a href="#3-4-middlewares-x2F-auth-go" class="headerlink" title="3.4 middlewares/auth.go"></a>3.4 middlewares/auth.go</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> middlewares<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token string">"jwt-test/pkg/jwt"</span><span class="token string">"net/http"</span><span class="token string">"strings"</span><span class="token punctuation">)</span><span class="token comment">// JWTAuthMiddleware 基于JWT的认证中间件</span><span class="token keyword">func</span> <span class="token function">JWTAuthMiddleware</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI</span><span class="token comment">// 这里假设Token放在Header的Authorization中,并使用Bearer开头</span><span class="token comment">// 这里的具体实现方式要依据你的实际业务情况决定</span>authHeader <span class="token operator">:=</span> c<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Header<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"Authorization"</span><span class="token punctuation">)</span><span class="token keyword">if</span> authHeader <span class="token operator">==</span> <span class="token string">""</span> <span class="token punctuation">{</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2003</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"请求头中auth为空"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">Abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 按空格分割</span>parts <span class="token operator">:=</span> strings<span class="token punctuation">.</span><span class="token function">SplitN</span><span class="token punctuation">(</span>authHeader<span class="token punctuation">,</span> <span class="token string">" "</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token keyword">if</span> <span class="token operator">!</span><span class="token punctuation">(</span><span class="token function">len</span><span class="token punctuation">(</span>parts<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span> <span class="token operator">&&</span> parts<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"Bearer"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2004</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"请求头中auth格式有误"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">Abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它</span>mc<span class="token punctuation">,</span> err <span class="token operator">:=</span> jwt<span class="token punctuation">.</span><span class="token function">ParseToken</span><span class="token punctuation">(</span>parts<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>c<span class="token punctuation">.</span><span class="token function">JSON</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> gin<span class="token punctuation">.</span>H<span class="token punctuation">{</span><span class="token string">"code"</span><span class="token punctuation">:</span> <span class="token number">2005</span><span class="token punctuation">,</span><span class="token string">"msg"</span><span class="token punctuation">:</span> <span class="token string">"无效的Token"</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">Abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 将当前请求的username信息保存到请求的上下文c上</span>c<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"username"</span><span class="token punctuation">,</span> mc<span class="token punctuation">.</span>Username<span class="token punctuation">)</span>c<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="04-测试"><a href="#04-测试" class="headerlink" title="04.测试"></a>04.测试</h2><h3 id="4-1-登录获取token"><a href="#4-1-登录获取token" class="headerlink" title="4.1 登录获取token"></a>4.1 登录获取token</h3><ul><li><a href="http://127.0.0.1:8000/auth">http://127.0.0.1:8000/auth</a></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"username"</span><span class="token operator">:</span><span class="token string">"zhangsan"</span><span class="token punctuation">,</span> <span class="token property">"password"</span><span class="token operator">:</span> <span class="token string">"123456"</span><span class="token punctuation">,</span> <span class="token property">"confirm_password"</span><span class="token operator">:</span> <span class="token string">"123456"</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><p><img src="/img/image-20210614165132336.0766e8a7.png"></p><h3 id="4-2-携带token访问"><a href="#4-2-携带token访问" class="headerlink" title="4.2 携带token访问"></a>4.2 携带token访问</h3><ul><li><a href="http://127.0.0.1:8000/home">http://127.0.0.1:8000/home</a></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">AuthorizationBearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9<span class="token punctuation">.</span>eyJ1c2VybmFtZSI6InpoYW5nc2FuIiwiZXhwIjoxNjIzNjY3NzUzLCJpc3MiOiJteS1wcm9qZWN0In0<span class="token punctuation">.</span>j9SFygMnMq1<span class="token operator">-</span>ymsDcTLN59svQb4<span class="token operator">-</span>BTgO3DLaBeUAAVY<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><p><img src="http://v5blog.cn/assets/img/image-20210614165555199.4798dd56.png" alt="img"></p>]]></content>
<summary type="html"><h1 id="14-jwt-go"><a href="#14-jwt-go" class="headerlink" title="14.jwt-go"></a>14.jwt-go</h1><h2 id="01-JWT介绍"><a href="#01-JWT介绍" class="</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>gRPC</title>
<link href="http://coderedeng.github.io/2022/06/11/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20gRPC/"/>
<id>http://coderedeng.github.io/2022/06/11/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20gRPC/</id>
<published>2022-06-11T14:11:19.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="13-gRPC"><a href="#13-gRPC" class="headerlink" title="13.gRPC"></a>13.gRPC</h1><h2 id="01-gRPC基础"><a href="#01-gRPC基础" class="headerlink" title="01.gRPC基础"></a>01.gRPC基础</h2><h3 id="1-1-RPC是什么"><a href="#1-1-RPC是什么" class="headerlink" title="1.1 RPC是什么"></a>1.1 RPC是什么</h3><ul><li>在分布式计算,远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。</li><li>该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序</li><li>而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。</li><li>RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过<code>发送请求-接受回应</code>进行信息交互的系统。</li></ul><h3 id="1-2-gRPC是什么"><a href="#1-2-gRPC是什么" class="headerlink" title="1.2 gRPC是什么"></a>1.2 gRPC是什么</h3><ul><li><code>gRPC</code>是一种现代化开源的高性能RPC框架,能够运行于任意环境之中。</li><li>最初由谷歌进行开发,它使用HTTP/2作为传输协议。</li><li>在gRPC里,客户端可以像调用本地方法一样直接调用其他机器上的服务端应用程序的方法,帮助你更容易创建分布式应用程序和服务。</li><li>与许多RPC系统一样,gRPC是基于定义一个服务,指定一个可以远程调用的带有参数和返回类型的的方法。</li><li>在服务端程序中实现这个接口并且运行gRPC服务处理客户端调用。</li><li>在客户端,有一个stub提供和服务端相同的方</li></ul><h3 id="1-3-为什么要用gRPC"><a href="#1-3-为什么要用gRPC" class="headerlink" title="1.3 为什么要用gRPC"></a>1.3 为什么要用gRPC</h3><ul><li>使用gRPC, 我们可以一次性的在一个<code>.proto</code>文件中定义服务并使用任何支持它的语言去实现客户端和服务端</li><li>反过来,它们可以应用在各种场景中,从Google的服务器到你自己的平板电脑—— gRPC帮你解决了不同语言及环境间通信的复杂性。</li><li>使用<code>protocol buffers</code>还能获得其他好处,包括高效的序列号,简单的IDL以及容易进行接口更新。</li><li>总之一句话,使用gRPC能让我们更容易编写跨语言的分布式代码。</li></ul><h2 id="02-安装gRPC"><a href="#02-安装gRPC" class="headerlink" title="02.安装gRPC"></a>02.安装gRPC</h2><h3 id="2-1-安装gRPC"><a href="#2-1-安装gRPC" class="headerlink" title="2.1 安装gRPC"></a>2.1 安装gRPC</h3><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go get <span class="token parameter variable">-u</span> google.golang.org/grpc<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="2-2-安装Protocol-Buffers-v3"><a href="#2-2-安装Protocol-Buffers-v3" class="headerlink" title="2.2 安装Protocol Buffers v3"></a>2.2 安装Protocol Buffers v3</h3><ul><li>安装用于生成gRPC服务代码的协议编译器,最简单的方法是从下面的链接</li><li><a href="https://github.com/google/protobuf/releases">https://github.com/google/protobuf/releases</a></li><li>下载适合你平台的预编译好的二进制文件(<code>protoc-<version>-<platform>.zip</code>)。</li><li>下载完之后,执行下面的步骤:<ul><li>1、解压下载好的文件</li><li>2、把<code>protoc</code>二进制文件的路径加到环境变量中</li></ul></li><li>接下来执行下面的命令安装protoc的Go插件</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go get <span class="token parameter variable">-u</span> github.com/golang/protobuf/protoc-gen-go<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>编译插件<code>protoc-gen-go</code>将会安装到<code>$GOBIN</code>,默认是<code>$GOPATH/bin</code>,它必须在你的<code>$PATH</code>中以便协议编译器<code>protoc</code>能够找到它。</li></ul>]]></content>
<summary type="html"><h1 id="13-gRPC"><a href="#13-gRPC" class="headerlink" title="13.gRPC"></a>13.gRPC</h1><h2 id="01-gRPC基础"><a href="#01-gRPC基础" class="header</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>viper配置管理</title>
<link href="http://coderedeng.github.io/2022/06/09/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20viper/"/>
<id>http://coderedeng.github.io/2022/06/09/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20viper/</id>
<published>2022-06-09T13:24:37.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="12-viper配置管理"><a href="#12-viper配置管理" class="headerlink" title="12.viper配置管理"></a>12.viper配置管理</h1><h2 id="01-viper介绍"><a href="#01-viper介绍" class="headerlink" title="01.viper介绍"></a>01.viper介绍</h2><ul><li><a href="https://www.liwenzhou.com/posts/Go/viper_tutorial/">参考博客(opens new window)</a></li></ul><h3 id="1-1-viper是什么?"><a href="#1-1-viper是什么?" class="headerlink" title="1.1 viper是什么?"></a>1.1 viper是什么?</h3><ul><li><a href="https://github.com/spf13/viper">Viper (opens new window)</a>是适用于Go应用程序的完整配置解决方案。</li><li>它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式</li><li>viper功能<ul><li>设置默认值</li><li>从<code>JSON</code>、<code>TOML</code>、<code>YAML</code>、<code>HCL</code>、<code>envfile</code>和<code>Java properties</code>格式的配置文件读取配置信息</li><li>实时监控和重新读取配置文件(可选)</li><li>从环境变量中读取</li><li>从远程配置系统(etcd或Consul)读取并监控配置变化</li><li>从命令行参数读取配置</li><li>从buffer读取配置</li><li>显式配置值</li></ul></li></ul><h3 id="1-2-为什么选择Viper"><a href="#1-2-为什么选择Viper" class="headerlink" title="1.2 为什么选择Viper?"></a>1.2 为什么选择Viper?</h3><ul><li>在构建现代应用程序时,你无需担心配置文件格式;</li><li>Viper能够为你执行下列操作:<ul><li>查找、加载和反序列化JSON、TOML、YAML、HCL、INI、envfile和Java properties格式的配置文件。</li><li>提供一种机制为你的不同配置选项设置默认值。</li><li>提供一种机制来通过命令行参数覆盖指定选项的值。</li><li>提供别名系统,以便在不破坏现有代码的情况下轻松重命名参数。</li><li>当用户提供了与默认值相同的命令行或配置文件时,可以很容易地分辨出它们之间的区别。</li></ul></li><li><code>Viper会按照下面的优先,每个项目的优先级都高于它下面的项目</code><ul><li>显示调用<code>Set</code>设置值</li><li>命令行参数(flag)</li><li>环境变量</li><li>配置文件</li><li>key/value存储</li><li>默认值</li></ul></li><li><strong>重要:</strong> 目前Viper配置的键(Key)是<code>大小写不敏感的</code></li></ul><h3 id="1-3-viper安装"><a href="#1-3-viper安装" class="headerlink" title="1.3 viper安装"></a>1.3 viper安装</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">go</span> get github<span class="token punctuation">.</span>com<span class="token operator">/</span>spf13<span class="token operator">/</span>viper<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h2 id="02-viper设置配置"><a href="#02-viper设置配置" class="headerlink" title="02.viper设置配置"></a>02.viper设置配置</h2><h3 id="2-1-建立默认值"><a href="#2-1-建立默认值" class="headerlink" title="2.1 建立默认值"></a>2.1 建立默认值</h3><ul><li>一个好的配置系统应该支持默认值。</li><li>键不需要默认值,但如果没有通过配置文件、环境变量、远程配置或命令行标志(flag)设置键,则默认值非常有用。</li><li>例如:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span><span class="token string">"ContentDir"</span><span class="token punctuation">,</span> <span class="token string">"content"</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span><span class="token string">"LayoutDir"</span><span class="token punctuation">,</span> <span class="token string">"layouts"</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span><span class="token string">"Taxonomies"</span><span class="token punctuation">,</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"tag"</span><span class="token punctuation">:</span> <span class="token string">"tags"</span><span class="token punctuation">,</span> <span class="token string">"category"</span><span class="token punctuation">:</span> <span class="token string">"categories"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-读取配置文件"><a href="#2-2-读取配置文件" class="headerlink" title="2.2 读取配置文件"></a>2.2 读取配置文件</h3><ul><li>Viper需要最少知道在哪里查找配置文件的配置。</li><li>Viper支持<code>JSON</code>、<code>TOML</code>、<code>YAML</code>、<code>HCL</code>、<code>envfile</code>和<code>Java properties</code>格式的配置文件。</li><li>Viper可以搜索多个路径,但目前单个Viper实例只支持单个配置文件。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">viper<span class="token punctuation">.</span><span class="token function">SetConfigFile</span><span class="token punctuation">(</span><span class="token string">"./config.yaml"</span><span class="token punctuation">)</span> <span class="token comment">// 指定配置文件路径</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigName</span><span class="token punctuation">(</span><span class="token string">"config"</span><span class="token punctuation">)</span> <span class="token comment">// 配置文件名称(无扩展名)</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span><span class="token string">"yaml"</span><span class="token punctuation">)</span> <span class="token comment">// 如果配置文件的名称中没有扩展名,则需要配置此项</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"/etc/appname/"</span><span class="token punctuation">)</span> <span class="token comment">// 查找配置文件所在的路径</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"$HOME/.appname"</span><span class="token punctuation">)</span> <span class="token comment">// 多次调用以添加多个搜索路径</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span> <span class="token comment">// 还可以在工作目录中查找配置</span>err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 查找并读取配置文件</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// 处理读取配置文件的错误</span><span class="token function">panic</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Fatal error config file: %s \n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-写入配置文件"><a href="#2-3-写入配置文件" class="headerlink" title="2.3 写入配置文件"></a>2.3 写入配置文件</h3><ul><li>从配置文件中读取配置文件是有用的,但是有时你想要存储在运行时所做的所有修改。</li><li>为此,可以使用下面一组命令,每个命令都有自己的用途</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">viper<span class="token punctuation">.</span><span class="token function">WriteConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 将当前配置写入“viper.AddConfigPath()”和“viper.SetConfigName”设置的预定义路径</span>viper<span class="token punctuation">.</span><span class="token function">SafeWriteConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">WriteConfigAs</span><span class="token punctuation">(</span><span class="token string">"/path/to/my/.config"</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">SafeWriteConfigAs</span><span class="token punctuation">(</span><span class="token string">"/path/to/my/.config"</span><span class="token punctuation">)</span> <span class="token comment">// 因为该配置文件写入过,所以会报错</span>viper<span class="token punctuation">.</span><span class="token function">SafeWriteConfigAs</span><span class="token punctuation">(</span><span class="token string">"/path/to/my/.other_config"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-监控并重新读取配置文件"><a href="#2-4-监控并重新读取配置文件" class="headerlink" title="2.4 监控并重新读取配置文件"></a>2.4 监控并重新读取配置文件</h3><ul><li>确保在调用<code>WatchConfig()</code>之前添加了所有的配置路径。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">viper<span class="token punctuation">.</span><span class="token function">WatchConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">OnConfigChange</span><span class="token punctuation">(</span><span class="token keyword">func</span><span class="token punctuation">(</span>e fsnotify<span class="token punctuation">.</span>Event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 配置文件发生变更之后会调用的回调函数</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Config file changed:"</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-覆盖设置"><a href="#2-4-覆盖设置" class="headerlink" title="2.4 覆盖设置"></a>2.4 覆盖设置</h3><ul><li>这些可能来自命令行标志,也可能来自你自己的应用程序逻辑。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">viper<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"Verbose"</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"LogFile"</span><span class="token punctuation">,</span> LogFile<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><h2 id="03-viper读取配置"><a href="#03-viper读取配置" class="headerlink" title="03.viper读取配置"></a>03.viper读取配置</h2><h3 id="3-1-几种访问值的方法"><a href="#3-1-几种访问值的方法" class="headerlink" title="3.1 几种访问值的方法"></a>3.1 几种访问值的方法</h3><ul><li>在Viper中,有几种方法可以根据值的类型获取值</li><li><code>Get(key string) : interface{}</code></li><li><code>GetBool(key string) : bool</code></li><li><code>GetFloat64(key string) : float64</code></li><li><code>GetInt(key string) : int</code></li><li><code>GetIntSlice(key string) : []int</code></li><li><code>GetString(key string) : string</code></li><li><code>GetStringMap(key string) : map[string]interface{}</code></li><li><code>GetStringMapString(key string) : map[string]string</code></li><li><code>GetStringSlice(key string) : []string</code></li><li><code>GetTime(key string) : time.Time</code></li><li><code>GetDuration(key string) : time.Duration</code></li><li><code>IsSet(key string) : bool</code></li><li><code>AllSettings() : map[string]interface{}</code></li></ul><p>例如:</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">viper<span class="token punctuation">.</span><span class="token function">GetString</span><span class="token punctuation">(</span><span class="token string">"logfile"</span><span class="token punctuation">)</span> <span class="token comment">// 不区分大小写的设置和获取</span><span class="token keyword">if</span> viper<span class="token punctuation">.</span><span class="token function">GetBool</span><span class="token punctuation">(</span><span class="token string">"verbose"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"verbose enabled"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-2-访问嵌套的键"><a href="#3-2-访问嵌套的键" class="headerlink" title="3.2 访问嵌套的键"></a>3.2 访问嵌套的键</h3><ul><li>问器方法也接受深度嵌套键的格式化路径</li><li>例如,如果加载下面的JSON文件</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"host"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"address"</span><span class="token operator">:</span> <span class="token string">"localhost"</span><span class="token punctuation">,</span> <span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">5799</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"datastore"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"metric"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"host"</span><span class="token operator">:</span> <span class="token string">"127.0.0.1"</span><span class="token punctuation">,</span> <span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">3099</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"warehouse"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"host"</span><span class="token operator">:</span> <span class="token string">"198.0.0.1"</span><span class="token punctuation">,</span> <span class="token property">"port"</span><span class="token operator">:</span> <span class="token number">2112</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>Viper可以通过传入<code>.</code>分隔的路径来访问嵌套字段:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token function">GetString</span><span class="token punctuation">(</span><span class="token string">"datastore.metric.host"</span><span class="token punctuation">)</span> <span class="token comment">// (返回 "127.0.0.1")</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><h3 id="3-3-提取子树"><a href="#3-3-提取子树" class="headerlink" title="3.3 提取子树"></a>3.3 提取子树</h3><ul><li>例如,<code>viper</code>实例现在代表了以下配置:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">app</span><span class="token punctuation">:</span> <span class="token key atrule">cache1</span><span class="token punctuation">:</span> <span class="token key atrule">max-items</span><span class="token punctuation">:</span> <span class="token number">100</span> <span class="token key atrule">item-size</span><span class="token punctuation">:</span> <span class="token number">64</span> <span class="token key atrule">cache2</span><span class="token punctuation">:</span> <span class="token key atrule">max-items</span><span class="token punctuation">:</span> <span class="token number">200</span> <span class="token key atrule">item-size</span><span class="token punctuation">:</span> <span class="token number">80</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>执行后:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">subv <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">Sub</span><span class="token punctuation">(</span><span class="token string">"app.cache1"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li><code>subv</code>现在就代表:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">max-items</span><span class="token punctuation">:</span> <span class="token number">100</span><span class="token key atrule">item-size</span><span class="token punctuation">:</span> <span class="token number">64</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><ul><li>假设我们现在有这么一个函数:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">NewCache</span><span class="token punctuation">(</span>cfg <span class="token operator">*</span>Viper<span class="token punctuation">)</span> <span class="token operator">*</span>Cache <span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><ul><li>它基于<code>subv</code>格式的配置信息创建缓存。现在,可以轻松地分别创建这两个缓存,如下所示:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">cfg1 <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">Sub</span><span class="token punctuation">(</span><span class="token string">"app.cache1"</span><span class="token punctuation">)</span>cache1 <span class="token operator">:=</span> <span class="token function">NewCache</span><span class="token punctuation">(</span>cfg1<span class="token punctuation">)</span>cfg2 <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">Sub</span><span class="token punctuation">(</span><span class="token string">"app.cache2"</span><span class="token punctuation">)</span>cache2 <span class="token operator">:=</span> <span class="token function">NewCache</span><span class="token punctuation">(</span>cfg2<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-4-反序列化"><a href="#3-4-反序列化" class="headerlink" title="3.4 反序列化"></a>3.4 反序列化</h3><p>你还可以选择将所有或特定的值解析到结构体、map等。</p><p>有两种方法可以做到这一点:</p><ul><li><code>Unmarshal(rawVal interface{}) : error</code></li><li><code>UnmarshalKey(key string, rawVal interface{}) : error</code></li></ul><p><img src="/img/image-20210609094655146.46d6e697.png"></p><ul><li><code>main.go</code></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/spf13/viper"</span><span class="token punctuation">)</span><span class="token keyword">type</span> Config <span class="token keyword">struct</span> <span class="token punctuation">{</span>Port <span class="token builtin">int</span> <span class="token string">`mapstructure:"port"`</span>Version <span class="token builtin">string</span> <span class="token string">`mapstructure:"version"`</span>MySQLConfig <span class="token string">`mapstructure:"mysql"`</span><span class="token punctuation">}</span><span class="token keyword">type</span> MySQLConfig <span class="token keyword">struct</span> <span class="token punctuation">{</span>Host <span class="token builtin">string</span> <span class="token string">`mapstructure:"host"`</span>DbName <span class="token builtin">string</span> <span class="token string">`mapstructure:"dbname"`</span>Port <span class="token builtin">int</span> <span class="token string">`mapstructure:"port"`</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 读取配置文件</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigFile</span><span class="token punctuation">(</span><span class="token string">"./config.yaml"</span><span class="token punctuation">)</span> <span class="token comment">// 指定配置文件路径</span>err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 查找并读取配置文件</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// 处理读取配置文件的错误</span><span class="token function">panic</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Fatal error config file: %s \n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">var</span> c Config<span class="token keyword">if</span> err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">Unmarshal</span><span class="token punctuation">(</span><span class="token operator">&</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"viper.Unmarshal failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"c:%#v\n"</span><span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>config.yaml</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8081</span><span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">"v0.0.2"</span><span class="token key atrule">mysql</span><span class="token punctuation">:</span> <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"127.0.0.1"</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">13306</span> <span class="token key atrule">dbname</span><span class="token punctuation">:</span> <span class="token string">"sql_demo"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="04-使用Viper示例"><a href="#04-使用Viper示例" class="headerlink" title="04.使用Viper示例"></a>04.使用Viper示例</h2><ul><li>目录结构</li></ul><p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATIAAAClCAYAAADf0nGKAAAgAElEQVR4nO2de1Bc95XnP7cRCMSzuyX0tCMQMIptybIaeQxrxxkTawxRElkR+JHE8sxmmsxW7dbCOrVTW1bKFWd3djYOpGpSKdMzdqzYlcSgTOREBgcFPyJHnVhCfuDEuBtoWbb14v0SCETf/aMf3G4aaOhuuhvOp6qr4N7f4/SF/vY55/e79ygtLS2qTqcjOTmZm2++mYGRCQRBEGKd/u5PAZicnEQXZVsEQRBCRoRMEIS4R4RMEIS4R4RMEIS4R4RMEOIAT2JbCIwImSAIcY8ImSAIcc+qaBsgxD9Op5PRkSGujg4zNXUdgISEVaxJTSc1LQOdLrzfl39ue5f3295ZVN9bduzi5h23htWeeGRs7CoAKSlromxJeAhJyLp7+xmfuBaSAclJq1ln1Ic0hhAdVFVloK+bvp4rOJ1TM84PDfSh0yVgWJtNlmEdiqKEZd7BwQE+Pv/RovrecOPWsNigZXx8jLdbT6OqasDziqJwm2kPyckpYZ97MdT/4nnOn3MAcOPWHCoe/EaULQqdkITscm8vQyMjIRmQkZYmQhaHOJ1TXPj4HGNXR1AUHRlZBhITk+jtvgSAcd0GJicnGB4coOfKRUZHhtl0w1Z0uoSQ587MzOKGGz+z6L7hJjk5hfSMDH7b+JsZYqYoCn9b9qWYETGAkeHhgD/HMys+tFTVLp75yu18l+/xxkuV5ITJa1jOqKrqFbGk1cls2pJDYlISV0env9SSU1IxrF2PwbieC584GLs6woWPz7H5xtyQPbObd9wac+HhLTt2AfiImUfEPOeiwdWrVxkZHiJ7/QaczinefecsQ4MD3vNDgwO8ffY0t+7ajU6XwJXLl0hLz2DNmvgKOWNeyByWUu5+4rTvwUIRnWgy0NftFbEbtubPmQNLTErihq35fHzOztjVEQb6utEbsyNil9Pp5PWWZgA+X7I37Lm5+dCKGRB1EVNVlVdefomuTjs3bs1h/OpVrly57NPm+vXrtDQ30fbOWZLXrOH8OQe52/K5/+CDYUsFLAUhC1m20UC20bCovld6+xi/FsRN6hrh8nhQd3+FsIiZouTyzV/38M2QRpmJqrbwT5sfgBe6+Zd74ucfYj6cU1P09VxBUXRs2pITlFjodK62H3V9SF/PFTKzjOgSQg8xtUxMTHD817+kq8MOwMBgP/u+/FWSkpLCOs98aIUrmiIG4OjqoKvTdT08ObHZ0ApcV6cdR1cHudvyI2pfOAlZyFYnJaFLXkPHaOBE52zkpSqsThoJTsg0KEou//mH3+PlOx/n6dcq+Zd7FtRdCJHR0SGczikyMg0k+olEUlIShrXrvT9rSUxKIj0ji6HBPkZHh0jPCF9edGRkmP+o/5nvh7HDzi9e+AkHKh4mLS09bHMFQ7QFzMOlixcCHt+x8zZ23HobAG3vvk3be28H7LuihAxgeArODk0uqM/65BC+KXPyKdD8+vq31/LIh9/jp/te4pEnTvt6cF1Pc+DOx2nVtH/Iz0vy9Nd6eDP6BQhnPV7Xz/3a8G9l0+Hw19fxc8D0xFv8ypwb1Nu7fOkinR22oNpuyytg/YaNQbUNB2NXRwFm5MSSkpJYlZiEcd0G77HrkxNMTEx/UXmEb+zqaNiE7Nr4OL9/7XesTk4OuADw+9d+R8m9paxOTg7LfB6GBgcYHBxcVN/MzEwyIrDo4E/xnXeTkZnJiVdeZmrKtaq8Y+dt/G3Zl7xtNm3eAuAVs4SEBO6974thEeOlvEYxnyMLiMOOjT18UbuSfuZxHvmrFzl/ocR7SH21ms98/ac89EI3v7pHK1DruH8OYXH1+4DvvNnNr3IDh7OesU1PvMV59zhq19M88xp809zER/+w+NByXfZ6/nDyNbo6O+Zsl7stjzuK71rQ2KFyfdL1heVZnfRgWLveR8QABgf66OvxzcloxwgHq5OTKfvS/WEbL1jeb3uXU2++sai+xXfeTfGdd4fZosDcsmMXr554ZVrIdt02o82OXbf5CFm4PMqlvEZxt7NfVVv4pzsfp/Vrj/HNXK1APMJP/989vu3cQqMVEiX3W9Q8sYfWJ37E6wH2/ahqF8/88Kc89EKjd3xPOGs68xItjuk2fO1FHzFUcr/FN8OQD9PpdHxpfzkbN22etc3GTZv50v7yJU9oC0IsEh8e2ZnHuXvz495ftV6Ql8ICfAILhx0bUJCXM2O4rXmfBT6gwwGf93fKHM28fAZa3SGhPwXnAFxtHvrvkUvQJSYmcqD8IX7+/E/o6+v1OWcwGDlQ/hCJiYkRm382VrnnNK7bQHJKqvd4oKR6ZpaBlDVp3t/Hx0bp7b7kHSMcXBsfp+VEE8PDQwHPp6dnRCS0vGXHrYveXJuZmRlWW+bi/bZ3vN4YQNs7b7Np0xafNm3vTOfIpqameL/tnbB4ZUt5jeJDyKKw3cI/j6ZF7VoaG1JS1nDwga/xs+d/wsiIa+NiWlo6Bx/4WtRuLUlZk8pgfy+TExPexL6H65MTDA70AS4RW5Xoypt5GB7s944RLlYnJ/O5v/nCjGQ/QHb2er745QNhFzGAjMysJclzhcKpN9+YEdp5QsjZkv1TU1O88vKvGRocDDn8XcprtHzjEveCgK1j5rLzuY4PgM8SwFmbs9+C2oSJjMwsDlQ8zOrkZFYnJ3Og4uGofoBSUzPQ6RIYHhpgcsJ3xXliYoK+nsv09Vz2SfIDTE5MMDw0gE6XQGpqRlhtSktL58Gv/x25edOrbLl5+Tz49b9b8hVLcHlBi70XNJxs2Lgp4PG2997mZ88/y8+efzbgiuVcfWOVZStkilLCt57YQ+sTt/M/X53OhamvVnP3E6d56IUf8PkAHt6s/bqe5v5vt8w7tvZ3CI/YZWevZ/+BB9h/4AGys9fP3yGC6BJc906qqpMLnzhwOp3z9nE6XW1V1YlhbXbY95CBK7Tdf+ABdptuZ7fpdvYfeGDJ95CBS8R+2/gbftv4m6iLWU5unncLxY1bc+b838nOXs+NW13f7Lnb8snJzVsSG8NFfISWiyTH3MRHedV8xifftYfvvNntt1Aws98blHK3tl/h93jjpXt82swY+2svcv77ngWCEv7vC4/wma/fzo1PLGz7RSAWe29hJMgyrGN0ZJixqyN8fM7uvUUpEJMTE1z4xMHEtXFS1qSRZVgXMbt0Oh333HtfxMafD4+IeW5R8uzwj9a+MkVRuO+LX5lxi9Ibr57g+nXXU0pWrVrF3ffcO+MWpXja1Q+ghFIO7n1bB5npaaxfv4Heyfm/mbUYE3VcvnyJweERbimIrvq//u21PMKLnP9+yfyNBWDmTePpmVmz3jSuqk5S1qSF7abxWHyMj7+IeQjX/Zb93Z+iXzf7KvZCePbffkxfbw8ABuNa/v4f/ktYxl1qtOXgQvbIBodHgEvztvPnsrdvdFHVLjo+BNO+QAkzYTZ0ugQ235jrfYzPkDvJ78EjaDpdAsZ1G5f9Y3yGh4Yo+k+fC3h+eGiI8fGxmHkCRlp6ulfI0tKXPocYCUISsrQ1KYxcHQtJkNLWRPmP+9qP+O6ZPXznhyJkC0VRFPTGbDL1a5f0wYqx+Bif2UQsFql48BvyYEUtW7eEx9WNBtqnajz0wtw5M2FudDod6RlZpGcszWpqLD7GJ95YLgLmYVkn++cix9zEeXO0rRAEIRws2+0XgiCsHETIBCEOCNeK5XJFhEwQhLhHhEwQhLhHhEwQhLhnxqqlZ7esIAhCvOAjZOnp6ezcuTNatgiCIATNe++95/1ZQktBEOIeETJBEOIeETJBEOIeETJBEOKemLrX8vyFy4yOjfkcS1q1ivXrjNF/SoYgCDFLTHlkH316ifbOj3xe733YScsfTmM/93FkJ7fXUqwoKEoxtfbIThUSTZUoioKiVNIUbVuEGTha6qg72kp/tA2Zk35aj9ZR1xL5mhNLRUx5ZLPhVFXet3XhdDr5q9zYeeSzsBgctNQ106l5yOK2e82UzHgcnKsdeysDnIu8fYubt5++2FawZUtcCJmHv3Sc4y8d5+Zss1afxV17FvGsqvwqTqlVi7RMCIb+1qM0tPaxbW8lZo1IOFqO0pp1EJNe2zqH3Dxobm1ld44Jvf9gESOUefWYDlZiipBlwuwsOLS8ePEiTz/9NP39s3/12Gw2nnnmGcb88l3CCsbRQv0ZMJXP9L5ySvxFzH18dyHGXgddS+zlRGteYfEs2CMbGRmhp6eH5557jkcffRS93vc/0GazUV9fT2pqKuPj46SkRD9J31SpUGYBMNOo1lEKgJ3a4gKqrYC5EfWxdooLqrFSRI3tFFX5TVQqZViAohobRzhEQbXVPaJ2HO8kKK5JXBTVYDtVhasY1/RY5sZGKCvD4p1nbtvttcW+8zbO+iaDmn/GezE3otaV+vUP4v25xzo13xsAoJ/W1g6MhRUBBcun3dF6zlBIxUETen0uOcYzOLr6Mc3o6Grb2ucKUVV1G3srS8gJx/lZ5nW01NHc77ZNY4n2+IBfG++5EmipP0OfO6RWt91Lpb+i97dyVNvGYPKZyzPW3hwHJ1r7ZpyfFUcLlhOd3l/VbSYKA7ULZn72UpHVSkNrn08b3N524Gs9c2yYLaWwOBYsZPn5+VRUVFBfXz9DzLQiFkjkokXpYzUUWaqxYuFYUx2lpYD9OA1WgCJqHisF2mftb60uoMDniIWy4u1eofAVG28nCorRiIm7Z1lZ0HZPC7Bm3gDdFzI/DYcosGraWsoobivCqj0WzPvDdV2UdrcQzkV/F45e0M+tYgHQYzLlcab5LA6T7wejv/UslFRi1oNHlJqPZnk/eKGdDzxvTm4eNDvo6jdNC3J/K60dkLfXNe5AgHeh9LVS32KiorLSJQr9rRytb6aOvdNi5mihrrmfwopKDmpsqj+Kj5gofa006++l0hykArhFTCsajpY6TvQp+KlxcPN3nqDFVI7ZrPeKU4Ol1SXM5hw8Ocbmllyf92Y50cm2vZUc9Jjd38rR+jqOFlZwcMH/FzNZ1KplQUEBFRUVjI6O8txzz9Hf3x+zIgZA/j7Ki1w/trW7liTtxxtw6Vg5++ZzKopqsKkqqqpiq3EPZG3guB2giafcH3Jzo6uNqtqoKQKs1Tzlv7ToHWseb8xey5NeB6nRd1wfFja/lXL3/I14nvRttUKNzdW30XuwnQ63HYe03pv/dbA8GeQqr5GseR/p78oxVWq9jJxc8uigy2+BTW8q0Xh3LuGhd8ArJKGeDzhvzm4Kjb04NDFnf5eDXvLInUNXVHUbe7XvSW+ipNAIHV24hnd5rHl7D/raVDIzxFXVbewN2o1x0NLcgcFU7uP55JRUYDJoy9YtYH6DiRJPI70JU56/Ta4cI/197pXbwDZ4rkHvmbOEY+100cl+j5jV19fz7LPPMjY2FpsiBkA++8qLqLZasTYcx15VRUe768NZVL6P+XTMfHjaq8mvOoy5ugwLVto7gI5jePTGUqZg8evb1m5HG6Npx5qTjnbcFro9Rtf7qDpsplrrpjUtbP7p91vKfjNYLPiIean34Fx2+F6HhuN2qoIKMReDO/ne5aAkx/cD7Gip40SntmjMtjCeDzSvntwcI2ccXfSbTOjpp8vRi7HQL4zyx5iFv4brDXo8q5w5uDzWvhMWOv37KqAfYNp7CjDWrLjFxDXXXO0WML/eMDOU9bMpK8uId//JHDb4XIMQJSOkVUutmMWuiLnI31dOUbUVq7WB4/bttFsAiiif1x3zw95OWyQMjGN2bJ/nGuoN6Jkt1zU/ObsLMda30ro7x+0xuMKXDvKmczGOFuqaPT1CPT/bvKDPzcF45gxnHSZKsrpw9BrJKQnP/3w4c0bxOH8ohLwhtqCggAcffDCmRQzQhJdWGg496fJczIfnTbYDWJ6sxRM9NT1V7fVQtucBedvxRHvTod30K7hkeAC847o8Hhd2ap+0zNIuzPMHsKNaE6faa93X0HMd5sTl3Sw6jNDnkqMN6RxddGCksGLaE+rXbuAK9fxs84I3nOrocrjCyjzTPAsY+IasXhM6wJhDrh6vlxPQhlCYddwBBnqDaRdJGzzH9MznMAZDWHb25+XlxbaIAZ7wEvAmts3750lSe7BWU6AoKIom+e4RwfwqDrvzSpYyxb3r3v0qnhbAhZs7ndezVhe4x3Svsvq0i9D8AcbHUuYde3rVM7gvg5ySCgqNHTTXHaXV53+6n9ajnmPuHeczdsa7QrpeR5f3uKL0MeBRh/5WWs70+vQI9fxs84I76d/RSoujl7y5kmPeuTpp1u6id7RwolMhz+TJm+Wwu9BIX2sDPpvt+1s5GtLue88XSIvPNXe0+G5Ijtz8c4ztaKGhtY+8vfOE5UESVxtiQ2U6rwNgJlgdK6pppLyhbFpEzL4rdaV1KrbtAVb2dmwPLh8W2FqqTtmguMBnXtv2J2fME5n5fcdX98/cfmFuVJlvwXIaVyI/t/UoDQ0WWt1HVdVAYUXgfWQ+vU0m8s40u0K6nBLKTf00uHM6qsFExd486j2hYajnZ53X0z+XPJrpoDCoUEw1mNib1YrFcsJ7zD+M05sOUs5Rr02efhUHQ/uY55RUspc6Tmiu+bZ7KzD113NG0y5S83vGNhtasGjHVg0UVpjn92aDRGlpaVF1Oh3Jycnccccd4Rl1kZw8/S49/YEWsINn0Tv7Z+C79yrkEE0IGc8+phn7r2J43tn2ngmh43lC7OTkZGx5ZDu35zF5fTKkMRJXJYbJGiHWCJR8X87zCsETU0KWmZ4abROEWEZv4mBlFO5kjNa8QtDElJCtLKZD1xn43F4kCLMx80kiHoK+fWmZEFM5MkEQhGDR5shi6sGKgiAIi2HZhJZ9N0ggJiwew8ex/FhgYT7EIxMEIe4RIRMEIe5ZNqGlFgkThGCQdMTyQTwyQRDiHhEyQRDinmUZWvojhX8FYXmzIoTso08vBbwZXWfr4qb8HPK33hAFq0LAXusulCI3tAsCrBAhm434LPzbRGVBNdTYUEXABAFY4ULmIaKFf8ONvZ22xTyiWxCWMTGT7JfCv0HiLQYiCIKHmBEybeHfQGLmKTc3NDTE+Ph4FCycjyYq53rMtL2WYsX3UdSVPqXa7NQWKyiVTdhrizXtKvE0a6pU3E9ptVJdoKAoxUGWYhOE5U3MCJmn8K+2VqaHmK6ZCa4q3EoZbTW26cIfR+B4k+Z8QTU7tMVBbDW0lSkU+yuRpYxDHHG3a8SMhTK34pXWqaiNZqDIXYdy/krlgrASiBkhgzgs/At4KxuZG31XD/OrqCoFaKKyzEJRjc33+fb5VRypKcJa/RQ+jllRDUe845TyWE0RWI7hX+dXEIRpYkrIwFfMnn322RgXMcB+nAbrHBWZ3HUwA9V+zN++A2ijXeuUhalgiCCsJGJOyGBazGK7erkgCLFCzG6/8BT+NRqNsS1i+dvZAbS126E0gC81x3l7exuwg/kKdQuCMDcx6ZF5iI/Cv648lrW6wHcVsqnS/fvs5wuqrZgb6wi6NKQgCAGJWY8snsivOoW6vRKlTJkuJmJuRK2b47x75VFWHQUhdJZN8RHts6X8n0cWW4V/hVhhrv8ZIfaJ2QK9kUIK/wrC8mZFCJkU/hWE5U1MJ/sFQRCCQYRMEIS4R4RMEIS4R4RMEIS4R4RMEIS4R4RMEIS4R4RMEIS4R4RMEIS4Z9WePXvQ6XR0dnZG25Zlx+WePtavNXDy9LsBz+/cniebdQUhDKyInf3R4IPOc/T0DbJ+rWHW+zxDvW1KEAQXElpGgA86z9He+VG0zVg4mgIpxbV2V7ET/yIqcUS82y8EjwhZmIlbEdMW/lVVTlVBe1u0bRKE4JDQMozEr4gRoPBvPlWnVKqiapQgBIcIWZhwOp2sM2SxzpDlPRZXj/5xF/4tj7YdgrAIJLQME2PXJujuG/B5XbjSzQed50IcOXqFfwPmmJoqfeYqrm3yjh/Q+lnyVL7H3TYGsM3b1t9+d9/Z3pOwshAhCxNj4+O0d34U8LVoYqzwr722GKXMglkz3+H2Mqqts7+F0v1msDZw3FcNOWYB8+Eq8gF77VNwxDOmjZoiC2X+4qe131ZDkbWaAkWhoP1wwPckrCxEyGKWWCv828RT1dYZ85XW2agpmqNb6WPUFFlp0CpZ0zEsmPGUAs2vqtMIZz5Vh81gbadjNvvzqzhsBjDT6DWmlP1moK1dVilXIJIjCxNr9Vncv/fu8A3oKfx7OLTCv94KdKEW/p1jvrnJZ195EdUNx7FXVZHvFuiiGptP9aimSoUyi7af2XeYQPYXbSdP82ve9iKQldYViQhZiDjbbKgDw2EZK+EuU1jGiTXy95VTVN3AcXsVVRynwVpE+RGPLDVRqZRhwUyj6i6N11SJUhZFg4W4Q4QsRN47+zajV7r567ztAEw5nZx47yzpa1KZmLzG39y8y9v2rc4PmZpyUlTwWY6f/RNFBTdhTEsPPHCsFf6ddb4O2q3Ajrn67qO8qJqG43b20YC1qJxpHTuGhSJqbNP1PT32C0KwSI4sRDYY1/KLP7xO15VLAPyu7Sytjg56hwept57kRNvbALRf+JjnXm/mj/YPAHj57Fv0jgzNMXKsFf515aCs1YfQriM0VZbhExF6Vll9jHLlvawNT/FUg9Wb5J/GSrsnIWav5dBcqweCEAARshBZl5nFlwvv4Ke//x0f93bzyjtneORzJegUhTVJq3np9ClaHXaefe23pCSuXtDY+VWnUBvNWMo0WxOO7fcm2wOeL2ujxqb6LgCEidI6lUazZ4uG63Vsv3+yP4/tgZL/pfsxWy1YrNNJfveg2GqKpt/DITjSaA4wgCDMjjI0NKR6nn6xc+fOaNuzaKJVbHXqZCvOnj5+cPyXfNLTwxd23sa+3X/N8bN/wn7xU3LXb+SVd85QsHEzues30nX5IlVfPMA//vu/UrXvAAUbNnvHWnX/F5bM7vBhp7a4gOodjahzqmew7ZYOKdAb32gL9IpHFgYUFMp27eHa9Unu21Xoc+7LhXdwzy27MH+hjATdMrzcntXV/fOIk92V5K95LDZETFheSLI/TKxa5bqUq3QJPscVFMrvuCtgnz/aPsB24RMAbv1MLjmRNTFk7LXFHOKIZl+b60Zza1ENR+bRp6anqrGaGzkVzgUIQXAjQhYmUhKTKNg4HSYa0zIYM074tNEeK9i4md7hIXqHXQn/3OwNS2fsIsmvOsXhSgVF0Rw0zx0q2muLKai2QlENtlPijQmRQXJkITJ1shW1pz8sY8Vnjix+kRxZfKPNkYlHFiLLdROrIMQTyzD7LAjCSkOETBCEuEeETBCEuEeETBCEuGdZJvu1q1GCICx/xCMTBCHuESETBCHuicnQ8uLFi4yMjJCfH3yIuNANjZ7SbWv1Wdy151Z+1fxGwHZ37bmVtfqsgOeWN7F3k7cgzEbMeWSqqvLSSy/x4osvYrPZIjJHXNefFARhBjEnZIqi8MADD5Camkp9fX3YxUxETBCWHzEZWur1eh599FGee+456uvrqaiooKCgIORx476IriAIAYk5j8yDR8zC6ZlFroguwReunbegbqChQyhQG+x8fvYrlccXfy0EYYmJWSGDaTFLSUmhvr6ejo6O+TvNgbaI7qk/nea1k3/wvi5e+MT76uu5sqBxgy5cu5CCuv4spkBtsPM1Vc6wv5HqOQvvCkIsEdNCBtDd3c3Y2BipqakYjcawjfvWyTd45T8aAr7eOhl4BTMwwRauXWBBXX8WXKA22PkCt5u38K4gxBAxmSPzYLPZqK+vJzU1lUcffRS9Xh+2se+7vzw8AwVbuHahBXX9WWiB2mDnY7GFdwUhdohZIYukiAG89eYb9HUHDiEN67K5/c4wVg0XBCGixGRoGWkRAzCsXcf6LTcEfBnWrgt+IG3hWh/chWvnbbcEBXXnmi9Y+wUhhok5IVNVlZMnT0ZUxAAMa9eyYfMWn9ctO2/jtj13kLf9pgWMFGzh2mAL6gYqcLsYgp0vWPsFIXaJudBSURQefvhhrl27RlZW5G4NeuvkG1z89BOfY/cdKGfjpi0LHqu0TqURhbIChWr3MXOjjZq2Au/v4C6ou70SpUzRiEQRNTYVb2Gi2QrcLoLg5gvefkGIVZZN8ZFg6Okf4OTpdxfUZ/H3Wsq9ioIQSaRA71IQbOFaQRBCRoQsDNhri/02tE4XrpXC2oIQeWIuRxZJElclLjhMDOZezMUUrhUEIXysqByZIAjLh2VXoFee0R8/SEVvIRJIjkwQhLhHhEwQhLhnWYSWWiR0iT0k9BcijXhkgiDEPSJkgiDEPcsutFwI5wau0XZljO7RSQCyUxO5JTuFrVmro2yZIAgLYcUJ2dVJJzqg4YM+/nxlzOecrXecN88Pc3N2CuWfNeAE1iSK0yoIsc6KErKB8Sl+cOoiqYkJDFy7Pmu7P18Z49PBy4xOTvE/ijeSlZywhFZO01SpUNZWg+1U1cynwwqC4GVFCZlOAQUYuHadGzKTuDc3k+7RSX5jGyAxQWHPplQATl8YZeDadZJ0Cjpl7jEjh532tvlbCYKwwpL9GasT+OpNBpJ0Ov6xcD2nX/klyidtPLzDSPlNBtb1tTN0+mXKbzIA8NWbDGSsjo43BvlUnVJRxRsThHlZUUIGkJqkIzttFToFDh8+zNNPP83m9CS2ZCRRV1fH448/zpaMJEDyY4IQL6y4T2pqYgKfDE0wfG2KkydP8uMf/5hf/fHP/Pqtdv71Rz/i5JtvYv14BIC0pOC8scUX0LW7ivjOcr6pcnocb1v/eQIV5Z1pYMSKBwtCLLDihGz1KlfS659PXuD3fcnUne3F9ovv8+HPvk/d2V5+/lECJ88PAwv0yBZRQNde+xQc8RTFtVFTZKHMK1xBzBOoKK8fS1I8WBCizIoSMtXpZMjxFxScOIF3L1+le0LHff/tf/OF//pdeiZ09I25VjMVnAw4/ozqdAY3+IIL6EJ+VZ3m2fn5VB02g7WdOeupa5rfYNYAAAISSURBVOdxFxjBcmwWr2yJigcLQpRZUUI2NjzAh68dI3ly1Od48yUdJy75XorkyVHaXz3G2PBAcIMHW0DXj6ZKTShXFkTdokDzzEaYiwcLQqyyooRsTaaBwkceYywxfd62Y4npmL7xGGsyDRGyxlX2rcxiptETyjWaIzSXICxvVpSQAVxbwNa58Uhus2s6hoUiamye+pKewrlhJFaLBwtCmFlxQjY8EWTOCxiZmIqgJQBW2j0JMXsth2Zk4BeKf3HfcBcPFoTYZMUJ2ZRTDbrtApounNI6bDVFWMrc+bFDcCTk0HJmcd/SOpVGs5Xqgulc3LH9/sl+dzHfRvO0PYqCUtZGjU1FaqgIsc6yKD6ifXDffA9WHJmY4v+cvDCvSOkU+F93bQp6L1l8sbTFgxfy9xGEYFnRBXrTkhL4/NaMedt9fmvGMhUxpHiwsOxYcUIGkPLH5ylZc5lVAe4IX6VTKFlzmZQ/Ph8Fy8KPFA8WVgIr6ukXHnJ334lhy1aKV2fQ3jtG/5grqa9PSWC7MQXdtTT69Mvj0kjxYGElsDw+rQtk82d3eX82bUyd2SApy6dNvFNap6LWRdsKQYgcKzK0FARhebHsPDIpPSYIKw/xyARBiHtEyARBiHuWRWgpmywFYWUjHpkgCHFP3Hhkg8OjvOe+w3rn9jwy0wNsmxAEYUUSN0I2eX2Snv4B78+CIAge/j8UQ8AdT/eHsAAAAABJRU5ErkJggg==" alt="img"></p><h3 id="4-1-x2F-conf-x2F-config-yaml"><a href="#4-1-x2F-conf-x2F-config-yaml" class="headerlink" title="4.1 ./conf/config.yaml"></a>4.1 ./conf/config.yaml</h3><figure><div class="code-wrapper"><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8123</span><span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">"v1.2.3"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></div></figure><h3 id="4-2-gin中使用viper案例"><a href="#4-2-gin中使用viper案例" class="headerlink" title="4.2 gin中使用viper案例"></a>4.2 gin中使用viper案例</h3><ul><li>这里用一个demo演示如何在gin框架搭建的web项目中使用<code>viper</code>,使用viper加载配置文件中的信息</li><li>并在代码中直接使用<code>viper.GetXXX()</code>方法获取对应的配置值。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/fsnotify/fsnotify"</span><span class="token string">"github.com/gin-gonic/gin"</span><span class="token string">"github.com/spf13/viper"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 第一:viper配置</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span> <span class="token comment">// 还可以在工作目录中查找配置</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigName</span><span class="token punctuation">(</span><span class="token string">"config"</span><span class="token punctuation">)</span> <span class="token comment">// 配置文件名称(无扩展名)</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span><span class="token string">"yaml"</span><span class="token punctuation">)</span> <span class="token comment">// 如果配置文件的名称中没有扩展名,则需要配置此项</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"./conf/"</span><span class="token punctuation">)</span> <span class="token comment">// 指定查找配置文件的路径</span>err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 读取配置信息</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// 读取配置信息失败</span><span class="token function">panic</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Fatal error config file: %s \n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二:实时监控配置文件的变化</span>viper<span class="token punctuation">.</span><span class="token function">WatchConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span>viper<span class="token punctuation">.</span><span class="token function">OnConfigChange</span><span class="token punctuation">(</span><span class="token keyword">func</span><span class="token punctuation">(</span>e fsnotify<span class="token punctuation">.</span>Event<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 配置文件发生变更之后会调用的回调函数</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Config file changed:"</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token comment">// 第三:读取配置</span>r <span class="token operator">:=</span> gin<span class="token punctuation">.</span><span class="token function">Default</span><span class="token punctuation">(</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/version"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>c <span class="token operator">*</span>gin<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>c<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">,</span> viper<span class="token punctuation">.</span><span class="token function">GetString</span><span class="token punctuation">(</span><span class="token string">"version"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>r<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="4-3-结构体变量保存配置"><a href="#4-3-结构体变量保存配置" class="headerlink" title="4.3 结构体变量保存配置"></a>4.3 结构体变量保存配置</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"github.com/spf13/viper"</span><span class="token punctuation">)</span><span class="token keyword">type</span> Config <span class="token keyword">struct</span> <span class="token punctuation">{</span>Port <span class="token builtin">int</span> <span class="token string">`mapstructure:"port"`</span>Version <span class="token builtin">string</span> <span class="token string">`mapstructure:"version"`</span><span class="token punctuation">}</span><span class="token keyword">var</span> Conf <span class="token operator">=</span> <span class="token function">new</span><span class="token punctuation">(</span>Config<span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 第一:viper配置</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span> <span class="token comment">// 还可以在工作目录中查找配置</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigName</span><span class="token punctuation">(</span><span class="token string">"config"</span><span class="token punctuation">)</span> <span class="token comment">// 配置文件名称(无扩展名)</span>viper<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span><span class="token string">"yaml"</span><span class="token punctuation">)</span> <span class="token comment">// 如果配置文件的名称中没有扩展名,则需要配置此项</span>viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"./conf/"</span><span class="token punctuation">)</span> <span class="token comment">// 指定查找配置文件的路径</span>err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 读取配置信息</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// 读取配置信息失败</span><span class="token function">panic</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Fatal error config file: %s \n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二:将读取的配置信息保存至全局变量Conf</span><span class="token keyword">if</span> err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">Unmarshal</span><span class="token punctuation">(</span>Conf<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token function">panic</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"unmarshal conf failed, err:%s \n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Conf:%#v\n"</span><span class="token punctuation">,</span> Conf<span class="token punctuation">)</span> <span class="token comment">// 打印</span><span class="token punctuation">}</span><span class="token comment">/*Conf:&main.Config{Port:8123, Version:"v1.2.3"} */</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="12-viper配置管理"><a href="#12-viper配置管理" class="headerlink" title="12.viper配置管理"></a>12.viper配置管理</h1><h2 id="01-viper介绍"><a href="#01-</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>zap</title>
<link href="http://coderedeng.github.io/2022/06/07/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20zap/"/>
<id>http://coderedeng.github.io/2022/06/07/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20zap/</id>
<published>2022-06-07T12:12:28.000Z</published>
<updated>2024-03-27T12:43:14.617Z</updated>
<content type="html"><![CDATA[<h1 id="11-zap日志包"><a href="#11-zap日志包" class="headerlink" title="11.zap日志包"></a>11.zap日志包</h1><h2 id="01-日志模块介绍"><a href="#01-日志模块介绍" class="headerlink" title="01.日志模块介绍"></a>01.日志模块介绍</h2><ul><li><a href="https://www.liwenzhou.com/posts/Go/zap/">参考博客(opens new window)</a></li></ul><h3 id="1-1-介绍"><a href="#1-1-介绍" class="headerlink" title="1.1 介绍"></a>1.1 介绍</h3><p>在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能</p><ul><li>能够将事件记录到文件中,而不是应用程序控制台。</li><li>日志切割-能够根据文件大小、时间或间隔等来切割日志文件。</li><li>支持不同的日志级别。例如INFO,DEBUG,ERROR等。</li><li>能够打印基本信息,如调用文件/函数名和行号,日志时间等。</li></ul><h3 id="1-2-默认的Go-Logger"><a href="#1-2-默认的Go-Logger" class="headerlink" title="1.2 默认的Go Logger"></a>1.2 默认的Go Logger</h3><ul><li>实现一个Go语言中的日志记录器非常简单——创建一个新的日志文件,然后设置它为日志的输出位置。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"log"</span><span class="token string">"net/http"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token comment">// 第一:设置Logger</span><span class="token keyword">func</span> <span class="token function">SetupLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>logFileLocation<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span><span class="token string">"./test.log"</span><span class="token punctuation">,</span> os<span class="token punctuation">.</span>O_CREATE<span class="token operator">|</span>os<span class="token punctuation">.</span>O_APPEND<span class="token operator">|</span>os<span class="token punctuation">.</span>O_RDWR<span class="token punctuation">,</span> <span class="token number">0744</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">SetOutput</span><span class="token punctuation">(</span>logFileLocation<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二:使用Logger</span><span class="token keyword">func</span> <span class="token function">simpleHttpGet</span><span class="token punctuation">(</span>url <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Error fetching url %s : %s"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> err<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Status Code for %s : %s"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Status<span class="token punctuation">)</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">SetupLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"www.baidu.com"</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"http://www.baidu.com"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-3-Go-Logger的优势和劣势"><a href="#1-3-Go-Logger的优势和劣势" class="headerlink" title="1.3 Go Logger的优势和劣势"></a>1.3 Go Logger的优势和劣势</h3><ul><li>优势<ul><li>它最大的优点是使用非常简单。</li><li>我们可以设置任何<code>io.Writer</code>作为日志记录输出并向其发送要写入的日志。</li></ul></li><li>劣势<ul><li>仅限基本的日志级别</li><li>只有一个<code>Print</code>选项。不支持<code>INFO</code>/<code>DEBUG</code>等多个级别。</li><li>缺乏日志格式化的能力——例如记录调用者的函数名和行号,格式化日期和时间格式。等等。</li><li>不提供日志切割的能力。</li></ul></li></ul><h2 id="02-zap基本使用"><a href="#02-zap基本使用" class="headerlink" title="02.zap基本使用"></a>02.zap基本使用</h2><h3 id="2-1-zap介绍"><a href="#2-1-zap介绍" class="headerlink" title="2.1 zap介绍"></a>2.1 zap介绍</h3><ul><li>Uber-go zap优势<ul><li>它同时提供了结构化日志记录和printf风格的日志记录</li><li>它非常的快</li></ul></li><li>安装</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">go get <span class="token parameter variable">-u</span> go.uber.org/zap<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></div></figure><p>1</p><h3 id="2-2-Sugared-Logger和Logger"><a href="#2-2-Sugared-Logger和Logger" class="headerlink" title="2.2 Sugared Logger和Logger"></a>2.2 Sugared Logger和Logger</h3><ul><li>Zap提供了两种类型的日志记录器—<code>Sugared Logger</code>和<code>Logger</code>。</li><li>在性能很好但不是很关键的上下文中,使用<code>SugaredLogger</code>。</li><li>它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。</li><li>在每一微秒和每一次内存分配都很重要的上下文中,使用<code>Logger</code>。</li><li>它甚至比<code>SugaredLogger</code>更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。</li></ul><h3 id="2-3-zap日志记录器Logger"><a href="#2-3-zap日志记录器Logger" class="headerlink" title="2.3 zap日志记录器Logger"></a>2.3 zap日志记录器Logger</h3><ul><li>通过调用<code>zap.NewProduction()</code>/<code>zap.NewDevelopment()</code>或者<code>zap.Example()</code>创建一个Logger。</li><li>唯一的区别在于它将记录的信息不同<ul><li>例如production logger默认记录调用函数信息、日期和时间等。</li></ul></li><li>通过Logger调用Info/Error等。</li><li>默认情况下日志都会打印到应用程序的console界面。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"go.uber.org/zap"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token comment">// 第一步:创建一个Logger</span><span class="token keyword">var</span> logger <span class="token operator">*</span>zap<span class="token punctuation">.</span>Logger<span class="token keyword">func</span> <span class="token function">InitLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>logger<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> zap<span class="token punctuation">.</span><span class="token function">NewProduction</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二步:通过Logger调用Info/Error等输出日志</span><span class="token keyword">func</span> <span class="token function">simpleHttpGet</span><span class="token punctuation">(</span>url <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>logger<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token string">"Error fetching url.."</span><span class="token punctuation">,</span>zap<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token string">"url"</span><span class="token punctuation">,</span> url<span class="token punctuation">)</span><span class="token punctuation">,</span>zap<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>logger<span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"Success.."</span><span class="token punctuation">,</span>zap<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token string">"statusCode"</span><span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Status<span class="token punctuation">)</span><span class="token punctuation">,</span>zap<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token string">"url"</span><span class="token punctuation">,</span> url<span class="token punctuation">)</span><span class="token punctuation">)</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">InitLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">defer</span> logger<span class="token punctuation">.</span><span class="token function">Sync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"www.google.com"</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"http://www.google.com"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*在控制台会输出一下日志信息:{"level":"error","ts":1623143450.9749353,"caller":"gin_demo/main.go:23","msg":"Error fetching url..","url":"www.google.com","error":"Get \"www.google.com\": unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\tC:/aaa/gin_demo/main.go:23\nmain.main\n\tC:/aaa/gin_demo/main.go:12\nruntime.main\n\tC:/Go/src/runtime/proc.go:225"}{"level":"error","ts":1623143472.030105,"caller":"gin_demo/main.go:23","msg":"Error fetching url..","url":"http://www.google.com","error":"Get \"http://www.google.com\": dial tcp 69.171.247.32:80: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.","stacktrace":"main.simpleHttpGet\n\tC:/aaa/gin_demo/main.go:23\nmain.main\n\tC:/aaa/gin_demo/main.go:13\nruntime.main\n\tC:/Go/src/runtime/proc.go:225"}*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-zap日志记录器Sugared-Logger"><a href="#2-4-zap日志记录器Sugared-Logger" class="headerlink" title="2.4 zap日志记录器Sugared Logger"></a>2.4 zap日志记录器Sugared Logger</h3><p>现在让我们使用Sugared Logger来实现相同的功能。</p><ul><li>大部分的实现基本都相同。</li><li>惟一的区别是,我们通过调用主logger的<code>. Sugar()</code>方法来获取一个<code>SugaredLogger</code>。</li><li>然后使用<code>SugaredLogger</code>以<code>printf</code>格式记录语句</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"go.uber.org/zap"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token comment">// 第一步:创建一个Sugared Logger</span><span class="token keyword">var</span> sugarLogger <span class="token operator">*</span>zap<span class="token punctuation">.</span>SugaredLogger<span class="token keyword">func</span> <span class="token function">InitLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>logger<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> zap<span class="token punctuation">.</span><span class="token function">NewProduction</span><span class="token punctuation">(</span><span class="token punctuation">)</span>sugarLogger <span class="token operator">=</span> logger<span class="token punctuation">.</span><span class="token function">Sugar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第二步:通过Logger调用Info/Error等输出日志</span><span class="token keyword">func</span> <span class="token function">simpleHttpGet</span><span class="token punctuation">(</span>url <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>sugarLogger<span class="token punctuation">.</span><span class="token function">Debugf</span><span class="token punctuation">(</span><span class="token string">"Trying to hit GET request for %s"</span><span class="token punctuation">,</span> url<span class="token punctuation">)</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>sugarLogger<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Error fetching URL %s : Error = %s"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>sugarLogger<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"Success! statusCode = %s for URL %s"</span><span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Status<span class="token punctuation">,</span> url<span class="token punctuation">)</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">InitLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">defer</span> sugarLogger<span class="token punctuation">.</span><span class="token function">Sync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"www.google.com"</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"http://www.google.com"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*在控制台会输出一下日志信息:{"level":"error","ts":1623143450.9749353,......{"level":"error","ts":1623143450.9749353,......*/</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="03-定制logger"><a href="#03-定制logger" class="headerlink" title="03.定制logger"></a>03.定制logger</h2><h3 id="3-1-测试定制logger"><a href="#3-1-测试定制logger" class="headerlink" title="3.1 测试定制logger"></a>3.1 测试定制logger</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"github.com/natefinch/lumberjack"</span> <span class="token comment">// Lumberjack进行日志切割归档</span><span class="token string">"go.uber.org/zap"</span><span class="token string">"go.uber.org/zap/zapcore"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token comment">// 第一步:创建一个Sugared Logger</span><span class="token keyword">var</span> sugarLogger <span class="token operator">*</span>zap<span class="token punctuation">.</span>SugaredLogger<span class="token comment">// 第二步:将编码器从JSON Encoder更改为普通Encoder</span><span class="token keyword">func</span> <span class="token function">getEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> zapcore<span class="token punctuation">.</span>Encoder <span class="token punctuation">{</span>encoderConfig <span class="token operator">:=</span> zap<span class="token punctuation">.</span><span class="token function">NewProductionEncoderConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span>encoderConfig<span class="token punctuation">.</span>EncodeTime <span class="token operator">=</span> zapcore<span class="token punctuation">.</span>ISO8601TimeEncoderencoderConfig<span class="token punctuation">.</span>EncodeLevel <span class="token operator">=</span> zapcore<span class="token punctuation">.</span>CapitalLevelEncoder<span class="token comment">// 为此,我们需要将NewJSONEncoder()更改为NewConsoleEncoder()</span><span class="token keyword">return</span> zapcore<span class="token punctuation">.</span><span class="token function">NewConsoleEncoder</span><span class="token punctuation">(</span>encoderConfig<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第三步:使用Lumberjack进行日志切割归档</span><span class="token keyword">func</span> <span class="token function">getLogWriter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> zapcore<span class="token punctuation">.</span>WriteSyncer <span class="token punctuation">{</span>lumberJackLogger <span class="token operator">:=</span> <span class="token operator">&</span>lumberjack<span class="token punctuation">.</span>Logger<span class="token punctuation">{</span>Filename<span class="token punctuation">:</span> <span class="token string">"./test.log"</span><span class="token punctuation">,</span> <span class="token comment">// 指定日志将写到哪里去</span>MaxSize<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token comment">// 每次满1M进行切割</span>MaxBackups<span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token comment">// 最多报错5个文件</span>MaxAge<span class="token punctuation">:</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token comment">// 文件最多保存30天</span>Compress<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// 是否压缩/归档旧文件</span><span class="token punctuation">}</span><span class="token keyword">return</span> zapcore<span class="token punctuation">.</span><span class="token function">AddSync</span><span class="token punctuation">(</span>lumberJackLogger<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 第四步:重写InitLogger()方法</span><span class="token keyword">func</span> <span class="token function">InitLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>writeSyncer <span class="token operator">:=</span> <span class="token function">getLogWriter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>encoder <span class="token operator">:=</span> <span class="token function">getEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// zapcore.Core需要三个配置——Encoder,WriteSyncer,LogLevel</span>core <span class="token operator">:=</span> zapcore<span class="token punctuation">.</span><span class="token function">NewCore</span><span class="token punctuation">(</span>encoder<span class="token punctuation">,</span> writeSyncer<span class="token punctuation">,</span> zapcore<span class="token punctuation">.</span>DebugLevel<span class="token punctuation">)</span><span class="token comment">/*Encoder:编码器(如何写入日志),我们将使用开箱即用的NewJSONEncoder(),并使用预先设置的WriterSyncer :指定日志将写到哪里去。我们使用zapcore.AddSync()函数并且将打开的文件句柄传进去Log Level:哪种级别的日志将被写入。*/</span><span class="token comment">// 我们将使用zap.New(…)方法来手动传递所有配置,而不是使用像zap.NewProduction()这样的预置方法来创建logger。</span>logger <span class="token operator">:=</span> zap<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span>core<span class="token punctuation">,</span> zap<span class="token punctuation">.</span><span class="token function">AddCaller</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>sugarLogger <span class="token operator">=</span> logger<span class="token punctuation">.</span><span class="token function">Sugar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 实例化全局变量sugarLogger</span><span class="token punctuation">}</span><span class="token comment">// 第五步:函数调用全局sugarLogger写入log日志</span><span class="token keyword">func</span> <span class="token function">simpleHttpGet</span><span class="token punctuation">(</span>url <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>sugarLogger<span class="token punctuation">.</span><span class="token function">Debugf</span><span class="token punctuation">(</span><span class="token string">"Trying to hit GET request for %s"</span><span class="token punctuation">,</span> url<span class="token punctuation">)</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>sugarLogger<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Error fetching URL %s : Error = %s"</span><span class="token punctuation">,</span> url<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>sugarLogger<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"Success! statusCode = %s for URL %s"</span><span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Status<span class="token punctuation">,</span> url<span class="token punctuation">)</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">InitLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">defer</span> sugarLogger<span class="token punctuation">.</span><span class="token function">Sync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"www.sogo.com"</span><span class="token punctuation">)</span><span class="token function">simpleHttpGet</span><span class="token punctuation">(</span><span class="token string">"http://www.sogo.com"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="11-zap日志包"><a href="#11-zap日志包" class="headerlink" title="11.zap日志包"></a>11.zap日志包</h1><h2 id="01-日志模块介绍"><a href="#01-日志模块介绍" class</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>reflect</title>
<link href="http://coderedeng.github.io/2022/06/05/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20reflect/"/>
<id>http://coderedeng.github.io/2022/06/05/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20reflect/</id>
<published>2022-06-05T14:14:35.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="10-reflect"><a href="#10-reflect" class="headerlink" title="10.reflect"></a>10.reflect</h1><h2 id="01-反射"><a href="#01-反射" class="headerlink" title="01.反射"></a>01.反射</h2><ul><li><code>反射是指在程序运行期对程序本身进行访问和修改的能力</code></li></ul><h3 id="1-1-变量的内在机制"><a href="#1-1-变量的内在机制" class="headerlink" title="1.1 变量的内在机制"></a>1.1 变量的内在机制</h3><ul><li>变量包含类型信息和值信息 var arr [10]int arr[0] = 10</li><li>类型信息:是静态的元信息,是预先定义好的</li><li>值信息:是程序运行过程中动态改变的</li></ul><h3 id="1-2-反射的使用"><a href="#1-2-反射的使用" class="headerlink" title="1.2 反射的使用"></a>1.2 反射的使用</h3><ul><li>反射是指<code>在程序运行期对程序本身进行访问和修改的能力</code>。</li><li>程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。</li><li>在运行程序时,程序无法获取自身的信息。</li><li>支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中</li><li>并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。</li><li>Go程序在运行期使用reflect包访问程序的反射信息。</li></ul><h2 id="02-反射方法"><a href="#02-反射方法" class="headerlink" title="02.反射方法"></a>02.反射方法</h2><ul><li><code>反射可以在运行时动态获取程序的各种详细信息</code></li></ul><h3 id="2-1-TypeOf"><a href="#2-1-TypeOf" class="headerlink" title="2.1 TypeOf"></a>2.1 TypeOf</h3><ul><li><code>reflect.TypeOf()</code>获取<code>类型信息</code></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">reflectType</span><span class="token punctuation">(</span>x <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">TypeOf</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type:%v\n"</span><span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">var</span> a <span class="token builtin">float32</span> <span class="token operator">=</span> <span class="token number">3.14</span><span class="token function">reflectType</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token comment">// type:float32</span><span class="token keyword">var</span> b <span class="token builtin">int64</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token function">reflectType</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span> <span class="token comment">// type:int64</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-ValueOf"><a href="#2-2-ValueOf" class="headerlink" title="2.2 ValueOf"></a>2.2 ValueOf</h3><ul><li><code>reflect.Value</code>获取值</li><li><code>reflect.Value</code>类型提供的获取原始值的方法如下</li></ul><table><thead><tr><th align="center">方法</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">Interface() interface {}</td><td align="center">将值以 interface{} 类型返回,可以通过类型断言转换为指定类型</td></tr><tr><td align="center">Int() int64</td><td align="center">将值以 int 类型返回,所有有符号整型均可以此方式返回</td></tr><tr><td align="center">Uint() uint64</td><td align="center">将值以 uint 类型返回,所有无符号整型均可以此方式返回</td></tr><tr><td align="center">Float() float64</td><td align="center">将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回</td></tr><tr><td align="center">Bool() bool</td><td align="center">将值以 bool 类型返回</td></tr><tr><td align="center">Bytes() []bytes</td><td align="center">将值以字节数组 []bytes 类型返回</td></tr><tr><td align="center">String() string</td><td align="center">将值以字符串类型返回</td></tr></tbody></table><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">reflectValue</span><span class="token punctuation">(</span>x <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span>k <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">switch</span> k <span class="token punctuation">{</span><span class="token keyword">case</span> reflect<span class="token punctuation">.</span>Int64<span class="token punctuation">:</span><span class="token comment">// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type is int64, value is %d\n"</span><span class="token punctuation">,</span> <span class="token function">int64</span><span class="token punctuation">(</span>v<span class="token punctuation">.</span><span class="token function">Int</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">case</span> reflect<span class="token punctuation">.</span>Float32<span class="token punctuation">:</span><span class="token comment">// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type is float32, value is %f\n"</span><span class="token punctuation">,</span> <span class="token function">float32</span><span class="token punctuation">(</span>v<span class="token punctuation">.</span><span class="token function">Float</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">case</span> reflect<span class="token punctuation">.</span>Float64<span class="token punctuation">:</span><span class="token comment">// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type is float64, value is %f\n"</span><span class="token punctuation">,</span> <span class="token function">float64</span><span class="token punctuation">(</span>v<span class="token punctuation">.</span><span class="token function">Float</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">var</span> a <span class="token builtin">float32</span> <span class="token operator">=</span> <span class="token number">3.14</span><span class="token keyword">var</span> b <span class="token builtin">int64</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token function">reflectValue</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token comment">// type is float32, value is 3.140000</span><span class="token function">reflectValue</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span> <span class="token comment">// type is int64, value is 100</span><span class="token comment">// 将int类型的原始值转换为reflect.Value类型</span>c <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type c :%T\n"</span><span class="token punctuation">,</span> c<span class="token punctuation">)</span> <span class="token comment">// type c :reflect.Value</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-修改值"><a href="#2-3-修改值" class="headerlink" title="2.3 修改值"></a>2.3 修改值</h3><ul><li>想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。</li><li>而反射中使用专有的<code>Elem()</code>方法来获取指针对应的值。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">reflectSetValue1</span><span class="token punctuation">(</span>x <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token keyword">if</span> v<span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> reflect<span class="token punctuation">.</span>Int64 <span class="token punctuation">{</span>v<span class="token punctuation">.</span><span class="token function">SetInt</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span> <span class="token comment">//修改的是副本,reflect包会引发panic</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">reflectSetValue2</span><span class="token punctuation">(</span>x <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token comment">// 反射中使用 Elem()方法获取指针对应的值</span><span class="token keyword">if</span> v<span class="token punctuation">.</span><span class="token function">Elem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> reflect<span class="token punctuation">.</span>Int64 <span class="token punctuation">{</span>v<span class="token punctuation">.</span><span class="token function">Elem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">SetInt</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">var</span> a <span class="token builtin">int64</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token comment">// reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable value</span><span class="token function">reflectSetValue2</span><span class="token punctuation">(</span><span class="token operator">&</span>a<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-isNil-和isValid"><a href="#2-4-isNil-和isValid" class="headerlink" title="2.4 isNil()和isValid()"></a>2.4 isNil()和isValid()</h3><ul><li><p>isNil()</p><ul><li><code>IsNil()</code>报告v持有的值是否为nil。</li><li>v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;</li><li>否则IsNil函数会导致panic。</li></ul></li><li><p>isValid()</p><ul><li><code>IsValid()</code>返回v是否持有一个值。</li><li>如果v是Value零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic。</li></ul></li><li><p><code>IsNil()</code>常被用于判断指针是否为空;<code>IsValid()</code>常被用于判定返回值是否有效。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// *int类型空指针</span><span class="token keyword">var</span> a <span class="token operator">*</span><span class="token builtin">int</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"var a *int IsNil:"</span><span class="token punctuation">,</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsNil</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// nil值</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"nil IsValid:"</span><span class="token punctuation">,</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span><span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 实例化一个匿名结构体</span>b <span class="token operator">:=</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token comment">// 尝试从结构体中查找"abc"字段</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"不存在的结构体成员:"</span><span class="token punctuation">,</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">FieldByName</span><span class="token punctuation">(</span><span class="token string">"abc"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 尝试从结构体中查找"abc"方法</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"不存在的结构体方法:"</span><span class="token punctuation">,</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">MethodByName</span><span class="token punctuation">(</span><span class="token string">"abc"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// map</span>c <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token comment">// 尝试从map中查找一个不存在的键</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"map中不存在的键:"</span><span class="token punctuation">,</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">MapIndex</span><span class="token punctuation">(</span>reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span><span class="token string">"娜扎"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure></li></ul><h2 id="03-结构体与反射"><a href="#03-结构体与反射" class="headerlink" title="03.结构体与反射"></a>03.结构体与反射</h2><h3 id="3-1-查看类型、字段和方法"><a href="#3-1-查看类型、字段和方法" class="headerlink" title="3.1 查看类型、字段和方法"></a>3.1 查看类型、字段和方法</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token comment">// 定义结构体</span><span class="token keyword">type</span> User <span class="token keyword">struct</span> <span class="token punctuation">{</span> Id <span class="token builtin">int</span> Name <span class="token builtin">string</span> Age <span class="token builtin">int</span><span class="token punctuation">}</span><span class="token comment">// 绑方法</span><span class="token keyword">func</span> <span class="token punctuation">(</span>u User<span class="token punctuation">)</span> <span class="token function">Hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Hello"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 传入interface{}</span><span class="token keyword">func</span> <span class="token function">Poni</span><span class="token punctuation">(</span>o <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> t <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">TypeOf</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"类型:"</span><span class="token punctuation">,</span> t<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"字符串类型:"</span><span class="token punctuation">,</span> t<span class="token punctuation">.</span><span class="token function">Name</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 获取值</span> v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> <span class="token comment">// 可以获取所有属性</span> <span class="token comment">// 获取结构体字段个数:t.NumField()</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> t<span class="token punctuation">.</span><span class="token function">NumField</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> <span class="token comment">// 取每个字段</span> f <span class="token operator">:=</span> t<span class="token punctuation">.</span><span class="token function">Field</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%s : %v"</span><span class="token punctuation">,</span> f<span class="token punctuation">.</span>Name<span class="token punctuation">,</span> f<span class="token punctuation">.</span>Type<span class="token punctuation">)</span> <span class="token comment">// 获取字段的值信息</span> <span class="token comment">// Interface():获取字段对应的值</span> val <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">Field</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Interface</span><span class="token punctuation">(</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"val :"</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"=================方法===================="</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> t<span class="token punctuation">.</span><span class="token function">NumMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span> m <span class="token operator">:=</span> t<span class="token punctuation">.</span><span class="token function">Method</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>m<span class="token punctuation">.</span>Name<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>m<span class="token punctuation">.</span>Type<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> u <span class="token operator">:=</span> User<span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"zs"</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">}</span> <span class="token function">Poni</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-2-查看匿名字段"><a href="#3-2-查看匿名字段" class="headerlink" title="3.2 查看匿名字段"></a>3.2 查看匿名字段</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token comment">// 定义结构体</span><span class="token keyword">type</span> User <span class="token keyword">struct</span> <span class="token punctuation">{</span> Id <span class="token builtin">int</span> Name <span class="token builtin">string</span> Age <span class="token builtin">int</span><span class="token punctuation">}</span><span class="token comment">// 匿名字段</span><span class="token keyword">type</span> Boy <span class="token keyword">struct</span> <span class="token punctuation">{</span> User Addr <span class="token builtin">string</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> m <span class="token operator">:=</span> Boy<span class="token punctuation">{</span>User<span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"zs"</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"bj"</span><span class="token punctuation">}</span> t <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">TypeOf</span><span class="token punctuation">(</span>m<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span> <span class="token comment">// Anonymous:匿名</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%#v\n"</span><span class="token punctuation">,</span> t<span class="token punctuation">.</span><span class="token function">Field</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 值信息</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%#v\n"</span><span class="token punctuation">,</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>m<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Field</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-3-修改结构体的值"><a href="#3-3-修改结构体的值" class="headerlink" title="3.3 修改结构体的值"></a>3.3 修改结构体的值</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token comment">// 定义结构体</span><span class="token keyword">type</span> User <span class="token keyword">struct</span> <span class="token punctuation">{</span> Id <span class="token builtin">int</span> Name <span class="token builtin">string</span> Age <span class="token builtin">int</span><span class="token punctuation">}</span><span class="token comment">// 修改结构体值</span><span class="token keyword">func</span> <span class="token function">SetValue</span><span class="token punctuation">(</span>o <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span> <span class="token comment">// 获取指针指向的元素</span> v <span class="token operator">=</span> v<span class="token punctuation">.</span><span class="token function">Elem</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 取字段</span> f <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">FieldByName</span><span class="token punctuation">(</span><span class="token string">"Name"</span><span class="token punctuation">)</span> <span class="token keyword">if</span> f<span class="token punctuation">.</span><span class="token function">Kind</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> reflect<span class="token punctuation">.</span>String <span class="token punctuation">{</span> f<span class="token punctuation">.</span><span class="token function">SetString</span><span class="token punctuation">(</span><span class="token string">"kuteng"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> u <span class="token operator">:=</span> User<span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"5lmh.com"</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">}</span> <span class="token function">SetValue</span><span class="token punctuation">(</span><span class="token operator">&</span>u<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-4-调用方法"><a href="#3-4-调用方法" class="headerlink" title="3.4 调用方法"></a>3.4 调用方法</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token comment">// 定义结构体</span><span class="token keyword">type</span> User <span class="token keyword">struct</span> <span class="token punctuation">{</span> Id <span class="token builtin">int</span> Name <span class="token builtin">string</span> Age <span class="token builtin">int</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>u User<span class="token punctuation">)</span> <span class="token function">Hello</span><span class="token punctuation">(</span>name <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Hello:"</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> u <span class="token operator">:=</span> User<span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"5lmh.com"</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">}</span> v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span> <span class="token comment">// 获取方法</span> m <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">MethodByName</span><span class="token punctuation">(</span><span class="token string">"Hello"</span><span class="token punctuation">)</span> <span class="token comment">// 构建一些参数</span> args <span class="token operator">:=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>reflect<span class="token punctuation">.</span>Value<span class="token punctuation">{</span>reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span><span class="token string">"6666"</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token comment">// 没参数的情况下:var args2 []reflect.Value</span> <span class="token comment">// 调用方法,需要传入方法的参数</span> m<span class="token punctuation">.</span><span class="token function">Call</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-5-获取字段的tag"><a href="#3-5-获取字段的tag" class="headerlink" title="3.5 获取字段的tag"></a>3.5 获取字段的tag</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"reflect"</span><span class="token punctuation">)</span><span class="token keyword">type</span> Student <span class="token keyword">struct</span> <span class="token punctuation">{</span> Name <span class="token builtin">string</span> <span class="token string">`json:"name1" db:"name2"`</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> s Student v <span class="token operator">:=</span> reflect<span class="token punctuation">.</span><span class="token function">ValueOf</span><span class="token punctuation">(</span><span class="token operator">&</span>s<span class="token punctuation">)</span> <span class="token comment">// 类型</span> t <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">Type</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 获取字段</span> f <span class="token operator">:=</span> t<span class="token punctuation">.</span><span class="token function">Elem</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Field</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>f<span class="token punctuation">.</span>Tag<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"json"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>f<span class="token punctuation">.</span>Tag<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"db"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="04-反射练习"><a href="#04-反射练习" class="headerlink" title="04.反射练习"></a>04.反射练习</h2><ul><li>任务:解析如下配置文件<ul><li>序列化:将结构体序列化为配置文件数据并保存到硬盘</li><li>反序列化:将配置文件内容反序列化到程序的结构体</li></ul></li><li>配置文件有server和mysql相关配置</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-text" data-language="text"><code class="language-text">#this is comment;this a comment;[]表示一个section[server]ip = 10.238.2.2port = 8080[mysql]username = rootpasswd = admindatabase = testhost = 192.168.10.10port = 8000timeout = 1.2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="10-reflect"><a href="#10-reflect" class="headerlink" title="10.reflect"></a>10.reflect</h1><h2 id="01-反射"><a href="#01-反射" class="he</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>Context</title>
<link href="http://coderedeng.github.io/2022/06/03/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20Context/"/>
<id>http://coderedeng.github.io/2022/06/03/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20Context/</id>
<published>2022-06-03T14:42:57.000Z</published>
<updated>2024-04-06T15:03:14.175Z</updated>
<content type="html"><![CDATA[<h1 id="09-Context"><a href="#09-Context" class="headerlink" title="09.Context"></a>09.Context</h1><h2 id="01-context介绍"><a href="#01-context介绍" class="headerlink" title="01.context介绍"></a>01.context介绍</h2><h3 id="1-1-context由来"><a href="#1-1-context由来" class="headerlink" title="1.1 context由来"></a>1.1 context由来</h3><ul><li>context在Go1.7之后就进入标准库中了,是在于<code>控制goroutine的生命周期</code>。</li><li>由于在Golang severs中,每个request都是在单个goroutine中完成</li><li>并且在单个goroutine(不妨称之为A)中也会有请求其他服务(启动另一个goroutine(称之为B)去完成)的场景</li><li>这就会涉及多个Goroutine之间的调用</li><li>如果某一时刻请求其他服务被取消或者超时,则作为深陷其中的当前goroutine B需要立即退出,然后系统才可回收B所占用的资源。</li><li>即一个request中通常包含多个goroutine,这些goroutine之间通常会有交互。</li></ul><p><img src="/img/image-20210616213116924.6104cd92.png"></p><ul><li>那么,如何有效管理这些goroutine成为一个问题(主要是退出通知和元数据传递问题)</li><li>Google的解决方法是Context机制,相互调用的goroutine之间通过传递context变量保持关联</li><li>这样在不用暴露各goroutine内部实现细节的前提下,有效地控制各goroutine的运行。</li></ul><p><img src="/img/image-20210616213314610.60383589.png"></p><ul><li>如此一来,通过传递Context就可以追踪goroutine调用树,并在这些调用树之间传递通知和元数据。</li><li>虽然goroutine之间是平行的,没有继承关系,但是Context设计成是包含父子关系的,这样可以更好的描述goroutine调用之间的树型关系。</li></ul><h3 id="1-2-context常用方法"><a href="#1-2-context常用方法" class="headerlink" title="1.2 context常用方法"></a>1.2 context常用方法</h3><ul><li><code>context.Context</code>是一个接口,该接口定义了四个需要实现的方法</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">type</span> Context <span class="token keyword">interface</span> <span class="token punctuation">{</span> <span class="token function">Deadline</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>deadline time<span class="token punctuation">.</span>Time<span class="token punctuation">,</span> ok <span class="token builtin">bool</span><span class="token punctuation">)</span> <span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token function">Err</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token function">Value</span><span class="token punctuation">(</span>key <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li><strong>Done</strong> 方法<ul><li>在Context被取消或超时时返回一个close的channel,close的channel可以作为广播通知</li><li>告诉给context相关的函数要停止当前工作然后返回。</li><li>当一个父operation启动一个goroutine用于子operation,这些子operation不能够取消父operation。</li><li>下面描述的WithCancel函数提供一种方式可以取消新创建的Context.</li><li>开发者可以把一个Context传递给任意多个goroutine然后cancel这个context的时候就能够通知到所有的goroutine。</li></ul></li><li><strong>Err</strong>方法<ul><li>返回context为什么被取消</li></ul></li><li><strong>Deadline</strong><ul><li>返回context何时会超时。</li></ul></li><li><strong>Value</strong><ul><li>返回context相关的数据。</li></ul></li></ul><h2 id="02-为什么需要context"><a href="#02-为什么需要context" class="headerlink" title="02.为什么需要context"></a>02.为什么需要context</h2><ul><li>在 Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理。</li><li>请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库和RPC服务。</li><li>用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。</li><li>当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速退出,然后系统才能释放这些 goroutine 占用的资源。</li></ul><h3 id="2-1-全局变量解决"><a href="#2-1-全局变量解决" class="headerlink" title="2.1 全局变量解决"></a>2.1 全局变量解决</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// 全局变量方式存在的问题:</span><span class="token comment">// 1. 使用全局变量在跨包调用时不容易统一</span><span class="token comment">// 2. 如果worker中再启动goroutine,就不太好控制了。</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup<span class="token keyword">var</span> exit <span class="token builtin">bool</span><span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"worker"</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token keyword">if</span> exit <span class="token punctuation">{</span><span class="token keyword">break</span><span class="token punctuation">}</span><span class="token punctuation">}</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment">// sleep3秒以免程序过快退出</span>exit <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token comment">// 修改全局变量实现子goroutine的退出</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"over"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-通道方式"><a href="#2-2-通道方式" class="headerlink" title="2.2 通道方式"></a>2.2 通道方式</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup<span class="token comment">// 管道方式存在的问题:</span><span class="token comment">// 1. 使用全局变量在跨包调用时不容易实现规范和统一,需要维护一个共用的channel</span><span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span>exitChan <span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>LOOP<span class="token punctuation">:</span><span class="token keyword">for</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"worker"</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>exitChan<span class="token punctuation">:</span> <span class="token comment">// 等待接收上级通知</span><span class="token keyword">break</span> LOOP<span class="token keyword">default</span><span class="token punctuation">:</span><span class="token punctuation">}</span><span class="token punctuation">}</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">var</span> exitChan <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>exitChan<span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment">// sleep3秒以免程序过快退出</span> exitChan <span class="token operator"><-</span> <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 给子goroutine发送退出信号</span><span class="token function">close</span><span class="token punctuation">(</span>exitChan<span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"over"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-官方版的方案"><a href="#2-3-官方版的方案" class="headerlink" title="2.3 官方版的方案"></a>2.3 官方版的方案</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup<span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>LOOP<span class="token punctuation">:</span><span class="token keyword">for</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"worker"</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment">// 等待上级通知</span><span class="token keyword">break</span> LOOP<span class="token keyword">default</span><span class="token punctuation">:</span><span class="token punctuation">}</span><span class="token punctuation">}</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithCancel</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 通知子goroutine结束</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"over"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="03-With系列函数"><a href="#03-With系列函数" class="headerlink" title="03.With系列函数"></a>03.With系列函数</h2><p>此外,<code>context</code>包中还定义了四个With系列函数。</p><h3 id="3-1-WithCancel"><a href="#3-1-WithCancel" class="headerlink" title="3.1 WithCancel"></a>3.1 WithCancel</h3><ul><li><code>WithCancel</code>返回带有新Done通道的父节点的副本。</li><li>当调用返回的cancel函数或当关闭父上下文的Done通道时,将关闭返回上下文的Done通道,无论先发生什么情况。</li><li>取消此上下文将释放与其关联的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel。</li><li>示例<ul><li>上面的示例代码中,<code>gen</code>函数在单独的goroutine中生成整数并将它们发送到返回的通道。</li><li>gen的调用者在使用生成的整数之后需要取消上下文,以免<code>gen</code>启动的内部goroutine发生泄漏。</li></ul></li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">gen</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token operator"><-</span><span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">{</span>dst <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>n <span class="token operator">:=</span> <span class="token number">1</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">return</span> <span class="token comment">// return结束该goroutine,防止泄露</span><span class="token keyword">case</span> dst <span class="token operator"><-</span> n<span class="token punctuation">:</span>n<span class="token operator">++</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> dst<span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithCancel</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">defer</span> <span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 当我们取完需要的整数后调用cancel</span><span class="token keyword">for</span> n <span class="token operator">:=</span> <span class="token keyword">range</span> <span class="token function">gen</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token keyword">if</span> n <span class="token operator">==</span> <span class="token number">5</span> <span class="token punctuation">{</span><span class="token keyword">break</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">/*12345 */</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-2-WithDeadline"><a href="#3-2-WithDeadline" class="headerlink" title="3.2 WithDeadline"></a>3.2 WithDeadline</h3><ul><li>下面的代码中,定义了一个50毫秒之后过期的deadline</li><li>然后我们调用<code>context.WithDeadline(context.Background(), d)</code>得到一个上下文(ctx)和一个取消函数(cancel)</li><li>然后使用一个select让主程序陷入等待:等待1秒后打印<code>overslept</code>退出或者等待ctx过期后退出。</li><li>因为ctx50秒后就过期,所以<code>ctx.Done()</code>会先接收到值,上面的代码会打印ctx.Err()取消原因。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>d <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">50</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Millisecond<span class="token punctuation">)</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithDeadline</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> d<span class="token punctuation">)</span><span class="token comment">// 尽管ctx会过期,但在任何情况下调用它的cancel函数都是很好的实践。</span><span class="token comment">// 如果不这样做,可能会使上下文及其父类存活的时间超过必要的时间。</span><span class="token keyword">defer</span> <span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>time<span class="token punctuation">.</span><span class="token function">After</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token punctuation">:</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"overslept"</span><span class="token punctuation">)</span><span class="token keyword">case</span> <span class="token operator"><-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span><span class="token function">Err</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-3WithTimeout"><a href="#3-3WithTimeout" class="headerlink" title="3.3WithTimeout"></a>3.3WithTimeout</h3><ul><li>取消此上下文将释放与其相关的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel</li><li>通常用于数据库或者网络连接的超时控制</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// context.WithTimeout</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup<span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>LOOP<span class="token punctuation">:</span><span class="token keyword">for</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"db connecting ..."</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">// 假设正常连接数据库耗时10毫秒</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment">// 50毫秒后自动调用</span><span class="token keyword">break</span> LOOP<span class="token keyword">default</span><span class="token punctuation">:</span><span class="token punctuation">}</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"worker done!"</span><span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 设置一个50毫秒的超时</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithTimeout</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span>Millisecond<span class="token operator">*</span><span class="token number">50</span><span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 通知子goroutine结束</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"over"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-4-WithValue"><a href="#3-4-WithValue" class="headerlink" title="3.4 WithValue"></a>3.4 WithValue</h3><ul><li><code>WithValue</code>函数能够将请求作用域的数据与 Context 对象建立关系</li><li><code>WithValue</code>返回父节点的副本,其中与key关联的值为val。</li><li>仅对API和进程间传递请求域的数据使用上下文值,而不是使用它来传递可选参数给函数。</li><li>所提供的键必须是可比较的,并且不应该是<code>string</code>类型或任何其他内置类型,以避免使用上下文在包之间发生冲突。</li><li><code>WithValue</code>的用户应该为键定义自己的类型。为了避免在分配给interface{}时进行分配,上下文键通常具有具体类型<code>struct{}</code>。</li><li>或者,导出的上下文关键变量的静态类型应该是指针或接口。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// context.WithValue</span><span class="token keyword">type</span> TraceCode <span class="token builtin">string</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup<span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>key <span class="token operator">:=</span> <span class="token function">TraceCode</span><span class="token punctuation">(</span><span class="token string">"TRACE_CODE"</span><span class="token punctuation">)</span>traceCode<span class="token punctuation">,</span> ok <span class="token operator">:=</span> ctx<span class="token punctuation">.</span><span class="token function">Value</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token comment">// 在子goroutine中获取trace code</span><span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"invalid trace code"</span><span class="token punctuation">)</span><span class="token punctuation">}</span>LOOP<span class="token punctuation">:</span><span class="token keyword">for</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"worker, trace code:%s\n"</span><span class="token punctuation">,</span> traceCode<span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">// 假设正常连接数据库耗时10毫秒</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment">// 50毫秒后自动调用</span><span class="token keyword">break</span> LOOP<span class="token keyword">default</span><span class="token punctuation">:</span><span class="token punctuation">}</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"worker done!"</span><span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 设置一个50毫秒的超时</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithTimeout</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span>Millisecond<span class="token operator">*</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token comment">// 在系统的入口中设置trace code传递给后续启动的goroutine实现日志数据聚合</span>ctx <span class="token operator">=</span> context<span class="token punctuation">.</span><span class="token function">WithValue</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> <span class="token function">TraceCode</span><span class="token punctuation">(</span><span class="token string">"TRACE_CODE"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"12512312234"</span><span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 通知子goroutine结束</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"over"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="04-客户端超时取消示例"><a href="#04-客户端超时取消示例" class="headerlink" title="04.客户端超时取消示例"></a>04.客户端超时取消示例</h2><p>调用服务端API时如何在客户端实现超时控制?</p><h3 id="4-1-server端"><a href="#4-1-server端" class="headerlink" title="4.1 server端"></a>4.1 server端</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// context_timeout/server/main.go</span><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"math/rand"</span><span class="token string">"net/http"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// server端,随机出现慢响应</span><span class="token keyword">func</span> <span class="token function">indexHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>number <span class="token operator">:=</span> rand<span class="token punctuation">.</span><span class="token function">Intn</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token keyword">if</span> number <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">// 耗时10秒的慢响应</span>fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"slow response"</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Fprint</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"quick response"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>http<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> indexHandler<span class="token punctuation">)</span>err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":8000"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="4-2-client端"><a href="#4-2-client端" class="headerlink" title="4.2 client端"></a>4.2 client端</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// context_timeout/client/main.go</span><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"context"</span><span class="token string">"fmt"</span><span class="token string">"io/ioutil"</span><span class="token string">"net/http"</span><span class="token string">"sync"</span><span class="token string">"time"</span><span class="token punctuation">)</span><span class="token comment">// 客户端</span><span class="token keyword">type</span> respData <span class="token keyword">struct</span> <span class="token punctuation">{</span>resp <span class="token operator">*</span>http<span class="token punctuation">.</span>Responseerr <span class="token builtin">error</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">doCall</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">)</span> <span class="token punctuation">{</span>transport <span class="token operator">:=</span> http<span class="token punctuation">.</span>Transport<span class="token punctuation">{</span> <span class="token comment">// 请求频繁可定义全局的client对象并启用长链接</span> <span class="token comment">// 请求不频繁使用短链接</span> DisableKeepAlives<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span>client <span class="token operator">:=</span> http<span class="token punctuation">.</span>Client<span class="token punctuation">{</span>Transport<span class="token punctuation">:</span> <span class="token operator">&</span>transport<span class="token punctuation">,</span><span class="token punctuation">}</span>respChan <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token operator">*</span>respData<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>req<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">NewRequest</span><span class="token punctuation">(</span><span class="token string">"GET"</span><span class="token punctuation">,</span> <span class="token string">"http://127.0.0.1:8000/"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"new requestg failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>req <span class="token operator">=</span> req<span class="token punctuation">.</span><span class="token function">WithContext</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token comment">// 使用带超时的ctx创建一个新的client request</span><span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroupwg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> client<span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"client.do resp:%v, err:%v\n"</span><span class="token punctuation">,</span> resp<span class="token punctuation">,</span> err<span class="token punctuation">)</span>rd <span class="token operator">:=</span> <span class="token operator">&</span>respData<span class="token punctuation">{</span>resp<span class="token punctuation">:</span> resp<span class="token punctuation">,</span>err<span class="token punctuation">:</span> err<span class="token punctuation">,</span><span class="token punctuation">}</span>respChan <span class="token operator"><-</span> rdwg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{</span><span class="token keyword">case</span> <span class="token operator"><-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token comment">//transport.CancelRequest(req)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"call api timeout"</span><span class="token punctuation">)</span><span class="token keyword">case</span> result <span class="token operator">:=</span> <span class="token operator"><-</span>respChan<span class="token punctuation">:</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"call server api success"</span><span class="token punctuation">)</span><span class="token keyword">if</span> result<span class="token punctuation">.</span>err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"call server api failed, err:%v\n"</span><span class="token punctuation">,</span> result<span class="token punctuation">.</span>err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> result<span class="token punctuation">.</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>data<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"resp:%v\n"</span><span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 定义一个100毫秒的超时</span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:=</span> context<span class="token punctuation">.</span><span class="token function">WithTimeout</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span>Millisecond<span class="token operator">*</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token keyword">defer</span> <span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 调用cancel释放子goroutine资源</span><span class="token function">doCall</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="09-Context"><a href="#09-Context" class="headerlink" title="09.Context"></a>09.Context</h1><h2 id="01-context介绍"><a href="#01-contex</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>net/http</title>
<link href="http://coderedeng.github.io/2022/06/01/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20net%20http/"/>
<id>http://coderedeng.github.io/2022/06/01/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20net%20http/</id>
<published>2022-06-01T13:35:53.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="08-net-x2F-http"><a href="#08-net-x2F-http" class="headerlink" title="08.net/http"></a>08.net/http</h1><h2 id="01-net-x2F-http(GET)"><a href="#01-net-x2F-http(GET)" class="headerlink" title="01.net/http(GET)"></a>01.net/http(GET)</h2><ul><li>Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现。</li></ul><h3 id="1-1-无参GET"><a href="#1-1-无参GET" class="headerlink" title="1.1 无参GET"></a>1.1 无参GET</h3><ul><li>使用net/http包编写一个简单的发送HTTP请求的Client端</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"io/ioutil"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"https://www.baidu.com/"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"get failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>body<span class="token punctuation">,</span> err <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"read from resp.Body failed,err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-2-带参GET"><a href="#1-2-带参GET" class="headerlink" title="1.2 带参GET"></a>1.2 带参GET</h3><ul><li>关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。</li></ul><p>关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。</p><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> apiUrl <span class="token operator">:=</span> <span class="token string">"http://127.0.0.1:9090/get"</span> <span class="token comment">// URL param</span> data <span class="token operator">:=</span> url<span class="token punctuation">.</span>Values<span class="token punctuation">{</span><span class="token punctuation">}</span> data<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">,</span> <span class="token string">"枯藤"</span><span class="token punctuation">)</span> data<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"age"</span><span class="token punctuation">,</span> <span class="token string">"18"</span><span class="token punctuation">)</span> u<span class="token punctuation">,</span> err <span class="token operator">:=</span> url<span class="token punctuation">.</span><span class="token function">ParseRequestURI</span><span class="token punctuation">(</span>apiUrl<span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"parse url requestUrl failed,err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token punctuation">}</span> u<span class="token punctuation">.</span>RawQuery <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">Encode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// URL encode</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>u<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>u<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"post failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> b<span class="token punctuation">,</span> err <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"get resp failed,err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>对应的Server端HandlerFunc如下:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">getHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">defer</span> r<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> data <span class="token operator">:=</span> r<span class="token punctuation">.</span>URL<span class="token punctuation">.</span><span class="token function">Query</span><span class="token punctuation">(</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"age"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> answer <span class="token operator">:=</span> <span class="token string">`{"status": "ok"}`</span> w<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-3-自定义Client"><a href="#1-3-自定义Client" class="headerlink" title="1.3 自定义Client"></a>1.3 自定义Client</h3><ul><li>要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">client <span class="token operator">:=</span> <span class="token operator">&</span>http<span class="token punctuation">.</span>Client<span class="token punctuation">{</span> CheckRedirect<span class="token punctuation">:</span> redirectPolicyFunc<span class="token punctuation">,</span><span class="token punctuation">}</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> client<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"http://5lmh.com"</span><span class="token punctuation">)</span><span class="token comment">// ...</span>req<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">NewRequest</span><span class="token punctuation">(</span><span class="token string">"GET"</span><span class="token punctuation">,</span> <span class="token string">"http://5lmh.com"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token comment">// ...</span>req<span class="token punctuation">.</span>Header<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token string">"If-None-Match"</span><span class="token punctuation">,</span> <span class="token string">`W/"wyzzy"`</span><span class="token punctuation">)</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> client<span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token comment">// ...</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-4-自定义Transport"><a href="#1-4-自定义Transport" class="headerlink" title="1.4 自定义Transport"></a>1.4 自定义Transport</h3><ul><li>要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个Transport</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">tr <span class="token operator">:=</span> <span class="token operator">&</span>http<span class="token punctuation">.</span>Transport<span class="token punctuation">{</span> TLSClientConfig<span class="token punctuation">:</span> <span class="token operator">&</span>tls<span class="token punctuation">.</span>Config<span class="token punctuation">{</span>RootCAs<span class="token punctuation">:</span> pool<span class="token punctuation">}</span><span class="token punctuation">,</span> DisableCompression<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><span class="token punctuation">}</span>client <span class="token operator">:=</span> <span class="token operator">&</span>http<span class="token punctuation">.</span>Client<span class="token punctuation">{</span>Transport<span class="token punctuation">:</span> tr<span class="token punctuation">}</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> client<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"https://5lmh.com"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>Client和Transport类型都可以安全的被多个go程同时使用,出于效率考虑,应该一次建立、尽量重用。</li></ul><h2 id="02-net-x2F-http(POST)"><a href="#02-net-x2F-http(POST)" class="headerlink" title="02.net/http(POST)"></a>02.net/http(POST)</h2><h3 id="2-1-Post请求示例"><a href="#2-1-Post请求示例" class="headerlink" title="2.1 Post请求示例"></a>2.1 Post请求示例</h3><ul><li>上面演示了使用net/http包发送GET请求的示例,发送POST请求的示例代码如下:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"io/ioutil"</span> <span class="token string">"net/http"</span> <span class="token string">"strings"</span><span class="token punctuation">)</span><span class="token comment">// net/http post demo</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> url <span class="token operator">:=</span> <span class="token string">"http://127.0.0.1:9090/post"</span> <span class="token comment">// 表单数据</span> <span class="token comment">//contentType := "application/x-www-form-urlencoded"</span> <span class="token comment">//data := "name=枯藤&age=18"</span> <span class="token comment">// json</span> contentType <span class="token operator">:=</span> <span class="token string">"application/json"</span> data <span class="token operator">:=</span> <span class="token string">`{"name":"枯藤","age":18}`</span> resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Post</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> contentType<span class="token punctuation">,</span> strings<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"post failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> b<span class="token punctuation">,</span> err <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"get resp failed,err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-Server端"><a href="#2-2-Server端" class="headerlink" title="2.2 Server端"></a>2.2 Server端</h3><ul><li>对应的Server端HandlerFunc如下:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">postHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">defer</span> r<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1. 请求类型是application/x-www-form-urlencoded时解析form数据</span> r<span class="token punctuation">.</span><span class="token function">ParseForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>r<span class="token punctuation">.</span>PostForm<span class="token punctuation">)</span> <span class="token comment">// 打印form数据</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>r<span class="token punctuation">.</span>PostForm<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> r<span class="token punctuation">.</span>PostForm<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"age"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 2. 请求类型是application/json时从r.Body读取数据</span> b<span class="token punctuation">,</span> err <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>r<span class="token punctuation">.</span>Body<span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"read request.Body failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">)</span> answer <span class="token operator">:=</span> <span class="token string">`{"status": "ok"}`</span> w<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="03-服务端"><a href="#03-服务端" class="headerlink" title="03.服务端"></a>03.服务端</h2><h3 id="3-1-默认的Server"><a href="#3-1-默认的Server" class="headerlink" title="3.1 默认的Server"></a>3.1 默认的Server</h3><ul><li>ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。</li><li>处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。</li><li>Handle和HandleFunc函数可以向DefaultServeMux添加处理器。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">http<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span><span class="token string">"/foo"</span><span class="token punctuation">,</span> fooHandler<span class="token punctuation">)</span>http<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/bar"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"Hello, %q"</span><span class="token punctuation">,</span> html<span class="token punctuation">.</span><span class="token function">EscapeString</span><span class="token punctuation">(</span>r<span class="token punctuation">.</span>URL<span class="token punctuation">.</span>Path<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":8080"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>示例</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"net/http"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">sayHello</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Fprintln</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"Hello World!"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>http<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> sayHello<span class="token punctuation">)</span>err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":9090"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"http server failed, err:%v\n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-2-自定义Server"><a href="#3-2-自定义Server" class="headerlink" title="3.2 自定义Server"></a>3.2 自定义Server</h3><ul><li>要管理服务端的行为,可以创建一个自定义的Server:</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go">s <span class="token operator">:=</span> <span class="token operator">&</span>http<span class="token punctuation">.</span>Server<span class="token punctuation">{</span> Addr<span class="token punctuation">:</span> <span class="token string">":8080"</span><span class="token punctuation">,</span> Handler<span class="token punctuation">:</span> myHandler<span class="token punctuation">,</span> ReadTimeout<span class="token punctuation">:</span> <span class="token number">10</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">,</span> WriteTimeout<span class="token punctuation">:</span> <span class="token number">10</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">,</span> MaxHeaderBytes<span class="token punctuation">:</span> <span class="token number">1</span> <span class="token operator"><<</span> <span class="token number">20</span><span class="token punctuation">,</span><span class="token punctuation">}</span>log<span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="08-net-x2F-http"><a href="#08-net-x2F-http" class="headerlink" title="08.net&#x2F;http"></a>08.net&#x2F;http</h1><h2 id="01-net-x2F-</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>Strconv</title>
<link href="http://coderedeng.github.io/2022/05/31/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20Strconv/"/>
<id>http://coderedeng.github.io/2022/05/31/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20Strconv/</id>
<published>2022-05-31T14:24:31.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="07-Strconv"><a href="#07-Strconv" class="headerlink" title="07.Strconv"></a>07.Strconv</h1><h2 id="01-string与int类型转换"><a href="#01-string与int类型转换" class="headerlink" title="01.string与int类型转换"></a>01.string与int类型转换</h2><h3 id="1-0-strconv包介绍"><a href="#1-0-strconv包介绍" class="headerlink" title="1.0 strconv包介绍"></a>1.0 strconv包介绍</h3><ul><li>strconv包实现了基本数据类型与其字符串表示的转换</li><li>主要有以下常用函数: Atoi()、Itia()、parse系列、format系列、append系列。</li><li>更多函数请查看<a href="https://golang.org/pkg/strconv/">官方文档 (opens new window)</a>。</li></ul><h3 id="1-1-Atoi-转int"><a href="#1-1-Atoi-转int" class="headerlink" title="1.1 Atoi()转int"></a>1.1 Atoi()转int</h3><ul><li><code>Atoi()</code>函数用于将字符串类型的整数转换为int类型,函数签名如下。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"strconv"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> s1 <span class="token operator">:=</span> <span class="token string">"100"</span> i1<span class="token punctuation">,</span> err <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">Atoi</span><span class="token punctuation">(</span>s1<span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"can't convert to int"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type:%T value:%#v\n"</span><span class="token punctuation">,</span> i1<span class="token punctuation">,</span> i1<span class="token punctuation">)</span> <span class="token comment">//type:int value:100</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-2-Itoa-转str"><a href="#1-2-Itoa-转str" class="headerlink" title="1.2 Itoa()转str"></a>1.2 Itoa()转str</h3><ul><li><code>Itoa()</code>函数用于将int类型数据转换为对应的字符串表示</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"strconv"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>i2 <span class="token operator">:=</span> <span class="token number">200</span>s2 <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">Itoa</span><span class="token punctuation">(</span>i2<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"type:%T value:%#v\n"</span><span class="token punctuation">,</span> s2<span class="token punctuation">,</span> s2<span class="token punctuation">)</span> <span class="token comment">//type:string value:"200"</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-3-string转字符"><a href="#1-3-string转字符" class="headerlink" title="1.3 string转字符"></a>1.3 string转字符</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>s <span class="token operator">:=</span> <span class="token string">"hello 张三"</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> r <span class="token operator">:=</span> <span class="token keyword">range</span> s <span class="token punctuation">{</span> <span class="token comment">//rune</span><span class="token comment">// 104(h) 101(e) 108(l) 108(l) 111(o) 32( ) 24352(张) 19977(三) </span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%v(%c) "</span><span class="token punctuation">,</span> r<span class="token punctuation">,</span> r<span class="token punctuation">)</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="02-Parse系列函数"><a href="#02-Parse系列函数" class="headerlink" title="02.Parse系列函数"></a>02.Parse系列函数</h2><h3 id="1-1-ParseInt"><a href="#1-1-ParseInt" class="headerlink" title="1.1 ParseInt()"></a>1.1 ParseInt()</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"strconv"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">var</span> s <span class="token operator">=</span> <span class="token string">"1234"</span>i64<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">ParseInt</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">64</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"值:%v 类型:%T"</span><span class="token punctuation">,</span> i64<span class="token punctuation">,</span> i64<span class="token punctuation">)</span> <span class="token comment">// 值:1234 类型:int64</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-2-ParseFloat"><a href="#1-2-ParseFloat" class="headerlink" title="1.2 ParseFloat()"></a>1.2 ParseFloat()</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"strconv"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>str <span class="token operator">:=</span> <span class="token string">"3.1415926535"</span>v1<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">ParseFloat</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">)</span>v2<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">ParseFloat</span><span class="token punctuation">(</span>str<span class="token punctuation">,</span> <span class="token number">64</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"值:%v 类型:%T\n"</span><span class="token punctuation">,</span> v1<span class="token punctuation">,</span> v1<span class="token punctuation">)</span> <span class="token comment">// 值:3.1415927410125732 类型:float64</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"值:%v 类型:%T"</span><span class="token punctuation">,</span> v2<span class="token punctuation">,</span> v2<span class="token punctuation">)</span> <span class="token comment">// 值:3.1415926535 类型:float64</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="1-3-ParseBool"><a href="#1-3-ParseBool" class="headerlink" title="1.3 ParseBool()"></a>1.3 ParseBool()</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"strconv"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>b<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">ParseBool</span><span class="token punctuation">(</span><span class="token string">"true"</span><span class="token punctuation">)</span> <span class="token comment">// string 转 bool</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"值:%v 类型:%T"</span><span class="token punctuation">,</span> b<span class="token punctuation">,</span> b<span class="token punctuation">)</span> <span class="token comment">// 值:true 类型:bool</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="03-Format系列函数"><a href="#03-Format系列函数" class="headerlink" title="03.Format系列函数"></a>03.Format系列函数</h2><ul><li>Format系列函数实现了将给定类型数据格式化为string类型数据的功能</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"strconv"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>s1 <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">FormatBool</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>s2 <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">FormatFloat</span><span class="token punctuation">(</span><span class="token number">3.1415</span><span class="token punctuation">,</span> <span class="token char">'E'</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">64</span><span class="token punctuation">)</span>s3 <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">FormatInt</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span>s4 <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">FormatUint</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%v --%T \n"</span><span class="token punctuation">,</span> s1<span class="token punctuation">,</span> s1<span class="token punctuation">)</span> <span class="token comment">// true --string </span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%v --%T \n"</span><span class="token punctuation">,</span> s2<span class="token punctuation">,</span> s2<span class="token punctuation">)</span> <span class="token comment">// 3.1415E+00 --string </span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%v --%T \n"</span><span class="token punctuation">,</span> s3<span class="token punctuation">,</span> s3<span class="token punctuation">)</span> <span class="token comment">// -2 --string</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%v --%T \n"</span><span class="token punctuation">,</span> s4<span class="token punctuation">,</span> s4<span class="token punctuation">)</span> <span class="token comment">// 2 --string </span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="07-Strconv"><a href="#07-Strconv" class="headerlink" title="07.Strconv"></a>07.Strconv</h1><h2 id="01-string与int类型转换"><a href="#01-s</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
<entry>
<title>IO</title>
<link href="http://coderedeng.github.io/2022/05/29/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20IO%E6%93%8D%E4%BD%9C/"/>
<id>http://coderedeng.github.io/2022/05/29/Go%E5%B8%B8%E7%94%A8%E5%BA%93%E4%BB%8B%E7%BB%8D%20-%20IO%E6%93%8D%E4%BD%9C/</id>
<published>2022-05-29T14:13:26.000Z</published>
<updated>2024-01-22T12:58:59.000Z</updated>
<content type="html"><![CDATA[<h1 id="06-IO操作"><a href="#06-IO操作" class="headerlink" title="06.IO操作"></a>06.IO操作</h1><h2 id="01-打开和关闭文件"><a href="#01-打开和关闭文件" class="headerlink" title="01.打开和关闭文件"></a>01.打开和关闭文件</h2><ul><li>os.Open()函数能够打开一个文件,返回一个*File和一个err。</li><li>对得到的文件实例调用close()方法能够关闭文件。</li><li>为了防止文件忘记关闭,我们通常使用defer注册文件关闭语句。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"os"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 只读方式打开当前目录下的main.go文件</span> file<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token string">"./main.go"</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"open file failed!, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token comment">// 关闭文件</span> file<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="02-读取文件"><a href="#02-读取文件" class="headerlink" title="02.读取文件"></a>02.读取文件</h2><h3 id="2-1-file-Read-指定读取size"><a href="#2-1-file-Read-指定读取size" class="headerlink" title="2.1 file.Read()指定读取size"></a>2.1 file.Read()指定读取size</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"io"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 只读方式打开当前目录下的main.go文件</span>file<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token string">"./main.go"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"open file failed!, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> file<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 使用Read方法读取数据,每次只读取128个字节</span><span class="token keyword">var</span> tmp <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">byte</span><span class="token punctuation">,</span> <span class="token number">128</span><span class="token punctuation">)</span>n<span class="token punctuation">,</span> err <span class="token operator">:=</span> file<span class="token punctuation">.</span><span class="token function">Read</span><span class="token punctuation">(</span>tmp<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">==</span> io<span class="token punctuation">.</span>EOF <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"文件读完了"</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"read file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"读取了%d字节数据\n"</span><span class="token punctuation">,</span> n<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>tmp<span class="token punctuation">[</span><span class="token punctuation">:</span>n<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-2-循环读取"><a href="#2-2-循环读取" class="headerlink" title="2.2 循环读取"></a>2.2 循环读取</h3><ul><li>使用for循环读取文件中的所有数据。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"io"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 只读方式打开当前目录下的main.go文件</span>file<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token string">"./main.go"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"open file failed!, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> file<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 循环读取文件</span><span class="token keyword">var</span> content <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">byte</span><span class="token keyword">var</span> tmp <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">byte</span><span class="token punctuation">,</span> <span class="token number">128</span><span class="token punctuation">)</span><span class="token keyword">for</span> <span class="token punctuation">{</span>n<span class="token punctuation">,</span> err <span class="token operator">:=</span> file<span class="token punctuation">.</span><span class="token function">Read</span><span class="token punctuation">(</span>tmp<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">==</span> io<span class="token punctuation">.</span>EOF <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"文件读完了"</span><span class="token punctuation">)</span><span class="token keyword">break</span><span class="token punctuation">}</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"read file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>content <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>content<span class="token punctuation">,</span> tmp<span class="token punctuation">[</span><span class="token punctuation">:</span>n<span class="token punctuation">]</span><span class="token operator">...</span><span class="token punctuation">)</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-3-bufio按行读取"><a href="#2-3-bufio按行读取" class="headerlink" title="2.3 bufio按行读取"></a>2.3 bufio按行读取</h3><ul><li>bufio是在file的基础上封装了一层API,支持更多的功能。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"bufio"</span><span class="token string">"fmt"</span><span class="token string">"io"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token comment">// bufio按行读取示例</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>file<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span><span class="token string">"./main.go"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"open file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> file<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>reader <span class="token operator">:=</span> bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span><span class="token keyword">for</span> <span class="token punctuation">{</span>line<span class="token punctuation">,</span> err <span class="token operator">:=</span> reader<span class="token punctuation">.</span><span class="token function">ReadString</span><span class="token punctuation">(</span><span class="token char">'\n'</span><span class="token punctuation">)</span> <span class="token comment">//注意是字符</span><span class="token keyword">if</span> err <span class="token operator">==</span> io<span class="token punctuation">.</span>EOF <span class="token punctuation">{</span><span class="token keyword">if</span> <span class="token function">len</span><span class="token punctuation">(</span>line<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>line<span class="token punctuation">)</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"文件读完了"</span><span class="token punctuation">)</span><span class="token keyword">break</span><span class="token punctuation">}</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"read file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>line<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="2-4-ioutil读取整个文件"><a href="#2-4-ioutil读取整个文件" class="headerlink" title="2.4 ioutil读取整个文件"></a>2.4 ioutil读取整个文件</h3><ul><li><code>io/ioutil</code>包的<code>ReadFile</code>方法能够读取完整的文件,只需要将文件名作为参数传入。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"io/ioutil"</span><span class="token punctuation">)</span><span class="token comment">// ioutil.ReadFile读取整个文件</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>content<span class="token punctuation">,</span> err <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">ReadFile</span><span class="token punctuation">(</span><span class="token string">"./main.go"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"read file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token function">string</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="03-写入文件"><a href="#03-写入文件" class="headerlink" title="03.写入文件"></a>03.写入文件</h2><h3 id="3-0-参数说明"><a href="#3-0-参数说明" class="headerlink" title="3.0 参数说明"></a>3.0 参数说明</h3><ul><li><code>os.OpenFile()</code>函数能够以指定模式打开文件,从而实现文件写入相关功能。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">OpenFile</span><span class="token punctuation">(</span>name <span class="token builtin">string</span><span class="token punctuation">,</span> flag <span class="token builtin">int</span><span class="token punctuation">,</span> perm FileMode<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>File<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></div></figure><ul><li>其中:<ul><li><code>name</code>:要打开的文件名</li><li><code>flag</code>:打开文件的模式,模式有以下几种</li></ul></li></ul><table><thead><tr><th align="center">模式</th><th align="center">含义</th></tr></thead><tbody><tr><td align="center"><code>os.O_WRONLY</code></td><td align="center">只写</td></tr><tr><td align="center"><code>os.O_CREATE</code></td><td align="center">创建文件</td></tr><tr><td align="center"><code>os.O_RDONLY</code></td><td align="center">只读</td></tr><tr><td align="center"><code>os.O_RDWR</code></td><td align="center">读写</td></tr><tr><td align="center"><code>os.O_TRUNC</code></td><td align="center">清空</td></tr><tr><td align="center"><code>os.O_APPEND</code></td><td align="center">追加</td></tr></tbody></table><ul><li><figure><div class="code-wrapper"><pre class="language-none"><code class="language-none">perm<pre class="line-numbers language-none"><code class="language-none"> :文件权限,一个八进制数 - r(读)04,w(写)02,x(执行)01。### 3.1 Write和WriteString```gopackage mainimport ("fmt""os")func main() {file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)if err != nil {fmt.Println("open file failed, err:", err)return}defer file.Close()str := "hello 沙河"file.Write([]byte(str)) //写入字节切片数据file.WriteString("hello 小王子") //直接写入字符串数据}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure></code></pre></li></ul><h3 id="3-2-bufio-NewWriter"><a href="#3-2-bufio-NewWriter" class="headerlink" title="3.2 bufio.NewWriter"></a>3.2 bufio.NewWriter</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"bufio"</span><span class="token string">"fmt"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>file<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span><span class="token string">"xx.txt"</span><span class="token punctuation">,</span> os<span class="token punctuation">.</span>O_CREATE<span class="token operator">|</span>os<span class="token punctuation">.</span>O_TRUNC<span class="token operator">|</span>os<span class="token punctuation">.</span>O_WRONLY<span class="token punctuation">,</span> <span class="token number">0666</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"open file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> file<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>writer <span class="token operator">:=</span> bufio<span class="token punctuation">.</span><span class="token function">NewWriter</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>writer<span class="token punctuation">.</span><span class="token function">WriteString</span><span class="token punctuation">(</span><span class="token string">"hello沙河\n"</span><span class="token punctuation">)</span> <span class="token comment">//将数据先写入缓存</span><span class="token punctuation">}</span>writer<span class="token punctuation">.</span><span class="token function">Flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//将缓存中的内容写入文件</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="3-3-ioutil-WriteFile"><a href="#3-3-ioutil-WriteFile" class="headerlink" title="3.3 ioutil.WriteFile"></a>3.3 ioutil.WriteFile</h3><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"fmt"</span><span class="token string">"io/ioutil"</span><span class="token punctuation">)</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>str <span class="token operator">:=</span> <span class="token string">"hello 沙河"</span>err <span class="token operator">:=</span> ioutil<span class="token punctuation">.</span><span class="token function">WriteFile</span><span class="token punctuation">(</span><span class="token string">"./xx.txt"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0666</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"write file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h2 id="04-练习"><a href="#04-练习" class="headerlink" title="04.练习"></a>04.练习</h2><h3 id="4-1-copyFile"><a href="#4-1-copyFile" class="headerlink" title="4.1 copyFile"></a>4.1 copyFile</h3><ul><li>借助<code>io.Copy()</code>实现一个拷贝文件函数。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token comment">// CopyFile 拷贝文件函数</span><span class="token keyword">func</span> <span class="token function">CopyFile</span><span class="token punctuation">(</span>dstName<span class="token punctuation">,</span> srcName <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>written <span class="token builtin">int64</span><span class="token punctuation">,</span> err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 以读方式打开源文件</span>src<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span>srcName<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"open %s failed, err:%v.\n"</span><span class="token punctuation">,</span> srcName<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> src<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 以写|创建的方式打开目标文件</span>dst<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">OpenFile</span><span class="token punctuation">(</span>dstName<span class="token punctuation">,</span> os<span class="token punctuation">.</span>O_WRONLY<span class="token operator">|</span>os<span class="token punctuation">.</span>O_CREATE<span class="token punctuation">,</span> <span class="token number">0644</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"open %s failed, err:%v.\n"</span><span class="token punctuation">,</span> dstName<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token keyword">defer</span> dst<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">return</span> io<span class="token punctuation">.</span><span class="token function">Copy</span><span class="token punctuation">(</span>dst<span class="token punctuation">,</span> src<span class="token punctuation">)</span> <span class="token comment">//调用io.Copy()拷贝内容</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token boolean">_</span><span class="token punctuation">,</span> err <span class="token operator">:=</span> <span class="token function">CopyFile</span><span class="token punctuation">(</span><span class="token string">"dst.txt"</span><span class="token punctuation">,</span> <span class="token string">"src.txt"</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"copy file failed, err:"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"copy done!"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure><h3 id="4-2-实现一个cat命令"><a href="#4-2-实现一个cat命令" class="headerlink" title="4.2 实现一个cat命令"></a>4.2 实现一个cat命令</h3><ul><li>使用文件操作相关知识,模拟实现linux平台<code>cat</code>命令的功能。</li></ul><figure><div class="code-wrapper"><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"bufio"</span><span class="token string">"flag"</span><span class="token string">"fmt"</span><span class="token string">"io"</span><span class="token string">"os"</span><span class="token punctuation">)</span><span class="token comment">// cat命令实现</span><span class="token keyword">func</span> <span class="token function">cat</span><span class="token punctuation">(</span>r <span class="token operator">*</span>bufio<span class="token punctuation">.</span>Reader<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">{</span>buf<span class="token punctuation">,</span> err <span class="token operator">:=</span> r<span class="token punctuation">.</span><span class="token function">ReadBytes</span><span class="token punctuation">(</span><span class="token char">'\n'</span><span class="token punctuation">)</span> <span class="token comment">//注意是字符</span><span class="token keyword">if</span> err <span class="token operator">==</span> io<span class="token punctuation">.</span>EOF <span class="token punctuation">{</span><span class="token comment">// 退出之前将已读到的内容输出</span>fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdout<span class="token punctuation">,</span> <span class="token string">"%s"</span><span class="token punctuation">,</span> buf<span class="token punctuation">)</span><span class="token keyword">break</span><span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdout<span class="token punctuation">,</span> <span class="token string">"%s"</span><span class="token punctuation">,</span> buf<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>flag<span class="token punctuation">.</span><span class="token function">Parse</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 解析命令行参数</span><span class="token keyword">if</span> flag<span class="token punctuation">.</span><span class="token function">NArg</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span><span class="token comment">// 如果没有参数默认从标准输入读取内容</span><span class="token function">cat</span><span class="token punctuation">(</span>bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdin<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 依次读取每个指定文件的内容并打印到终端</span><span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> flag<span class="token punctuation">.</span><span class="token function">NArg</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>f<span class="token punctuation">,</span> err <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Open</span><span class="token punctuation">(</span>flag<span class="token punctuation">.</span><span class="token function">Arg</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdout<span class="token punctuation">,</span> <span class="token string">"reading from %s failed, err:%v\n"</span><span class="token punctuation">,</span> flag<span class="token punctuation">.</span><span class="token function">Arg</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token keyword">continue</span><span class="token punctuation">}</span><span class="token function">cat</span><span class="token punctuation">(</span>bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></div></figure>]]></content>
<summary type="html"><h1 id="06-IO操作"><a href="#06-IO操作" class="headerlink" title="06.IO操作"></a>06.IO操作</h1><h2 id="01-打开和关闭文件"><a href="#01-打开和关闭文件" class="head</summary>
<category term="Go常用库" scheme="http://coderedeng.github.io/categories/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
<category term="Go常用库" scheme="http://coderedeng.github.io/tags/Go%E5%B8%B8%E7%94%A8%E5%BA%93/"/>
</entry>
</feed>