-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
841 lines (706 loc) · 167 KB
/
atom.xml
File metadata and controls
841 lines (706 loc) · 167 KB
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
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>7byte</title>
<subtitle>骐骥一跃,不能十步;驽马十驾,功在不舍</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://7byte.github.io/"/>
<updated>2019-07-03T15:43:30.993Z</updated>
<id>https://7byte.github.io/</id>
<author>
<name>7byte</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>protobuf编码</title>
<link href="https://7byte.github.io/2019/05/09/protobuf-encoding/"/>
<id>https://7byte.github.io/2019/05/09/protobuf-encoding/</id>
<published>2019-05-09T04:21:27.000Z</published>
<updated>2019-07-03T15:43:30.993Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>英文原文:<a href="https://developers.google.com/protocol-buffers/docs/encoding" target="_blank" rel="external">https://developers.google.com/protocol-buffers/docs/encoding</a></p>
</blockquote>
<p>本文描述了protocol buffer 消息的二进制格式。当你在你的应用中使用protocol buffer 时无需了解这些细节。但是,要想理解不同的protocol buffer 格式对最终编码生成的消息大小有何影响,了解这些将非常有帮助。</p>
<h2 id="一个简单消息"><a href="#一个简单消息" class="headerlink" title="一个简单消息"></a>一个简单消息</h2><p>假设你有如下简单的消息定义:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">message Test1 {</div><div class="line"> required int32 a = 1;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>在应用中,创建一个名为<code>Test1</code>的消息并且对<code>a</code>赋值150。然后将这个消息序列化到一个输出流。如果查看编码出的消息内容,你将看到如下的三个字节:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="number">08</span> <span class="number">96</span> <span class="number">01</span></div></pre></td></tr></table></figure>
<p>只有几个数值——这些东西代表什么?且看下文……</p>
<h2 id="Base-128-Varints"><a href="#Base-128-Varints" class="headerlink" title="Base 128 Varints"></a>Base 128 Varints</h2><p>在理解上面的简单消息是如何编码之前,你需要先了解什么是Varints。Varints是使用一个或多个字节对整型数字序列化的方法。数值越小,序列化后所占的字节数越少。</p>
<p>除了最后一个字节,Varints中的每个字节设置了最高有效位(msb)用来标示后续的字节也是该数字的一部分。一个以补码表示的数字按7位一组的方式分成若干组,每组存储在字节的低7位,低位数字在前面(即小端序)。</p>
<p>例如,数字1只有一个字节,所以不需要设置msb:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="number">0000</span> <span class="number">0001</span></div></pre></td></tr></table></figure>
<p>数字300要稍微复杂一些:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="number">1010</span> <span class="number">1100</span> <span class="number">0000</span> <span class="number">0010</span></div></pre></td></tr></table></figure>
<p>你怎么知道这是300?首先,去掉每个字节中的msb,因为msb的作用只是告诉我们是否到达了数字的末尾(正如你所看到的,第一个字节设置了msb,表示后续字节也是varint的一部分):</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="number">1010</span> <span class="number">1100</span> <span class="number">0000</span> <span class="number">0010</span></div><div class="line">→ <span class="number">010</span> <span class="number">1100</span> <span class="number">000</span> <span class="number">0010</span></div></pre></td></tr></table></figure>
<p>反转两组7位数值,因为varints将数字的低位放在前面。然后把两组数值拼接起来:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="number">000</span> <span class="number">0010</span> <span class="number">010</span> <span class="number">1100</span></div><div class="line">→ <span class="number">000</span> <span class="number">0010</span> ++ <span class="number">010</span> <span class="number">1100</span></div><div class="line">→ <span class="number">100101100</span></div><div class="line">→ <span class="number">256</span> + <span class="number">32</span> + <span class="number">8</span> + <span class="number">4</span> = <span class="number">300</span></div></pre></td></tr></table></figure>
<h2 id="消息结构"><a href="#消息结构" class="headerlink" title="消息结构"></a>消息结构</h2><p>正如你所知道的,protocol buffer消息是一系列键值对。一个二进制消息使用字段的编号作为键——字段的名字和声明类型只有在解码结束后参考消息类型定义(比如<code>.proto</code>文件)才能确定。</p>
<p>编码消息时,所有的键和值被拼接在一起写入字节流。解码消息时,分析器需要能够跳过无法识别的字段。这样的话,就可以在消息中加入新的字段时无须破坏旧程序,即使旧程序不知道这些新字段。为了这个目的,每个键值对的“key”实际上是由两个值组成——<code>.proto</code>文件中字段的编号和一个<em>wire type</em>,wire type提供了“value”的长度信息。</p>
<p>可用的wire type如下:</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>含义</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>Varint</td>
<td>int32, int64, uint32, uint64, sint32, sint64, bool, enum</td>
</tr>
<tr>
<td>1</td>
<td>64-bit</td>
<td>fixed64, sfixed64, double</td>
</tr>
<tr>
<td>2</td>
<td>Length-delimited</td>
<td>string, bytes, embedded messages, packed repeated fields</td>
</tr>
<tr>
<td>3</td>
<td>Start group</td>
<td>groups (deprecated)</td>
</tr>
<tr>
<td>4</td>
<td>End group</td>
<td>groups (deprecated)</td>
</tr>
<tr>
<td>5</td>
<td>32-bit</td>
<td>fixed32, sfixed32, float</td>
</tr>
</tbody>
</table>
<p>消息流里的每个键是一个varint值:<code>(field_number << 3) | wire_type</code>,也就是说数字的低3位用来记录wire type。</p>
<p>再次回到我们的简单示例,你现在知道了字节流的第一个数字永远是一个varint类型的键值,即示例中的08,也就是(去掉msb后):</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="number">000</span> <span class="number">1000</span></div></pre></td></tr></table></figure>
<p>取最后3位可得wire type(0),然后右移3位可得字段编号(1)。现在你知道了tag是1,并且字段的值是varint类型。用上一节中学到的varint解码相关知识,你将会看到后面两个字节存储了150这个数字。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="number">96</span> <span class="number">01</span> = <span class="number">1001</span> <span class="number">0110</span> <span class="number">0000</span> <span class="number">0001</span></div><div class="line"> → <span class="number">000</span> <span class="number">0001</span> ++ <span class="number">001</span> <span class="number">0110</span> (drop the msb and reverse the groups of <span class="number">7</span> bits)</div><div class="line"> → <span class="number">10010110</span></div><div class="line"> → <span class="number">2</span> + <span class="number">4</span> + <span class="number">16</span> + <span class="number">128</span> = <span class="number">150</span></div></pre></td></tr></table></figure>
<h2 id="其它值类型"><a href="#其它值类型" class="headerlink" title="其它值类型"></a>其它值类型</h2><h3 id="有符号整型"><a href="#有符号整型" class="headerlink" title="有符号整型"></a>有符号整型</h3><p>正如你在上一节里看到的,protocol buffer中所有wire type为0的类型都被编码成varint。然而,有符号int类型(<code>sint32</code>和<code>sint64</code>)和“标准”int类型(<code>int32</code>和<code>int64</code>)这两者在处理负数编码时有很重要的区别。如果你使用<code>int32</code>或者<code>int64</code>作为一个负数的类型,varint编码后的结果<em>永远有10字节之长</em>,实际上就像是在处理一个非常大的无符号整型。如果你使用有符号int类型(<code>sint32</code>或<code>sint64</code>),varint将使用更高效的ZigZag(之字形)编码。</p>
<p>ZigZag编码将有符号整数映射到无符号整数,这样,具有较小绝对值的数字(例如-1)也具有较小的varint编码值。它以“之字形”来回处理正负整数,所以-1被编码成1,1被编码成2,-2被编码成3,以此类推,如下表所示:</p>
<table>
<thead>
<tr>
<th>有符号原始数字</th>
<th>编码为</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>-1</td>
<td>1</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>-2</td>
<td>3</td>
</tr>
<tr>
<td>2147483647</td>
<td>4294967294</td>
</tr>
<tr>
<td>-2147483648</td>
<td>4294967295</td>
</tr>
</tbody>
</table>
<p>换句话说,对每个<code>sint32</code>类型的<code>n</code>值以如下方式编码:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">(n << <span class="number">1</span>) ^ (n >> <span class="number">31</span>)</div></pre></td></tr></table></figure>
<p>对64位:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">(n << <span class="number">1</span>) ^ (n >> <span class="number">63</span>)</div></pre></td></tr></table></figure>
<p>注意,第二个位移操作——<code>(n >> 31)</code>——是一个算数位移。也就是说,位移的结果要么所有位全是0(如果<code>n</code>是正数),要么全是1(如果<code>n</code>是负数)。</p>
<p>当解析到<code>sint32</code>或是<code>sint64</code>时,对应的值被解码为原始的、有符号形式。</p>
<h3 id="非varint数字"><a href="#非varint数字" class="headerlink" title="非varint数字"></a>非varint数字</h3><p>非varint数字类型比较简单——<code>double</code>和<code>fixed64</code>使用wire type 1来告诉解析器有一块64位大小的数据,同理,<code>float</code>和<code>fixed32</code>使用wire type 5来告诉解析器有一块32位大小的数据。两种类型中的数值均以小端字节序编码。</p>
<h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><p>wire type 2(长度分隔)表示值为一个包含长度信息、携带指定个数字节的varint。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">message Test2 {</div><div class="line"> required string b = 2;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>对b赋值“testing”将会得到:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="number">12</span> <span class="number">07</span> <span class="number">74</span> <span class="number">65</span> <span class="number">73</span> <span class="number">74</span> <span class="number">69</span> <span class="number">6</span>e <span class="number">67</span></div></pre></td></tr></table></figure>
<p>红色的字节(<code>74 65 73 74 69 6e 67</code>)是“testing”的UTF8编码。这里的键是0x12→ tag = 2, type = 2。表示长度的varint值为7,你瞧,我们看到在它后面有七个字节——我们的字符串。</p>
<h2 id="嵌套消息"><a href="#嵌套消息" class="headerlink" title="嵌套消息"></a>嵌套消息</h2><p>这里有一个message定义,它以嵌套了我们的示例消息:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">message Test3 {</div><div class="line"> required Test1 c = 3;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>同样对Test1中的<code>a</code>赋值为150,编码后:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="number">1</span>a <span class="number">03</span> <span class="number">08</span> <span class="number">96</span> <span class="number">01</span></div></pre></td></tr></table></figure>
<p>可以看到,最后三个字节与第一个例子中的完全相同(<code>08 96 01</code>)。在它们前面是数字3——嵌套消息与字符串(wire type = 2)的处理方式完全相同。</p>
<h2 id="可选和repeated元素"><a href="#可选和repeated元素" class="headerlink" title="可选和repeated元素"></a>可选和repeated元素</h2><p>如果一个proto2消息定义了<code>repeated</code>元素(没有设置<code>[packed]=true</code>),那么编码后的消息会有0个或多个拥有相同tag编号的键值对。这些重复值不必连续,它们可能和其它的字段交错在一起。在解析时,元素相对于彼此的顺序保持不变,但是相对于其它字段的顺序信息会丢失。在proto3中,repeated字段使用<a href="https://developers.google.com/protocol-buffers/docs/encoding#packed" target="_blank" rel="external">packed 编码</a>,你可以阅读下面的内容。</p>
<p>对proto3中的任何一个non-repeated字段,或是proto2中的<code>optional</code>字段,编码后的消息可能有,也可能没有这个tag编号字段的键值对。</p>
<p>通常来说,编码的消息永远不会有一个non-repeated字段的多个示例。然而,真碰到这种情况时我们期望解析器也能够正常处理。对数字类型和字符串,如果同一个字段出现多次,解析器将接受它所看到的最后哪个值。对于嵌套消息字段,解析器会合并同一个字段的的多个实例,就像使用<code>Message::MergeFrom</code>方法——所有的歧义字段都会用后一个实例中的字段替换,歧义嵌套消息被合并,并且repeated字段会拼接起来。这些规则的效果就是,解析两个消息的串联,与你分别解析这两条消息然后合并的结果完全相同。即:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">MyMessage message;</div><div class="line">message.ParseFromString(str1 + str2);</div></pre></td></tr></table></figure>
<p>等于:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">MyMessage message, message2;</div><div class="line">message.ParseFromString(str1);</div><div class="line">message2.ParseFromString(str2);</div><div class="line">message.MergeFrom(message2);</div></pre></td></tr></table></figure>
<p>这个特性有时候很有用,因为它允许你在完全不知道两个消息的类型时合并它们。</p>
<h3 id="Packed-repeated字段"><a href="#Packed-repeated字段" class="headerlink" title="Packed repeated字段"></a>Packed repeated字段</h3><p>2.1.0版本中引入了packed repeated字段,在proto2中它被声明成带有<code>[packed=true]</code>选项的repeated字段。在proto3中,repeated字段默认会按packed处理。这些功能与repeated字段很类似,但是有不一样的编码规则。编码生成的消息中不会出现包含零元素的packed repeated字段。否则,所有的元素都被打包成一个wire type为2(长度分隔)的键值对。每个元素都按正常的、相同的方式编码,除了前面没有tag。</p>
<p>例如,想象你有这样一个消息类型:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">message Test4 {</div><div class="line"> repeated int32 d = 4 [packed=true];</div><div class="line">}</div></pre></td></tr></table></figure>
<p>现在,假设你构造了一个<code>Test4</code>,给repeated字段<code>d</code>设值3、270和86942。然后,编码的形式将是:</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="number">22</span> <span class="comment">// tag (field number 4, wire type 2)</span></div><div class="line"><span class="number">06</span> <span class="comment">// payload size (6 bytes)</span></div><div class="line"><span class="number">03</span> <span class="comment">// first element (varint 3)</span></div><div class="line"><span class="number">8</span>E <span class="number">02</span> <span class="comment">// second element (varint 270)</span></div><div class="line"><span class="number">9</span>E A7 <span class="number">05</span> <span class="comment">// third element (varint 86942)</span></div></pre></td></tr></table></figure>
<p>只有原始数字类型的repeated字段(使用varint、32-bit、或者64-bit类型)才能声明为“packed”。</p>
<p>请注意,尽管通常没有理由将多个键值对编码成一个packed repeated字段,但编码器必须做好接受多个键值对的准备。在这种情况下,所有载荷(payloads)应该拼接到一起。每一对都必须包含完整的元素。</p>
<p>Protocol buffer解析器必须能够解析以<code>packed</code>方式编译而成的repeated字段,就好像它们没有被打包一样,反之亦然。这样就能允许以向前和向后兼容的方式向现有字段添加<code>[packed=true]</code>。</p>
<h2 id="字段顺序"><a href="#字段顺序" class="headerlink" title="字段顺序"></a>字段顺序</h2><p>虽然可以在<code>.proto</code>中以任意顺序使用字段号,但当消息被序列化时,已知字段应该按字段号顺序写入,正如所提供的C++、Java和Python序列化代码。这使得解析代码可以使用依赖于字段号的优化。但是,protocol buffer解析器必须能够以任意顺序解析字段,因为并非所有消息都是通过简单地序列化一个对象来创建的——例如,通过简单地拼接两个消息来合并它们有时候是很有用的。</p>
<p>如果一个消息具有<a href="https://developers.google.com/protocol-buffers/docs/proto.html#updating" target="_blank" rel="external">未知字段</a>,当前的Java和C++实现会在顺序排列已知字段之后按任意顺序写入未知字段。当前的Python实现不处理未知字段。</p>
<h2 id="版权说明"><a href="#版权说明" class="headerlink" title="版权说明"></a>版权说明</h2><p>除另有说明外,本页面的内容是根据<a href="http://creativecommons.org/licenses/by/3.0/" target="_blank" rel="external">知识共享署名3.0许可证</a>授权的,代码示例根据<a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank" rel="external">Apache 2.0许可证</a>授权。有关详细信息,请参阅我们的<a href="https://developers.google.com/terms/site-policies" target="_blank" rel="external">网站政策</a>。Java是甲骨文和/或其子公司的注册商标。</p>
]]></content>
<summary type="html">
<blockquote>
<p>英文原文:<a href="https://developers.google.com/protocol-buffers/docs/encoding" target="_blank" rel="external">https://developer
</summary>
<category term="算法" scheme="https://7byte.github.io/categories/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>轻量级在线游戏服务器框架skynet</title>
<link href="https://7byte.github.io/2019/04/16/introduce2skynet/"/>
<id>https://7byte.github.io/2019/04/16/introduce2skynet/</id>
<published>2019-04-16T03:30:27.000Z</published>
<updated>2019-07-03T15:44:33.726Z</updated>
<content type="html"><![CDATA[<h2 id="skynet是什么"><a href="#skynet是什么" class="headerlink" title="skynet是什么"></a>skynet是什么</h2><p><img src="http://ou1s4jkow.bkt.clouddn.com/skynet.png" alt=""></p>
<p>按照作者的说法,<a href="https://github.com/cloudwu/skynet/wiki" title="skynet官方wiki" target="_blank" rel="external"><em>skynet是一个轻量级的为在线游戏服务器打造的框架</em> </a>。 </p>
<h2 id="skynet可以做什么"><a href="#skynet可以做什么" class="headerlink" title="skynet可以做什么"></a>skynet可以做什么</h2><h2 id="skynet怎么用"><a href="#skynet怎么用" class="headerlink" title="skynet怎么用"></a>skynet怎么用</h2><h3 id="常用Lua-API"><a href="#常用Lua-API" class="headerlink" title="常用Lua API"></a>常用Lua API</h3><ul>
<li><strong>skynet.newservice(name, …)</strong> 启动一个名为 name 的新服务</li>
<li><strong>skynet.start(func)</strong> 用 func 函数初始化服务,并将消息处理函数注册到 C 层,让该服务可以工作</li>
<li><strong>skynet.dispatch(type, func)</strong> 为 type 类型的消息设定一个处理函数。</li>
<li><strong>skynet.fork(func, …)</strong> 启动一个新的任务去执行函数 func </li>
<li><strong>skynet.call(addr, type, …)</strong> 用 type 类型发送一个消息到 addr ,并等待对方的回应</li>
<li><strong>skynet.send(addr, type, …)</strong> 用 type 类型向 addr 发送一个消息</li>
</ul>
<h3 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">.</div><div class="line">├── bin</div><div class="line">│ └── skynet</div><div class="line">├── conf</div><div class="line">│ └── config.example</div><div class="line">├── lib</div><div class="line">│ ├── cservice</div><div class="line">│ ├── luaclib</div><div class="line">│ ├── lualib</div><div class="line">│ └── service</div><div class="line">├── script</div><div class="line">└── src</div><div class="line"> └── main.lua</div></pre></td></tr></table></figure>
<h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><p>启动 skynet 服务需要提供一个配置文件,以下是一个简单的配置文件示例:</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">root = <span class="string">"./"</span></div><div class="line">thread = <span class="number">8</span></div><div class="line">harbor = <span class="number">0</span></div><div class="line">start = <span class="string">"main"</span> <span class="comment">-- main script</span></div><div class="line">luaservice = root .. <span class="string">"src/?.lua;"</span> .. root .. <span class="string">"lib/service/?.lua"</span></div><div class="line">lualoader = root .. <span class="string">"lib/lualib/loader.lua"</span></div><div class="line">lua_path = root .. <span class="string">"lib/lualib/?.lua;"</span> .. root .. <span class="string">"lib/lualib/?/init.lua;"</span></div><div class="line">lua_cpath = root .. <span class="string">"lib/luaclib/?.so;"</span></div><div class="line">cpath = root .. <span class="string">"lib/cservice/?.so"</span></div></pre></td></tr></table></figure>
<ul>
<li><strong>root</strong> 项目根目录</li>
<li><strong>thread</strong> 启动多少个工作线程。通常不要将它配置超过你实际拥有的 CPU 核心数</li>
<li><strong>bootstrap</strong> skynet 启动的第一个服务以及其启动参数。默认配置为<code>snlua bootstrap</code>,即启动一个名为 bootstrap 的 lua 服务。通常指的是 service/bootstrap.lua 这段代码</li>
<li><strong>harbor</strong> 可以是 1-255 间的任意整数。一个 skynet 网络最多支持 255 个节点。每个节点有必须有一个唯一的编号。harbor 为 0 时skynet 工作在单节点模式下</li>
<li><strong>start</strong> 这是 bootstrap 最后一个环节将启动的 lua 服务,也就是你定制的 skynet 节点的主程序。默认为 main ,即启动 main.lua 这个脚本。这个 lua 服务的路径由下面的 <strong>luaservice</strong> 指定</li>
<li><strong>lualoader</strong> 用哪一段 lua 代码加载 lua 服务。通常配置为 lualib/loader.lua ,再由这段代码解析服务名称,进一步加载 lua 代码。snlua 会将下面几个配置项取出,放在初始化好的 lua 虚拟机的全局变量中。具体可参考实现</li>
<li><strong>luaservice</strong> lua 服务代码所在的位置。可以配置多项,以 ; 分割。 如果在创建 lua 服务时,以一个目录而不是单个文件提供,最终找到的路径还会被添加到 package.path 中。比如,在编写 lua 服务时,有时候会希望把该服务用到的库也放到同一个目录下</li>
<li><strong>lua_path</strong> 将添加到 package.path 中的路径,供 require 调用</li>
<li><strong>lua_cpath</strong> 将添加到 package.cpath 中的路径,供 require 调用</li>
</ul>
<ul>
<li><strong>cpath</strong> 用 C 编写的服务模块的位置,通常指 cservice 下那些 .so 文件。如果你的系统的动态库不是以 .so 为后缀,需要做相应的修改</li>
</ul>
<h3 id="启动skynet节点"><a href="#启动skynet节点" class="headerlink" title="启动skynet节点"></a>启动skynet节点</h3><p><code>main.lua</code></p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">local</span> skynet = <span class="built_in">require</span> <span class="string">"skynet"</span></div><div class="line"></div><div class="line">skynet.start(<span class="function"><span class="keyword">function</span><span class="params">()</span></span></div><div class="line">true<span class="built_in">print</span>(<span class="string">"hello, skynet!"</span>)</div><div class="line">trueskynet.exit()</div><div class="line"><span class="keyword">end</span>)</div></pre></td></tr></table></figure>
<p>使用skynet启动一个节点:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">$ ./bin/skynet ./conf/config.example</div><div class="line">[:00000001] LAUNCH logger </div><div class="line">[:00000002] LAUNCH snlua bootstrap</div><div class="line">[:00000003] LAUNCH snlua launcher</div><div class="line">[:00000004] LAUNCH snlua cdummy</div><div class="line">[:00000005] LAUNCH harbor 0 4</div><div class="line">[:00000006] LAUNCH snlua datacenterd</div><div class="line">[:00000007] LAUNCH snlua service_mgr</div><div class="line">[:00000008] LAUNCH snlua main</div><div class="line">hello, skynet!</div><div class="line">[:00000008] KILL self</div><div class="line">[:00000002] KILL self</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h2 id="skynet是什么"><a href="#skynet是什么" class="headerlink" title="skynet是什么"></a>skynet是什么</h2><p><img src="http://ou1s4jkow.bkt.clouddn.com
</summary>
<category term="算法" scheme="https://7byte.github.io/categories/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>golang包初始化</title>
<link href="https://7byte.github.io/2018/05/20/golangPackageInit/"/>
<id>https://7byte.github.io/2018/05/20/golangPackageInit/</id>
<published>2018-05-20T09:16:27.000Z</published>
<updated>2019-07-03T15:45:09.510Z</updated>
<content type="html"><![CDATA[<p>相比于C/C++等语言,Golang提供了一个非常重要的特性:包。包可以提供类似其它编程语言中的库或者模块的功能,这在Java和Python等语言中已经很常见,但是golang的包自有其特点,比如禁止循环引用、通过首字母大小写区分可见性而不是使用<code>public</code>和<code>private</code>这样的关键字。</p>
<p>另外还有一个被大量使用的特性:init函数机制。</p>
<ul>
<li>每个包的go文件都能包含任意数量的init函数;</li>
<li>init函数不能被用户调用或引用;</li>
<li>多个init函数在程序启动时按照声明的顺序(执行main函数之前)自动调用。</li>
</ul>
<div align="center"><img src="/images/golang_init.png" alt="golang初始化顺序"></div>
<p>(图片来自beego官方文档:<a href="https://beego.me/docs/quickstart/router.md" target="_blank" rel="external">https://beego.me/docs/quickstart/router.md</a> )</p>
<h3 id="包内的初始化顺序"><a href="#包内的初始化顺序" class="headerlink" title="包内的初始化顺序"></a>包内的初始化顺序</h3><ol>
<li><code>const</code>常量首先被初始化。</li>
<li>接着是包内的全局变量,初始化的顺序为变量的声明的顺序。但是当变量之间存在依赖关系时,优先初始化被依赖的变量。</li>
<li>最后执行init函数。</li>
</ol>
<p>当包内有多个go源文件时,<code>go tool</code>按照文件名排序后依次送入编译器初始化。</p>
<h3 id="依赖包的初始化"><a href="#依赖包的初始化" class="headerlink" title="依赖包的初始化"></a>依赖包的初始化</h3><p>在初始化一个包之前,必须完全初始化它所依赖的包。例如上图中的pkg1引入了pkg2,那么在初始化pkg1之前必须先初始化pkg2,而pkg2引入了pkg3,所以在初始化pkg2之前需要先初始化pkg3,pkg3没有引入任何包,所以直接执行包内初始化。由上图也可以发现,main总是最后一个被初始化的包。</p>
<p>另外还有一个很重要的特性:每个包只会初始化一次。例如,即使<code>fmt</code>被多个包引入,也只会在第一次时被初始化。</p>
<p>用下面的测试代码来验证一下以上特性。</p>
<p><code>testInit/pkg2/pkg2.go</code></p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> pkg2</div><div class="line"></div><div class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> (</div><div class="line"> X = <span class="number">10</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</div><div class="line"> X++</div><div class="line"> fmt.Printf(<span class="string">"x init to %d in pkg2\n"</span>, X)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>pkg2声明了一个可导出的变量<code>X</code>,初始值是10。init函数将<code>X</code>的值加<code>1</code>,并打印出<code>X</code>当前的值。</p>
<p><code>testInit/pkg1/pkg1.go</code></p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> pkg1</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line"> <span class="string">"fmt"</span></div><div class="line"> <span class="string">"testInit/pkg2"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</div><div class="line"> pkg2.X += <span class="number">2</span></div><div class="line"> fmt.Printf(<span class="string">"x init to %d in pkg1\n"</span>, pkg2.X)</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">PrintX</span><span class="params">()</span></span> {</div><div class="line"> fmt.Printf(<span class="string">"x = %d in pkg1\n"</span>, pkg2.X)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>pkg1引入了pkg2,并且在inti函数中将<code>X</code>的值加2,然后打印出<code>X</code>当前的值。</p>
<p><code>testInit/main.go</code></p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line"> <span class="string">"fmt"</span></div><div class="line"> <span class="string">"testInit/pkg1"</span></div><div class="line"> <span class="string">"testInit/pkg2"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> {</div><div class="line"> pkg2.X += <span class="number">3</span></div><div class="line"> fmt.Printf(<span class="string">"x init to %d in main\n"</span>, pkg2.X)</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</div><div class="line"> fmt.Printf(<span class="string">"x = %d in main\n"</span>, pkg2.X)</div><div class="line"> pkg1.PrintX()</div><div class="line">}</div></pre></td></tr></table></figure>
<p>main同时引入了pkg1和pkg2,并且在inti函数中将<code>X</code>的值加3,然后打印出<code>X</code>当前的值。</p>
<p>输出:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">[ `go run main.go` | done: 541.4397ms ]</div><div class="line"> x init to 11 in pkg2</div><div class="line"> x init to 13 in pkg1</div><div class="line"> x init to 16 in main</div><div class="line"> x = 16 in main</div><div class="line"> x = 16 in pkg1</div></pre></td></tr></table></figure>
<p>不考虑<code>fmt</code>包,以上代码中有两条引用关系<code>main->pkg1->pkg2</code>和<code>main->pkg2</code>,但是从输出结果可以看到,pkg2只被初始化了一次,符合我们的预期。</p>
]]></content>
<summary type="html">
<p>相比于C/C++等语言,Golang提供了一个非常重要的特性:包。包可以提供类似其它编程语言中的库或者模块的功能,这在Java和Python等语言中已经很常见,但是golang的包自有其特点,比如禁止循环引用、通过首字母大小写区分可见性而不是使用<code>public</
</summary>
<category term="golang" scheme="https://7byte.github.io/categories/golang/"/>
<category term="golang" scheme="https://7byte.github.io/tags/golang/"/>
</entry>
<entry>
<title>二叉查找树(BST)</title>
<link href="https://7byte.github.io/2018/04/16/binary-search-tree/"/>
<id>https://7byte.github.io/2018/04/16/binary-search-tree/</id>
<published>2018-04-16T03:30:27.000Z</published>
<updated>2019-07-03T15:44:40.745Z</updated>
<content type="html"><![CDATA[<h2 id="二叉查找树定义"><a href="#二叉查找树定义" class="headerlink" title="二叉查找树定义"></a>二叉查找树定义</h2><blockquote>
<p><a href="https://zh.wikipedia.org/zh-cn/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9" target="_blank" rel="external"><strong>二叉查找树</strong></a>(英语:<em>Binary Search Tree</em>),也称二叉搜索树、有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tree),是指一棵空树或者具有下列性质的<a href="https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%8F%89%E6%A0%91" target="_blank" rel="external">二叉树</a>:</p>
<ol>
<li>若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;</li>
<li>若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;</li>
<li>任意节点的左、右子树也分别为二叉查找树;</li>
<li>没有键值相等的节点。</li>
</ol>
</blockquote>
<h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><p>二叉查找树基本操作的复杂度与树的高度成正比,即一颗高度为<code>h</code>的树上各操作的时间复杂度为<code>O(h)</code>,而树的高度与树的平衡性有关,最好情况下对于一颗含<code>n</code>个节点的完全二叉树,操作的复杂度为<code>O(lgn)</code>,但是在最坏情况下二叉树退化成线性链,操作复杂度为<code>O(n)</code>。由此可知,在实际使用时应尽量保证数据的随机性,以使树的高度不至于过大。但是在现实中并不总能保证二叉查找树是随机构造成的,所以就出现了一些二叉查找树的变形,例如AVL树、红黑树等<a href="https://zh.wikipedia.org/wiki/%E8%87%AA%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91" target="_blank" rel="external">自平衡二叉查找树</a>,后面的文章会逐一分析这些树的特性和算法实现。二叉查找树作为其它各类自平衡二叉查找树的基础,值得好好研究一下。</p>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><h3 id="数据结构定义"><a href="#数据结构定义" class="headerlink" title="数据结构定义"></a>数据结构定义</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span><span class="params">(*compare)</span><span class="params">(<span class="keyword">void</span>*, <span class="keyword">void</span>*)</span></span>;</div><div class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span><span class="params">(*print)</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</div><div class="line"></div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> binarySearchTree BST;</div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> nodeTag node;</div><div class="line"></div><div class="line"><span class="keyword">struct</span> nodeTag</div><div class="line">{</div><div class="line"> node *parent; <span class="comment">//父节点</span></div><div class="line"> node *left; <span class="comment">//左儿子</span></div><div class="line"> node *right; <span class="comment">//右儿子</span></div><div class="line"> <span class="keyword">void</span> *data; <span class="comment">//数据域</span></div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">struct</span> binarySearchTree</div><div class="line">{</div><div class="line"> node *root; <span class="comment">//根节点</span></div><div class="line"> compare cmp; <span class="comment">//比较函数</span></div><div class="line"> print prt; <span class="comment">//打印函数</span></div><div class="line">};</div></pre></td></tr></table></figure>
<p>每个节点<code>node</code>对象包含<code>parent</code>、<code>left</code>、<code>right</code>,分别指向节点的父节点、左儿子和右儿子。如果某个儿子节点或者父节点不存在,则相应域的值为<code>NULL</code>。数据域<code>data</code>指向数据地址。</p>
<p><code>BST</code>是二叉查找树的结构定义,记录了树的根节点指针以及两个函数指针。<code>cmp</code>用于定义数据大小的比较,<code>prt</code>用于调试时的打印输出。</p>
<h3 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h3><p>插入数据可以分为以下几种情况:</p>
<ol>
<li>根节点为空,则创建根节点,插入完成;</li>
<li>当前节点非空,key值<strong>小于</strong>要插入的数据key值,设置当前节点为左儿子节点;</li>
<li>当前节点非空,key值<strong>大于</strong>要插入的数据key值,设置当前节点为右儿子节点;</li>
<li>当前节点非空,key值<strong>等于</strong>要插入的数据key值,根据查找二叉树的定义不能有重复的节点,返回插入失败。</li>
</ol>
<p>重复2、3、4步,直到当前节点为空,这个位置即是我们要插入节点的地方。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">insert</span><span class="params">(BST *tree, <span class="keyword">void</span> *val)</span></span></div><div class="line">{</div><div class="line"> node *nd = <span class="literal">NULL</span>;</div><div class="line"> <span class="keyword">if</span> (!tree || !val)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line"> }</div><div class="line"> nd = (node *)<span class="built_in">calloc</span>(<span class="number">1</span>, <span class="keyword">sizeof</span>(node));</div><div class="line"> <span class="keyword">if</span> (!nd)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line"> }</div><div class="line"> nd->data = val;</div><div class="line"> </div><div class="line"> <span class="keyword">if</span> (!tree->root)</div><div class="line"> {</div><div class="line"> <span class="comment">// 创建根节点</span></div><div class="line"> nd->parent = <span class="literal">NULL</span>;</div><div class="line"> nd->left = <span class="literal">NULL</span>;</div><div class="line"> nd->right = <span class="literal">NULL</span>;</div><div class="line"> tree->root = nd;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> <span class="keyword">int</span> cmpResult = <span class="number">0</span>;</div><div class="line"> node *pre = <span class="literal">NULL</span>, *cur = <span class="literal">NULL</span>;</div><div class="line"> cur = tree->root;</div><div class="line"> <span class="keyword">while</span> (<span class="number">1</span>)</div><div class="line"> {</div><div class="line"> pre = cur;</div><div class="line"> cmpResult = (*tree->cmp)(nd->data, cur->data);</div><div class="line"> <span class="keyword">if</span> (<span class="number">0</span> == cmpResult)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (cmpResult < <span class="number">0</span>)</div><div class="line"> { </div><div class="line"> cur = cur->left;</div><div class="line"> <span class="keyword">if</span> (!cur)</div><div class="line"> {</div><div class="line"> nd->parent = pre;</div><div class="line"> pre->left = nd;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> cur = cur->right;</div><div class="line"> <span class="keyword">if</span> (!cur)</div><div class="line"> {</div><div class="line"> nd->parent = pre;</div><div class="line"> pre->right = nd;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><p>查找过程可以使用递归的方式来实现。该过程从树的根节点开始,对碰到的每个节点与<code>val</code>做比较。如果比较结果相等,则查找结束。如果节点key值小于<code>val</code>,则继续查找左子树。如果节点key值大于<code>val</code>则继续查找右子树。也可以使用非递归的方式实现查找,原理与递归方式是一样的。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="function">node* <span class="title">find</span><span class="params">(BST *tree, <span class="keyword">void</span> *val)</span></span></div><div class="line">{</div><div class="line"> <span class="keyword">return</span> findNode(tree, tree->root, val);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function">node* <span class="title">findNode</span><span class="params">(BST *tree, node* root, <span class="keyword">void</span> *val)</span></span></div><div class="line">{</div><div class="line"> <span class="keyword">int</span> cmpResult = <span class="number">0</span>;</div><div class="line"> <span class="keyword">if</span> (!tree || !root || !val)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</div><div class="line"> }</div><div class="line"> cmpResult = (*tree->cmp)(root->data, val);</div><div class="line"> <span class="keyword">if</span> (cmpResult == <span class="number">0</span>)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> root;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (cmpResult < <span class="number">0</span>)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> findNode(tree, root->right, val);</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> findNode(tree, root->left, val);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="遍历"><a href="#遍历" class="headerlink" title="遍历"></a>遍历</h3><p>树的遍历,即不重复地访问树的所有节点。与线性数据结构不同的是,从树的根节点出发,可以有多种路径可供选择,所以树的遍历就有了多种方式。</p>
<ol>
<li><a href="https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2" target="_blank" rel="external">深度优先遍历</a>:沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。<ol>
<li>前序遍历:先访问根节点,然后访问左子树,最后访问右子树;</li>
<li>中序遍历:先访问左子树,然后访问根节点,最后访问右子树;</li>
<li>后续遍历:先访问左子树,然后访问右子树,最后访问根节点。</li>
</ol>
</li>
<li><a href="https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2" target="_blank" rel="external">广度优先遍历</a>:从根节点开始,沿着树的宽度遍历树的节点。</li>
</ol>
<p>下面给出中序遍历的递归实现。前序遍历和后续遍历的实现与中序遍历大体相同,只需调整根节点、左子树和右子树的访问顺序即可。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">inOrder</span><span class="params">(BST *tree, node *nd)</span></span></div><div class="line">{</div><div class="line"> <span class="keyword">return</span> inOrderDepth(tree, nd, <span class="number">0</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">inOrderDepth</span><span class="params">(BST *tree, node *nd, <span class="keyword">int</span> sp)</span></span></div><div class="line">{</div><div class="line"> <span class="keyword">int</span> i = <span class="number">0</span>;</div><div class="line"> <span class="keyword">if</span> (!tree || !nd)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">//遍历左子树</span></div><div class="line"> inOrderDepth(tree, nd->left, sp + <span class="number">1</span>);</div><div class="line"> </div><div class="line"> <span class="comment">//打印根节点</span></div><div class="line"> <span class="keyword">for</span> (i ; i < sp; i++)</div><div class="line"> {</div><div class="line"> <span class="built_in">printf</span>(<span class="string">"----"</span>);</div><div class="line"> } </div><div class="line"> (*tree->prt)(nd->data);</div><div class="line"> </div><div class="line"> <span class="comment">//遍历右子树</span></div><div class="line"> inOrderDepth(tree, nd->right, sp + <span class="number">1</span>);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>inOrderDepth</code>函数参数<code>sp</code>辅助记录当前节点的深度,打印树结构时更加直观:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">------------0</div><div class="line">--------1</div><div class="line">------------7</div><div class="line">----12</div><div class="line">------------13</div><div class="line">--------14</div><div class="line">36</div><div class="line">--------57</div><div class="line">----72</div><div class="line">------------76</div><div class="line">--------87</div><div class="line">------------89</div></pre></td></tr></table></figure>
<p>广度优先遍历通常会使用队列来实现,首先将根节点放入队列中,然后从队列中取出第一个节点,同时把这个节点的子节点加入队列,重复从队列取节点的过程直到队列为空,则遍历完成。</p>
<h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><p>二叉查找树删除节点相对来说较前面的插入、遍历等操作要复杂一些,因为删除节点不能破坏整棵树的结构,也就是说要保证删除节点之后依然是一颗二叉查找树。可以分为四种情况处理(nd为待删除的节点):</p>
<ol>
<li>nd为叶子节点:最简单的情况,修改父节点的left或right指针(nd为左儿子修改left指针,nd为右儿子修改right指针)为<code>NULL</code>即可;</li>
<li>nd左子树不为空、右子树为空:修改nd的左子树为nd父节点的左子树;</li>
<li>nd右子树不为空、左子树为空:修改nd的右子树为nd父节点的右子树;</li>
<li>nd左右子树都不为空:令nd的直接前驱(即nd左子树中最大的节点)或直接后继(即右子树中最小的节点)替代nd,然后再从二叉查找树中删去它的直接前驱(或直接后继)。</li>
</ol>
<div align="center"><br><br><img src="/images/bst_delete_node.png" alt="BST删除一个有左、右子树的节点"><br><br></div>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">del</span><span class="params">(BST *tree, <span class="keyword">void</span> *val)</span></span></div><div class="line">{</div><div class="line"> node *nd = <span class="literal">NULL</span>;</div><div class="line"> <span class="keyword">if</span> (!tree || !val)</div><div class="line"> {</div><div class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line"> }</div><div class="line"> nd = find(tree, val);</div><div class="line"> <span class="keyword">if</span> (nd)</div><div class="line"> { </div><div class="line"> node *q, *s;</div><div class="line"> <span class="keyword">if</span> (!nd->left && !nd->right)</div><div class="line"> {</div><div class="line"> <span class="comment">// 该节点为叶子节点</span></div><div class="line"> <span class="keyword">if</span> (nd->parent)</div><div class="line"> {</div><div class="line"> <span class="keyword">if</span> (nd->parent->left == nd)</div><div class="line"> {</div><div class="line"> nd->parent->left = <span class="literal">NULL</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> nd->parent->right = <span class="literal">NULL</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="built_in">free</span>(nd);</div><div class="line"> nd = <span class="literal">NULL</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (!nd->right)</div><div class="line"> {</div><div class="line"> <span class="comment">// 左子树不为空</span></div><div class="line"> q = nd->left;</div><div class="line"> nd->data = q->data;</div><div class="line"> nd->left = q->left;</div><div class="line"> nd->right = q->right;</div><div class="line"> <span class="keyword">if</span> (q->left)</div><div class="line"> {</div><div class="line"> q->left->parent = nd;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (q->right)</div><div class="line"> {</div><div class="line"> q->right->parent = nd;</div><div class="line"> }</div><div class="line"> <span class="built_in">free</span>(q);</div><div class="line"> q = <span class="literal">NULL</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (!nd->left)</div><div class="line"> {</div><div class="line"> <span class="comment">// 右子树不为空</span></div><div class="line"> q = nd->right;</div><div class="line"> nd->data = q->data;</div><div class="line"> nd->left = q->left;</div><div class="line"> nd->right = q->right;</div><div class="line"> <span class="keyword">if</span> (q->left)</div><div class="line"> {</div><div class="line"> q->left->parent = nd;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (q->right)</div><div class="line"> {</div><div class="line"> q->right->parent = nd;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> <span class="comment">// 左右子树都不为空</span></div><div class="line"> s = nd;</div><div class="line"> q = nd->left;</div><div class="line"> <span class="keyword">while</span> (q->right)</div><div class="line"> {</div><div class="line"> s = q;</div><div class="line"> q = q->right;</div><div class="line"> }</div><div class="line"> nd->data = q->data;</div><div class="line"> <span class="keyword">if</span> (s != nd)</div><div class="line"> {</div><div class="line"> s->right = q->left;</div><div class="line"> }</div><div class="line"> <span class="keyword">else</span></div><div class="line"> {</div><div class="line"> s->left = q->left;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (q->left)</div><div class="line"> {</div><div class="line"> q->left->parent = s;</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="built_in">free</span>(q);</div><div class="line"> q = <span class="literal">NULL</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>完整代码:<a href="https://github.com/7byte/AlgorithmPractice/tree/master/%E6%A0%91" target="_blank" rel="external">https://github.com/7byte/AlgorithmPractice/tree/master/%E6%A0%91</a></p>
]]></content>
<summary type="html">
<h2 id="二叉查找树定义"><a href="#二叉查找树定义" class="headerlink" title="二叉查找树定义"></a>二叉查找树定义</h2><blockquote>
<p><a href="https://zh.wikipedia.org/zh-
</summary>
<category term="算法" scheme="https://7byte.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="树" scheme="https://7byte.github.io/tags/%E6%A0%91/"/>
<category term="搜索" scheme="https://7byte.github.io/tags/%E6%90%9C%E7%B4%A2/"/>
</entry>
<entry>
<title>openresty中连接池的应用</title>
<link href="https://7byte.github.io/2018/03/05/trytouse-conn-pool/"/>
<id>https://7byte.github.io/2018/03/05/trytouse-conn-pool/</id>
<published>2018-03-05T07:16:27.000Z</published>
<updated>2019-07-03T15:45:23.461Z</updated>
<content type="html"><![CDATA[<p>在各种形式的数据交互中,创建连接、传输数据、销毁连接这三个步骤都是必不可少的。但是,当并发量持续增加时,耗费在创建和销毁连接上的时间将越来越不容忽视,所以理想中的情况应该是这样:创建连接-传输数据-……-传输数据-销毁连接。连接池正是为了解决这种问题而产生的技术。<br>在 OpenResty 中,所有具备 set_keepalive 的类、库函数,说明他都是支持连接池的。这里举个mysql连接池的例子,其它类型的连接池比如redis连接池、线程池、内存池思路是一样的。</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">local</span> MYSQL = <span class="built_in">require</span>(<span class="string">"resty.mysql"</span>)</div><div class="line"><span class="keyword">local</span> JSON = <span class="built_in">require</span>(<span class="string">"cjson"</span>)</div><div class="line"></div><div class="line"><span class="keyword">local</span> mysql_server_ip = <span class="string">"127.0.0.1"</span></div><div class="line"><span class="keyword">local</span> mysql_server_port = <span class="number">3306</span></div><div class="line"><span class="keyword">local</span> mysql_databases = <span class="string">"mysql"</span></div><div class="line"><span class="keyword">local</span> mysql_user_name = <span class="string">"7byte"</span></div><div class="line"><span class="keyword">local</span> mysql_user_pass = <span class="string">"abcdefg"</span></div><div class="line"><span class="keyword">local</span> mysql_max_packet_size = <span class="number">1024</span> * <span class="number">1024</span></div><div class="line"></div><div class="line"><span class="comment">-- 获取mysql连接,mysql:connect先在连接池中查找是否有可用的连接,没有才创建一个新连接</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getMysql</span><span class="params">()</span></span></div><div class="line"> <span class="keyword">local</span> mysql = MYSQL:new()</div><div class="line"> mysql:set_timeout(<span class="number">1000</span>)</div><div class="line"> <span class="keyword">local</span> ok, err, errno, sqlstate = mysql:connect{</div><div class="line"> host = mysql_server_ip,</div><div class="line"> port = mysql_server_port,</div><div class="line"> database = mysql_databases,</div><div class="line"> user = mysql_user_name,</div><div class="line"> password = mysql_user_pass,</div><div class="line"> max_packet_size = mysql_max_packet_size,</div><div class="line"> charset=utf8 }</div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> ok <span class="keyword">then</span></div><div class="line"> <span class="keyword">return</span> <span class="keyword">nil</span>, err, errno, sqlstate</div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="keyword">return</span> mysql,<span class="keyword">nil</span>,<span class="keyword">nil</span>,sqlstate</div><div class="line"><span class="keyword">end</span></div><div class="line"></div><div class="line"><span class="comment">-- 释放mysql连接,放入连接池</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">releaseConn</span><span class="params">(mysqlConn)</span></span></div><div class="line"> <span class="keyword">local</span> ok = <span class="keyword">true</span></div><div class="line"> <span class="keyword">local</span> db_type = <span class="number">0</span></div><div class="line"> <span class="keyword">local</span> err = <span class="string">""</span></div><div class="line"> <span class="keyword">if</span> mysqlConn <span class="keyword">then</span></div><div class="line"> <span class="keyword">local</span> res, err, errno, sqlstate = mysqlConn:read_result()</div><div class="line"> <span class="keyword">while</span> err == <span class="string">"again"</span> <span class="keyword">do</span></div><div class="line"> res, err, errno, sqlstate = mysqlConn:read_result()</div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="comment">-- setkeepalive(maxidletimeout, poolsize)</span></div><div class="line"> <span class="keyword">local</span> ok, err = mysqlConn:set_keepalive(<span class="number">0</span>, <span class="number">1000</span>)</div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> ok <span class="keyword">then</span></div><div class="line"> mysqlConn:close()</div><div class="line"> ok = <span class="keyword">false</span></div><div class="line"> err = <span class="string">"MySQL.Error ( "</span>..(err <span class="keyword">or</span> <span class="string">"null"</span>)..<span class="string">" ) "</span></div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="keyword">end</span> </div><div class="line"> <span class="keyword">return</span> ok, db_type, err</div><div class="line"><span class="keyword">end</span></div><div class="line"></div><div class="line"><span class="keyword">local</span> mysql, err = getMysql()</div><div class="line"><span class="keyword">if</span> <span class="keyword">not</span> mysql <span class="keyword">then</span></div><div class="line"> ngx.say(err)</div><div class="line"><span class="keyword">else</span></div><div class="line"> <span class="keyword">local</span> sql = <span class="string">"SELECT * FROM help_topic"</span></div><div class="line"> <span class="keyword">local</span> res1, err, errno, sqlstate = mysql:query(sql)</div><div class="line"></div><div class="line"> <span class="comment">-- ngx.log(ngx.ERR, "get_reused_times: "..mysql:get_reused_times())</span></div><div class="line"></div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> res1 <span class="keyword">then</span></div><div class="line"> ngx.say(JSON.encode({<span class="string">"query failed!"</span>}))</div><div class="line"> <span class="keyword">else</span></div><div class="line"> ngx.say(JSON.encode(res1))</div><div class="line"> <span class="keyword">end</span></div><div class="line"> releaseConn(mysql)</div><div class="line"> ngx.exit(ngx.HTTP_OK)</div><div class="line"> <span class="keyword">return</span></div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<p>wrk本机压测结果。<br>不使用连接池<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">$ ./wrk -t10 -d30s -c200 http://127.0.0.1/testMysql </div><div class="line">Running 30s test @ http://127.0.0.1/testMysql</div><div class="line"> 10 threads and 200 connections</div><div class="line"> Thread Stats Avg Stdev Max +/- Stdev</div><div class="line"> Latency 1.16s 386.87ms 1.99s 62.06%</div><div class="line"> Req/Sec 19.50 15.57 90.00 79.12%</div><div class="line"> 2454 requests in 30.08s, 1.72GB read</div><div class="line"> Socket errors: connect 0, read 0, write 0, timeout 100</div><div class="line">Requests/sec: 81.59</div><div class="line">Transfer/sec: 58.51MB</div></pre></td></tr></table></figure></p>
<p>使用连接池<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">$ ./wrk -t10 -d30s -c200 http://127.0.0.1/testMysql</div><div class="line">Running 30s test @ http://127.0.0.1/testMysql</div><div class="line"> 10 threads and 200 connections</div><div class="line"> Thread Stats Avg Stdev Max +/- Stdev</div><div class="line"> Latency 93.98ms 113.50ms 1.81s 99.24%</div><div class="line"> Req/Sec 57.05 31.40 111.00 47.95%</div><div class="line"> 2673 requests in 30.09s, 1.87GB read</div><div class="line"> Socket errors: connect 0, read 18, write 0, timeout 30</div><div class="line">Requests/sec: 88.84</div><div class="line">Transfer/sec: 63.71MB</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<p>在各种形式的数据交互中,创建连接、传输数据、销毁连接这三个步骤都是必不可少的。但是,当并发量持续增加时,耗费在创建和销毁连接上的时间将越来越不容忽视,所以理想中的情况应该是这样:创建连接-传输数据-……-传输数据-销毁连接。连接池正是为了解决这种问题而产生的技术。<br>在
</summary>
<category term="openresty" scheme="https://7byte.github.io/categories/openresty/"/>
<category term="nginx" scheme="https://7byte.github.io/tags/nginx/"/>
<category term="openresty" scheme="https://7byte.github.io/tags/openresty/"/>
</entry>
<entry>
<title>根据经纬度计算两个城市间的距离</title>
<link href="https://7byte.github.io/2018/01/05/distanceoftwocity/"/>
<id>https://7byte.github.io/2018/01/05/distanceoftwocity/</id>
<published>2018-01-05T08:16:27.000Z</published>
<updated>2019-07-03T15:46:27.487Z</updated>
<content type="html"><![CDATA[<p>非常简单的golang小练习,这个问题最大的难点在于求两点的球面距离,所以这实际上是一道立体几何题,当然如果不记得公式咱们可以百度……废话不多说了,直接上代码。</p>
<p>city.go<br><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> city</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line"> <span class="string">"encoding/csv"</span></div><div class="line"> <span class="string">"math"</span></div><div class="line"> <span class="string">"os"</span></div><div class="line"> <span class="string">"strconv"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="keyword">var</span> (</div><div class="line"> radius = <span class="keyword">float64</span>(<span class="number">6371000</span>)</div><div class="line"> rad = math.Pi / <span class="number">180.0</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="comment">// info 城市经纬度信息</span></div><div class="line"><span class="keyword">type</span> info <span class="keyword">struct</span> {</div><div class="line"> name <span class="keyword">string</span></div><div class="line"> lng <span class="keyword">float64</span> <span class="comment">//经度</span></div><div class="line"> lat <span class="keyword">float64</span> <span class="comment">//纬度</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// Manager 城市管理</span></div><div class="line"><span class="keyword">type</span> Manager <span class="keyword">struct</span> {</div><div class="line"> citys <span class="keyword">map</span>[<span class="keyword">string</span>]*info</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// NewCityConfig 读取坐标配置,创建管理器</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewCityConfig</span><span class="params">(file <span class="keyword">string</span>)</span> <span class="params">(c *Manager, err error)</span></span> {</div><div class="line"> c = &Manager{}</div><div class="line"> c.citys, err = c.parseCityInfo(file)</div><div class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</div><div class="line"> <span class="keyword">return</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// GetDistance 计算两个城市间的距离</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Manager)</span> <span class="title">GetDistance</span><span class="params">(city1 <span class="keyword">string</span>, city2 <span class="keyword">string</span>)</span> <span class="params">(dist <span class="keyword">float64</span>, err error)</span></span> {</div><div class="line"> c1 := c.citys[city1]</div><div class="line"> c2 := c.citys[city2]</div><div class="line"> <span class="keyword">if</span> c1 == <span class="literal">nil</span> || c2 == <span class="literal">nil</span> {</div><div class="line"> <span class="keyword">return</span> <span class="number">0.0</span>, <span class="literal">nil</span></div><div class="line"> }</div><div class="line"></div><div class="line"> dist = c.earthDistance(c1, c2)</div><div class="line"></div><div class="line"> <span class="keyword">return</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// earthDistance 计算距离</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Manager)</span> <span class="title">earthDistance</span><span class="params">(city1 *info, city2 *info)</span> <span class="title">float64</span></span> {</div><div class="line"> lng1 := city1.lng * rad</div><div class="line"> lat1 := city1.lat * rad</div><div class="line"> lng2 := city2.lng * rad</div><div class="line"> lat2 := city2.lat * rad</div><div class="line"> theta := lng2 - lng1</div><div class="line"> dist := math.Acos(math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta))</div><div class="line"> <span class="keyword">return</span> dist * radius</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// parseCityInfo 载入城市经纬度</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Manager)</span> <span class="title">parseCityInfo</span><span class="params">(filepath <span class="keyword">string</span>)</span> <span class="params">(locations <span class="keyword">map</span>[<span class="keyword">string</span>]*info, err error)</span></span> {</div><div class="line"> f, err := os.Open(filepath)</div><div class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</div><div class="line"> <span class="keyword">return</span> <span class="literal">nil</span>, err</div><div class="line"> }</div><div class="line"> <span class="keyword">defer</span> f.Close()</div><div class="line"></div><div class="line"> reader := csv.NewReader(f)</div><div class="line"> record, err := reader.ReadAll()</div><div class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</div><div class="line"> <span class="keyword">return</span> <span class="literal">nil</span>, err</div><div class="line"> }</div><div class="line"></div><div class="line"> locations = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="keyword">string</span>]*info, <span class="number">0</span>)</div><div class="line"> <span class="keyword">for</span> _, a := <span class="keyword">range</span> record {</div><div class="line"> city := &info{name: a[<span class="number">0</span>]}</div><div class="line"> lng, err := strconv.ParseFloat(a[<span class="number">1</span>], <span class="number">64</span>)</div><div class="line"> <span class="keyword">if</span> err == <span class="literal">nil</span> {</div><div class="line"> city.lng = lng</div><div class="line"> }</div><div class="line"> lat, err := strconv.ParseFloat(a[<span class="number">2</span>], <span class="number">64</span>)</div><div class="line"> <span class="keyword">if</span> err == <span class="literal">nil</span> {</div><div class="line"> city.lat = lat</div><div class="line"> }</div><div class="line"> locations[city.name] = city</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> locations, <span class="literal">nil</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>城市坐标使用csv格式存储:<br>city_info.csv<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">上海上海,121.48,31.22</div><div class="line">上海嘉定,121.24,31.4</div><div class="line">上海宝山,121.48,31.41</div><div class="line">上海川沙,121.7,31.19</div><div class="line">上海南汇,121.76,31.05</div><div class="line">上海奉贤,121.46,30.92</div><div class="line">上海松江,121.24,31</div><div class="line">上海金山,121.16,30.89</div><div class="line">上海青浦,121.1,31.15</div><div class="line">上海崇明,121.4,31.73</div><div class="line">……</div></pre></td></tr></table></figure></p>
<p>city_test.go<br><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> city</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line"> <span class="string">"fmt"</span></div><div class="line"> <span class="string">"testing"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestGetDistance</span><span class="params">(t *testing.T)</span></span> {</div><div class="line"> c, err := NewCityConfig(<span class="string">"city_info.csv"</span>)</div><div class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</div><div class="line"> t.Errorf(<span class="string">"new city config failed, %v"</span>, err)</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">var</span> dist <span class="keyword">float64</span></div><div class="line"> dist, err = c.GetDistance(<span class="string">"广东深圳"</span>, <span class="string">"广东汕头"</span>)</div><div class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</div><div class="line"> t.Errorf(<span class="string">"GetDistance, %v"</span>, err)</div><div class="line"> }</div><div class="line"> fmt.Printf(<span class="string">"distance of `广东深圳` and `广东汕头`: %.1f(m)\n"</span>, dist)</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p><code>go test</code>一下,得到如下结果:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">distance of `广东深圳` and `广东汕头`: 281492.0(m)</div><div class="line"> PASS</div><div class="line"> ok citydistant 0.410s</div></pre></td></tr></table></figure></p>
<p>百度地图测距结果:<br><img src="/images/20170205182525.jpg" alt=""></p>
<p>代码已放在github上:<a href="https://github.com/7byte/citydistant" target="_blank" rel="external">https://github.com/7byte/citydistant</a></p>
]]></content>
<summary type="html">
<p>非常简单的golang小练习,这个问题最大的难点在于求两点的球面距离,所以这实际上是一道立体几何题,当然如果不记得公式咱们可以百度……废话不多说了,直接上代码。</p>
<p>city.go<br><figure class="highlight go"><table><t
</summary>
<category term="golang" scheme="https://7byte.github.io/categories/golang/"/>
<category term="golang" scheme="https://7byte.github.io/tags/golang/"/>
</entry>
<entry>
<title>nginx反向代理</title>
<link href="https://7byte.github.io/2017/12/11/nginx-reverse-proxy/"/>
<id>https://7byte.github.io/2017/12/11/nginx-reverse-proxy/</id>
<published>2017-12-11T03:16:27.000Z</published>
<updated>2019-07-03T15:50:38.887Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>英文原文:<a href="https://www.nginx.com/resources/admin-guide/reverse-proxy/" target="_blank" rel="external">https://www.nginx.com/resources/admin-guide/reverse-proxy/</a></p>
</blockquote>
<p>本文描述了代理服务器的基本配置。你将学会怎样使用各种协议把一个请求从NGINX转发到代理服务器、怎样修改发送给代理服务器的客户端请求头,以及怎样为来自代理服务器的请求响应配置缓存。</p>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li>介绍</li>
<li>转发请求到代理服务器</li>
<li>转发请求头</li>
<li>配置缓存</li>
<li>选择输出IP</li>
</ul>
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>代理通常用来把负载分发到若干服务器,由不同的网站无缝地提供内容,或者经由非HTTP的其它协议转发请求到应用服务器做处理。</p>
<h2 id="转发请求到代理服务器"><a href="#转发请求到代理服务器" class="headerlink" title="转发请求到代理服务器"></a>转发请求到代理服务器</h2><p>当NGINX代理请求时,NGINX把请求发送给指定的代理服务器——获取到请求结果——把结果返回给客户端。请求可能被发送给HTTP服务器(另一个NGINX服务或者其它HTTP服务器),也可能通过特定的协议发送给非HTTP服务(运行基于特定框架开发的应用服务,例如PHP、Python)。支持的协议包括<a href="http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html" target="_blank" rel="external">FastCGI</a>、<a href="http://nginx.org/en/docs/http/ngx_http_uwsgi_module.html" target="_blank" rel="external">uwsgi</a>、<a href="http://nginx.org/en/docs/http/ngx_http_scgi_module.html" target="_blank" rel="external">SCGI</a>和<a href="http://nginx.org/en/docs/http/ngx_http_memcached_module.html" target="_blank" rel="external">memcached</a>。</p>
<p>为了把一个请求转发到HTTP代理服务器,将<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_pass" target="_blank" rel="external">proxy_pass</a>指令添加到<a href="http://nginx.org/en/docs/http/ngx_http_core_module.html?#location" target="_blank" rel="external">location</a>里面。例如:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">location /some/path/ {</div><div class="line"> proxy_pass http://www.example.com/link/;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这个配置示例会使所有由该location处理的请求转发到指定地址的代理服务器。这里的地址可以指定为一个域名或是一个IP,地址也可以带上端口:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">location ~ \.php {</div><div class="line"> proxy_pass http://127.0.0.1:8000;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>注意到在第一个示例中,代理服务器的地址后面是一个URI:<strong>/link/</strong>。如果地址中指定了该URI,那么它会替换掉原始请求URI中与location参数匹配的部分。例如,这里的原始请求URI包含<strong>/some/path/page.html</strong>,转发的请求会替换成<strong><a href="http://www.example.com/link/page.html" target="_blank" rel="external">http://www.example.com/link/page.html</a></strong>。如果地址中没有指明URI,或者无法判断需要替换请求URI的哪部分,则会转发完整的请求(可能被修改过)。</p>
<p>为了把一个请求转发到非HTTP代理服务器,必须使用适当的<strong>**_pass</strong>指令:</p>
<ul>
<li><a href="http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html?#fastcgi_pass" target="_blank" rel="external">fastcgi_pass</a> 转发请求到FastCGI服务器</li>
<li><a href="http://nginx.org/en/docs/http/ngx_http_uwsgi_module.html?#uwsgi_pass" target="_blank" rel="external">uwsgi_pass</a> 转发请求到uwsgi服务器</li>
<li><a href="http://nginx.org/en/docs/http/ngx_http_scgi_module.html?#scgi_pass" target="_blank" rel="external">scgi_pass</a> 转发请求到SCGI服务器</li>
<li><a href="http://nginx.org/en/docs/http/ngx_http_memcached_module.html?#memcached_pass" target="_blank" rel="external">memcached_pass</a> 转发请求到memcached服务器</li>
</ul>
<p>请注意,在这些情况下,指定地址的规则可能会有所不同。你可能还需要将附加参数传递到服务器(请参见<a href="http://nginx.org/en/docs" target="_blank" rel="external">参考文档</a>的更多细节)。<br><a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_pass" target="_blank" rel="external">proxy_pass</a>指令也可以指向一组<a href="http://nginx.org/en/docs/http/load_balancing.html?#algorithms" target="_blank" rel="external">命名服务器</a>。在这种情况下,请求按照<a href="https://www.nginx.com/resources/admin-guide/load-balancer/" target="_blank" rel="external">指定的方式</a>被分发到组中的服务器。</p>
<h2 id="转发请求头"><a href="#转发请求头" class="headerlink" title="转发请求头"></a>转发请求头</h2><p>NGINX默认会重新定义代理请求中的两个header字段:Host和Connection,并且去除值为空字符串的字段。Host设置为<strong>$proxy_host</strong>变量,Connection设置为<strong>close</strong>。</p>
<p>如果要修改这些设置,或者修改其它header字段,就需要用到<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_set_header" target="_blank" rel="external">proxy_set_header</a>指令。这条指令可以放在<a href="http://nginx.org/en/docs/http/ngx_http_core_module.html?#location" target="_blank" rel="external">location</a>或更高一级的位置,此外也可以放在特定的<a href="http://nginx.org/en/docs/http/ngx_http_core_module.html?#server" target="_blank" rel="external">server</a>上下文或者<a href="http://nginx.org/en/docs/http/ngx_http_core_module.html?#http" target="_blank" rel="external">http</a>块中,例如:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">location /some/path/ {</div><div class="line"> proxy_set_header Host $host;</div><div class="line"> proxy_set_header X-Real-IP $remote_addr;</div><div class="line"> proxy_pass http://localhost:8000;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>在这个配置中,Host字段被设置成<a href="http://nginx.org/en/docs/http/ngx_http_core_module.html?#variables" target="_blank" rel="external">$host</a>变量。</p>
<p>为了防止一个header字段被传递给代理服务器,可以将其设置为空字符串:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">location /some/path/ {</div><div class="line"> proxy_set_header Accept-Encoding "";</div><div class="line"> proxy_pass http://localhost:8000;</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="配置缓存"><a href="#配置缓存" class="headerlink" title="配置缓存"></a>配置缓存</h2><p>在默认情况下,NGINX会缓存来自代理服务器的响应。响应会存储在内部缓冲区中,直到接收到完整的数据才将响应内容发送给客户端,这样就有助于优化与慢客户端之间的交互体验,反之,如果响应以同步的方式从NGINX服务器发送给客户端,将会对代理服务器造成浪费。当启用缓存,NGINX允许代理服务器迅速地处理响应(而无须等待客户端接收),与此同时NGINX存储响应数据以保证客户端有充足的时间下载。</p>
<p>负责启用和禁用缓存的指令是<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_buffering" target="_blank" rel="external">proxy_buffering</a>。默认情况下它被设置为<strong>on</strong>:启用缓存。</p>
<p><a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_buffers" target="_blank" rel="external">proxy_buffers</a>指令控制分配给一个请求的缓冲区大小和数量。来自代理服务器的第一份响应数据被存储在单独的缓冲区,这块缓冲区的大小由<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_buffer_size" target="_blank" rel="external">proxy_buffer_size</a>指令指定。该缓冲区通常包含相对较小的响应header以及剩余可以可以被塞进缓冲区的响应数据。</p>
<p>在下面的例子中,缓冲区的默认数量被加大了,并且第一份响应数据的缓冲区大小小于默认的缓冲区大小。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">location /some/path/ {</div><div class="line"> proxy_buffers 16 4k;</div><div class="line"> proxy_buffer_size 2k;</div><div class="line"> proxy_pass http://localhost:8000;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>如果缓存被禁用,NGINX一旦接收到来自代理服务器的响应将会同步地发送给客户端。对要求尽可能实时的快速交互式客户端来说这种方式是可取的。</p>
<p>如果要禁用特定location中的缓存,则将location中的<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_buffering" target="_blank" rel="external">proxy_buffering</a>设置为<strong>off</strong>,例如:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">location /some/path/ {</div><div class="line"> proxy_buffering off;</div><div class="line"> proxy_pass http://localhost:8000;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>在这种情况下,NGINX仅使用由<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_buffer_size" target="_blank" rel="external">proxy_buffer_size</a>配置的缓存区来存储当前响应数据。</p>
<p>反向代理的一个常见用途是提供负载平衡。阅读电子书<a href="https://www.nginx.com/resources/library/five-reasons-choose-software-load-balancer/" target="_blank" rel="external"> Five Reasons to Choose a Software Load Balancer</a>,了解如何提高性能、专注快速部署你的应用。</p>
<h2 id="选择输出IP"><a href="#选择输出IP" class="headerlink" title="选择输出IP"></a>选择输出IP</h2><p>如果你的代理服务器有多个网络接口,有时候你可能需要选择一个特定的源IP用于连接到代理服务器或上游服务器(upstream)。当NGINX后端的代理服务器配置为接受来自特定IP网络或IP地址范围的连接时,这样是设置是很有用处的。</p>
<p>指定<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html?#proxy_bind" target="_blank" rel="external">proxy_bind</a>指令和必要的网络接口的IP地址:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">location /app1/ {</div><div class="line"> proxy_bind 127.0.0.1;</div><div class="line"> proxy_pass http://example.com/app1/;</div><div class="line">}</div><div class="line"></div><div class="line">location /app2/ {</div><div class="line"> proxy_bind 127.0.0.2;</div><div class="line"> proxy_pass http://example.com/app2/;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>也可以用一个变量来指定IP地址。例如,<a href="http://nginx.org/en/docs/http/ngx_http_core_module.html?#var_server_addr" target="_blank" rel="external">$var_server_addr</a>变量表示接收客户端请求的网络接口的IP地址:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">location /app3/ {</div><div class="line"> proxy_bind $server_addr;</div><div class="line"> proxy_pass http://example.com/app3/;</div><div class="line">}</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<blockquote>
<p>英文原文:<a href="https://www.nginx.com/resources/admin-guide/reverse-proxy/" target="_blank" rel="external">https://www.nginx.c
</summary>
<category term="openresty" scheme="https://7byte.github.io/categories/openresty/"/>
<category term="nginx" scheme="https://7byte.github.io/tags/nginx/"/>
<category term="反向代理" scheme="https://7byte.github.io/tags/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86/"/>
</entry>
<entry>
<title>nginx gzip</title>
<link href="https://7byte.github.io/2017/01/17/trytouse-gzip/"/>
<id>https://7byte.github.io/2017/01/17/trytouse-gzip/</id>
<published>2017-01-17T07:21:27.000Z</published>
<updated>2019-07-03T15:50:12.511Z</updated>
<content type="html"><![CDATA[<h2 id="nginx-gzip"><a href="#nginx-gzip" class="headerlink" title="nginx gzip"></a>nginx gzip</h2><blockquote>
<p>The <a href="http://nginx.org/en/docs/http/ngx_http_gzip_module.html" target="_blank" rel="external">ngx_http_gzip_module</a> module is a filter that compresses responses using the “gzip” method. This often helps to reduce the size of transmitted data by half or even more.</p>
</blockquote>
<p>根据官网文档说明,通过开启nginx gzip压缩,数据传输量可以减少一半甚至更多。在网络带宽成为瓶颈的情况下,gzip带来的提升无疑是相当有诱惑力的。</p>
<p><strong>未开启gzip:</strong><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ ./wrk -t10 -c200 -d30s http://120.25.176.131/testMysql</div><div class="line">Running 30s test @ http://120.25.176.131/testMysql</div><div class="line"> 10 threads and 200 connections</div><div class="line"> Thread Stats Avg Stdev Max +/- Stdev</div><div class="line"> Latency 0.00us 0.00us 0.00us -nan%</div><div class="line"> Req/Sec 0.00 0.00 0.00 -nan%</div><div class="line"> 0 requests in 30.01s, 4.04MB read</div><div class="line">Requests/sec: 0.00</div><div class="line">Transfer/sec: 137.73KB</div></pre></td></tr></table></figure></p>
<p>惨不忍睹,基本上是把服务器压死了。</p>
<p><strong>开启gzip:</strong><br>按照官网示例,在nginx.conf添加下面的配置<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">gzip on;</div><div class="line">gzip_min_length 1000;</div><div class="line">gzip_proxied expired no-cache no-store private auth;</div><div class="line">gzip_types text/plain application/xml;</div></pre></td></tr></table></figure></p>
<p>压测结果<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ ./wrk -t10 -c200 -d30s http://120.25.176.131/testMysql</div><div class="line">Running 30s test @ http://120.25.176.131/testMysql</div><div class="line"> 10 threads and 200 connections</div><div class="line"> Thread Stats Avg Stdev Max +/- Stdev</div><div class="line"> Latency 0.00us 0.00us 0.00us -nan%</div><div class="line"> Req/Sec 0.00 0.00 0.00 -nan%</div><div class="line"> 0 requests in 30.02s, 4.02MB read</div><div class="line">Requests/sec: 0.00</div><div class="line">Transfer/sec: 137.12KB</div></pre></td></tr></table></figure></p>
<p>好吧,从压测结果来看,并没有什么明显的改善。。。没办法,这台服务器配置实在够差。<br>但是,通过chrome浏览器提供的调试工具,可以看到API的返回数据两压缩了6倍左右,从用户的角度来说就是接口响应变快了,用户体验当然更好。</p>
<table>
<thead>
<tr>
<th>-</th>
<th style="text-align:right">数据长度</th>
<th style="text-align:right">Waiting(TTFB)</th>
<th style="text-align:right">Content Download</th>
</tr>
</thead>
<tbody>
<tr>
<td>关闭gzip</td>
<td style="text-align:right">734KB</td>
<td style="text-align:right">20.67ms</td>
<td style="text-align:right">4.56s</td>
</tr>
<tr>
<td>开启gzip</td>
<td style="text-align:right">229KB</td>
<td style="text-align:right">29.58ms</td>
<td style="text-align:right">699.19ms</td>
</tr>
</tbody>
</table>
]]></content>
<summary type="html">
<h2 id="nginx-gzip"><a href="#nginx-gzip" class="headerlink" title="nginx gzip"></a>nginx gzip</h2><blockquote>
<p>The <a href="http://nginx
</summary>
<category term="openresty" scheme="https://7byte.github.io/categories/openresty/"/>
<category term="nginx" scheme="https://7byte.github.io/tags/nginx/"/>
<category term="openresty" scheme="https://7byte.github.io/tags/openresty/"/>
</entry>
<entry>
<title>golang环境搭建</title>
<link href="https://7byte.github.io/2017/01/14/trytouse-golang/"/>
<id>https://7byte.github.io/2017/01/14/trytouse-golang/</id>
<published>2017-01-14T06:33:04.000Z</published>
<updated>2019-07-03T15:36:21.497Z</updated>
<content type="html"><![CDATA[<p>最近由于工作上的原因开始接触golang,虽然早前就已听说golang的大名,但是也仅仅只有一个大概的印象,比如其对C语言系程序员来说别扭的声明、集其它程序语言优秀设计于一身的大杂烩式语法、天生的并发机制、谷歌亲爹的强大背景。毫无疑问这是一门实用至上的语言,非常值得学习。<br>本文记录Linux和Windows下golang环境的搭建过程,以便将来查阅。内容参照官方安装说明文档(<a href="https://golang.org/doc/install" target="_blank" rel="external">安装golang</a>),以及安装包下载页面(<a href="https://golang.org/dl/" target="_blank" rel="external">下载安装包</a>)。<br>本人目前还买不起Mac,故无法验证Mac上安装流程的可行性,等将来剁手了再来补充相关内容。</p>
<p><strong><em>温馨提示:部分步骤可能需要科学上网。</em></strong></p>
<h2 id="Linux环境"><a href="#Linux环境" class="headerlink" title="Linux环境"></a>Linux环境</h2><ol>
<li><p>下载压缩包,例如当前最新的版本是1.7.3:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ wget https://storage.googleapis.com/golang/go1.7.3.linux-amd64.tar.gz</div></pre></td></tr></table></figure>
</li>
<li><p>提取压缩包内容到 /usr/local 目录:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ sudo tar -C /usr/local -xzf go1.7.3.linux-amd64.tar.gz</div></pre></td></tr></table></figure>
</li>
<li><p>将 /usr/local/go/bin 添加到PATH环境变量,将此行添加到你的 /etc/profile(全系统安装)或 $HOME/.bash_profile 文件中:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">export PATH=$PATH:/usr/local/go/bin</div></pre></td></tr></table></figure>
<p>然后让新添加的配置生效:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ source /etc/profile</div></pre></td></tr></table></figure>
</li>
<li><p>添加自己的工作目录,将此行添加到 $HOME/.bash_profile 文件中:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">GOPATH=/home/7byte/code/gowork</div><div class="line">GOBIN=$GOPATH/bin</div><div class="line">PATH=$PATH:$GOBIN</div><div class="line"></div><div class="line">export GOPATH</div><div class="line">export GOBIN</div><div class="line">export PATH</div></pre></td></tr></table></figure>
<p>同样的让新添加的配置生效,gowork目录如果不存在则创建:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ mkdir $HOME/gowork</div><div class="line">$ source $HOME/.bash_profile</div></pre></td></tr></table></figure>
</li>
<li><p>验证安装。使用<code>go env</code>命令查看golang环境:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">$ go env</div><div class="line">GOARCH="amd64"</div><div class="line">GOBIN="/home/7byte/code/gowork/bin"</div><div class="line">GOEXE=""</div><div class="line">GOHOSTARCH="amd64"</div><div class="line">GOHOSTOS="linux"</div><div class="line">GOOS="linux"</div><div class="line">GOPATH="/home/7byte/code/gowork"</div><div class="line">GORACE=""</div><div class="line">GOROOT="/usr/lib/golang"</div><div class="line">GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64"</div><div class="line">GO15VENDOREXPERIMENT="1"</div><div class="line">CC="gcc"</div><div class="line">GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"</div><div class="line">CXX="g++"</div><div class="line">CGO_ENABLED="1"</div></pre></td></tr></table></figure>
<p>在 $GOPATH/src/ 创建 hello 目录,新建 helloworld.go 文件并输入以下代码:</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</div><div class="line"> fmt.Printf(<span class="string">"hello, world\n"</span>)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>运行我们的hello程序,如果得到以下输出说明配置成功。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ go run helloworld.go </div><div class="line">hello, world</div></pre></td></tr></table></figure>
</li>
</ol>
<h2 id="Windows环境"><a href="#Windows环境" class="headerlink" title="Windows环境"></a>Windows环境</h2><p>Windows环境下golang环境搭建和Linux类似,官网上提供了msi安装包,安装过程和普通PC软件安装一致,基本只要“下一步”就可以了。安装程序会设置默认的<code>%GOROOT%\bin</code>(c:\Go\bin)目录到<code>Path</code>环境变量,如果自定义安装目录,需要手动修改<code>GOROOT</code>和<code>Path</code>环境变量。<br>同样的,我们需要添加自己的工作目录。在环境变量中添加<code>GOPATH</code>,变量值即工作目录,例如<code>E:\code\gowork</code>。<br>设置GOBIN环境变量<code>%GOPATH%\bin</code>,并添加到Path环境变量<br>验证安装的方式与Linux中相同,参考上一小节第5条。</p>
<h2 id="安装git"><a href="#安装git" class="headerlink" title="安装git"></a>安装git</h2><p>必须安装git客户端,否则 go get 将无法正常执行。git的安装过程作为程序员的基本技能不在此详细记录。</p>
<h2 id="安装golint和goimports"><a href="#安装golint和goimports" class="headerlink" title="安装golint和goimports"></a>安装golint和goimports</h2><p>什么是<a href="https://github.com/golang/lint" target="_blank" rel="external">golint</a>:</p>
<blockquote>
<p>Golint is a linter for Go source code. </p>
</blockquote>
<p>wiki对<a href="https://zh.wikipedia.org/wiki/Lint" target="_blank" rel="external">lint</a>的解释:</p>
<blockquote>
<p>在计算机科学中,lint是一种工具程序的名称,它用来标记源代码中,某些可疑的、不具结构性(可能造成bug)的段落。它是一种静态程序分析工具,最早适用于C语言,在UNIX平台上开发出来。后来它成为通用术语,可用于描述在任何一种计算机程序语言中,用来标记源代码中有疑义段落的工具。</p>
</blockquote>
<p>安装golint:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">go get -u github.com/golang/lint/golint</div></pre></td></tr></table></figure></p>
<p>安装完成后会在GOPATH\bin目录下生成一个名为<code>golint</code>的执行文件。golint支持对Go源文件、目录或包做静态分析,例如要检查当前目录下的所有Go源文件:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">golint ./...</div></pre></td></tr></table></figure></p>
<p>什么是<a href="https://godoc.org/golang.org/x/tools/cmd/goimports" target="_blank" rel="external">goimports</a>:</p>
<blockquote>
<p>Command goimports updates your Go import lines, adding missing ones and removing unreferenced ones.</p>
</blockquote>
<p>安装goimports<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">go get golang.org/x/tools/cmd/goimports</div></pre></td></tr></table></figure></p>
<p>后文对golint和goimports这两个工具在编辑器中的配置将做进一步说明。</p>
<h2 id="测试工具GoConvey"><a href="#测试工具GoConvey" class="headerlink" title="测试工具GoConvey"></a>测试工具GoConvey</h2><p>完善的测试用例对提高代码质量的帮助不言而喻,在github上随手翻几个golang开源项目,可以发现每一个项目都带有详细的测试用例,可见为自己的golang代码写测试case已经是一项约定俗成的圈内规范。Go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试。但是go test的结果不够直观,并且每次都要手动敲命令也比较麻烦,所以我用到了<a href="http://goconvey.co/" target="_blank" rel="external">GoConvey</a>这个开源测试工具。<br>GoConvey使用起来非常简单:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ go get github.com/smartystreets/goconvey</div><div class="line">$ $GOPATH/bin/goconvey</div></pre></td></tr></table></figure></p>
<p>然后在浏览器打开<a href="http://127.0.0.1:8080/" target="_blank" rel="external">http://127.0.0.1:8080/</a> ,就能看到当前工程目录下所有*_test.go的测试情况了。<br>添加一个测试文件helloworld_test.go<br><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line"> <span class="string">"fmt"</span></div><div class="line"> <span class="string">"testing"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestHello</span><span class="params">(t *testing.T)</span></span> {</div><div class="line"> fmt.Println(<span class="string">"test OK!"</span>)</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>保存之后可以看到浏览器页面几秒后自动刷新,测试通过效果图:</p>
<div align="center"><br><img src="/images/20161207214519.png" alt=""><br></div>
<p>修改TestHello的代码:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestHello</span><span class="params">(t *testing.T)</span></span> {</div><div class="line"> fmt.Println(<span class="string">"test OK?"</span>)</div><div class="line"> t.Error(<span class="string">"test not OK!"</span>)</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>再次刷新后测试不通过:</p>
<div align="center"><br><img src="/images/20161207220023.png" alt=""><br></div>
<h2 id="配置Sublime-Text"><a href="#配置Sublime-Text" class="headerlink" title="配置Sublime Text"></a>配置Sublime Text</h2><p><a href="https://www.sublimetext.com/" target="_blank" rel="external">Sublime Text</a>是一款非常好用的编辑器,自带很多强大的文本编辑功能,并且有丰富的插件扩展,完全可以胜任golang日常实际开发。</p>
<p><a href="https://github.com/DisposaBoy/GoSublime" target="_blank" rel="external">GoSublime</a>插件提供了很多类golang IDE的功能,例如自动代码补全等等,能够很好地提高开发效率。</p>
<p>Sublime的插件一般都是基于Python开发,所以测试机上Python环境是必不可少的。Linux系统默认自带Python,但如果是Windows则需要自己安装。与git安装相同,Python安装过程不在此详细记录。</p>
<p>Python环境搭建完成,然后我们首先需要安装Sublime插件管理器Package Control,借助该工具我们可以非常方便地搜索、安装、更新Sublime上的众多插件。依据官方安装说明的建议,我在这里只贴出原始安装文档页面链接,请到官方页面获取安装代码:<a href="https://packagecontrol.io/installation" target="_blank" rel="external">https://packagecontrol.io/installation</a></p>
<div align="center"><br><img src="/images/20170113230448.jpg" alt=""><br></div>
<p>Package Control安装完成,使用快捷键<code>ctrl+shift+p</code>或者打开菜单栏 Preferences > Package Control 调出Package Control对话框,输入<code>install</code>,在下拉选项中选择<code>Package Control: Install Package</code>,然后输入<code>GoSublime</code>,Package Control将会安装对应插件。<br>接下来是对GoSublime插件的基本设置,打开菜单栏 Preferences > Package Settings > GoSublime > Settiongs-User 修改用户设置:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> "env":{</div><div class="line"> "GOPATH":"E:/code/gowork"</div><div class="line"> },</div><div class="line"> "fmt_cmd": [</div><div class="line"> "goimports"</div><div class="line"> ],</div><div class="line"> "on_save":[</div><div class="line"> {</div><div class="line"> "cmd":"gs9o_open",</div><div class="line"> "args":{</div><div class="line"> "run":[</div><div class="line"> "sh",</div><div class="line"> "go vet"</div><div class="line"> ],</div><div class="line"> "focus_view":false</div><div class="line"> }</div><div class="line"> },</div><div class="line"> {</div><div class="line"> "cmd":"gs9o_open",</div><div class="line"> "args":{</div><div class="line"> "run":[</div><div class="line"> "golint",</div><div class="line"> "."</div><div class="line"> ],</div><div class="line"> "focus_view":false</div><div class="line"> }</div><div class="line"> }</div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>该配置参考了这篇文章:<a href="https://www.goinggo.net/2016/05/installing-go-and-your-workspace.html" target="_blank" rel="external">https://www.goinggo.net/2016/05/installing-go-and-your-workspace.html</a></p>
<p>至此,golang环境搭建基本完成,可以尽情地写代码了!</p>
]]></content>
<summary type="html">
<p>最近由于工作上的原因开始接触golang,虽然早前就已听说golang的大名,但是也仅仅只有一个大概的印象,比如其对C语言系程序员来说别扭的声明、集其它程序语言优秀设计于一身的大杂烩式语法、天生的并发机制、谷歌亲爹的强大背景。毫无疑问这是一门实用至上的语言,非常值得学习。<
</summary>
<category term="golang" scheme="https://7byte.github.io/categories/golang/"/>
<category term="golang" scheme="https://7byte.github.io/tags/golang/"/>
</entry>
<entry>
<title>wrk压力测试</title>
<link href="https://7byte.github.io/2017/01/12/trytouse-wrk/"/>
<id>https://7byte.github.io/2017/01/12/trytouse-wrk/</id>
<published>2017-01-12T07:12:27.000Z</published>
<updated>2019-07-03T15:35:22.250Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/wg/wrk" target="_blank" rel="external">wrk</a>是一个开源的http性能测试工具,项目在github维护<br>wrk的使用非常简单,首先需要已经安装了git,gcc这两个基础工具,然后依次执行下面的3个命令:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git clone https://github.com/wg/wrk.git </div><div class="line">$ cd wrk </div><div class="line">$ make</div></pre></td></tr></table></figure></p>
<p>编译成功会在目录下生成执行文件wrk,准备工作完成。<br>参考github上给出的例子,做一下简单的使用说明。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html</div></pre></td></tr></table></figure></p>
<p>-t 使用的线程个数<br>-c HTTP连接的最大个数<br>-d 压测时间</p>
<p> Output:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Running 30s test @ http://127.0.0.1:8080/index.html</div><div class="line"> 12 threads and 400 connections</div><div class="line"> Thread Stats Avg Stdev Max +/- Stdev</div><div class="line"> Latency 635.91us 0.89ms 12.92ms 93.69%</div><div class="line"> Req/Sec 56.20k 8.07k 62.00k 86.54%</div><div class="line"> 22464657 requests in 30.00s, 17.76GB read</div><div class="line">Requests/sec: 748868.53</div><div class="line">Transfer/sec: 606.33MB</div></pre></td></tr></table></figure></p>
<p>Latency:响应时间<br>Req/Sec:每个线程每秒完成的请求数<br>Requests/sec:每秒完成的请求数<br>Transfer/sec:每秒数据传输量</p>
]]></content>
<summary type="html">
<p><a href="https://github.com/wg/wrk" target="_blank" rel="external">wrk</a>是一个开源的http性能测试工具,项目在github维护<br>wrk的使用非常简单,首先需要已经安装了git,gcc这两个基
</summary>
<category term="性能测试" scheme="https://7byte.github.io/categories/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95/"/>
<category term="性能测试" scheme="https://7byte.github.io/tags/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95/"/>
</entry>
<entry>
<title>动态追踪工具——火焰图</title>
<link href="https://7byte.github.io/2017/01/02/trytouse-FlameGraph/"/>
<id>https://7byte.github.io/2017/01/02/trytouse-FlameGraph/</id>
<published>2017-01-02T07:10:27.000Z</published>
<updated>2019-07-03T15:47:12.672Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>火焰图就像是给一个软件系统拍的 X 光照片,可以很自然地把时间和空间两个维度上的信息融合在一张图上,以非常直观的形式展现出来,从而反映系统在性能方面的很多定量的统计规律。 ——<a href="https://openresty.org/posts/dynamic-tracing/" target="_blank" rel="external">动态追踪技术漫谈</a></p>
</blockquote>
<p>下面介绍下火焰图相关工具的安装和使用。</p>
<ul>
<li><p>首先需要安装内核开发包和调试包。查看当前系统的内核版本:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ uname -r</div><div class="line">3.10.0-327.28.2.el7.x86_64</div></pre></td></tr></table></figure>
</li>
<li><p>然后进入 <a href="http://debuginfo.centos.org/" target="_blank" rel="external">http://debuginfo.centos.org/</a> ,可以看到有 4/ 5/ 6/ 7/ 这样的目录,这些目录分别对应了centos系统的大版本。找到并下载自己的系统内核版本对应的包:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ wget "http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-($version).rpm"</div><div class="line">$ wget "http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-common-($version).rpm"</div></pre></td></tr></table></figure>
</li>
<li><p>安装上面的开发包和调试包,并安装内核探测工具 systemtap</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ sudo rpm -ivh kernel-debuginfo-common-($version).rpm</div><div class="line">$ sudo rpm -ivh kernel-debuginfo-($version).rpm</div><div class="line">$ sudo yum install kernel-devel-($version)</div><div class="line">$ sudo yum install systemtap</div></pre></td></tr></table></figure>
</li>
<li><p>下载火焰图绘制相关工具</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git clone https://github.com/openresty/nginx-systemtap-toolkit.git</div><div class="line">$ git clone https://github.com/brendangregg/FlameGraph.git</div></pre></td></tr></table></figure>
</li>
<li><p>然后测试下有没有安装成功<br>通过ps命令查看当前 nginx worker 进程的PID,如果有多个worker选择其中一个即可,下面是我测试时查询到的结果 PID=14489</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ ps -ef | grep nginx</div><div class="line">root 1580 1 0 11:36 ? 00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -c /home/7byte/openresty-test/conf/nginx.conf</div><div class="line">nobody 14489 1580 0 13:46 ? 00:00:00 nginx: worker process</div></pre></td></tr></table></figure>
<p> 这里我选用了工具包里的 sample-bt 来验证,它抓取的是C级别的运行状态,参数 -p 表示要抓取的进程id,-t是探测的时间,单位是秒,-u表示抓取用户空间,对应的-k表示内核空间,探测结果输出到 a.bt</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ mkdir svg</div><div class="line">$ sudo nginx-systemtap-toolkit/./sample-bt -p 14489 -t 20 -u > svg/a.bt</div><div class="line">WARNING: Tracing 14489 (/usr/local/openresty/nginx/sbin/nginx) in user-space only...</div><div class="line">WARNING: Time's up. Quitting now...(it may take a while)</div></pre></td></tr></table></figure>
<p> 得到 a.bt 后再用 FlameGraph 提供的两个脚本处理输出结果</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ FlameGraph/stackcollapse-stap.pl svg/a.bt > svg/a.cbt</div><div class="line">$ FlameGraph/flamegraph.pl svg/a.cbt > svg/a.svg</div></pre></td></tr></table></figure>
<p> 最后生成的 a.svg 就是火焰图,用浏览器打开即可,效果如下图:<br><img src="/images/08-30-13-36-12.jpg" alt=""><br> 每个框代表一个栈里的一个函数;<br>Y轴代表栈深度(栈桢数)。最顶端的框显示正在运行的函数,这之下的框都是调用者。在下面的函数是上面函数的父函数;<br>X轴代表采样总量。从左到右并不代表时间变化,从左到右也不具备顺序性;<br>框的宽度代表占用CPU总时间。宽的框代表的函数可能比窄的运行慢,或者被调用了更多次数。框的颜色深浅也没有任何意义;<br>如果是多线程同时采样,采样总数会超过总时间。</p>
</li>
<li><p>然后再测试下抓取lua级别的火焰图,这里需要使用另外一个工具 ngx-sample-lua-bt , –luajit20 表示nginx使用luajit2.0,如果使用的是标准lua则使用 –lua51</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ sudo nginx-systemtap-toolkit/./ngx-sample-lua-bt -p 2834 --luajit20 -t 20 > svg/a.bt</div><div class="line">WARNING: missing unwind/symbol data for module 'kernel'</div><div class="line">WARNING: Tracing 2836 (/usr/local/openresty/nginx/sbin/nginx) for LuaJIT 2.0...</div><div class="line">WARNING: Time's up. Quitting now...</div></pre></td></tr></table></figure>
<p> 对输出文件 a.bt 的后续处理与上文基本相同。另外可以预处理一下输出文件,增强可读性:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ nginx-systemtap-toolkit/./fix-lua-bt svg/a.bt > svg/tmp.bt</div><div class="line">$ FlameGraph/stackcollapse-stap.pl svg/tmp.bt > svg/a.cbt</div><div class="line">$ FlameGraph/flamegraph.pl svg/a.cbt > svg/a.svg</div></pre></td></tr></table></figure>
<p> 输出效果如下图:<br><img src="/images/08-30-13-36-13.jpg" alt=""></p>
<p> 遇到的问题:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">$ sudo nginx-systemtap-toolkit/./ngx-sample-lua-bt -p 2039 --luajit20 -t 5 > svg/a.bt</div><div class="line">WARNING: cannot find module /usr/local/openresty/luajit/lib/libluajit-5.1.so.2.1.0 debuginfo: No DWARF information found [man warning::debuginfo]</div><div class="line">WARNING: Bad $context variable being substituted with literal 0: identifier '$L' at <input>:17:30</div><div class="line"> source: lua_states[my_pid] = $L</div><div class="line"> ^</div><div class="line">semantic error: type definition 'TValue' not found in '/usr/local/openresty/luajit/lib/libluajit-5.1.so.2.1.0': operator '@cast' at :62:12</div><div class="line"> source: return @cast(tvalue, "TValue", "/usr/local/openresty/luajit/lib/libluajit-5.1.so.2.1.0")->fr->tp->ftsz</div><div class="line"> ^</div><div class="line"></div><div class="line">Pass 2: analysis failed. [man error::pass2]</div><div class="line">Number of similar warning messages suppressed: 100.</div><div class="line">Rerun with -v to see them.</div></pre></td></tr></table></figure>
<p> 在使用 ngx-sample-lua-bt 探测lua级别的火焰图时遇到了失败的情况,从错误信息来看是找不到 DWARF 调试信息。查找官网资料,貌似在很早以前 openresty 自带的 LuaJIT 2.0 默认就会启用 DWARF 调试信息,那我用的最新版怎么就是没有调试信息呢?折腾了好久,最后我到 luajit 官网下载了当前 openresty 版本对应的 luajit 源码,然后编译</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ make CCDEBUG=-g -B -j8</div></pre></td></tr></table></figure>
<p> 备份然后替换 /usr/local/openresty/luajit 目录下的两个文件,重启 openresty之后问题解决。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$ cd /usr/local/openresty/luajit/bin/</div><div class="line">$ sudo cp luajit-2.1.0-beta2 luajit-2.1.0-beta2_20160829</div><div class="line">$ sudo cp ~/LuaJIT-2.1.0-beta2/src/luajit/luajit luajit-2.1.0-beta2</div><div class="line"></div><div class="line">$ cd /usr/local/openresty/luajit/lib/</div><div class="line">$ sudo cp libluajit-5.1.so.2.1.0 libluajit-5.1.so.2.1.0_20160829</div><div class="line">$ sudo cp ~/LuaJIT-2.1.0-beta2/src/libluajit.so libluajit-5.1.so.2.1.0</div></pre></td></tr></table></figure>
<p> 从上面的分析可以看出,使用火焰图可以精确地定位 nginx + lua 潜在的性能问题,对CPU占用率低、吐吞量低的情况也可以使用火焰图的方式排查程序中是否有阻塞调用导致整个架构的吞吐量低下。<br>根据官网的说明,火焰图本身对系统性能的影响较小,每秒请求数会下降11%:</p>
<blockquote>
<p>The overhead exposed on the target process is usually small. For example, the throughput (req/sec) limit of an nginx worker process doing simplest “hello world” requests drops by only 11% (only when this tool is running), as measured by ab -k -c2 -n100000 when using Linux kernel 3.6.10 and systemtap 2.5. The impact on full-fledged production processes is usually smaller than even that, for instance, only 6% drop in the throughput limit is observed in a production-level Lua CDN application.</p>
</blockquote>
</li>
</ul>
]]></content>
<summary type="html">
<blockquote>
<p>火焰图就像是给一个软件系统拍的 X 光照片,可以很自然地把时间和空间两个维度上的信息融合在一张图上,以非常直观的形式展现出来,从而反映系统在性能方面的很多定量的统计规律。 ——<a href="https://openresty.org/
</summary>
<category term="openresty" scheme="https://7byte.github.io/categories/openresty/"/>
<category term="nginx" scheme="https://7byte.github.io/tags/nginx/"/>
<category term="openresty" scheme="https://7byte.github.io/tags/openresty/"/>
<category term="性能测试" scheme="https://7byte.github.io/tags/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95/"/>
<category term="动态追踪" scheme="https://7byte.github.io/tags/%E5%8A%A8%E6%80%81%E8%BF%BD%E8%B8%AA/"/>
</entry>
<entry>
<title>初尝openresty</title>
<link href="https://7byte.github.io/2016/08/20/trytouse-openresty/"/>
<id>https://7byte.github.io/2016/08/20/trytouse-openresty/</id>
<published>2016-08-20T06:07:06.000Z</published>
<updated>2016-11-20T10:59:54.518Z</updated>
<content type="html"><![CDATA[<h2 id="测试机配置"><a href="#测试机配置" class="headerlink" title="测试机配置"></a>测试机配置</h2><ul>
<li>CPU: 1核 Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz </li>
<li>内存: 1024 MB</li>
<li>操作系统: CentOS 7.2 64位</li>
<li>带宽: 1Mbps<br>没错,就是阿里云最低配的服务器。</li>
</ul>
<h2 id="OpenResty"><a href="#OpenResty" class="headerlink" title="OpenResty"></a>OpenResty</h2><blockquote>
<p><a href="http://openresty.org/cn/" target="_blank" rel="external">OpenResty</a> ™ 是一个基于 <a href="http://nginx.org" target="_blank" rel="external">Nginx</a> 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。</p>
</blockquote>
<h3 id="安装OpenResty"><a href="#安装OpenResty" class="headerlink" title="安装OpenResty"></a>安装OpenResty</h3><p>因为测试机安装的是CentOS系统,所以我选择了OpenResty官方Yum资源库提供的PRM包。<br>首先在CentOS系统中添加openresty资源库,创建一个名为/etc/yum.repos.d/OpenResty.repo 的文件,内容如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">[openresty]</div><div class="line">name=Official OpenResty Repository</div><div class="line">baseurl=https://copr-be.cloud.fedoraproject.org/results/openresty/openresty/epel-$releasever-$basearch/</div><div class="line">skip_if_unavailable=True</div><div class="line">gpgcheck=1</div><div class="line">gpgkey=https://copr-be.cloud.fedoraproject.org/results/openresty/openresty/pubkey.gpg</div><div class="line">enabled=1</div><div class="line">enabled_metadata=1</div></pre></td></tr></table></figure></p>
<p>然后就可以通过yum指令安装openresty了<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ sudo yum install openresty</div></pre></td></tr></table></figure></p>
<p>其它操作系统和安装方式,可以参考OpenResty官网说明。</p>
<h3 id="创建自己的应用目录"><a href="#创建自己的应用目录" class="headerlink" title="创建自己的应用目录"></a>创建自己的应用目录</h3><p>为了避免污染/usr/local/openresty/下的OpenResty安装内容,我们最好创建自己的工作目录:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ mkdir ~/openresty-test ~/openresty-test/logs/ ~/openresty-test/conf/</div></pre></td></tr></table></figure></p>
<p>在刚刚创建的 ~/openresty-test/conf/ 目录下新建配置文件nginx.conf,可以从安装目录拷贝一份过来:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ cp /usr/local/openresty/nginx/conf/nginx.conf ~/openresty-test/conf/</div></pre></td></tr></table></figure></p>
<p>修改nginx.conf,返回经典的“Hello, world!”<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">worker_processes 1; #nginx worker 数量</div><div class="line"></div><div class="line">events {</div><div class="line"> worker_connections 1024;</div><div class="line">}</div><div class="line"></div><div class="line">http {</div><div class="line"> server {</div><div class="line"> listen 80; #监听端口</div><div class="line"> </div><div class="line"> error_log /home/7byte/openresty-test/logs/error.log info;</div><div class="line"> access_log /home/7byte/openresty-test/logs/access.log;</div><div class="line"></div><div class="line"> location / {</div><div class="line"> default_type text/html;</div><div class="line"> content_by_lua_block {</div><div class="line"> ngx.say("Hello, world!")</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>为了能够使用service对openresty-test执行start、stop、reload等操作,我们还需要在 /etc/init.d/ 目录下创建一个启动脚本openresty-test,内容参考默认的 /etc/init.d/openresty<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div></pre></td><td class="code"><pre><div class="line">#!/bin/sh</div><div class="line">#</div><div class="line"># openresty - this script starts and stops the nginx daemon of OpenResty</div><div class="line">#</div><div class="line"># chkconfig: - 85 15</div><div class="line"># description: OpenResty is a scalable web platform by extending</div><div class="line"># NGINX with Lua</div><div class="line"># processname: openresty</div><div class="line"># config: /usr/local/openresty/nginx/conf/nginx.conf</div><div class="line"># config: /etc/sysconfig/openresty</div><div class="line"># pidfile: /usr/local/openresty/nginx/logs/nginx.pid</div><div class="line"></div><div class="line"># Source function library.</div><div class="line">. /etc/rc.d/init.d/functions</div><div class="line"></div><div class="line"># Source networking configuration.</div><div class="line">. /etc/sysconfig/network</div><div class="line"></div><div class="line"># Check that networking is up.</div><div class="line">[ "$NETWORKING" = "no" ] && exit 0</div><div class="line"></div><div class="line">nginx="/usr/local/openresty/nginx/sbin/nginx"</div><div class="line">prog=$(basename $nginx)</div><div class="line">pidfile=/home/7byte/openresty-test/logs/nginx.pid</div><div class="line"></div><div class="line">NGINX_CONF_FILE="/home/7byte/openresty-test/conf/nginx.conf"</div><div class="line"></div><div class="line">[ -f /etc/sysconfig/openresty ] && . /etc/sysconfig/openresty</div><div class="line"></div><div class="line">lockfile=/var/lock/subsys/openresty</div><div class="line"></div><div class="line">start() {</div><div class="line"> [ -x $nginx ] || exit 5</div><div class="line"> [ -f $NGINX_CONF_FILE ] || exit 6</div><div class="line"> echo -n $"Starting $prog: "</div><div class="line"> daemon $nginx -c $NGINX_CONF_FILE</div><div class="line"> retval=$?</div><div class="line"> echo</div><div class="line"> [ $retval -eq 0 ] && touch $lockfile</div><div class="line"> return $retval</div><div class="line">}</div><div class="line"></div><div class="line">stop() {</div><div class="line"> echo -n $"Stopping $prog: "</div><div class="line"> killproc $prog -QUIT</div><div class="line"> retval=$?</div><div class="line"> echo</div><div class="line"> [ $retval -eq 0 ] && rm -f $lockfile</div><div class="line"> return $retval</div><div class="line">}</div><div class="line"></div><div class="line">restart() {</div><div class="line"> configtest || return $?</div><div class="line"> stop</div><div class="line"> sleep 1</div><div class="line"> start</div><div class="line">}</div><div class="line"></div><div class="line">reload() {</div><div class="line"> configtest || return $?</div><div class="line"> echo -n $"Reloading $prog: "</div><div class="line"> killproc $nginx -HUP</div><div class="line"> RETVAL=$?</div><div class="line"> echo</div><div class="line">}</div><div class="line"></div><div class="line">force_reload() {</div><div class="line"> restart</div><div class="line">}</div><div class="line"></div><div class="line">configtest() {</div><div class="line"> $nginx -q -t -c $NGINX_CONF_FILE</div><div class="line">}</div><div class="line"></div><div class="line">rh_status() {</div><div class="line"> status $nginx</div><div class="line">}</div><div class="line"></div><div class="line">rh_status_q() {</div><div class="line"> rh_status >/dev/null 2>&1</div><div class="line">}</div><div class="line"></div><div class="line">case "$1" in</div><div class="line"> start)</div><div class="line"> rh_status_q && exit 0</div><div class="line"> $1</div><div class="line"> ;;</div><div class="line"> stop)</div><div class="line"> rh_status_q || exit 0</div><div class="line"> $1</div><div class="line"> ;;</div><div class="line"> restart|configtest)</div><div class="line"> $1</div><div class="line"> ;;</div><div class="line"> reload)</div><div class="line"> rh_status_q || exit 7</div><div class="line"> $1</div><div class="line"> ;;</div><div class="line"> force-reload)</div><div class="line"> force_reload</div><div class="line"> ;;</div><div class="line"> status)</div><div class="line"> rh_status</div><div class="line"> ;;</div><div class="line"> condrestart|try-restart)</div><div class="line"> rh_status_q || exit 0</div><div class="line"> ;;</div><div class="line"> *)</div><div class="line"> echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"</div><div class="line"> exit 2</div><div class="line">esac</div></pre></td></tr></table></figure></p>
<p>保存退出后,下一步启动openresty-test。需要注意的是,如果开启了防火墙,需要放开nginx默认监听的80端口,或是其它自定义的端口<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ sudo service openresty-test start</div><div class="line">Starting openresty-test (via systemctl): [ OK ]</div></pre></td></tr></table></figure></p>
<p>看到上面的结果,说明启动成功了。然后通过curl在试着访问一下<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ curl http://127.0.0.1</div><div class="line">Hello, world!</div></pre></td></tr></table></figure></p>
<p>访问 <a href="http://127.0.0.1" target="_blank" rel="external">http://127.0.0.1</a> 返回了“Hello, world!”,搭建完成。</p>
]]></content>
<summary type="html">
<h2 id="测试机配置"><a href="#测试机配置" class="headerlink" title="测试机配置"></a>测试机配置</h2><ul>
<li>CPU: 1核 Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz </
</summary>
<category term="openresty" scheme="https://7byte.github.io/categories/openresty/"/>
<category term="nginx" scheme="https://7byte.github.io/tags/nginx/"/>
<category term="openresty" scheme="https://7byte.github.io/tags/openresty/"/>
</entry>
<entry>
<title>基于hexo搭建github个人博客</title>
<link href="https://7byte.github.io/2016/07/22/%E5%9F%BA%E4%BA%8Ehexo%E6%90%AD%E5%BB%BAgithub%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/"/>
<id>https://7byte.github.io/2016/07/22/基于hexo搭建github个人博客/</id>
<published>2016-07-22T02:38:04.000Z</published>
<updated>2016-11-20T10:59:54.519Z</updated>
<content type="html"><![CDATA[<h2 id="为什么要写个人博客"><a href="#为什么要写个人博客" class="headerlink" title="为什么要写个人博客"></a>为什么要写个人博客</h2><blockquote>
<p>“好记性不如烂笔头”</p>
</blockquote>
<p>一直倔强地认为只要我记性足够好,就根本不需要把时间花在做笔记、总结这种多余的事情上面。然而事实证明,我的这个想法的确没错,问题出就出在我的记性还不够好,至少还没好到完全不用做笔记的地步。当我意识到这点的时候,结合自身程序员的身份,很自然地萌生了“创建一个只属于自己的个人博客”的想法,于是有了现在你看到的这篇文章。</p>
<h2 id="配置Hexo"><a href="#配置Hexo" class="headerlink" title="配置Hexo"></a>配置Hexo</h2><blockquote>
<p><a href="https://hexo.io/" target="_blank" rel="external">Hexo</a>是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。</p>
</blockquote>
<ul>
<li>下载安装<a href="https://nodejs.org/en/" target="_blank" rel="external">Node.js</a></li>
<li><p>安装 Hexo</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm install -g hexo-cli</div></pre></td></tr></table></figure>
<p> 不知道是不是网络原因,上面的命令我在家里自己电脑上卡了二十多分钟才执行完,不过从最终结果来看,并没有什么问题。</p>
</li>
<li><p>创建hexo工作目录<br>新建一个文件夹Hexo,进入文件夹后</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm install</div></pre></td></tr></table></figure>
</li>
<li><p>启动本地预览</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ hexo init</div></pre></td></tr></table></figure>
<p> 我执行上面这条命令时在最后一步报了个警告:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm WARN deprecated minimatch@0.3.0: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue</div></pre></td></tr></table></figure>
<p> 这时候<code>Ctrl+C</code>就可以了,这个警告不会对后续流程有什么影响,但本着追求完美的态度,我们执行下面的命令更新这个有问题的包</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm install minimatch@"3.0.2"</div></pre></td></tr></table></figure>
</li>
<li><p>然后生成静态文件并启动服务</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ hexo g</div><div class="line">$ hexo s</div></pre></td></tr></table></figure>
<p> 启动服务后的默认访问网址为: <a href="http://localhost:4000/" target="_blank" rel="external">http://localhost:4000/</a><br>如果以上各步都没问题,就能在浏览器看到新建的网页了,这里看到的是默认主题,将来根据自己的需要修改,参考官方<a href="https://hexo.io/themes/" target="_blank" rel="external">主题</a></p>
</li>
</ul>
<h2 id="部署到github"><a href="#部署到github" class="headerlink" title="部署到github"></a>部署到github</h2><ul>
<li><p>安装hexo git部署插件 <a href="https://github.com/hexojs/hexo-deployer-git" target="_blank" rel="external">hexo-deployer-git</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm install hexo-deployer-git --save</div></pre></td></tr></table></figure>
</li>
<li><p>修改hexo配置<br>工作目录中的 _config.yml 在这个文件里面可以设置网站的各项配置信息,包括网站标题、作者名等等。这里我们修改deploy参数为github仓库路径。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">deploy: </div><div class="line"> type: git</div><div class="line"> repo: https://github.com/7byte/7byte.github.com.git</div></pre></td></tr></table></figure>
</li>
<li><p>部署github</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ hexo deploy</div></pre></td></tr></table></figure>
<p> 第一次部署的时候需要输入github账号和密码,以后再部署不用重新输入。<br> 执行上面的命令后会把 \public 目录下的文件全部同步到指定的github pages仓库,仓库中原来的<strong>所有文件都会被清空</strong>,然后替换成我们新提交的文件。</p>
<p> 部署完成之后,在浏览器输入<a href="http://yourname.github.com" target="_blank" rel="external">http://yourname.github.com</a> 或者 <a href="http://yourname.github.io" target="_blank" rel="external">http://yourname.github.io</a> 就能看到新的博客页面,和本地预览时看到的内容是一致的。</p>
</li>
</ul>
<h2 id="The-End"><a href="#The-End" class="headerlink" title="The End"></a>The End</h2><p>至此,我的博客基本搭建完成了!<br>当然,这只是开始,搭建博客的目的是为了敦促自己多多总结、多多表达、多多分享,改掉身上那些臭毛病,从而成为一个更好的人。仔细想想,我发现可以写的东西还挺多的。项目中用到的新思路、解决一道棘手的难题、学到好玩的新知识、自己实现的小游戏等等。<br>骐骥一跃,不能十步,驽马十驾,功在不舍,与君共勉。</p>
]]></content>
<summary type="html">
<h2 id="为什么要写个人博客"><a href="#为什么要写个人博客" class="headerlink" title="为什么要写个人博客"></a>为什么要写个人博客</h2><blockquote>
<p>“好记性不如烂笔头”</p>
</blockquote>
</summary>
<category term="搭建个人博客" scheme="https://7byte.github.io/categories/myBlog/"/>
<category term="github" scheme="https://7byte.github.io/tags/github/"/>
<category term="hexo" scheme="https://7byte.github.io/tags/hexo/"/>
</entry>
</feed>