-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
1786 lines (1581 loc) · 368 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
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
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>ARTS 0x00 week</title>
<url>/2019/03/17/ARTS-0x00-week/</url>
<content><![CDATA[]]></content>
</entry>
<entry>
<title>ARTS</title>
<url>/2019/03/17/ARTS/</url>
<content><![CDATA[<p>加入极客时间《左耳听风》第3期 ARTS 打卡小组</p>
<span id="more"></span>
<h1 id="ARTS-定义"><a href="#ARTS-定义" class="headerlink" title="ARTS 定义"></a>ARTS 定义</h1><ul>
<li>Algorithm:主要是为了编程训练和学习。每周至少做一个 leetcode 的算法题(先从Easy开始,然后再Medium,最后才Hard)。进行编程训练,如果不训练你看再多的算法书,你依然不会做算法题,看完书后,你需要训练。关于做Leetcode的的优势,你可以看一下我在coolshell上的文章 <a href="https://coolshell.cn/articles/12052.html">Leetcode 编程训练 - 酷 壳 - CoolShell</a>。</li>
<li>Review:主要是为了学习英文,如果你的英文不行,你基本上无缘技术高手。所以,需要你阅读并点评至少一篇英文技术文章,我个人最喜欢去的地方是 <a href="http://medium.com/">http://Medium.com</a> (需要梯子)以及各个公司的技术 blog,如 Netflix 的。</li>
<li>Tip:主要是为了总结和归纳你在是常工作中所遇到的知识点。学习至少一个技术技巧。你在工作中遇到的问题,踩过的坑,学习的点滴知识。</li>
<li>Share:主要是为了建立你的影响力,能够输出价值观。分享一篇有观点和思考的技术文章。</li>
</ul>
<h1 id="计划"><a href="#计划" class="headerlink" title="计划"></a>计划</h1><ul>
<li>(1)每周至少做一个 leetcode 的算法题</li>
<li>(2)阅读并点评至少一篇英文技术文章</li>
<li>(3)学习至少一个技术技巧</li>
<li>(4)分享一篇有观点和思考的技术文章</li>
</ul>
<blockquote>
<p>目前主要从事后端开发,主要使用 C/C++ 开发,目前逐渐开始过渡使用 Golang。希望通过这种坚持写下来分享的方式,来敦促自己进步!</p>
</blockquote>
]]></content>
<categories>
<category>ARTS</category>
</categories>
<tags>
<tag>ARTS</tag>
</tags>
</entry>
<entry>
<title>DB2配置远程数据库</title>
<url>/2019/10/24/DB2%E9%85%8D%E7%BD%AE%E8%BF%9C%E7%A8%8B%E6%95%B0%E6%8D%AE%E5%BA%93/</url>
<content><![CDATA[<p>在DB2客户机上连接远程DB2服务之前,必须正确设置服务端通信协议。DB2支持的协议有TCP/IP、NetBIOS、NPIPE等。</p>
<p>基本分成2步:</p>
<ol>
<li>在客户机上对远程DB2节点进行设置。</li>
<li>在客户机上对远程数据库进行设置。</li>
</ol>
<span id="more"></span>
<h1 id="第一步"><a href="#第一步" class="headerlink" title="第一步"></a>第一步</h1><p>首先在客户机上对远程节点进行设置,这里需要确认远程主机在客户机上的名称、IP地址、端口号等基本命令如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">db2 => catalog tcpip node <span class="variable">${nodename}</span> remote <span class="variable">${ip/hostname}</span> server <span class="variable">${prot}</span> <span class="comment"># ${nodename} 不能超过8个字符</span></span><br><span class="line">db2 => TERMINATE <span class="comment">#刷新目录高速缓存</span></span><br><span class="line">db2 => LIST NODE DIRECTORY <span class="comment">#查看客户机目录节点</span></span><br><span class="line">db2 => UNCATALOG NODE <span class="variable">${nodename}</span> <span class="comment">#查看客户机目录节点</span></span><br></pre></td></tr></table></figure>
<h1 id="第二步"><a href="#第二步" class="headerlink" title="第二步"></a>第二步</h1><p>再把远程节点上的数据库,设置本地别名</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">db2 => catalog db <span class="variable">${remote_dbname}</span> as <span class="variable">${local_alias_name}</span> at node <span class="variable">${nodename}</span></span><br><span class="line">db2 => TERMINATE <span class="comment">#刷新目录高速缓存</span></span><br><span class="line">db2 => LIST DB DIRECTORY <span class="comment">#查看本地数据库目录</span></span><br><span class="line">db2 => UNCATALOG DB <span class="variable">${local_alias_name}</span> <span class="comment">#删除数据库编目${local_alias_name} </span></span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>DB2</tag>
</tags>
</entry>
<entry>
<title>Docker Operation not permitted</title>
<url>/2020/12/29/Docker-Operation-not-permitted/</url>
<content><![CDATA[<h1 id="Docker容器使用问题"><a href="#Docker容器使用问题" class="headerlink" title="Docker容器使用问题"></a>Docker容器使用问题</h1><h2 id="Failed-to-get-D-Bus-connection-Operation-not-permitted"><a href="#Failed-to-get-D-Bus-connection-Operation-not-permitted" class="headerlink" title="Failed to get D-Bus connection: Operation not permitted"></a>Failed to get D-Bus connection: Operation not permitted</h2><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>使用centos7镜像创建容器后,在里面使用systemctl启动服务报错。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run -itd --name centos7 centos:7</span><br><span class="line">docker attach centos7</span><br><span class="line">yum install vsftpd</span><br><span class="line">systemctl start vsftpd</span><br><span class="line">Failed to get D-Bus connection: Operation not permitted</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p>Docker的设计理念是在容器里面不运行后台服务,容器本身就是宿主机上的一个独立的主进程,也可以间接的理解为就是容器里运行服务的应用进程。一个容器的生命周期是围绕这个主进程存在的,所以正确的使用容器方法是将里面的服务运行在前台。</p>
<p>再说到systemd,这个套件已经成为主流Linux发行版(比如CentOS7、Ubuntu14+)默认的服务管理,取代了传统的SystemV风格服务管理。systemd维护系统服务程序,它需要特权去会访问Linux内核。而容器并不是一个完整的操作系统,只有一个文件系统,而且默认启动只是普通用户这样的权限访问Linux内核,也就是没有特权,所以自然就用不了!</p>
<p>因此,请遵守容器设计原则,一个容器里运行一个前台服务!</p>
<h3 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h3><p>以特权模式运行容器。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 创建容器:</span></span><br><span class="line">docker run -d -name centos7 --privileged=<span class="literal">true</span> centos:7 /usr/sbin/init</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进入容器:</span></span><br><span class="line">docker <span class="built_in">exec</span> -it centos7 /bin/bash</span><br></pre></td></tr></table></figure>
<p>这样可以使用systemctl启动服务了。</p>
]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>Docker use 32-bit centos image</title>
<url>/2021/11/22/Docker-use-32-bit-centos-image/</url>
<content><![CDATA[<h1 id="拉取-32bit-镜像"><a href="#拉取-32bit-镜像" class="headerlink" title="拉取 32bit 镜像"></a>拉取 32bit 镜像</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker pull i386/centos:centos6</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h1 id="替换-centos6-yum-repo"><a href="#替换-centos6-yum-repo" class="headerlink" title="替换 centos6 yum repo"></a>替换 centos6 yum repo</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sed -i <span class="string">"s|enabled=1|enabled=0|g"</span> /etc/yum/pluginconf.d/fastestmirror.conf</span><br><span class="line"><span class="built_in">mv</span> /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup</span><br><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo https://www.xmpan.com/Centos-6-Vault-Aliyun.repo</span><br></pre></td></tr></table></figure>
<h1 id="使用-32bit-repo"><a href="#使用-32bit-repo" class="headerlink" title="使用 32bit repo"></a>使用 32bit repo</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># arch --> x86_64</span></span><br><span class="line">sed -i <span class="string">'s/\$arch/i686/g'</span> /etc/yum.repos.d/*</span><br><span class="line">sed -i <span class="string">'s/\$basearch/i386/g'</span> /etc/yum.repos.d/*</span><br><span class="line"></span><br><span class="line">yum clean all</span><br><span class="line">yum makecache</span><br></pre></td></tr></table></figure>
<h1 id="更新-git-gt-git-2-x"><a href="#更新-git-gt-git-2-x" class="headerlink" title="更新 git -> git 2.x"></a>更新 git -> git 2.x</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 32bit</span></span><br><span class="line">wget http://opensource.wandisco.com/centos/6/git/i686/wandisco-git-release-6-1.noarch.rpm && rpm -ivh wandisco-git-release-6-1.noarch.rpm</span><br><span class="line"></span><br><span class="line"><span class="comment"># 64bit</span></span><br><span class="line">wget http://opensource.wandisco.com/centos/6/git/x86_64/wandisco-git-release-6-1.noarch.rpm && rpm -ivh wandisco-git-release-6-1.noarch.rpm</span><br><span class="line"></span><br><span class="line"><span class="comment"># centos7 64bit</span></span><br><span class="line">wget http://opensource.wandisco.com/centos/7/git/x86_64/wandisco-git-release-7-2.noarch.rpm && rpm -ivh wandisco-git-release-7-2.noarch.rpm</span><br><span class="line"></span><br><span class="line">yum update git</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>docker</tag>
<tag>git</tag>
<tag>centos6</tag>
<tag>yum</tag>
</tags>
</entry>
<entry>
<title>Go 空结构体比较</title>
<url>/2019/04/23/Go%E7%A9%BA%E7%BB%93%E6%9E%84%E4%BD%93%E6%AF%94%E8%BE%83/</url>
<content><![CDATA[<h1 id="下面代码的输出是什么(判断-a-x3D-x3D-b-)的部分?为什么?"><a href="#下面代码的输出是什么(判断-a-x3D-x3D-b-)的部分?为什么?" class="headerlink" title="下面代码的输出是什么(判断 a == b )的部分?为什么?"></a>下面代码的输出是什么(判断 a == b )的部分?为什么?</h1><span id="more"></span>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 由于 struct 里面是空的,所以2个 struct 的地址可能会相同</span></span><br><span class="line"> a := &<span class="keyword">struct</span>{}{}</span><br><span class="line"> b := &<span class="keyword">struct</span>{}{}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// a,b 里面的值都是空 struct 的地址,所以一样</span></span><br><span class="line"> fmt.Printf(<span class="string">"%p\n"</span>, a)</span><br><span class="line"> fmt.Printf(<span class="string">"%p\n"</span>, b)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// a,b 是不同的变量,本身的地址不同</span></span><br><span class="line"> fmt.Printf(<span class="string">"%p\n"</span>, &a)</span><br><span class="line"> fmt.Printf(<span class="string">"%p\n"</span>, &b)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// true</span></span><br><span class="line"> fmt.Println(a == b)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<blockquote>
<p>引申 切片的引用</p>
</blockquote>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="number">100</span>)</span><br><span class="line"> b := a</span><br><span class="line"></span><br><span class="line"> a[<span class="number">10</span>] = <span class="string">'y'</span></span><br><span class="line"> b[<span class="number">10</span>] = <span class="string">'x'</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"a[10] ="</span>, a[<span class="number">10</span>])</span><br><span class="line"> fmt.Println(<span class="string">"b[10] ="</span>, b[<span class="number">10</span>])</span><br><span class="line"></span><br><span class="line"> c := [<span class="number">3</span>]<span class="type">byte</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"> d := c</span><br><span class="line"></span><br><span class="line"> c[<span class="number">2</span>] = <span class="string">'x'</span></span><br><span class="line"> d[<span class="number">2</span>] = <span class="string">'y'</span></span><br><span class="line"> fmt.Println(<span class="string">"c[2] ="</span>, c[<span class="number">2</span>])</span><br><span class="line"> fmt.Println(<span class="string">"d[2] ="</span>, d[<span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"> g := c[:]</span><br><span class="line"> h := g</span><br><span class="line"></span><br><span class="line"> g[<span class="number">2</span>] = <span class="string">'x'</span></span><br><span class="line"> h[<span class="number">2</span>] = <span class="string">'y'</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"g[2] = "</span>, g[<span class="number">2</span>])</span><br><span class="line"> fmt.Println(<span class="string">"h[2] = "</span>, h[<span class="number">2</span>])</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">Output</span></span><br><span class="line"><span class="comment">a[10] = 120</span></span><br><span class="line"><span class="comment">b[10] = 120</span></span><br><span class="line"><span class="comment">c[2] = 120</span></span><br><span class="line"><span class="comment">d[2] = 121</span></span><br><span class="line"><span class="comment">g[2] = 121</span></span><br><span class="line"><span class="comment">h[2] = 121</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>Golang</category>
</categories>
<tags>
<tag>面试题</tag>
<tag>Golang</tag>
</tags>
</entry>
<entry>
<title>Go防缓存击穿 singleflight</title>
<url>/2020/03/15/Go%E9%98%B2%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BFsingleflight/</url>
<content><![CDATA[<h1 id="缓存更新问题"><a href="#缓存更新问题" class="headerlink" title="缓存更新问题"></a>缓存更新问题</h1><p>缓存是高并发业务的基石,当访问量突然上升的时候,缓存失效回源时会将请求打到后台数据库,导致服务器响应延迟或者宕机的情况。</p>
<p><code>通常缓存更新方案:</code></p>
<ul>
<li>1.业务代码中,根据key从缓存拿不到数据,访问存储层获取数据后更新缓存</li>
<li>2.由专门的定时脚本在缓存失效前对其进行更新</li>
<li>3.通过分布式锁,实现只有一个请求负责缓存更新,其他请求等待:<a href="http://yangxikun.github.io/%E7%BC%93%E5%AD%98/2015/07/02/cache-access.html">一种基于哨兵的缓存访问策略</a></li>
</ul>
<p>通常获取缓存这样写:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">data = getCache(key)</span><br><span class="line"><span class="keyword">if</span> !data {</span><br><span class="line"> data = selectDB(key)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h1 id="并发获取"><a href="#并发获取" class="headerlink" title="并发获取"></a>并发获取</h1><p>当缓存失效,集中查询DB时,此时需要考虑加锁。</p>
<h2 id="加锁"><a href="#加锁" class="headerlink" title="加锁"></a>加锁</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">checkAccess</span><span class="params">(key)</span></span> <span class="type">bool</span> {</span><br><span class="line"> startTime = time.now()</span><br><span class="line"> <span class="keyword">for</span> <span class="literal">true</span> {</span><br><span class="line"> <span class="keyword">if</span> isLock(key) == <span class="literal">false</span> {</span><br><span class="line"> setLock(key)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (time.now() - startTime) > _maxLockTime {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> time.sleep(<span class="number">20</span>ms)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在读取缓存失败,查询 DB 之前,先来一个锁判断,如果锁不存在,那么就加把锁,再去查 DB。如果锁存在,那么就等待,然后回头再去读 redis 或者进入 DB 查询。</p>
<h2 id="singleflight"><a href="#singleflight" class="headerlink" title="singleflight"></a>singleflight</h2><p>上面的代码使用起来没什么问题,但是依靠无限循环 + sleep 实现的方法比较低效。而在 go 语言中,借助非常轻量和高效的协程,可以很优雅的实现这种功能,这就是 <a href="https://github.com/golang/groupcache/tree/master/singleflight">singleflight</a>。</p>
<h3 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h3><p>使用方法很简单,可以参考其<a href="https://github.com/golang/groupcache/blob/master/singleflight/singleflight_test.go">test</a> :</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestDo</span><span class="params">(t *testing.T)</span></span> {</span><br><span class="line"> <span class="keyword">var</span> g Group</span><br><span class="line"> v, err := g.Do(<span class="string">"key"</span>, <span class="function"><span class="keyword">func</span><span class="params">()</span></span> (<span class="keyword">interface</span>{}, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"bar"</span>, <span class="literal">nil</span></span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">if</span> got, want := fmt.Sprintf(<span class="string">"%v (%T)"</span>, v, v), <span class="string">"bar (string)"</span>; got != want {</span><br><span class="line"> t.Errorf(<span class="string">"Do = %v; want %v"</span>, got, want)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> t.Errorf(<span class="string">"Do error = %v"</span>, err)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>可见使用起来非常简单,对外只需要这个Do 函数,传入 Key 和获取缓存的回调函数,如此 singleflight 就能自动帮我们处理同时请求下游服务的问题了。</p>
<p>那么这个Do函数到底做了什么事情?</p>
<p>源码分析</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> call <span class="keyword">struct</span> {</span><br><span class="line"> wg sync.WaitGroup</span><br><span class="line"> val <span class="keyword">interface</span>{}</span><br><span class="line"> err <span class="type">error</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Group <span class="keyword">struct</span> {</span><br><span class="line"> mu sync.Mutex <span class="comment">// protects m</span></span><br><span class="line"> m <span class="keyword">map</span>[<span class="type">string</span>]*call <span class="comment">// lazily initialized</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(g *Group)</span></span> Do(key <span class="type">string</span>, fn <span class="function"><span class="keyword">func</span><span class="params">()</span></span> (<span class="keyword">interface</span>{}, <span class="type">error</span>)) (v <span class="keyword">interface</span>{}, err <span class="type">error</span>, shared <span class="type">bool</span>) {</span><br><span class="line"> g.mu.Lock()</span><br><span class="line"> <span class="keyword">if</span> g.m == <span class="literal">nil</span> {</span><br><span class="line"> g.m = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]*call)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> c, ok := g.m[key]; ok {</span><br><span class="line"> c.dups++</span><br><span class="line"> g.mu.Unlock()</span><br><span class="line"> c.wg.Wait()</span><br><span class="line"> <span class="keyword">return</span> c.val, c.err, <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> c := <span class="built_in">new</span>(call)</span><br><span class="line"> c.wg.Add(<span class="number">1</span>)</span><br><span class="line"> g.m[key] = c</span><br><span class="line"> g.mu.Unlock()</span><br><span class="line"></span><br><span class="line"> g.doCall(c, key, fn)</span><br><span class="line"> <span class="keyword">return</span> c.val, c.err, c.dups > <span class="number">0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>go 的代码一直都很清晰易懂,可以看到先定义了结构体group和call,group.mu是保护group.m的互斥锁,group.m主要是保存请求的key,而call结构体是用来记录回调函数的结果。</p>
<p>在Do函数中,函数先是判断这个 key 是否是第一次调用,如果是,就会进入doCall调用回调函数获取结果,后续的请求就会阻塞在c.wg.Wait()这里,等待回调函数返回以后,直接拿到结果。</p>
<h3 id="singleflight-的应用"><a href="#singleflight-的应用" class="headerlink" title="singleflight 的应用"></a>singleflight 的应用</h3><p>所以依靠 singleflight ,针对并发缓存的更新,我们就可以这样实现:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">data = getCache(key)</span><br><span class="line"><span class="keyword">if</span> !data {</span><br><span class="line"> data = g.Do(key, <span class="function"><span class="keyword">func</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> selectDB(key)</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="Thanks"><a href="#Thanks" class="headerlink" title="Thanks"></a>Thanks</h1><p><a href="https://notes.wen.moe/2017/use-singleflight-instead-lock.html">使用 singleflight 代替传统的并发锁</a><br><a href="http://yangxikun.github.io/golang/2017/03/07/golang-singleflight.html">Golang singleflight 用武之地</a></p>
]]></content>
<tags>
<tag>go</tag>
<tag>cache</tag>
<tag>singleflight</tag>
</tags>
</entry>
<entry>
<title>MariaDB 修改存储路径</title>
<url>/2021/11/23/MySQL-%E4%BF%AE%E6%94%B9%E5%AD%98%E5%82%A8%E8%B7%AF%E5%BE%84/</url>
<content><![CDATA[<h1 id="1、检查-MariaDB-数据库存放目录"><a href="#1、检查-MariaDB-数据库存放目录" class="headerlink" title="1、检查 MariaDB 数据库存放目录"></a>1、检查 MariaDB 数据库存放目录</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">mysql -u root</span><br></pre></td></tr></table></figure>
<figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">MariaDB [(<span class="keyword">none</span>)]<span class="operator">></span> <span class="keyword">show</span> variables <span class="keyword">like</span> <span class="string">'%dir%'</span>;</span><br><span class="line"><span class="operator">+</span><span class="comment">-----------------------------------------+----------------------------+</span></span><br><span class="line"><span class="operator">|</span> Variable_name <span class="operator">|</span> <span class="keyword">Value</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">+</span><span class="comment">-----------------------------------------+----------------------------+</span></span><br><span class="line"><span class="operator">|</span> aria_sync_log_dir <span class="operator">|</span> NEWFILE <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> basedir <span class="operator">|</span> <span class="operator">/</span>usr<span class="operator">/</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> binlog_direct_non_transactional_updates <span class="operator">|</span> OFF <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> character_sets_dir <span class="operator">|</span> <span class="operator">/</span>usr<span class="operator">/</span>share<span class="operator">/</span>mysql<span class="operator">/</span>charsets<span class="operator">/</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> datadir <span class="operator">|</span> <span class="operator">/</span>var<span class="operator">/</span>lib<span class="operator">/</span>mysql <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> ignore_db_dirs <span class="operator">|</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> innodb_data_home_dir <span class="operator">|</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> innodb_log_group_home_dir <span class="operator">|</span> .<span class="operator">/</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> innodb_max_dirty_pages_pct <span class="operator">|</span> <span class="number">90.000000</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> innodb_max_dirty_pages_pct_lwm <span class="operator">|</span> <span class="number">0.000000</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> innodb_tmpdir <span class="operator">|</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> innodb_undo_directory <span class="operator">|</span> .<span class="operator">/</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> lc_messages_dir <span class="operator">|</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> plugin_dir <span class="operator">|</span> <span class="operator">/</span>usr<span class="operator">/</span>lib64<span class="operator">/</span>mysql<span class="operator">/</span>plugin<span class="operator">/</span> <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> slave_load_tmpdir <span class="operator">|</span> <span class="operator">/</span>tmp <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> tmpdir <span class="operator">|</span> <span class="operator">/</span>tmp <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> wsrep_data_home_dir <span class="operator">|</span> <span class="operator">/</span>var<span class="operator">/</span>lib<span class="operator">/</span>mysql <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span> wsrep_dirty_reads <span class="operator">|</span> OFF <span class="operator">|</span></span><br><span class="line"><span class="operator">+</span><span class="comment">-----------------------------------------+----------------------------+</span></span><br><span class="line"></span><br><span class="line">MariaDB [(<span class="keyword">none</span>)]<span class="operator">></span> status</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">mysql Ver <span class="number">15.1</span> Distrib <span class="number">10.7</span><span class="number">.1</span><span class="operator">-</span>MariaDB, <span class="keyword">for</span> Linux (x86_64) <span class="keyword">using</span> readline <span class="number">5.1</span></span><br><span class="line"></span><br><span class="line">Connection id: <span class="number">4</span></span><br><span class="line"><span class="keyword">Current</span> database:</span><br><span class="line"><span class="keyword">Current</span> <span class="keyword">user</span>: root<span class="variable">@localhost</span></span><br><span class="line">SSL: <span class="keyword">Not</span> <span class="keyword">in</span> use</span><br><span class="line"><span class="keyword">Current</span> pager: stdout</span><br><span class="line"><span class="keyword">Using</span> outfile: <span class="string">''</span></span><br><span class="line"><span class="keyword">Using</span> delimiter: ;</span><br><span class="line">Server: MariaDB</span><br><span class="line">Server version: <span class="number">10.7</span><span class="number">.1</span><span class="operator">-</span>MariaDB<span class="operator">-</span>log MariaDB Server</span><br><span class="line">Protocol version: <span class="number">10</span></span><br><span class="line">Connection: Localhost via UNIX socket</span><br><span class="line">Server characterset: utf8mb4</span><br><span class="line">Db characterset: utf8mb4</span><br><span class="line">Client characterset: utf8mb4</span><br><span class="line">Conn. characterset: utf8mb4</span><br><span class="line">UNIX socket: <span class="operator">/</span>var<span class="operator">/</span>lib<span class="operator">/</span>mysql<span class="operator">/</span>mysql.sock</span><br><span class="line">Uptime: <span class="number">9</span> min <span class="number">2</span> sec</span><br><span class="line"></span><br><span class="line">Threads: <span class="number">2</span> Questions: <span class="number">5</span> Slow queries: <span class="number">0</span> Opens: <span class="number">16</span> <span class="keyword">Open</span> tables: <span class="number">10</span> Queries <span class="keyword">per</span> <span class="keyword">second</span> avg: <span class="number">0.009</span></span><br><span class="line"><span class="comment">--------------</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>查看 datadir 那一行所指的路径 /var/lib/mysql</p>
</blockquote>
<blockquote>
<p>查看 UNIX socket</p>
</blockquote>
<span id="more"></span>
<h1 id="2、停止-mariadb-服务"><a href="#2、停止-mariadb-服务" class="headerlink" title="2、停止 mariadb 服务"></a>2、停止 mariadb 服务</h1><figure class="highlight arduino"><table><tr><td class="code"><pre><span class="line">systemctl stop mariadb.service</span><br></pre></td></tr></table></figure>
<h1 id="3、创建新的数据库存放目录"><a href="#3、创建新的数据库存放目录" class="headerlink" title="3、创建新的数据库存放目录"></a>3、创建新的数据库存放目录</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> /data/mysql_data</span><br></pre></td></tr></table></figure>
<h1 id="4、复制-x2F-移动之前存放数据库目录文件,到新的数据库存放目录位置"><a href="#4、复制-x2F-移动之前存放数据库目录文件,到新的数据库存放目录位置" class="headerlink" title="4、复制/移动之前存放数据库目录文件,到新的数据库存放目录位置"></a>4、复制/移动之前存放数据库目录文件,到新的数据库存放目录位置</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cp</span> -R /var/lib/mysql/* /data/mysql_data/</span><br><span class="line">or</span><br><span class="line"><span class="built_in">mv</span> /var/lib/mysql/* /data/mysql_data</span><br></pre></td></tr></table></figure>
<h1 id="5、修改-mariadb-数据库目录权限以及配置文件"><a href="#5、修改-mariadb-数据库目录权限以及配置文件" class="headerlink" title="5、修改 mariadb 数据库目录权限以及配置文件"></a>5、修改 mariadb 数据库目录权限以及配置文件</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">chown</span> mysql:mysql -R /data/mysql_data/</span><br></pre></td></tr></table></figure>
<h1 id="6、编辑-Mariadb-的配置文件-x2F-etc-x2F-my-cnf"><a href="#6、编辑-Mariadb-的配置文件-x2F-etc-x2F-my-cnf" class="headerlink" title="6、编辑 Mariadb 的配置文件 /etc/my.cnf"></a>6、编辑 Mariadb 的配置文件 /etc/my.cnf</h1><figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="section">[client-server]</span></span><br><span class="line"><span class="attr">socket</span>=/data/mysql_data/mysql.sock</span><br><span class="line"></span><br><span class="line"><span class="section">[mysqld]</span></span><br><span class="line"><span class="attr">datadir</span>=/data/mysql_data/</span><br></pre></td></tr></table></figure>
<h1 id="7、启动数据库服务"><a href="#7、启动数据库服务" class="headerlink" title="7、启动数据库服务"></a>7、启动数据库服务</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">systemctl start mariadb.service</span><br></pre></td></tr></table></figure>
<h1 id="8、其他"><a href="#8、其他" class="headerlink" title="8、其他"></a>8、其他</h1><p>MariaDB 10.7 @CentOS7</p>
<p><strong>/etc/my.cnf 例子</strong></p>
<figure class="highlight clean"><table><tr><td class="code"><pre><span class="line">#</span><br><span class="line"># This group is read both by the client and the server</span><br><span class="line"># use it for options that affect everything</span><br><span class="line">#</span><br><span class="line">[client-server]</span><br><span class="line">socket=/data/mysql_data/mysql.sock</span><br><span class="line"></span><br><span class="line">#</span><br><span class="line"># include *.cnf <span class="keyword">from</span> the config directory</span><br><span class="line">#</span><br><span class="line">!includedir /etc/my.cnf.d</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">[mysqld]</span><br><span class="line">port=<span class="number">3306</span></span><br><span class="line">init_connect=<span class="string">'SET collation_connection = utf8mb4_unicode_ci'</span></span><br><span class="line">init_connect=<span class="string">'SET NAMES utf8mb4'</span></span><br><span class="line">character_set_server=utf8mb4</span><br><span class="line">collation-server=utf8mb4_unicode_ci</span><br><span class="line"></span><br><span class="line">#此处是忽略客户端的字符集,使用服务器的设置</span><br><span class="line">#可以避免客户端程序误操作,使用其他字符集连接进来并写入数据,从而引发乱码问题。</span><br><span class="line">skip-character-set-client-handshake=true</span><br><span class="line">datadir=/data/mysql_data/</span><br><span class="line">max_connections=<span class="number">900</span></span><br><span class="line"></span><br><span class="line">############### binlog</span><br><span class="line"># binlog日志路径,格式为mariadb-log<span class="number">.00000</span>*,递增</span><br><span class="line">log-bin = /data/mysql_data/bin-log/mariadb-log</span><br><span class="line">log-bin-index = /data/mysql_data/bin-log/mariadb-log.index</span><br><span class="line"># binlog日志保留天数</span><br><span class="line">expire-logs-days = <span class="number">7</span></span><br><span class="line">server-id = <span class="number">1</span></span><br><span class="line"># binlog日志有三种格式,分别是Statement、MiXED、ROW</span><br><span class="line">binlog-format = ROW</span><br><span class="line"># max-binlog-size = <span class="number">100</span>M # binlog每个日志文件大小</span><br><span class="line">############### binlog end</span><br><span class="line"></span><br><span class="line">############### 慢查询</span><br><span class="line"># touch /data/mysql_data/slow_query_log.log</span><br><span class="line"># chown mysql:mysql /data/mysql_data/slow_query_log.log</span><br><span class="line">slow_query_log=on</span><br><span class="line">slow_query_log_file=/data/mysql_data/slow_query_log.log</span><br><span class="line">long_query_time=<span class="number">2</span></span><br><span class="line">############### 慢查询 end</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>MariaDB</tag>
</tags>
</entry>
<entry>
<title>Percona Data Recovery Tool for InnoDB</title>
<url>/2021/01/08/Percona-Data-Recovery-Tool-for-InnoDB/</url>
<content><![CDATA[<p>遇到这么一个问题,不小心删除关键数据(delete),MySQL 没有备份且没有开 binlog。网上搜寻了一遍,发现可以使用 percona 的恢复工具解决,本文做个恢复步骤记录。</p>
<span id="more"></span>
<h1 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h1><p><a href="https://launchpad.net/percona-data-recovery-tool-for-innodb/trunk/release-0.5/">网站</a> 或 <a href="https://launchpad.net/percona-data-recovery-tool-for-innodb/trunk/release-0.5/+download/percona-data-recovery-tool-for-innodb-0.5.tar.gz?spm=a2c6h.12873639.0.0.6e6e31damQzvqd&file=percona-data-recovery-tool-for-innodb-0.5.tar.gz">直接下载工具</a></p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">tar -xvf percona-data-recovery-tool-for-innodb-0.5.tar.gz</span><br><span class="line"><span class="built_in">cd</span> percona-data-recovery-tool-for-innodb-0.5/mysql-source</span><br><span class="line">./configure</span><br><span class="line"><span class="built_in">cd</span> ..</span><br><span class="line">make</span><br></pre></td></tr></table></figure>
<h1 id="idb-处理"><a href="#idb-处理" class="headerlink" title="idb 处理"></a>idb 处理</h1><p>提取 ibd 物理文件,按照每页16K,存放。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 参数</span></span><br><span class="line"><span class="comment"># -5: row format 为 Compact</span></span><br><span class="line"><span class="comment"># -f: 要解析的文件</span></span><br><span class="line">./page_parser -5 -f t_trans.ibd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果生成</span></span><br><span class="line">pages-1610078166/FIL_PAGE_INDEX</span><br><span class="line">pages-1610078166/FIL_PAGE_TYPE_BLOB</span><br></pre></td></tr></table></figure>
<p>FIL_PAGE_INDEX 按照主键索引目录生成,要恢复的表索引<code>id</code>使用<code>innodb_table_monitor</code>查询获取</p>
<h1 id="获取索引-id"><a href="#获取索引-id" class="headerlink" title="获取索引 id"></a>获取索引 id</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 创建innodb_table_monitor表,通知innodb存储引擎将数据输出到/etc/my.cnf中定义的log-error文件里</span></span><br><span class="line">mysql> create table innodb_table_monitor(a int) engine=innodb;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>log-error = /var/log/mysql/mysqld.log</p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">===========================================</span></span><br><span class="line"><span class="number">210108</span> <span class="number">13</span><span class="string">:18:22</span> <span class="string">INNODB</span> <span class="string">TABLE</span> <span class="string">MONITOR</span> <span class="string">OUTPUT</span></span><br><span class="line"><span class="string">===========================================</span></span><br><span class="line"><span class="string">--------------------------------------</span></span><br><span class="line"><span class="attr">TABLE:</span> <span class="string">name</span> <span class="string">test/t_trans,</span> <span class="string">id</span> <span class="number">51</span><span class="string">,</span> <span class="string">flags</span> <span class="number">1</span><span class="string">,</span> <span class="string">columns</span> <span class="number">28</span><span class="string">,</span> <span class="string">indexes</span> <span class="number">2</span><span class="string">,</span> <span class="string">appr.rows</span> <span class="number">942</span></span><br><span class="line"> <span class="attr">COLUMNS: id:</span> <span class="string">DATA_INT</span> <span class="string">DATA_UNSIGNED</span> <span class="string">DATA_BINARY_TYPE</span> <span class="string">DATA_NOT_NULL</span> <span class="string">len</span> <span class="number">8</span><span class="string">;</span> <span class="attr">transdate:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524559</span> <span class="string">len</span> <span class="number">8</span><span class="string">;</span> <span class="attr">mchid:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524559</span> <span class="string">len</span> <span class="number">15</span><span class="string">;</span> <span class="attr">refno:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524559</span> <span class="string">len</span> <span class="number">12</span><span class="string">;</span> <span class="attr">custacct:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">20</span><span class="string">;</span> <span class="attr">wtrq:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">8</span><span class="string">;</span> <span class="attr">djxh:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">32</span><span class="string">;</span> <span class="attr">name:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">32</span><span class="string">;</span> <span class="attr">zjlx:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">32</span><span class="string">;</span> <span class="attr">idno:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">32</span><span class="string">;</span> <span class="attr">rqrq:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">8</span><span class="string">;</span> <span class="attr">nsrsbh:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">20</span><span class="string">;</span> <span class="attr">swjylsh:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">32</span><span class="string">;</span> <span class="attr">payamount:</span> <span class="string">DATA_FIXBINARY</span> <span class="string">DATA_BINARY_TYPE</span> <span class="string">len</span> <span class="number">9</span><span class="string">;</span> <span class="attr">zoneno:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">5</span><span class="string">;</span> <span class="attr">devno:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">20</span><span class="string">;</span> <span class="attr">type:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">2</span><span class="string">;</span> <span class="attr">channel:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">2</span><span class="string">;</span> <span class="attr">posserno:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">27</span><span class="string">;</span> <span class="attr">rzzh:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">20</span><span class="string">;</span> <span class="attr">returnCode:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">3</span><span class="string">;</span> <span class="attr">returnMessage:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">100</span><span class="string">;</span> <span class="attr">jftype:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">1</span><span class="string">;</span> <span class="attr">reqpack:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">528399</span> <span class="string">len</span> <span class="number">40960</span><span class="string">;</span> <span class="attr">respack:</span> <span class="string">DATA_VARCHAR</span> <span class="string">prtype</span> <span class="number">524303</span> <span class="string">len</span> <span class="number">255</span><span class="string">;</span> <span class="attr">DB_ROW_ID:</span> <span class="string">DATA_SYS</span> <span class="string">prtype</span> <span class="number">256</span> <span class="string">len</span> <span class="number">6</span><span class="string">;</span> <span class="attr">DB_TRX_ID:</span> <span class="string">DATA_SYS</span> <span class="string">prtype</span> <span class="number">257</span> <span class="string">len</span> <span class="number">6</span><span class="string">;</span> <span class="attr">DB_ROLL_PTR:</span> <span class="string">DATA_SYS</span> <span class="string">prtype</span> <span class="number">258</span> <span class="string">len</span> <span class="number">7</span><span class="string">;</span></span><br><span class="line"> <span class="attr">INDEX:</span> <span class="string">name</span> <span class="string">PRIMARY,</span> <span class="string">id</span> <span class="number">77</span><span class="string">,</span> <span class="string">fields</span> <span class="number">4</span><span class="string">/27,</span> <span class="string">uniq</span> <span class="number">4</span><span class="string">,</span> <span class="string">type</span> <span class="number">3</span></span><br><span class="line"> <span class="string">root</span> <span class="string">page</span> <span class="number">98364</span><span class="string">,</span> <span class="string">appr.key</span> <span class="string">vals</span> <span class="number">942</span><span class="string">,</span> <span class="string">leaf</span> <span class="string">pages</span> <span class="number">209</span><span class="string">,</span> <span class="string">size</span> <span class="string">pages</span> <span class="number">289</span></span><br><span class="line"> <span class="attr">FIELDS:</span> <span class="string">id</span> <span class="string">transdate</span> <span class="string">mchid</span> <span class="string">refno</span> <span class="string">DB_TRX_ID</span> <span class="string">DB_ROLL_PTR</span> <span class="string">custacct</span> <span class="string">wtrq</span> <span class="string">djxh</span> <span class="string">name</span> <span class="string">zjlx</span> <span class="string">idno</span> <span class="string">rqrq</span> <span class="string">nsrsbh</span> <span class="string">swjylsh</span> <span class="string">payamount</span> <span class="string">zoneno</span> <span class="string">devno</span> <span class="string">type</span> <span class="string">channel</span> <span class="string">posserno</span> <span class="string">rzzh</span> <span class="string">returnCode</span> <span class="string">returnMessage</span> <span class="string">jftype</span> <span class="string">reqpack</span> <span class="string">respack</span></span><br><span class="line"> <span class="attr">INDEX:</span> <span class="string">name</span> <span class="string">Index_trans_sw_id,</span> <span class="string">id</span> <span class="number">78</span><span class="string">,</span> <span class="string">fields</span> <span class="number">3</span><span class="string">/4,</span> <span class="string">uniq</span> <span class="number">4</span><span class="string">,</span> <span class="string">type</span> <span class="number">0</span></span><br><span class="line"> <span class="string">root</span> <span class="string">page</span> <span class="number">98365</span><span class="string">,</span> <span class="string">appr.key</span> <span class="string">vals</span> <span class="number">702</span><span class="string">,</span> <span class="string">leaf</span> <span class="string">pages</span> <span class="number">4</span><span class="string">,</span> <span class="string">size</span> <span class="string">pages</span> <span class="number">5</span></span><br><span class="line"> <span class="attr">FIELDS:</span> <span class="string">transdate</span> <span class="string">mchid</span> <span class="string">refno</span> <span class="string">id</span></span><br><span class="line"><span class="string">-----------------------------------</span></span><br><span class="line"><span class="string">END</span> <span class="string">OF</span> <span class="string">INNODB</span> <span class="string">TABLE</span> <span class="string">MONITOR</span> <span class="string">OUTPUT</span></span><br><span class="line"><span class="string">==================================</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>关于上面一些特殊字段中的含义</p>
</blockquote>
<figure class="highlight livecodeserver"><table><tr><td class="code"><pre><span class="line">DB_TRX_ID - this field is managed <span class="keyword">by</span> InnoDBinternally <span class="keyword">and</span> <span class="keyword">contains</span> <span class="keyword">a</span> ID <span class="keyword">of</span> transaction which changed <span class="keyword">a</span> recordlast <span class="built_in">time</span></span><br><span class="line">DB_ROLL_PTR - <span class="literal">one</span> more internal InnoDB field (TODO: find out whatis <span class="keyword">it</span> used <span class="keyword">for</span>).</span><br><span class="line">DB_ROW_ID - this internally used field should be <span class="keyword">the</span> <span class="keyword">first</span> field intables <span class="keyword">without</span> primary <span class="built_in">keys</span> (<span class="keyword">it</span> is <span class="keyword">an</span> auto-increment field used byInnoDB <span class="built_in">to</span> identify rows <span class="keyword">in</span> such tables)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面信息可以知道,index_id 为 77,下面在恢复数据时需要用到</p>
</blockquote>
<h1 id="生成表结构"><a href="#生成表结构" class="headerlink" title="生成表结构"></a>生成表结构</h1><figure class="highlight jboss-cli"><table><tr><td class="code"><pre><span class="line"><span class="comment"># percona-data-recovery-tool-for-innodb-0.5</span></span><br><span class="line"><span class="comment"># -- host 主机地址</span></span><br><span class="line"><span class="comment"># -- port 端口</span></span><br><span class="line"><span class="comment"># -- user 用户名</span></span><br><span class="line"><span class="comment"># -- password 密码</span></span><br><span class="line"><span class="comment"># -- db 数据库名</span></span><br><span class="line"><span class="comment"># -- table 表名</span></span><br><span class="line"><span class="string">./create_defs.pl</span> <span class="params">--host</span> localhost <span class="params">--port</span> 3306 <span class="params">--user</span> root <span class="params">--password</span> 123456 <span class="params">--db</span> test <span class="params">--table</span> t_trans > include/table_defs.h</span><br></pre></td></tr></table></figure>
<p>上面的命令会将 t_trans 表的表结构定义传入到 table_defs.h 中,在生成了表结构定义后,重新make该恢复工具</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># percona-data-recovery-tool-for-innodb-0.5</span></span><br><span class="line">make</span><br></pre></td></tr></table></figure>
<h1 id="恢复数据"><a href="#恢复数据" class="headerlink" title="恢复数据"></a>恢复数据</h1><figure class="highlight awk"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 77 通过上面 innodb_table_monitor 查询得到</span></span><br><span class="line"><span class="comment"># -5 -f: 和page_parser相同</span></span><br><span class="line"><span class="comment"># -5: row format 为 Compact</span></span><br><span class="line"><span class="comment"># -f: 要解析的文件</span></span><br><span class="line"><span class="comment"># -D: 代表恢复删除的数据页</span></span><br><span class="line">.<span class="regexp">/constraints_parser -D -5 -f pages-1610078166/</span>FIL_PAGE_INDEX<span class="regexp">/0-77/</span> > <span class="regexp">/tmp/</span>t_trans.txt</span><br></pre></td></tr></table></figure>
<p>恢复完成后生成如下语句和文件:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">LOAD DATA INFILE <span class="string">'/mysql/percona-data-recovery-tool-for-innodb-0.5/dumps/default/t_trans'</span> REPLACE INTO TABLE `t_trans` FIELDS TERMINATED BY <span class="string">'\t'</span> OPTIONALLY ENCLOSED BY <span class="string">'"'</span> LINES STARTING BY <span class="string">'t_trans\t'</span> (<span class="built_in">id</span>, transdate, mchid, refno, custacct, wtrq, djxh, name, zjlx, idno, rqrq, nsrsbh, swjylsh, payamount, zoneno, devno, <span class="built_in">type</span>, channel, posserno, rzzh, returnCode, returnMessage, jftype, reqpack, respack);</span><br></pre></td></tr></table></figure>
<p>/tmp/t_trans.txt 该文件就是我们需要 load data 的文本文件</p>
<h1 id="导入数据"><a href="#导入数据" class="headerlink" title="导入数据"></a>导入数据</h1><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">LOAD DATA <span class="keyword">LOCAL</span> INFILE <span class="string">'/tmp/t_trans.txt'</span> REPLACE <span class="keyword">INTO</span> <span class="keyword">TABLE</span> `t_trans` FIELDS TERMINATED <span class="keyword">BY</span> <span class="string">'\t'</span> OPTIONALLY ENCLOSED <span class="keyword">BY</span> <span class="string">'"'</span> LINES STARTING <span class="keyword">BY</span> <span class="string">'t_trans\t'</span> (id, transdate, mchid, refno, custacct, wtrq, djxh, name, zjlx, idno, rqrq, nsrsbh, swjylsh, payamount, zoneno, devno, type, channel, posserno, rzzh, returnCode, returnMessage, jftype, reqpack, respack);</span><br></pre></td></tr></table></figure>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>1)该恢复工具只支持innodb存储引擎,文件的格式需要为:Compact<br>2)数据被误删除后,需要尽快将保护现场,停止数据库,把idb文件拷贝出来,防止ibd文件写入数据被覆盖(笔者恢复的一个表中,由于数据删除后,表中仍有大量写入,导致大部分数据没有恢复出来);<br>3)千叮嘱万嘱咐,数据库的备份重于泰山;</p>
<h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://developer.aliyun.com/article/9059">使用Percona Data Recovery Tool for InnoDB恢复数据</a></p>
<p><a href="https://blog.51cto.com/hcymysql/1552917">无全量备份、未开启binlog日志,利用percona工具恢复delete的数据</a></p>
]]></content>
<tags>
<tag>mysql</tag>
<tag>数据恢复</tag>
</tags>
</entry>
<entry>
<title>Ticker资源泄露</title>
<url>/2020/01/30/Ticker%E8%B5%84%E6%BA%90%E6%B3%84%E9%9C%B2/</url>
<content><![CDATA[<h1 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h1><p>项目中遇到的一个问题,项目运行一段时间后CPU使用率会逐步上升到100%,导致系统异常,排查是在Ticker上使用出现错误,网上也有类似的案例:在使用Tikcer后没有释放导致。在此做下记录。</p>
<span id="more"></span>
<p>引用一段代码</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p *Pinger)</span></span> run() {</span><br><span class="line"> timeout := time.NewTicker(p.Timeout) <span class="comment">// 创建Ticker timeout</span></span><br><span class="line"> interval := time.NewTicker(p.Interval) <span class="comment">// 创建Ticker</span></span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-p.done: <span class="comment">// 正常退出,未关闭Ticker</span></span><br><span class="line"> wg.Wait()</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">case</span> <-timeout.C: <span class="comment">// 超时退出,未关闭Ticker</span></span><br><span class="line"> <span class="built_in">close</span>(p.done)</span><br><span class="line"> wg.Wait()</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">case</span> <-interval.C:</span><br><span class="line"> <span class="keyword">if</span> p.Count > <span class="number">0</span> && p.PacketsSent >= p.Count {</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> }</span><br><span class="line"> err = p.sendICMP(conn)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"FATAL: "</span>, err.Error())</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> r := <-recv:</span><br><span class="line"> err := p.processPacket(r)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"FATAL: "</span>, err.Error())</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> p.Count > <span class="number">0</span> && p.PacketsRecv >= p.Count { <span class="comment">// 退出,未关闭Ticker</span></span><br><span class="line"> <span class="built_in">close</span>(p.done)</span><br><span class="line"> wg.Wait()</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<blockquote>
<p>该段代码可以看出,这个函数是有出口的,但在出口处没有关闭Ticker,导致资源泄露。这个问题已经被修复了,可以看到修复后的局部代码如下:</p>
</blockquote>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">timeout := time.NewTicker(p.Timeout)</span><br><span class="line"><span class="keyword">defer</span> timeout.Stop() <span class="comment">// 使用defer保证Ticker最后被关闭</span></span><br><span class="line">interval := time.NewTicker(p.Interval)</span><br><span class="line"><span class="keyword">defer</span> interval.Stop() <span class="comment">// 使用defer保证Ticker最后被关闭</span></span><br></pre></td></tr></table></figure>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>有一种情况使用Ticker不主动关闭也不会造成资源泄露,比如,函数创建Ticker后就不会退出,直到进程结束。这种情况下不会持续的创建Ticker,也就不会造成资源泄露。</p>
<p>但是,不管哪种情况,创建一个Ticker后,紧跟着使用defer语句关闭Ticker总是好的习惯。因为,有可能别人无意间拷贝了你的部分代码,而忽略了关闭Ticker的动作。</p>
]]></content>
<categories>
<category>Golang</category>
</categories>
<tags>
<tag>Golang</tag>
<tag>协程</tag>
</tags>
</entry>
<entry>
<title>UNIX网络编程-卷2-源代码编译</title>
<url>/2016/01/22/UNIX%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B-%E5%8D%B72-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%BC%96%E8%AF%91/</url>
<content><![CDATA[<blockquote>
<p>最近在看《UNIX网络编程-第二版-卷2:进程间通讯》,下载了源码,在SLES SP4 32bit 无法编译,需要做些修改如下</p>
</blockquote>
<h2 id="1-下载源码"><a href="#1-下载源码" class="headerlink" title="1 下载源码:"></a>1 下载源码:</h2><p>W. Richard Stevens的主页:<a href="http://www.kohala.com/start/">http://www.kohala.com/start/</a></p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">wget http://www.kohala.com/start/unpv22e/unpv22e.tar.gz -P /usr/local/src</span><br></pre></td></tr></table></figure>
<h2 id="2-解压"><a href="#2-解压" class="headerlink" title="2 解压"></a>2 解压</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">tar xvf /usr/local/src/unpv22e.tar.gz -C /root/bin</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h2 id="3-编译库文件,及修改头文件"><a href="#3-编译库文件,及修改头文件" class="headerlink" title="3 编译库文件,及修改头文件"></a>3 编译库文件,及修改头文件</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cd</span> /root/bin/unpv22e/</span><br><span class="line">./configure</span><br></pre></td></tr></table></figure>
<h3 id="3-1-修改生成config-h文件,注释以下几行"><a href="#3-1-修改生成config-h文件,注释以下几行" class="headerlink" title="3.1 修改生成config.h文件,注释以下几行"></a>3.1 修改生成config.h文件,注释以下几行</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">vi config.h</span><br><span class="line"><span class="number">56</span> <span class="comment">// #define uint8_t unsigned char</span></span><br><span class="line"><span class="number">57</span> <span class="comment">// #define uint16_t unsigned short</span></span><br><span class="line"><span class="number">58</span> <span class="comment">// #define uint32_t unsigned int</span></span><br></pre></td></tr></table></figure>
<h3 id="3-2-添加MSG-R和MSG-W定义"><a href="#3-2-添加MSG-R和MSG-W定义" class="headerlink" title="3.2 添加MSG_R和MSG_W定义"></a>3.2 添加MSG_R和MSG_W定义</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">vi config.h</span><br><span class="line"><span class="number">67</span> <span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">long</span> <span class="type">ulong_t</span>;</span><br><span class="line"><span class="number">68</span> <span class="meta">#<span class="keyword">define</span> MSG_R 0400</span></span><br><span class="line"><span class="number">69</span> <span class="meta">#<span class="keyword">define</span> MSG_W 0200</span></span><br></pre></td></tr></table></figure>
<h3 id="3-3-添加-GNU-SOURCE定义"><a href="#3-3-添加-GNU-SOURCE定义" class="headerlink" title="3.3 添加_GNU_SOURCE定义"></a>3.3 添加_GNU_SOURCE定义</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">vi config.h</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _GNU_SOURCE</span></span><br></pre></td></tr></table></figure>
<h3 id="3-4-编译warpunix-c,使用mkstemp函数替换mktemp函数"><a href="#3-4-编译warpunix-c,使用mkstemp函数替换mktemp函数" class="headerlink" title="3.4 编译warpunix.c,使用mkstemp函数替换mktemp函数"></a>3.4 编译warpunix.c,使用mkstemp函数替换mktemp函数</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">cd lib</span><br><span class="line"><span class="number">181</span> <span class="type">void</span></span><br><span class="line"><span class="number">182</span> Mktemp(<span class="type">char</span> *template)</span><br><span class="line"><span class="number">183</span> {</span><br><span class="line"><span class="number">184</span> <span class="keyword">if</span> (mkstemp(template) == <span class="literal">NULL</span> || template[<span class="number">0</span>] == <span class="number">0</span>)</span><br><span class="line"><span class="number">185</span> err_quit(<span class="string">"mktemp error"</span>);</span><br><span class="line"><span class="number">186</span> }</span><br></pre></td></tr></table></figure>
<h3 id="3-5-编译生成libunpipc-a"><a href="#3-5-编译生成libunpipc-a" class="headerlink" title="3.5 编译生成libunpipc.a"></a>3.5 编译生成libunpipc.a</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cd</span> lib</span><br><span class="line">make</span><br></pre></td></tr></table></figure>
<h2 id="4-构建自己的编写代码的目录"><a href="#4-构建自己的编写代码的目录" class="headerlink" title="4 构建自己的编写代码的目录"></a>4 构建自己的编写代码的目录</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /root/bin/unpv2</span><br><span class="line"><span class="built_in">cd</span> -</span><br><span class="line"><span class="built_in">cp</span> /root/bin/unpv22e/libunpipc.a /root/bin/unpv22e/config.h /root/bin/unpv22e/Make.defines .</span><br></pre></td></tr></table></figure>
<h2 id="5-编译各个目录自己的文件"><a href="#5-编译各个目录自己的文件" class="headerlink" title="5 编译各个目录自己的文件"></a>5 编译各个目录自己的文件</h2><p>复制各个子目录下得*.h头文件和Makfile文件,然后执行</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cp</span> /root/bin/unpv22e/dir/*.h /root/bin/unpv22e/dir/Makefile /root/bin/unpv2</span><br><span class="line">make filename</span><br></pre></td></tr></table></figure>
<p>即可编译各个子目录下的代码</p>
]]></content>
<tags>
<tag>UNIX</tag>
<tag>Source</tag>
</tags>
</entry>
<entry>
<title>在Mac上安装db2 driver</title>
<url>/2020/12/28/db2-driver-on-macos/</url>
<content><![CDATA[<h1 id="选择版本"><a href="#选择版本" class="headerlink" title="选择版本"></a>选择版本</h1><p><a href="https://www.ibm.com/support/pages/node/323035">此处选择</a></p>
<p>11 版本开始有 MacOS 版本支持,需下载11版本。<br>选择 【IBM Data Server Driver Package】</p>
<p><img data-src="https://i.loli.net/2020/12/28/neVHN5uGARgD9bM.png" alt="IBM Data Server Driver Package"></p>
<span id="more"></span>
<h1 id="安装或更新"><a href="#安装或更新" class="headerlink" title="安装或更新"></a>安装或更新</h1><p><img data-src="https://i.loli.net/2020/12/28/TVkvqnWQhU7BjzL.png" alt="安装或更新步骤"></p>
<ul>
<li>通过双击 v11.5.5_macos_dsdriver.dmg 文件,装载磁盘映像</li>
<li>这将打开新的 Finder 窗口,其中会显示该磁盘映像的内容</li>
<li>如果 Finder 窗口未打开,请双击桌面上的 dsvriver 图标</li>
<li>在 Finder 窗口中,双击 installDSDriver.sh 文件</li>
<li>驱动程序包将安装在以下缺省位置:/Applications/dsdriver</li>
<li>配置环境变量 vi ~/.zshrc<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># db2</span><br><span class="line">if [ -f /Applications/dsdriver/db2profile ]; then</span><br><span class="line"> . /Applications/dsdriver/db2profile</span><br><span class="line">fi</span><br></pre></td></tr></table></figure></li>
</ul>
<h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><ul>
<li><a href="https://www.ibm.com/support/knowledgecenter/zh/SS6NHC/com.ibm.swg.im.dashdb.doc/connecting/connect_driver_package_config.html">配置本地环境以连接到 Db2 数据库</a></li>
</ul>
]]></content>
<tags>
<tag>db2</tag>
<tag>MacOS</tag>
</tags>
</entry>
<entry>
<title>docker 时区调整方案</title>
<url>/2021/01/11/docker-timezone-localtime/</url>
<content><![CDATA[<h1 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h1><p>使用 docker 经常会遇到时区问题。<br>基本上都是UTC时间,默认时区为0时区,</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run --name <span class="built_in">test</span> --<span class="built_in">rm</span> -ti alpine /bin/sh</span><br><span class="line"><span class="comment"># date</span></span><br><span class="line">Mon Jan 11 02:52:18 UTC 2021</span><br></pre></td></tr></table></figure>
<p>而我们主要用的是 CST 时间,北京时间,位于东八区。时区代号: Asia/Shanghai</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run --name <span class="built_in">test</span> --<span class="built_in">rm</span> -ti -v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro alpine /bin/sh</span><br><span class="line"><span class="comment"># date</span></span><br><span class="line">Mon Jan 11 10:53:06 CST 2021</span><br></pre></td></tr></table></figure>
<p>下面汇总几个方案:</p>
<span id="more"></span>
<h1 id="方案"><a href="#方案" class="headerlink" title="方案"></a>方案</h1><h2 id="运行容器时调整时区"><a href="#运行容器时调整时区" class="headerlink" title="运行容器时调整时区"></a>运行容器时调整时区</h2><p>在 Linux 系统中,控制时区和时间的主要是两个地方:</p>
<figure class="highlight awk"><table><tr><td class="code"><pre><span class="line"><span class="regexp">/etc/</span>timezone 主要代表当前时区设置,一般链接指向<span class="regexp">/usr/</span>share/zoneinfo目录下的具体时区。</span><br><span class="line"><span class="regexp">/etc/</span>localtime 主要代表当前时区设置下的本地时间。</span><br></pre></td></tr></table></figure>
<h3 id="通用-docker-时区修改方案"><a href="#通用-docker-时区修改方案" class="headerlink" title="通用 docker 时区修改方案"></a>通用 docker 时区修改方案</h3><p>当宿主机为 Linux 系统时,我们可以直接将宿主机上的<code>/etc/timezone</code>和<code>/etc/localtime</code>挂载到容器中,这样可以保持容器和宿主机时区和时间一致。</p>
<figure class="highlight awk"><table><tr><td class="code"><pre><span class="line">-v <span class="regexp">/etc/</span>timezone:<span class="regexp">/etc/</span>timezone:ro -v <span class="regexp">/etc/</span>localtime:<span class="regexp">/etc/</span>localtime:ro</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run --name <span class="built_in">test</span> --<span class="built_in">rm</span> -ti -v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro alpine /bin/sh</span><br><span class="line"><span class="comment"># date</span></span><br><span class="line">Mon Jan 11 10:58:31 CST 2021</span><br></pre></td></tr></table></figure>
<h3 id="通过传递环境变量改变容器时区"><a href="#通过传递环境变量改变容器时区" class="headerlink" title="通过传递环境变量改变容器时区"></a>通过传递环境变量改变容器时区</h3><ul>
<li>适用于基于 Debian 基础镜像, CentOS 基础镜像 制作的 Docker 镜像</li>
<li>不适用于基于 Alpine 基础镜像, Ubuntu 基础镜像 制作的 Docker 镜像</li>
</ul>
<p>对于基于 Debian 基础镜像,CentOS 基础镜像制作的 Docker 镜像,在运行 Docker 容器时,传递环境变量-e TZ=Asia/Shanghai进去,能修改 docker 容器时区。</p>
<figure class="highlight routeros"><table><tr><td class="code"><pre><span class="line">-e <span class="attribute">TZ</span>=Asia/Shanghai</span><br></pre></td></tr></table></figure>
<p>使用示例如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run --name <span class="built_in">test</span> -e TZ=Asia/Shanghai --<span class="built_in">rm</span> -ti debian /bin/bash</span><br><span class="line"><span class="comment"># date</span></span><br><span class="line">Mon Jan 11 11:00:52 CST 2021</span><br></pre></td></tr></table></figure>
<h2 id="制作-Docker-镜像时调整时区"><a href="#制作-Docker-镜像时调整时区" class="headerlink" title="制作 Docker 镜像时调整时区"></a>制作 Docker 镜像时调整时区</h2><p>通过编写 Dockerfile,构建自己的 Docker 镜像,可以永久解决时区问题。</p>
<h3 id="Alpine"><a href="#Alpine" class="headerlink" title="Alpine"></a>Alpine</h3><p>根据《Setting the timezone》提示,我们可以将以下代码添加到 Dockerfile 中:</p>
<figure class="highlight dockerfile"><table><tr><td class="code"><pre><span class="line"><span class="keyword">ENV</span> TZ Asia/Shanghai</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apk add tzdata && <span class="built_in">cp</span> /usr/share/zoneinfo/<span class="variable">${TZ}</span> /etc/localtime \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">echo</span> <span class="variable">${TZ}</span> > /etc/timezone \</span></span><br><span class="line"><span class="language-bash"> && apk del tzdata</span></span><br></pre></td></tr></table></figure>
<h3 id="Debian"><a href="#Debian" class="headerlink" title="Debian"></a>Debian</h3><p>Debian 基础镜像 中已经安装了 tzdata 包,我们可以将以下代码添加到 Dockerfile 中:</p>
<figure class="highlight dockerfile"><table><tr><td class="code"><pre><span class="line"><span class="keyword">ENV</span> TZ=Asia/Shanghai \</span><br><span class="line"> DEBIAN_FRONTEND=noninteractive</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> <span class="built_in">ln</span> -fs /usr/share/zoneinfo/<span class="variable">${TZ}</span> /etc/localtime \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">echo</span> <span class="variable">${TZ}</span> > /etc/timezone \</span></span><br><span class="line"><span class="language-bash"> && dpkg-reconfigure --frontend noninteractive tzdata \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">rm</span> -rf /var/lib/apt/lists/*</span></span><br></pre></td></tr></table></figure>
<h3 id="Ubuntu"><a href="#Ubuntu" class="headerlink" title="Ubuntu"></a>Ubuntu</h3><p>Ubuntu 基础镜像中没有安装了 tzdata 包,因此我们需要先安装 tzdata 包。</p>
<figure class="highlight dockerfile"><table><tr><td class="code"><pre><span class="line"><span class="keyword">ENV</span> TZ=Asia/Shanghai \</span><br><span class="line"> DEBIAN_FRONTEND=noninteractive</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apt update \</span></span><br><span class="line"><span class="language-bash"> && apt install -y tzdata \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">ln</span> -fs /usr/share/zoneinfo/<span class="variable">${TZ}</span> /etc/localtime \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">echo</span> <span class="variable">${TZ}</span> > /etc/timezone \</span></span><br><span class="line"><span class="language-bash"> && dpkg-reconfigure --frontend noninteractive tzdata \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">rm</span> -rf /var/lib/apt/lists/*</span></span><br></pre></td></tr></table></figure>
<h3 id="CentOS"><a href="#CentOS" class="headerlink" title="CentOS"></a>CentOS</h3><p>CentOS 基础镜像 中已经安装了 tzdata 包,我们可以将以下代码添加到 Dockerfile 中。</p>
<figure class="highlight dockerfile"><table><tr><td class="code"><pre><span class="line"><span class="keyword">ENV</span> TZ Asia/Shanghai</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> <span class="built_in">ln</span> -fs /usr/share/zoneinfo/<span class="variable">${TZ}</span> /etc/localtime \</span></span><br><span class="line"><span class="language-bash"> && <span class="built_in">echo</span> <span class="variable">${TZ}</span> > /etc/timezone</span></span><br></pre></td></tr></table></figure>
<h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p>转载自<br><a href="https://www.itcoder.tech/posts/docker-timezone-localtime/">https://www.itcoder.tech/posts/docker-timezone-localtime/</a></p>
]]></content>
<tags>
<tag>docker</tag>
<tag>timezone</tag>
</tags>
</entry>
<entry>
<title>docker安装DB2</title>
<url>/2019/10/23/docker%E5%AE%89%E8%A3%85DB2/</url>
<content><![CDATA[<h1 id="拉取镜像"><a href="#拉取镜像" class="headerlink" title="拉取镜像"></a>拉取镜像</h1><p><a href="https://hub.docker.com/r/ibmcom/db2">https://hub.docker.com/r/ibmcom/db2</a></p>
<blockquote>
</blockquote>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker pull ibmcom/db2</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h1 id="数据持久化"><a href="#数据持久化" class="headerlink" title="数据持久化"></a>数据持久化</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker volume create db2-11.5</span><br></pre></td></tr></table></figure>
<h1 id="启动容器"><a href="#启动容器" class="headerlink" title="启动容器"></a>启动容器</h1><p>根据实际情况,执行设置 DB2INST1_PASSWORD、DBNAME、ENABLE_ORACLE_COMPATIBILITY、端口映射、及目录映射或者持久化</p>
<figure class="highlight routeros"><table><tr><td class="code"><pre><span class="line">docker <span class="built_in">run</span> -itd --name db2_11.5 <span class="attribute">--privileged</span>=<span class="literal">true</span> -p 50000:50000 \</span><br><span class="line"> -e <span class="attribute">LICENSE</span>=accept \</span><br><span class="line"> -e <span class="attribute">DB2INST1_PASSWORD</span>=BxFgy1ybgGl4nT7m6lEV9KtcZdpCiKMg \</span><br><span class="line"> -e <span class="attribute">DBNAME</span>=pay \</span><br><span class="line"> -e <span class="attribute">ENABLE_ORACLE_COMPATIBILITY</span>=<span class="literal">true</span> \</span><br><span class="line"> -v db2-11.5:/database ibmcom/db2</span><br></pre></td></tr></table></figure>
<h1 id="进入容器"><a href="#进入容器" class="headerlink" title="进入容器"></a>进入容器</h1><blockquote>
<p>docker exec -ti db2_11.5 bash -c “su - root”</p>
</blockquote>
<h2 id="新建用户"><a href="#新建用户" class="headerlink" title="新建用户"></a>新建用户</h2><figure class="highlight ebnf"><table><tr><td class="code"><pre><span class="line"><span class="attribute">useradd payplus</span></span><br><span class="line"><span class="attribute">passwd payplus</span> </span><br></pre></td></tr></table></figure>
<h2 id="创建schema"><a href="#创建schema" class="headerlink" title="创建schema"></a>创建schema</h2><blockquote>
<p>su - db2inst1</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 登录</span></span><br><span class="line">db2 connect to daifu user db2inst1 using BxFgy1ybgGl4nT7m6lEV9KtcZdpCiKMg</span><br><span class="line"><span class="comment"># 创建模式</span></span><br><span class="line">db2 create schema payplus authorization password </span><br><span class="line"><span class="comment"># 给用户赋权限</span></span><br><span class="line">db2 grant dbadm on database to user payplus </span><br></pre></td></tr></table></figure>
<h2 id="创建数据库例子"><a href="#创建数据库例子" class="headerlink" title="创建数据库例子"></a>创建数据库例子</h2><figure class="highlight n1ql"><table><tr><td class="code"><pre><span class="line">db2 <span class="keyword">create</span> db daifu <span class="keyword">on</span> /<span class="keyword">database</span>/daifu <span class="keyword">using</span> codeset GBK territory CN pagesize <span class="number">32</span> k</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>DB2</tag>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>docker搭建oracle: [helowin/oracle_11g]</title>
<url>/2019/10/28/docker%E6%90%AD%E5%BB%BAoracle-helowin-oracle-11g/</url>
<content><![CDATA[<h1 id="拉取镜像"><a href="#拉取镜像" class="headerlink" title="拉取镜像"></a>拉取镜像</h1><blockquote>
<p><a href="https://cr.console.aliyun.com/images/cn-hangzhou/oracle11-helowin/oracle11-helowin/detail?accounttraceid=24cfa63bff0f483db7d63268686a7a32yabl">https://cr.console.aliyun.com/images/cn-hangzhou/oracle11-helowin/oracle11-helowin/detail?accounttraceid=24cfa63bff0f483db7d63268686a7a32yabl</a></p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g</span><br><span class="line">docker tag registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g oracle_11g</span><br><span class="line">docker rmi registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h1 id="启动容器"><a href="#启动容器" class="headerlink" title="启动容器"></a>启动容器</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run -d -p 1521:1521 --name oracle_11g oracle_11g</span><br></pre></td></tr></table></figure>
<h1 id="数据持久化"><a href="#数据持久化" class="headerlink" title="数据持久化"></a>数据持久化</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker volume create oracle-11g</span><br><span class="line">docker run -d -p 1521:1521 --name oracle_11g -v oracle-11g:/home/oracle/app/oracle/oradata/ oracle_11g </span><br></pre></td></tr></table></figure>
<h1 id="oracle默认参数"><a href="#oracle默认参数" class="headerlink" title="oracle默认参数"></a>oracle默认参数</h1><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">hostname:</span> <span class="string">localhost</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">1521</span></span><br><span class="line"><span class="attr">sid:</span> <span class="string">helowin</span></span><br><span class="line"><span class="attr">username:</span> <span class="string">system</span></span><br><span class="line"><span class="attr">password:</span> <span class="string">helowin</span></span><br></pre></td></tr></table></figure>
<h1 id="修改默认参数"><a href="#修改默认参数" class="headerlink" title="修改默认参数"></a>修改默认参数</h1><h2 id="进入容器"><a href="#进入容器" class="headerlink" title="进入容器"></a>进入容器</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker <span class="built_in">exec</span> -it oracle_11g bash -c <span class="string">"su - root"</span></span><br></pre></td></tr></table></figure>
<h2 id="修改-x2F-etc-x2F-profile"><a href="#修改-x2F-etc-x2F-profile" class="headerlink" title="修改 /etc/profile"></a>修改 /etc/profile</h2><blockquote>
<p>vi /etc/profile</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># add</span></span><br><span class="line"><span class="built_in">export</span> ORACLE_HOME=/home/oracle/app/oracle/product/11.2.0/dbhome_2</span><br><span class="line"><span class="built_in">export</span> ORACLE_SID=helowin</span><br><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$ORACLE_HOME</span>/bin:<span class="variable">$PATH</span></span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 执行</span></span><br><span class="line"><span class="built_in">source</span> /etc/profile</span><br><span class="line"><span class="built_in">ln</span> -s <span class="variable">$ORACLE_HOME</span>/bin/sqlplus /usr/bin</span><br></pre></td></tr></table></figure>
<h2 id="登录oracle数据库,修改密码"><a href="#登录oracle数据库,修改密码" class="headerlink" title="登录oracle数据库,修改密码"></a>登录oracle数据库,修改密码</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">su oracle</span><br><span class="line">sqlplus /nolog</span><br><span class="line">conn /as sysdba</span><br></pre></td></tr></table></figure>
<figure class="highlight pgsql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">user</span> <span class="keyword">system</span> identified <span class="keyword">by</span> <span class="keyword">password</span>;</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">user</span> sys identified <span class="keyword">by</span> <span class="keyword">password</span>;</span><br><span class="line"><span class="keyword">ALTER</span> PROFILE <span class="keyword">DEFAULT</span> <span class="keyword">LIMIT</span> PASSWORD_LIFE_TIME UNLIMITED;</span><br></pre></td></tr></table></figure>
<h2 id="修改字符集"><a href="#修改字符集" class="headerlink" title="修改字符集"></a>修改字符集</h2><figure class="highlight pgsql"><table><tr><td class="code"><pre><span class="line"># 默认字符集 AL32UTF8 <span class="comment">--> ZHS16GBK</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">SQL</span>> conn /<span class="keyword">as</span> sysdba</span><br><span class="line"><span class="keyword">SQL</span>> shutdown <span class="keyword">immediate</span>;</span><br><span class="line"><span class="keyword">SQL</span>> startup mount</span><br><span class="line"></span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">ALTER</span> <span class="keyword">SYSTEM</span> <span class="keyword">ENABLE</span> RESTRICTED <span class="keyword">SESSION</span>;</span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">ALTER</span> <span class="keyword">SYSTEM</span> <span class="keyword">SET</span> AQ_TM_PROCESSES=<span class="number">0</span>;</span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">alter</span> <span class="keyword">database</span> <span class="keyword">open</span>;</span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">ALTER</span> <span class="keyword">DATABASE</span> <span class="type">CHARACTER</span> <span class="keyword">SET</span> ZHS16GBK;</span><br><span class="line">ERROR at <span class="type">line</span> <span class="number">1</span>:</span><br><span class="line">ORA<span class="number">-12712</span>: <span class="built_in">new</span> <span class="type">character</span> <span class="keyword">set</span> must be a superset <span class="keyword">of</span> <span class="built_in">old</span> <span class="type">character</span> <span class="keyword">set</span></span><br><span class="line">提示我们的字符集:新字符集必须为旧字符集的超集,这时我们可以跳过超集的检查做更改(但是已有数据这样做会乱码):</span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">ALTER</span> <span class="keyword">DATABASE</span> <span class="type">character</span> <span class="keyword">set</span> INTERNAL_USE ZHS16GBK;</span><br><span class="line"></span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">select</span> * <span class="keyword">from</span> v$nls_parameters;</span><br><span class="line"></span><br><span class="line"><span class="keyword">SQL</span>> shutdown <span class="keyword">immediate</span>;</span><br><span class="line"><span class="keyword">SQL</span>> startup</span><br><span class="line"></span><br><span class="line">至此,字符集的修改就完成了,我们可以通过输入命令验证一下,其结果已经变成了ZHS16GBK了。</span><br><span class="line"><span class="keyword">SQL</span>> <span class="keyword">select</span> userenv(<span class="string">'language'</span>) <span class="keyword">from</span> dual;</span><br></pre></td></tr></table></figure>
<h2 id="创建普通用户"><a href="#创建普通用户" class="headerlink" title="创建普通用户"></a>创建普通用户</h2><h3 id="用system登录"><a href="#用system登录" class="headerlink" title="用system登录"></a>用system登录</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">user</span> test identified <span class="keyword">by</span> <span class="number">123456</span>;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">connect</span>, resource, dba <span class="keyword">to</span> test;</span><br></pre></td></tr></table></figure>
<h3 id="使用普通用户登陆"><a href="#使用普通用户登陆" class="headerlink" title="使用普通用户登陆"></a>使用普通用户登陆</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- 查询数据库名, 表名</span></span><br><span class="line"><span class="keyword">select</span> name <span class="keyword">from</span> v$database;</span><br><span class="line"><span class="keyword">select</span> table_name <span class="keyword">from</span> all_tables <span class="comment">-- where table_name like '%PER' ; --where ROWNUM <10 ;</span></span><br></pre></td></tr></table></figure>
<h1 id="配置nginx:-反向代理oracle服务"><a href="#配置nginx:-反向代理oracle服务" class="headerlink" title="配置nginx: 反向代理oracle服务"></a>配置nginx: 反向代理oracle服务</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># nginx 反向代理</span></span><br><span class="line">stream { </span><br><span class="line"> upstream oracle{</span><br><span class="line"> <span class="built_in">hash</span> <span class="variable">$remote_addr</span> consistent;</span><br><span class="line"> server 192.168.99.101:1521 max_fails=3 fail_timeout=30s;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> server {</span><br><span class="line"> listen 1521;</span><br><span class="line"> proxy_connect_timeout 3000s;</span><br><span class="line"> proxy_timeout 6000s;</span><br><span class="line"> proxy_pass oracle;</span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<ul>
<li>1、网上还有针对sath89/oracle-xe-11g这个镜像的教程,需要注意的是:Oracle快捷版(Oracle XE)是一款基于 Oracle 11g 第2版代码库的小型入门级数据库。Oracle Database XE对安装主机的规模和CPU数量不作限制(每台计算机一个数据库), 但XE将最多存储11GB的用户数据,同时最多使用1GB内存和主机上的一个CPU。</li>
<li>2、如果仅作测试也可以安装这个镜像,镜像大约700兆。而教程中的镜像大约近7G。</li>
<li>3、<a href="https://github.com/wnameless/docker-oracle-xe-11g">https://github.com/wnameless/docker-oracle-xe-11g</a></li>
</ul>
</blockquote>
]]></content>
<tags>
<tag>docker</tag>
<tag>oracle</tag>
</tags>
</entry>
<entry>
<title>docker镜像自动编译程序</title>
<url>/2019/10/12/docker%E9%95%9C%E5%83%8F%E8%87%AA%E5%8A%A8%E7%BC%96%E8%AF%91%E7%A8%8B%E5%BA%8F/</url>
<content><![CDATA[<p>使用docker创建C++编译环境,自动创建二进制执行程序。</p>
<span id="more"></span>
<h1 id="docker-run-一次执行多条命令的方法"><a href="#docker-run-一次执行多条命令的方法" class="headerlink" title="docker run 一次执行多条命令的方法"></a>docker run 一次执行多条命令的方法</h1><p>有时在启动docker container时需要同时运行不止一条命令,这时在command处使用:sh -c ‘ cmd1 && cmd2 ‘即可。</p>
<p>如,docker run -it myimage sh -c ‘service mysql start && python test.py’</p>
<h1 id="编译-acl-rpm-包"><a href="#编译-acl-rpm-包" class="headerlink" title="编译 acl rpm 包"></a>编译 acl rpm 包</h1><h2 id="方法一:-创建-acl-编译镜像"><a href="#方法一:-创建-acl-编译镜像" class="headerlink" title="方法一: 创建 acl 编译镜像"></a>方法一: 创建 acl 编译镜像</h2><h3 id="编写-Dokcerfile"><a href="#编写-Dokcerfile" class="headerlink" title="编写 Dokcerfile"></a>编写 Dokcerfile</h3><figure class="highlight routeros"><table><tr><td class="code"><pre><span class="line"><span class="keyword">FROM</span> centos:6.10</span><br><span class="line"><span class="built_in">RUN</span> yum -y install gcc gcc-c++ zlib-devel git rpm-build</span><br></pre></td></tr></table></figure>
<h3 id="创建镜像"><a href="#创建镜像" class="headerlink" title="创建镜像"></a>创建镜像</h3><figure class="highlight mipsasm"><table><tr><td class="code"><pre><span class="line">docker <span class="keyword">build </span>-t rpm-<span class="keyword">build-image </span>.</span><br></pre></td></tr></table></figure>
<h3 id="创建容器编译"><a href="#创建容器编译" class="headerlink" title="创建容器编译"></a>创建容器编译</h3><figure class="highlight awk"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 本地拉取</span></span><br><span class="line">git clone https:<span class="regexp">//gi</span>tee.com<span class="regexp">/acl-dev/</span>acl.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译宿主机代码</span></span><br><span class="line">docker run --rm -v <span class="string">"$(pwd)"</span>:<span class="regexp">/usr/</span>app<span class="regexp">/ -w /u</span>sr<span class="regexp">/app/</span>acl/packaging rpm-build-image make</span><br></pre></td></tr></table></figure>
<p>or</p>
<figure class="highlight elixir"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 保留git仓库</span></span><br><span class="line">docker run --rm -v <span class="string">"$(pwd)"</span><span class="symbol">:/usr/app/</span> -w /usr/app rpm-build-image sh -c <span class="string">'git clone https://gitee.com/acl-dev/acl.git && cd acl/packaging && make '</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 只保留rpm包</span></span><br><span class="line">docker run --rm -v <span class="string">"$(pwd)"</span><span class="symbol">:/usr/app/</span> rpm-build-image sh -c <span class="string">'git clone https://gitee.com/acl-dev/acl.git && cd acl/packaging && make && mv x86_64 /usr/app'</span></span><br></pre></td></tr></table></figure>
<h2 id="方法二-不创建镜像,直接使用临时容器"><a href="#方法二-不创建镜像,直接使用临时容器" class="headerlink" title="方法二: 不创建镜像,直接使用临时容器"></a>方法二: 不创建镜像,直接使用临时容器</h2><figure class="highlight stata"><table><tr><td class="code"><pre><span class="line">docker <span class="keyword">run</span> --<span class="keyword">rm</span> -v <span class="string">"$(pwd)"</span>:/usr/<span class="keyword">app</span>/ centos:6.10 <span class="keyword">su</span> -c 'yum -y install gcc gcc-c++ zlib-devel git rpm-build \</span><br><span class="line">&& git clone https:<span class="comment">//gitee.com/acl-dev/acl.git \</span></span><br><span class="line">&& <span class="keyword">cd</span> acl/packaging \</span><br><span class="line">&& make \</span><br><span class="line">&& mv x86_64 /usr/<span class="keyword">app</span>'</span><br></pre></td></tr></table></figure>
<h1 id="CentOS-nginx-添加-ngx-http-proxy-connect-module"><a href="#CentOS-nginx-添加-ngx-http-proxy-connect-module" class="headerlink" title="CentOS nginx 添加 ngx_http_proxy_connect_module"></a>CentOS nginx 添加 ngx_http_proxy_connect_module</h1><figure class="highlight awk"><table><tr><td class="code"><pre><span class="line">docker run -it --rm -v <span class="string">"$(pwd)"</span>:<span class="regexp">/usr/</span>app<span class="regexp">/nginx -w /u</span>sr<span class="regexp">/app/</span>nginx centos:<span class="number">7.9</span>.<span class="number">2009</span> bash -c <span class="string">'/usr/app/nginx/build-env.sh'</span></span><br></pre></td></tr></table></figure>
<h2 id="build-env-sh"><a href="#build-env-sh" class="headerlink" title="build-env.sh"></a>build-env.sh</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新系统软件到最新</span></span><br><span class="line">yum -y update</span><br><span class="line"><span class="comment"># 安装编译环境</span></span><br><span class="line">yum -y install gcc gcc-c++ automake autoconf libtool make rpm-build rpmdevtools rpmlint wget git</span><br><span class="line"><span class="comment"># 安装Nginx所依赖的包</span></span><br><span class="line">yum -y install -y openssl openssl-devel zlib-devel pcre-devel gd-devel</span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建用户rpmbuild</span></span><br><span class="line">name=rpmbuild</span><br><span class="line">useradd <span class="variable">$name</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="variable">$name</span> ALL=(ALL) NOPASSWD:ALL"</span> >> /etc/sudoers</span><br><span class="line">su - <span class="variable">$name</span> -c <span class="string">'/usr/app/nginx/build.sh'</span></span><br></pre></td></tr></table></figure>
<h2 id="build-sh"><a href="#build-sh" class="headerlink" title="build.sh"></a>build.sh</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建rpm目录</span></span><br><span class="line">rpmdev-setuptree</span><br><span class="line"><span class="comment"># 在rpmbuild的home目录下面有了这几个目录</span></span><br><span class="line"><span class="comment"># BUILD BUILDROOT RPMS SOURCES SPECS SRPMS</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载源码</span></span><br><span class="line"><span class="built_in">cd</span> ~/rpmbuild/SOURCES</span><br><span class="line">wget http://nginx.org/packages/centos/7/SRPMS/nginx-1.18.0-2.el7.ngx.src.rpm</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压rpm</span></span><br><span class="line">rpm2cpio nginx-1.18.0-2.el7.ngx.src.rpm |cpio -dvi</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载补丁包</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/chobits/ngx_http_proxy_connect_module.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打补丁</span></span><br><span class="line">tar xfz nginx-1.18.0.tar.gz</span><br><span class="line"><span class="built_in">cd</span> nginx-1.18.0</span><br><span class="line">patch -p1 < ../ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_1018.patch</span><br><span class="line"><span class="built_in">cd</span> ..</span><br><span class="line">tar cfz nginx-1.18.0.tar.gz nginx-1.18.0</span><br><span class="line"><span class="built_in">rm</span> -r nginx-1.18.0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改 configure 编译参数</span></span><br><span class="line">sed -i <span class="string">'/configure %{BASE_CONFIGURE_ARGS}/a\ --add-module=/home/rpmbuild/rpmbuild/SOURCES/ngx_http_proxy_connect_module \\'</span> nginx.spec</span><br><span class="line"></span><br><span class="line"><span class="comment"># 制作二进制包</span></span><br><span class="line">rpmbuild -bb nginx.spec </span><br><span class="line"><span class="comment"># 表示既制作二进制包又制作src格式包</span></span><br><span class="line"><span class="comment"># rpmbuild -ba nginx.spec </span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cp</span> ../RPMS/x86_64/* /usr/app/nginx</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag>docker</tag>
<tag>acl</tag>
<tag>nginx</tag>
</tags>
</entry>
<entry>
<title>getopt和getopt_long函数</title>
<url>/2016/02/01/getopt%E5%92%8Cgetopt-long%E5%87%BD%E6%95%B0/</url>
<content><![CDATA[<blockquote>
<p>在linux下写程序时常常需要对命令行参数进行处理。下面比对下getopt、getopt_long、getopt_long_only。</p>
</blockquote>
<span id="more"></span>
<h3 id="头文件及函数声明"><a href="#头文件及函数声明" class="headerlink" title="头文件及函数声明"></a>头文件及函数声明</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">getopt</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="type">char</span> *optstring)</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="type">char</span> *optarg;</span><br><span class="line"><span class="keyword">extern</span> <span class="type">int</span> optind, opterr, optopt;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><getopt.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">getopt_long</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="type">char</span> *optstring,</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="keyword">struct</span> option *longopts, <span class="type">int</span> *longindex)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">getopt_long_only</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="type">char</span> *optstring,</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="keyword">struct</span> option *longopts, <span class="type">int</span> *longindex)</span>;</span><br></pre></td></tr></table></figure>
<p><strong>从最简单的getopt讲起,getopt函数的前两个参数,就是main函数的argc和argv,这两者直接传入即可,要考虑的就只剩下第三个参数。</strong></p>
<p>optstring的格式举例说明比较方便,例如:</p>
<pre><code>char *optstring = "abcd:";
</code></pre>
<p>上面这个optstring在传入之后,getopt函数将依次检查命令行是否指定了 -a, -b, -c及 -d(这需要多次调用getopt函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)</p>
<p>最后一个参数d后面带有冒号: 表示参数d是可以指定值的,如 -d 100 或 -d user。</p>
<p>optind表示的是下一个将被处理到的参数在argv中的下标值。</p>
<p>如果指定opterr = 0的话,在getopt、getopt_long、getopt_long_only遇到错误将不会输出错误信息到标准输出流。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> opt;</span><br><span class="line"> <span class="type">char</span> *optstring = <span class="string">"a:b:c:d"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> ((opt = getopt(argc, argv, optstring)) != <span class="number">-1</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"opt = %c\n"</span>, opt);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"optarg = %s\n"</span>, optarg);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"optind = %d\n"</span>, optind);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"argv[optind - 1] = %s\n\n"</span>, argv[optind - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>编译上述程序并运行,有如下结果:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">cashey@ubuntu:~/Desktop/getopt$ ./test_getopt -a 100 -b 200 -c admin -d</span><br><span class="line">opt = a</span><br><span class="line">optarg = 100</span><br><span class="line">optind = 3</span><br><span class="line">argv[optind - 1] = 100</span><br><span class="line"></span><br><span class="line">opt = b</span><br><span class="line">optarg = 200</span><br><span class="line">optind = 5</span><br><span class="line">argv[optind - 1] = 200</span><br><span class="line"></span><br><span class="line">opt = c</span><br><span class="line">optarg = admin</span><br><span class="line">optind = 7</span><br><span class="line">argv[optind - 1] = admin</span><br><span class="line"></span><br><span class="line">opt = d</span><br><span class="line">optarg = (null)</span><br><span class="line">optind = 8</span><br><span class="line">argv[optind - 1] = -d</span><br></pre></td></tr></table></figure>
<p>** 下面来讲getopt_long函数,getopt_long函数包含了getopt函数的功能,并且还可以指定“长参数”(或者说长选项),与getopt函数对比,getopt_long比其多了两个参数: **</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">getopt</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="type">char</span> *optstring)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">getopt_long</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="type">char</span> *optstring,</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="keyword">struct</span> option *longopts, <span class="type">int</span> *longindex)</span>;</span><br></pre></td></tr></table></figure>
<p>在这里,longopts指向的是一个由option结构体组成的数组,那个数组的每个元素,指明了一个“长参数”(即形如–name的参数)名称和性质:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">option</span> {</span></span><br><span class="line"> <span class="type">const</span> <span class="type">char</span> *name;</span><br><span class="line"> <span class="type">int</span> has_arg;</span><br><span class="line"> <span class="type">int</span> *flag;</span><br><span class="line"> <span class="type">int</span> val;</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<pre><code> name 是参数的名称
has_arg 指明是否带参数值,其数值可选:
no_argument (即 0) 表明这个长参数不带参数(即不带数值,如:--name)
required_argument (即 1) 表明这个长参数必须带参数(即必须带数值,如:--name Bob)
optional_argument(即2)表明这个长参数后面带的参数是可选的,(即--name和--name Bob均可)
flag 当这个指针为空的时候,函数直接将val的数值从getopt_long的返回值返回出去,当它非空时,
val的值会被赋到flag指向的整型数中,而函数返回值为0
val 用于指定函数找到该选项时的返回值,或者当flag非空时指定flag指向的数据的值。
</code></pre>
<p>** 另一个参数longindex,如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值。**</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><getopt.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span></span><br><span class="line"><span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> opt;</span><br><span class="line"> <span class="type">int</span> digit_optind = <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> option_index = <span class="number">0</span>;</span><br><span class="line"> <span class="type">char</span> *optstring = <span class="string">"a:b:c:d"</span>;</span><br><span class="line"> <span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">option</span> <span class="title">long_options</span>[] =</span> {</span><br><span class="line"> {<span class="string">"reqarg"</span>, required_argument, <span class="literal">NULL</span>, <span class="string">'r'</span>},</span><br><span class="line"> {<span class="string">"noarg"</span>, no_argument, <span class="literal">NULL</span>, <span class="string">'n'</span>},</span><br><span class="line"> {<span class="string">"optarg"</span>, optional_argument, <span class="literal">NULL</span>, <span class="string">'o'</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>}</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> ( (opt = getopt_long(argc, argv, optstring, long_options, &option_index)) != <span class="number">-1</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"opt = %c\n"</span>, opt);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"optarg = %s\n"</span>, optarg);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"optind = %d\n"</span>, optind);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"argv[optind - 1] = %s\n"</span>, argv[optind - <span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"option_index = %d\n"</span>, option_index);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>** 编译运行以上程序并运行,可以得到以下结果: **</p>
<figure class="highlight makefile"><table><tr><td class="code"><pre><span class="line"><span class="section">cashey@ubuntu:~/Desktop/getopt$ ./test_getopt_long -a 100 --reqarg 100 --nonarg</span></span><br><span class="line">opt = a</span><br><span class="line">optarg = 100</span><br><span class="line">optind = 3</span><br><span class="line">argv[optind - 1] = 100</span><br><span class="line">option_index = 0</span><br><span class="line">opt = r</span><br><span class="line">optarg = 100</span><br><span class="line">optind = 5</span><br><span class="line">argv[optind - 1] = 100</span><br><span class="line">option_index = 0</span><br><span class="line"><span class="section">./test_getopt_long: unrecognized option '--nonarg'</span></span><br><span class="line">opt = ?</span><br><span class="line">optarg = (null)</span><br><span class="line">optind = 6</span><br><span class="line">argv[optind - 1] = --nonarg</span><br><span class="line">option_index = 0</span><br></pre></td></tr></table></figure>
<p>** 当所给的参数存在问题时,opt(即函数返回值是’?’),如: **</p>
<figure class="highlight elixir"><table><tr><td class="code"><pre><span class="line">cashey<span class="variable">@ubuntu</span><span class="symbol">:~/Desktop/getopt</span><span class="variable">$ </span>./test_getopt_long -a</span><br><span class="line">./<span class="symbol">test_getopt_long:</span> option requires an argument -- <span class="string">'a'</span></span><br><span class="line">opt = ?</span><br><span class="line">optarg = (null)</span><br><span class="line">optind = <span class="number">2</span></span><br><span class="line">argv[optind - <span class="number">1</span>] = -a</span><br><span class="line">option_index = <span class="number">0</span></span><br><span class="line">cashey<span class="variable">@ubuntu</span><span class="symbol">:~/Desktop/getopt</span><span class="variable">$ </span>./test_getopt_long --reqarg</span><br><span class="line">./<span class="symbol">test_getopt_long:</span> option <span class="string">'--reqarg'</span> requires an argument</span><br><span class="line">opt = ?</span><br><span class="line">optarg = (null)</span><br><span class="line">optind = <span class="number">2</span></span><br><span class="line">argv[optind - <span class="number">1</span>] = --reqarg</span><br></pre></td></tr></table></figure>
<p>最后说说<code>getopt_long_only</code>函数,它与getopt_long函数使用相同的参数表,在功能上基本一致,只是getopt_long只将–name当作长参数,但getopt_long_only会将–name和-name两种选项都当作长参数来匹配。在getopt_long在遇到-name时,会拆解成-n -a -m -e到optstring中进行匹配,而getopt_long_only只在-name不能在longopts中匹配时才将其拆解成-n -a -m -e这样的参数到optstring中进行匹配。</p>
]]></content>
<tags>
<tag>linux</tag>
<tag>C</tag>
</tags>
</entry>
<entry>
<title>git use submodule</title>
<url>/2021/11/24/git-use-submodule/</url>
<content><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>项目中经常使用别人维护的模块,在 git 中使用子模块的功能能够大大提高开发效率。使用子模块后,不必负责子模块的维护,只需要在必要的时候同步更新子模块即可。本文主要讲解子模块相关的基础命令,详细使用请参考 man page。</p>
<h1 id="子模块的添加"><a href="#子模块的添加" class="headerlink" title="子模块的添加"></a>子模块的添加</h1><p>添加子模块非常简单,命令如下:<code>git submodule add <url> <path></code></p>
<p>其中,url 为子模块的路径,path 为该子模块存储的目录路径。</p>
<span id="more"></span>
<p>执行成功后,git status 会看到项目中修改了.gitmodules,并增加了一个新文件(为刚刚添加的路径)</p>
<p><code>git diff --cached</code> 查看修改内容可以看到增加了子模块,并且新文件下为子模块的提交 hash 摘要</p>
<p><code>git commit</code> 提交即完成子模块的添加</p>
<h1 id="子模块的使用"><a href="#子模块的使用" class="headerlink" title="子模块的使用"></a>子模块的使用</h1><p>克隆项目后,默认子模块目录下无任何内容。需要在项目根目录执行如下命令完成子模块的下载:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git submodule init</span><br><span class="line">git submodule update</span><br></pre></td></tr></table></figure>
<p>或:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></table></figure>
<p>执行后,子模块目录下就有了源码,再执行相应的 makefile 即可。</p>
<h1 id="子模块的更新"><a href="#子模块的更新" class="headerlink" title="子模块的更新"></a>子模块的更新</h1><p>子模块的维护者提交了更新后,使用子模块的项目必须手动更新才能包含最新的提交。</p>
<p>在项目中,进入到子模块目录下,执行 <code>git pull</code> 更新,查看 <code>git log</code> 查看相应提交。</p>
<p>完成后返回到项目目录,可以看到子模块有待提交的更新,使用 <code>git add</code> ,提交即可。</p>
<h1 id="删除子模块"><a href="#删除子模块" class="headerlink" title="删除子模块"></a>删除子模块</h1><p>有时子模块的项目维护地址发生了变化,或者需要替换子模块,就需要删除原有的子模块。</p>
<p>删除子模块较复杂,步骤如下:</p>
<ul>
<li><code>rm -rf</code> 子模块目录 删除子模块目录及源码</li>
<li><code>vi .gitmodules</code> 删除项目目录下.gitmodules 文件中子模块相关条目</li>
<li><code>vi .git/config</code> 删除配置项中子模块相关条目</li>
<li><code>rm .git/module/*</code> 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可</li>
</ul>
<p>执行完成后,再执行添加子模块命令即可,如果仍然报错,执行如下:</p>
<p><code>git rm --cached 子模块名称</code></p>
<p>完成删除后,提交到仓库即可。</p>
<h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97">https://git-scm.com/book/zh/v2/Git-工具-子模块</a></p>
<p>[转] <a href="https://blog.csdn.net/guotianqing/article/details/82391665">https://blog.csdn.net/guotianqing/article/details/82391665</a></p>
]]></content>
<tags>
<tag>git</tag>
<tag>submodule</tag>
</tags>
</entry>
<entry>
<title>go-cgo-oracle</title>
<url>/2020/04/09/go-cgo-oracle/</url>
<content><![CDATA[<h1 id="Linux-环境"><a href="#Linux-环境" class="headerlink" title="Linux 环境"></a>Linux 环境</h1><h2 id="安装-oracle-instantclient"><a href="#安装-oracle-instantclient" class="headerlink" title="安装 oracle-instantclient"></a>安装 oracle-instantclient</h2><h3 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h3><ul>
<li><p><a href="https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html">Oracle 官网</a> 下载 zip 包并解压</p>
<ul>
<li>instantclient-basic-linux.x64-12.2.0.1.0.zip</li>
<li>instantclient-basiclite-linux.x64-12.2.0.1.0.zip</li>
<li>instantclient-sdk-linux.x64-12.2.0.1.0.zip</li>
<li>instantclient-sqlplus-linux.x64-12.2.0.1.0.zip</li>
</ul>
</li>
<li><p><a href="https://github.com/bumpx/oracle-instantclient">https://github.com/bumpx/oracle-instantclient</a> 【推荐下载】</p>
</li>
</ul>
<span id="more"></span>
<h3 id="解压"><a href="#解压" class="headerlink" title="解压"></a>解压</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">unzip -ojd /usr/local instantclient-basiclite-linux.x64-12.2.0.1.0.zip</span><br><span class="line">or </span><br><span class="line">unzip -ojd /usr/local instantclient-basic-linux.x64-12.2.0.1.0.zip</span><br><span class="line"></span><br><span class="line"><span class="built_in">ln</span> -snf /usr/local/instantclient_12_2/libclntsh.so.12.1 /usr/local/instantclient_12_2/libclntsh.so</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开发使用</span></span><br><span class="line">unzip -ojd /usr/local instantclient-sdk-linux.x64-12.2.0.1.0.zip</span><br></pre></td></tr></table></figure>
<h3 id="设置"><a href="#设置" class="headerlink" title="设置"></a>设置</h3><h4 id="LD-LIBRARY-PATH"><a href="#LD-LIBRARY-PATH" class="headerlink" title="LD_LIBRARY_PATH"></a>LD_LIBRARY_PATH</h4><figure class="highlight elixir"><table><tr><td class="code"><pre><span class="line">export <span class="title class_">LD_LIBRARY_PATH</span>=<span class="variable">$LD_LIBRARY_PATH</span><span class="symbol">:/usr/local/instantclient_12_2</span></span><br></pre></td></tr></table></figure>
<h4 id="oci8-pc"><a href="#oci8-pc" class="headerlink" title="oci8.pc"></a>oci8.pc</h4><blockquote>
<p>修改 pc 文件 (可以参考 github.com/mattn/go-oci8/README.MD)</p>
</blockquote>
<figure class="highlight routeros"><table><tr><td class="code"><pre><span class="line"><span class="attribute">prefix</span>=/usr/local/instantclient_12_2</span><br><span class="line"><span class="attribute">includedir</span>=<span class="variable">${prefix}</span>/sdk/include</span><br><span class="line"><span class="attribute">libdir</span>=<span class="variable">${prefix}</span></span><br><span class="line"></span><br><span class="line">Name: oci8</span><br><span class="line">Description: Oracle Instant<span class="built_in"> Client</span></span><br><span class="line"><span class="built_in"></span>Version: 12.2</span><br><span class="line">Cflags: -I<span class="variable">${includedir}</span></span><br><span class="line">Libs: -L<span class="variable">${libdir}</span> -lclntsh</span><br></pre></td></tr></table></figure>
<ul>
<li>重新配置环境变量 PKG_CONFIG_PATH 目录,放入 oci8.pc<ul>
<li>export PKG_CONFIG_PATH=xxx</li>
</ul>
</li>
<li>放入系统路径, /usr/local/lib/pkgconfig/<ul>
<li>export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig</li>
</ul>
</li>
</ul>
<h4 id="检查开发环境"><a href="#检查开发环境" class="headerlink" title="检查开发环境"></a>检查开发环境</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ pkg-config --cflags oci8</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出这个则正常</span></span><br><span class="line">-I/usr/local/instantclient_12_2/sdk/include/ </span><br></pre></td></tr></table></figure>
<h1 id="windows-环境"><a href="#windows-环境" class="headerlink" title="windows 环境"></a>windows 环境</h1><h2 id="安装-oracle-instantclient-1"><a href="#安装-oracle-instantclient-1" class="headerlink" title="安装 oracle-instantclient"></a>安装 oracle-instantclient</h2><h3 id="下载-1"><a href="#下载-1" class="headerlink" title="下载"></a>下载</h3><ul>
<li><a href="https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html">Oracle 官网</a> 下载 zip 包并解压<ul>
<li>instantclient-basiclite-windows.x64-12.2.0.1.0.zip</li>
<li>instantclient-basic-windows.x64-12.2.0.1.0_2.zip</li>
<li>instantclient-sdk-windows.x64-12.2.0.1.0.zip</li>
</ul>
</li>
</ul>
<h3 id="解压-1"><a href="#解压-1" class="headerlink" title="解压"></a>解压</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">到C盘</span><br><span class="line">解压 instantclient-basiclite-windows.x64-12.2.0.1.0.zip</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开发使用</span></span><br><span class="line">解压 instantclient-sdk-windows.x64-12.2.0.1.0.zip</span><br></pre></td></tr></table></figure>
<h3 id="设置-1"><a href="#设置-1" class="headerlink" title="设置"></a>设置</h3><h4 id="oracle-环境"><a href="#oracle-环境" class="headerlink" title="oracle 环境"></a>oracle 环境</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ORACLE_HOME = C:\instantclient_12_2</span><br><span class="line">TNS_ADMIN = %ORACLE_HOME%\ADMIN</span><br><span class="line">NLS_LANG = SIMPLIFIED CHINESE_CHINA.AL32UTF8</span><br><span class="line">or</span><br><span class="line">NLS_LANG = SIMPLIFIED CHINESE_CHINA.ZHS16GBK</span><br></pre></td></tr></table></figure>
<h2 id="安装-Mingw-w64"><a href="#安装-Mingw-w64" class="headerlink" title="安装 Mingw-w64"></a>安装 Mingw-w64</h2><p><a href="http://sourceforge.net/projects/mingw-w64/">http://sourceforge.net/projects/mingw-w64/</a></p>
<p>8.1.0 posix seh x86_64-8.1.0-release-posix-seh-rt_v6-rev0</p>
<p>直接下载解压即可,解压的目录 %MINGW64_HOME% ,将 %MINGW64_HOME%\bin 配置在环境变量 Path 中<br>然后命令行输入 gcc -v 打印</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ gcc -v</span><br><span class="line"></span><br><span class="line">Using built-in specs.</span><br><span class="line">COLLECT_GCC=gcc</span><br><span class="line">COLLECT_LTO_WRAPPER=C:/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/lto-wrapper.exe</span><br><span class="line">Target: x86_64-w64-mingw32</span><br><span class="line">Configured with: ../../../src/gcc-8.1.0/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=<span class="built_in">yes</span> --enable-threads=posix --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=nocona --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-pkgversion=<span class="string">'x86_64-posix-seh-rev0, Built by MinGW-W64 project'</span> --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS=<span class="string">'-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include'</span> CXXFLAGS=<span class="string">'-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include'</span> CPPFLAGS=<span class="string">' -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include'</span> LDFLAGS=<span class="string">'-pipe -fno-ident -L/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/lib -L/c/mingw810/prerequisites/x86_64-zlib-static/lib -L/c/mingw810/prerequisites/x86_64-w64-mingw32-static/lib '</span></span><br><span class="line">Thread model: posix</span><br><span class="line">gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="配置全局-make"><a href="#配置全局-make" class="headerlink" title="配置全局 make"></a>配置全局 make</h3><p>进入mingw64 的安装目录,新建文件 make.bat 内容为</p>
<figure class="highlight llvm"><table><tr><td class="code"><pre><span class="line"><span class="title">@echo</span> off</span><br><span class="line">C:\mingw<span class="number">64</span>\bin\mingw<span class="number">32</span>-make.exe <span class="variable">%1</span> <span class="variable">%2</span> <span class="variable">%3</span> <span class="variable">%4</span> <span class="variable">%5</span> <span class="variable">%6</span> <span class="variable">%7</span> <span class="variable">%8</span> <span class="variable">%9</span></span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ make -v</span><br><span class="line"></span><br><span class="line">GNU Make 4.2.1</span><br><span class="line">Built <span class="keyword">for</span> x86_64-w64-mingw32</span><br><span class="line">Copyright (C) 1988-2016 Free Software Foundation, Inc.</span><br><span class="line">License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html></span><br><span class="line">This is free software: you are free to change and redistribute it.</span><br><span class="line">There is NO WARRANTY, to the extent permitted by law.</span><br></pre></td></tr></table></figure>
<h3 id="pkg-config"><a href="#pkg-config" class="headerlink" title="pkg-config"></a>pkg-config</h3><p>新建 C:\mingw64\lib\pkg-config\oci8.pc</p>
<figure class="highlight routeros"><table><tr><td class="code"><pre><span class="line"><span class="attribute">prefix</span>=C:/instantclient_12_2/instantclient_12_2</span><br><span class="line"><span class="attribute">exec_prefix</span>=<span class="variable">${prefix}</span></span><br><span class="line"><span class="attribute">libdir</span>=<span class="variable">${exec_prefix}</span></span><br><span class="line"><span class="attribute">includedir</span>=<span class="variable">${prefix}</span>/sdk/include/</span><br><span class="line"></span><br><span class="line">Name: oci8</span><br><span class="line">Description: oci8 library</span><br><span class="line">Version: 12.2</span><br><span class="line">Libs: -L<span class="variable">${libdir}</span> -loci</span><br><span class="line">Cflags: -I<span class="variable">${includedir}</span></span><br></pre></td></tr></table></figure>
<p>环境变量</p>
<figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="attr">PKG_CONFIG_PATH</span> = C:\mingw64\lib\pkg-config</span><br></pre></td></tr></table></figure>
<p>安装 pkg-config.exe </p>
<p><a href="https://pkg-config.freedesktop.org/releases/">https://pkg-config.freedesktop.org/releases/</a> </p>
<p><a href="https://stackoverflow.com/questions/1710922/how-to-install-pkg-config-in-windows">https://stackoverflow.com/questions/1710922/how-to-install-pkg-config-in-windows</a></p>
<p><a href="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/">http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/</a></p>
<p>C:\mingw64\bin\pkg-config.exe</p>
<h3 id="检查开发环境-1"><a href="#检查开发环境-1" class="headerlink" title="检查开发环境"></a>检查开发环境</h3><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">pkg-config --cflags oci8</span><br><span class="line">-<span class="variable constant_">IC</span><span class="symbol">:/instantclient_12_2/sdk/include/</span></span><br></pre></td></tr></table></figure>
<h1 id="示例代码-sql-go"><a href="#示例代码-sql-go" class="headerlink" title="示例代码 sql.go"></a>示例代码 sql.go</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cat</span> >sql.go <<\EOF</span><br><span class="line">package main</span><br><span class="line"></span><br><span class="line">import (</span><br><span class="line"> <span class="string">"database/sql"</span></span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"log"</span></span><br><span class="line"> <span class="string">"os"</span></span><br><span class="line"></span><br><span class="line"> _ <span class="string">"github.com/mattn/go-oci8"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">func <span class="function"><span class="title">main</span></span>() {</span><br><span class="line"> <span class="keyword">if</span> len(os.Args) != 2 {</span><br><span class="line"> log.Fatalln(os.Args[0] + <span class="string">" user/password@host:port/sid"</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> db, err := sql.Open(<span class="string">"oci8"</span>, os.Args[1])</span><br><span class="line"> <span class="keyword">if</span> err != nil {</span><br><span class="line"> log.Fatalln(err)</span><br><span class="line"> }</span><br><span class="line"> defer db.Close()</span><br><span class="line"></span><br><span class="line"> rows, err := db.Query(<span class="string">"select user from dual"</span>)</span><br><span class="line"> <span class="keyword">if</span> err != nil {</span><br><span class="line"> log.Fatalln(err)</span><br><span class="line"> }</span><br><span class="line"> defer rows.Close()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> rows.<span class="function"><span class="title">Next</span></span>() {</span><br><span class="line"> var data string</span><br><span class="line"> rows.Scan(&data)</span><br><span class="line"> fmt.Println(data)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> err = rows.Err(); err != nil {</span><br><span class="line"> log.Fatalln(err)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure>
<h2 id="database-x2F-sql"><a href="#database-x2F-sql" class="headerlink" title="database/sql"></a>database/sql</h2><p>参考 <a href="http://go-database-sql.org/">Go database/sql tutorial</a></p>
]]></content>
<tags>
<tag>go</tag>
<tag>oracle</tag>
<tag>cgo</tag>
<tag>Mingw-w64</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title>Go net/http unix 域套接字连接</title>
<url>/2021/12/14/go-http-unix-sockets/</url>
<content><![CDATA[<p>go package “net/http” 无法直接使用 unix 域套接字侦听的服务,会提示,不支持的协议。</p>
<h1 id="方案"><a href="#方案" class="headerlink" title="方案"></a>方案</h1><ul>
<li><p>实现自己的 <a href="https://pkg.go.dev/net/http#RoundTripper">RoundTripper</a> 支持 unix socket 。</p>
</li>
<li><p>伪造拨号功能并将其传递给传输:</p>
</li>
</ul>
<span id="more"></span>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fakeDial</span><span class="params">(proto, addr <span class="type">string</span>)</span></span> (conn net.Conn, err <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> net.Dial(<span class="string">"unix"</span>, sock) <span class="comment">// 为 sock path</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">tr := &http.Transport{</span><br><span class="line"> Dial: fakeDial,</span><br><span class="line">}</span><br><span class="line">client := &http.Client{Transport: tr}</span><br><span class="line">resp, err := client.Get(<span class="string">"http://unix/test"</span>)</span><br></pre></td></tr></table></figure>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"io"</span></span><br><span class="line"> <span class="string">"net"</span></span><br><span class="line"> <span class="string">"net/http"</span></span><br><span class="line"> <span class="string">"os"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">dieIf</span><span class="params">(err <span class="type">error</span>)</span></span> {</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Fprintf(os.Stderr, <span class="string">"error: %v\n"</span>, err)</span><br><span class="line"> os.Exit(<span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> sock = <span class="string">"/tmp/dummy.sock"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">server</span><span class="params">()</span></span> {</span><br><span class="line"> l, err := net.Listen(<span class="string">"unix"</span>, sock)</span><br><span class="line"> dieIf(err)</span><br><span class="line"> srv := &http.Server{</span><br><span class="line"> Handler: http.HandlerFunc(<span class="function"><span class="keyword">func</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> {</span><br><span class="line"> fmt.Fprintf(w, <span class="string">"hello: %v\n"</span>, r.RequestURI)</span><br><span class="line"> }),</span><br><span class="line"> }</span><br><span class="line"> srv.Serve(l)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fakeDial</span><span class="params">(proto, addr <span class="type">string</span>)</span></span> (conn net.Conn, err <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> net.Dial(<span class="string">"unix"</span>, sock)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> os.Remove(sock)</span><br><span class="line"> <span class="keyword">go</span> server()</span><br><span class="line"> time.Sleep(time.Second)</span><br><span class="line"> tr := &http.Transport{</span><br><span class="line"> Dial: fakeDial,</span><br><span class="line"> }</span><br><span class="line"> client := &http.Client{Transport: tr}</span><br><span class="line"> resp, err := client.Get(<span class="string">"http://unix/test"</span>)</span><br><span class="line"> dieIf(err)</span><br><span class="line"> io.Copy(os.Stdout, resp.Body)</span><br><span class="line"> resp.Body.Close()</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p><a href="https://go.dev/play/p/SYlVQGGN5Ng">playground</a></p>
<blockquote>
<p><strong>警告</strong>,你所有的 client.Get/.Post 调用必须是有效的 url( <a href="http://xxxx.xxx/path">http://xxxx.xxx/path</a> 不是 unix://… ),域名无关紧要,因为它不会用于连接。</p>
</blockquote>
<h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://www.coder.work/article/195027">sockets - Go net/http unix 域套接字连接</a></p>
<p><a href="https://stackoverflow.com/questions/26223839/go-net-http-unix-domain-socket-connection">Go net/http unix domain socket connection</a></p>
<p><a href="https://www.jianshu.com/p/1ea0020b233d">Go:Unix 域套接字</a></p>
<p><a href="https://blog.csdn.net/qq_33399567/article/details/107691339">golang 中基于 http 和 unix socket 的通信代码实现(服务端基于 gin 框架)</a></p>
]]></content>
<tags>
<tag>http</tag>
<tag>UDS</tag>
</tags>
</entry>
<entry>
<title>go-qt</title>
<url>/2021/12/17/go-qt/</url>
<content><![CDATA[<h1 id="Install"><a href="#Install" class="headerlink" title="Install"></a>Install</h1><h2 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h2><ul>
<li>macOS Monterey 12.1</li>
<li>go1.17.5</li>
<li>therecipe/qt</li>
<li>therecipe/env_darwin_amd64_513 (Qt5.13)</li>
<li>Xcode v13.2 / xcode-select –install</li>
</ul>
<span id="more"></span>
<h2 id="therecipe-x2F-qt-安装"><a href="#therecipe-x2F-qt-安装" class="headerlink" title="therecipe/qt 安装"></a>therecipe/qt 安装</h2><h3 id="In-GOPATH-global-installation-mode-(推荐使用这个)"><a href="#In-GOPATH-global-installation-mode-(推荐使用这个)" class="headerlink" title="In GOPATH (global installation) mode (推荐使用这个)"></a>In GOPATH (global installation) mode (推荐使用这个)</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">export</span> GO111MODULE=off; xcode-select --install; go get -v github.com/therecipe/qt/cmd/... && $(go <span class="built_in">env</span> GOPATH)/bin/qtsetup <span class="built_in">test</span> && $(go <span class="built_in">env</span> GOPATH)/bin/qtsetup -<span class="built_in">test</span>=<span class="literal">false</span></span><br><span class="line"></span><br><span class="line">xcode-select: error: <span class="built_in">command</span> line tools are already installed, use <span class="string">"Software Update"</span> to install updates</span><br><span class="line">github.com/therecipe/env_darwin_amd64_513 (download)</span><br><span class="line"></span><br><span class="line">INFO[0000] running: <span class="string">'qtsetup test darwin'</span> [docker=<span class="literal">false</span>] [vagrant=<span class="literal">false</span>]</span><br><span class="line">INFO[0000] testing showcases/wallet</span><br><span class="line">INFO[0073] testing qml/gallery</span><br><span class="line">INFO[0094] testing quick/calc</span><br><span class="line">INFO[0113] testing widgets/textedit</span><br><span class="line">INFO[0000] running: <span class="string">'qtsetup prep'</span></span><br><span class="line">INFO[0000] successfully created qtrcc symlink <span class="keyword">in</span> your PATH (/usr/local/bin/qtrcc)</span><br><span class="line">INFO[0000] successfully created qtmoc symlink <span class="keyword">in</span> your PATH (/usr/local/bin/qtmoc)</span><br><span class="line">INFO[0000] successfully created qtminimal symlink <span class="keyword">in</span> your PATH (/usr/local/bin/qtminimal)</span><br><span class="line">INFO[0000] successfully created qtdeploy symlink <span class="keyword">in</span> your PATH (/usr/local/bin/qtdeploy)</span><br><span class="line">INFO[0000] running: <span class="string">'qtsetup check darwin'</span> [docker=<span class="literal">false</span>] [vagrant=<span class="literal">false</span>]</span><br><span class="line">INFO[0000] GOOS: <span class="string">'darwin'</span></span><br><span class="line">INFO[0000] GOARCH: <span class="string">'amd64'</span></span><br><span class="line">INFO[0000] GOVERSION: <span class="string">'go1.17.5'</span></span><br><span class="line">INFO[0000] GOROOT: <span class="string">'/Users/foo/.gobrew/versions/1.17.5/go'</span></span><br><span class="line">INFO[0000] GOPATH: <span class="string">'/Users/foo/go'</span></span><br><span class="line">INFO[0000] GOBIN: <span class="string">'/Users/foo/go/bin'</span></span><br><span class="line">INFO[0000] GOMOD: <span class="string">''</span></span><br><span class="line">INFO[0000] QT_HASH: <span class="string">'c0c124a5770d357908f16fa57e0aa0ec6ccd3f91'</span></span><br><span class="line">INFO[0000] QT_API: <span class="string">''</span></span><br><span class="line">INFO[0000] QT_VERSION: <span class="string">'5.13.0'</span></span><br><span class="line">INFO[0000] QT_DIR: * <span class="string">'/Users/foo/go/src/github.com/therecipe/env_darwin_amd64_513'</span></span><br><span class="line">INFO[0000] QT_STUB: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_DEBUG: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_QMAKE_DIR: <span class="string">''</span></span><br><span class="line">INFO[0000] QT_WEBKIT: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_STATIC: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_GEN_GO: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_GEN_OPENGL: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_GEN_QUICK_EXTRAS: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_RESOURCES_BIG: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_NOT_CACHED: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_HOMEBREW: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_MACPORTS: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_NIX: <span class="string">'false'</span></span><br><span class="line">INFO[0000] QT_FELGO: <span class="string">'false'</span></span><br><span class="line">INFO[0000] XCODE_DIR: <span class="string">'/Applications/Xcode.app'</span></span><br><span class="line">INFO[0000] QT_PKG_CONFIG: <span class="string">'false'</span></span><br><span class="line">INFO[0000] running: <span class="string">'qtsetup generate darwin'</span> [docker=<span class="literal">false</span>] [vagrant=<span class="literal">false</span>]</span><br><span class="line">INFO[0003] generating full qt/core</span><br><span class="line">INFO[0017] generating full qt/androidextras</span><br><span class="line">INFO[0018] generating full qt/gui</span><br><span class="line">INFO[0034] generating full qt/network</span><br><span class="line">INFO[0039] generating full qt/xml</span><br><span class="line">INFO[0041] generating full qt/dbus</span><br><span class="line">INFO[0043] generating full qt/nfc</span><br><span class="line">INFO[0045] generating full qt/script</span><br><span class="line">INFO[0046] generating full qt/sensors</span><br><span class="line">INFO[0051] generating full qt/positioning</span><br><span class="line">INFO[0053] generating full qt/widgets</span><br><span class="line">INFO[0090] generating full qt/sql</span><br><span class="line">INFO[0093] generating full qt/macextras</span><br><span class="line">INFO[0094] generating full qt/qml</span><br><span class="line">INFO[0097] generating full qt/websockets</span><br><span class="line">INFO[0098] generating full qt/xmlpatterns</span><br><span class="line">INFO[0099] generating full qt/bluetooth</span><br><span class="line">INFO[0102] generating full qt/webchannel</span><br><span class="line">INFO[0103] generating full qt/svg</span><br><span class="line">INFO[0105] generating full qt/multimedia</span><br><span class="line">INFO[0119] generating full qt/quick</span><br><span class="line">INFO[0124] generating full qt/help</span><br><span class="line">INFO[0128] generating full qt/location</span><br><span class="line">INFO[0130] generating full qt/scripttools</span><br><span class="line">INFO[0131] generating full qt/uitools</span><br><span class="line">INFO[0132] generating full qt/webengine</span><br><span class="line">INFO[0135] generating full qt/testlib</span><br><span class="line">INFO[0136] generating full qt/serialport</span><br><span class="line">INFO[0137] generating full qt/serialbus</span><br><span class="line">INFO[0139] generating full qt/printsupport</span><br><span class="line">INFO[0142] generating full qt/designer</span><br><span class="line">INFO[0146] generating full qt/scxml</span><br><span class="line">INFO[0148] generating full qt/gamepad</span><br><span class="line">INFO[0150] generating full qt/purchasing</span><br><span class="line">INFO[0151] generating full qt/datavisualization [GPLv3]</span><br><span class="line">INFO[0157] generating full qt/charts [GPLv3]</span><br><span class="line">INFO[0167] generating full qt/virtualkeyboard [GPLv3]</span><br><span class="line">INFO[0169] generating full qt/speech</span><br><span class="line">INFO[0170] generating full qt/quickcontrols2</span><br><span class="line">INFO[0171] generating full qt/sailfish</span><br><span class="line">INFO[0171] generating full qt/webview</span><br><span class="line">INFO[0171] generating full qt/remoteobjects</span><br><span class="line">INFO[0175] running: <span class="string">'qtsetup install darwin'</span> [docker=<span class="literal">false</span>] [vagrant=<span class="literal">false</span>]</span><br><span class="line">INFO[0175] installing full qt/core</span><br><span class="line">INFO[0176] installing full qt/androidextras</span><br><span class="line">INFO[0176] installing full qt/gui</span><br><span class="line">INFO[0176] installing full qt/network</span><br><span class="line">INFO[0177] installing full qt/xml</span><br><span class="line">INFO[0177] installing full qt/dbus</span><br><span class="line">INFO[0177] installing full qt/nfc</span><br><span class="line">INFO[0178] installing full qt/script</span><br><span class="line">INFO[0178] installing full qt/sensors</span><br><span class="line">INFO[0178] installing full qt/positioning</span><br><span class="line">INFO[0178] installing full qt/widgets</span><br><span class="line">INFO[0179] installing full qt/sql</span><br><span class="line">INFO[0180] installing full qt/macextras</span><br><span class="line">INFO[0180] installing full qt/qml</span><br><span class="line">INFO[0180] installing full qt/websockets</span><br><span class="line">INFO[0181] installing full qt/xmlpatterns</span><br><span class="line">INFO[0181] installing full qt/bluetooth</span><br><span class="line">INFO[0181] installing full qt/webchannel</span><br><span class="line">INFO[0182] installing full qt/svg</span><br><span class="line">INFO[0182] installing full qt/multimedia</span><br><span class="line">INFO[0183] installing full qt/quick</span><br><span class="line">INFO[0184] installing full qt/help</span><br><span class="line">INFO[0185] installing full qt/location</span><br><span class="line">INFO[0185] installing full qt/scripttools</span><br><span class="line">INFO[0186] installing full qt/uitools</span><br><span class="line">INFO[0186] installing full qt/webengine</span><br><span class="line">INFO[0187] installing full qt/testlib</span><br><span class="line">INFO[0188] installing full qt/serialport</span><br><span class="line">INFO[0188] installing full qt/serialbus</span><br><span class="line">INFO[0188] installing full qt/printsupport</span><br><span class="line">INFO[0189] installing full qt/designer</span><br><span class="line">INFO[0189] installing full qt/scxml</span><br><span class="line">INFO[0190] installing full qt/gamepad</span><br><span class="line">INFO[0190] installing full qt/purchasing</span><br><span class="line">INFO[0190] installing full qt/datavisualization [GPLv3]</span><br><span class="line">INFO[0191] installing full qt/charts [GPLv3]</span><br><span class="line">INFO[0191] installing full qt/virtualkeyboard [GPLv3]</span><br><span class="line">INFO[0192] installing full qt/speech</span><br><span class="line">INFO[0192] installing full qt/quickcontrols2</span><br><span class="line">INFO[0192] installing full qt/sailfish</span><br><span class="line">INFO[0193] installing full qt/webview</span><br><span class="line">INFO[0193] installing full qt/remoteobjects</span><br><span class="line">INFO[0193] installing full qt/internal/binding/runtime</span><br></pre></td></tr></table></figure>
<h3 id="In-Module-per-project-mode-to-work-around-the-GFW-you-can-use-a-proxy-like-this-GOPROXY-x3D-https-goproxy-io"><a href="#In-Module-per-project-mode-to-work-around-the-GFW-you-can-use-a-proxy-like-this-GOPROXY-x3D-https-goproxy-io" class="headerlink" title="In Module (per project) mode (to work around the GFW you can use a proxy like this: GOPROXY=https://goproxy.io)"></a>In Module (per project) mode (to work around the GFW you can use a proxy like this: GOPROXY=<a href="https://goproxy.io/">https://goproxy.io</a>)</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">export</span> GO111MODULE=on; xcode-select --install; go get -v github.com/therecipe/qt && go install -v -tags=no_env github.com/therecipe/qt/cmd/... && go mod vendor && git <span class="built_in">clone</span> https://github.com/therecipe/env_darwin_amd64_513.git vendor/github.com/therecipe/env_darwin_amd64_513 && $(go <span class="built_in">env</span> GOPATH)/bin/qtsetup</span><br></pre></td></tr></table></figure>
<h1 id="deploy-打包"><a href="#deploy-打包" class="headerlink" title="deploy 打包"></a>deploy 打包</h1><ul>
<li>therecipe/qt 打包方式是使用 vendor 目录打包,而不是使用最新的 go mod。我们要把所有的依赖放到 vendor 目录下</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">go mod vendor</span><br></pre></td></tr></table></figure>
<ul>
<li>克隆环境依赖</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/therecipe/env_$(go <span class="built_in">env</span> GOOS)_amd64_513.git vendor/github.com/therecipe/env_$(go <span class="built_in">env</span> GOOS)_amd64_513</span><br></pre></td></tr></table></figure>
<ul>
<li>编译</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">qtdeploy build dektop</span><br><span class="line">or</span><br><span class="line">qtdeploy <span class="built_in">test</span> desktop</span><br></pre></td></tr></table></figure>
<h1 id="交叉编译"><a href="#交叉编译" class="headerlink" title="交叉编译"></a>交叉编译</h1><p>使用 Docker 容器交叉编译。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 编译 windows_64_shared 版本</span></span><br><span class="line">qtdeploy -docker build windows</span><br><span class="line"><span class="comment"># 编译 windows_64_static 版本</span></span><br><span class="line">qtdeploy -docker build windows_64_static</span><br></pre></td></tr></table></figure>
<h1 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h1><p><a href="https://studygolang.com/articles/31052">macOS 下搭建 golang 的 QT 开发环境</a></p>
<p><a href="https://simonding.com/post/2019/09/go-qt/">Go 语言 QT 开发踩坑</a></p>
<p><a href="https://zhuanlan.zhihu.com/p/191574722">Qt 开发之 Go 篇(一)</a></p>
<p>系列文章 <a href="https://blog.csdn.net/weixin_43968923/category_9825462.html">Golang-Qt5</a></p>
<p><del><a href="https://www.takenoteonceonly.com/go-qt5-gui/#Q3">Go (Golang) | Qt5 GUI 開發</a></del></p>
]]></content>
<tags>
<tag>Qt</tag>
</tags>
</entry>
<entry>
<title>Hello World</title>
<url>/2015/03/27/hello-world/</url>
<content><![CDATA[<p>Welcome to <a href="http://hexo.io/">Hexo</a>! This is your very first post. Check <a href="http://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="http://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p>
<span id="more"></span>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure>
<p>More info: <a href="http://hexo.io/docs/writing.html">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>
<p>More info: <a href="http://hexo.io/docs/server.html">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>
<p>More info: <a href="http://hexo.io/docs/generating.html">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>
<p>More info: <a href="http://hexo.io/docs/deployment.html">Deployment</a></p>
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="http://music.163.com/outchain/player?type=2&id=28947001&auto=1&height=66"></iframe>
]]></content>
</entry>
<entry>
<title>select的四大用法</title>
<url>/2020/03/16/select%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%94%A8%E6%B3%95/</url>
<content><![CDATA[<p><a href="https://tour.golang.org/concurrency/5">select</a> 跟 <a href="https://tour.golang.org/flowcontrol/9">switch</a> 有个共同特性就是都通过 case 的方式来处理,但是 select 跟 switch 处理的事情完全不同,也完全不相容。</p>
<h1 id="switch"><a href="#switch" class="headerlink" title="switch"></a>switch</h1><p>特性: </p>
<ul>
<li>1.各种类型及型别操作,接口 interface{} 型别判断 variable.(type)</li>
<li>2.会依照 case <code>顺序依序执行</code></li>
</ul>
<span id="more"></span>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line"> i <span class="keyword">interface</span>{}</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">convert</span><span class="params">(i <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line"> <span class="keyword">switch</span> t := i.(<span class="keyword">type</span>) {</span><br><span class="line"> caseint:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"i is interger"</span>, t)</span><br><span class="line"> casestring:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"i is string"</span>, t)</span><br><span class="line"> casefloat64:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"i is float64"</span>, t)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">println</span>(<span class="string">"type not found"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> i = <span class="number">100</span></span><br><span class="line"> convert(i)</span><br><span class="line"> i = <span class="type">float64</span>(<span class="number">45.55</span>)</span><br><span class="line"> convert(i)</span><br><span class="line"> i = <span class="string">"foo"</span></span><br><span class="line"> convert(i)</span><br><span class="line"> convert(<span class="type">float32</span>(<span class="number">10.0</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行出来的结果如下:</p>
<figure class="highlight angelscript"><table><tr><td class="code"><pre><span class="line">i <span class="keyword">is</span> <span class="built_in">int</span>erger <span class="number">100</span></span><br><span class="line">i <span class="keyword">is</span> <span class="built_in">float</span>64 +<span class="number">4.555000e+001</span></span><br><span class="line">i <span class="keyword">is</span> <span class="built_in">string</span> foo</span><br><span class="line">type <span class="keyword">not</span> found</span><br></pre></td></tr></table></figure>
<p>而 select 的特性就不同了,只能接收 channel,否则会出错,而 default 会直接执行,所以<code>没有 default 的 select 就会遇到 blocking</code>,假设没有送 value 进去 Channel 就会造成 panic,底下拿几个实际例子来解说。</p>
<h1 id="select"><a href="#select" class="headerlink" title="select"></a>select</h1><h2 id="Random-Select"><a href="#Random-Select" class="headerlink" title="Random Select"></a>Random Select</h2><p>同一个 channel 在 select 会随机选取,底下看个例子:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span><span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> ch <- <span class="number">1</span></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> fmt.Println(<span class="string">"random 01"</span>)</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> fmt.Println(<span class="string">"random 02"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>执行后会发现有时候拿到 random 01 有时候拿到 random 02,这就是 select 的特性之一,case 是随机选取,所以当 select 有两个 channel 以上时,如果同时对全部 channel 送资料,则会随机选取到不同的 Channel。而上面有提到另一个特性『假设没有送 value 进入 Channel 就会造成 panic』,拿上面例子来改:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> fmt.Println(<span class="string">"random 01"</span>)</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> fmt.Println(<span class="string">"random 02"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>执行后会发现变成 deadlock,造成 main 主程式爆炸,这时候可以直接用 default 方式解决此问题:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> fmt.Println(<span class="string">"random 01"</span>)</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> fmt.Println(<span class="string">"random 02"</span>)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">"exit"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>主程式 main 就不会因为读不到 channel value 造成整个程式 deadlock。</p>
<h2 id="Timeout-超时机制"><a href="#Timeout-超时机制" class="headerlink" title="Timeout 超时机制"></a>Timeout 超时机制</h2><p>用 select 读取 channle 时,一定会实作超过一定时间后就做其他事情,而不是一直 blocking 在 select 内。底下是简单的例子:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> timeout := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">bool</span>, <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> time.Sleep(<span class="number">2</span> * time.Second)</span><br><span class="line"> timeout <- <span class="literal">true</span></span><br><span class="line"> }()</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-ch:</span><br><span class="line"> <span class="keyword">case</span> <-timeout:</span><br><span class="line"> fmt.Println(<span class="string">"timeout 01"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>建立 timeout channel,让其他地方可以透过 trigger timeout channel 达到让 select 执行结束,也或者有另一个写法是透握 time.After 机制</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> {</span><br><span class="line"><span class="keyword">case</span> <-ch:</span><br><span class="line"><span class="keyword">case</span> <-timeout:</span><br><span class="line"> fmt.Println(<span class="string">"timeout 01"</span>)</span><br><span class="line"><span class="keyword">case</span> <-time.After(time.Second * <span class="number">1</span>):</span><br><span class="line"> fmt.Println(<span class="string">"timeout 02"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>可以注意 time.After 是回传 chan time.Time,所以执行 select 超过一秒时,就会输出 timeout 02。</p>
<h2 id="检查-channel-是否已满"><a href="#检查-channel-是否已满" class="headerlink" title="检查 channel 是否已满"></a>检查 channel 是否已满</h2><p>直接来看例子比较快:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">1</span>)</span><br><span class="line"> ch <- <span class="number">1</span></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> ch <- <span class="number">2</span>:</span><br><span class="line"> fmt.Println(<span class="string">"channel value is"</span>, <-ch)</span><br><span class="line"> fmt.Println(<span class="string">"channel value is"</span>, <-ch)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">"channel blocking"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>先宣告 buffer size 为 1 的 channel,先丢值把 channel 填满。这时候可以透过 select + default 方式来确保 channel 是否已满,上面例子会输出 channel blocking,我们再把程式改成底下</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(chanint, <span class="number">2</span>)</span><br><span class="line"> ch <- <span class="number">1</span></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> ch <- <span class="number">2</span>:</span><br><span class="line"> fmt.Println(<span class="string">"channel value is"</span>, <-ch)</span><br><span class="line"> fmt.Println(<span class="string">"channel value is"</span>, <-ch)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">"channel blocking"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>把 buffer size 改为 2 后,就可以继续在塞 value 进去 channel 了,这边的 buffer channel 观念可以看之前的文章『用五分钟了解什么是 unbuffered vs buffered channel』[1]</p>
<h2 id="select-for-loop-用法"><a href="#select-for-loop-用法" class="headerlink" title="select for loop 用法"></a>select for loop 用法</h2><p>如果你有多个 channel 需要读取,而读取是不间断的,就必须使用 for + select 机制来实现,更详细的实作可以参考『15 分钟学习 Go 语言如何处理多个 Channel 通道』</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> i := <span class="number">0</span></span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">string</span>, <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="built_in">close</span>(ch)</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> LOOP:</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line"> fmt.Println(time.Now().Unix())</span><br><span class="line"> i++</span><br><span class="line"></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> m := <-ch:</span><br><span class="line"> <span class="built_in">println</span>(m)</span><br><span class="line"> <span class="keyword">break</span> LOOP</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> time.Sleep(time.Second * <span class="number">4</span>)</span><br><span class="line"> ch <- <span class="string">"stop"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面例子可以发现执行后如下:</p>
<figure class="highlight dns"><table><tr><td class="code"><pre><span class="line"><span class="number">1257894001</span></span><br><span class="line"><span class="number">1257894002</span></span><br><span class="line"><span class="number">1257894003</span></span><br><span class="line"><span class="number">1257894004</span></span><br><span class="line"><span class="number">1257894005</span></span><br><span class="line">stop</span><br></pre></td></tr></table></figure>
<p>其实把 default 拿掉也可以达到目的</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> {</span><br><span class="line"><span class="keyword">case</span> m := <-ch:</span><br><span class="line"> <span class="built_in">println</span>(m)</span><br><span class="line"> <span class="keyword">break</span> LOOP</span><br></pre></td></tr></table></figure>
<p>当没有值送进来时,就会一直停在 select 区段,所以其实没有 default 也是可以正常运作的,而要结束 for 或 select 都需要透过 break 来结束,但是要<code>在 select 区间直接结束掉 for 回圈,只能使用 break variable 来结束,这边是大家需要注意的地方</code>。</p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p>[1]『用五分钟了解什么是 unbuffered vs buffered channel』: <a href="https://blog.wu-boy.com/2019/04/understand-unbuffered-vs-buffered-channel-in-five-minutes/">https://blog.wu-boy.com/2019/04/understand-unbuffered-vs-buffered-channel-in-five-minutes/</a></p>
<h1 id="Thinks"><a href="#Thinks" class="headerlink" title="Thinks"></a>Thinks</h1><blockquote>
<p>原文链接:<a href="https://blog.wu-boy.com/2019/11/four-tips-with-select-in-golang/">https://blog.wu-boy.com/2019/11/four-tips-with-select-in-golang/</a></p>
</blockquote>
]]></content>
<tags>
<tag>go</tag>
<tag>select</tag>
</tags>
</entry>
<entry>
<title>singleflight本地缓存实例</title>
<url>/2020/03/15/singleflight%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98%E5%AE%9E%E4%BE%8B/</url>
<content><![CDATA[<p>本地保存商户信息,模拟并发情况下缓存穿透情况,到 DB 获取 MCH 信息。</p>
<span id="more"></span>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"errors"</span></span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"sync"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"github.com/golang/groupcache/singleflight"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">500</span>; i++ {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(n <span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="keyword">var</span> mchId = <span class="string">"12345"</span></span><br><span class="line"> <span class="keyword">var</span> mch, err = mchCache.GetMch(mchId)</span><br><span class="line"> fmt.Println(n, mch, err)</span><br><span class="line"> wg.Done()</span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MchCache <span class="keyword">struct</span> {</span><br><span class="line"> Mchs <span class="keyword">map</span>[<span class="type">string</span>]MchInfo</span><br><span class="line"> sync.RWMutex</span><br><span class="line"> g singleflight.Group</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MchInfo <span class="keyword">struct</span> {</span><br><span class="line"> ID <span class="type">string</span></span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> mchCache MchCache</span><br><span class="line"><span class="keyword">var</span> errMchNotFound = errors.New(<span class="string">"mch not found in cache"</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *MchCache)</span></span> GetMch(id <span class="type">string</span>) (mch MchInfo, err <span class="type">error</span>) {</span><br><span class="line"> mch, err = c.getMch(id)</span><br><span class="line"> <span class="keyword">if</span> err == <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> m, err := c.g.Do(id, <span class="function"><span class="keyword">func</span><span class="params">()</span></span> (<span class="keyword">interface</span>{}, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> c.getMchFromDB(id)</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> mch = m.(MchInfo)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *MchCache)</span></span> getMch(id <span class="type">string</span>) (mch MchInfo, err <span class="type">error</span>) {</span><br><span class="line"> c.RLock()</span><br><span class="line"> <span class="keyword">defer</span> c.RUnlock()</span><br><span class="line"> <span class="keyword">if</span> c.Mchs == <span class="literal">nil</span> {</span><br><span class="line"> c.Mchs = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]MchInfo)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> mch, ok := c.Mchs[id]; ok {</span><br><span class="line"> <span class="keyword">return</span> mch, <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> mch, errMchNotFound</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *MchCache)</span></span> SetMch(mch MchInfo) {</span><br><span class="line"> c.Lock()</span><br><span class="line"> <span class="keyword">defer</span> c.Unlock()</span><br><span class="line"> <span class="keyword">if</span> c.Mchs == <span class="literal">nil</span> {</span><br><span class="line"> c.Mchs = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]MchInfo)</span><br><span class="line"> }</span><br><span class="line"> c.Mchs[mch.ID] = mch</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *MchCache)</span></span> getMchFromDB(id <span class="type">string</span>) (mch MchInfo, err <span class="type">error</span>) {</span><br><span class="line"> mch = MchInfo{</span><br><span class="line"> ID: id,</span><br><span class="line"> Name: <span class="string">"星巴克"</span>,</span><br><span class="line"> }</span><br><span class="line"> c.SetMch(mch)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag>go</tag>
<tag>cache</tag>
<tag>singleflight</tag>
</tags>
</entry>
<entry>
<title>在线ssl工具集</title>
<url>/2019/09/29/ssltools/</url>
<content><![CDATA[<p>最近遇到部署https网站,在iOS端访问正常,在Android端访问异常,检查发现是证书链补全导致,缺少中间证书。</p>
<span id="more"></span>
<h1 id="myssl-cn"><a href="#myssl-cn" class="headerlink" title="myssl.cn"></a>myssl.cn</h1><ul>
<li><a href="https://www.myssl.cn/tools/check-server-cert.html">SSL服务器证书安装检查器</a></li>
<li><a href="https://www.myssl.cn/tools/create-csr.html">CSR 在线生成</a></li>
<li><a href="https://www.myssl.cn/tools/downloadchain.html">获取证书信息 及 下载中间证书(可用于证书链修复)</a></li>
</ul>
<h1 id="myssl-com"><a href="#myssl-com" class="headerlink" title="myssl.com"></a>myssl.com</h1><ul>
<li><a href="https://myssl.com/chain_download.html">证书链下载/证书链修复</a></li>
<li><a href="https://myssl.com/ssl.html">SSL状态检测</a></li>
<li><a href="https://myssl.com/csr_create.html">CSR 在线生成</a></li>
<li><a href="https://myssl.com/cert_convert.html">证书格式转换</a></li>
</ul>
<h1 id="web-chacuo-net"><a href="#web-chacuo-net" class="headerlink" title="web.chacuo.net"></a>web.chacuo.net</h1><ul>
<li><a href="http://web.chacuo.net/netcsrdecoder">CSR请求证书文件信息</a></li>
</ul>
]]></content>
<tags>
<tag>ssl</tag>
<tag>https</tag>
</tags>
</entry>
<entry>
<title>supervisor 使用说明</title>
<url>/2020/12/29/supervisor/</url>
<content><![CDATA[<h1 id="Supervisor简单介绍"><a href="#Supervisor简单介绍" class="headerlink" title="Supervisor简单介绍"></a>Supervisor简单介绍</h1><p>supervisor是一个 Client/Server模式的系统,允许用户在类unix操作系统上监视和控制多个进程,或者可以说是多个程序。supervisor与launchd,daemontools,runit等程序有着相同的功能,与其中某些程序不同的是,它并不作为“id 为 1的进程”而替代init。相反,它用于控制应用程序,像启动其它程序一样,通俗理解就是,把Supervisor服务管理的进程程序,它们作为supervisor的子进程来运行,而supervisor是父进程。supervisor来监控管理子进程的启动关闭和异常退出后的自动启动。</p>
<p>至于为什么要用supervisor来管理进程,是因为相对于linux传统的进程管理(即系统自带的init 进程管理)方式来说,它有很多的优势:</p>
<span id="more"></span>
<h2 id="简单方便"><a href="#简单方便" class="headerlink" title="简单方便"></a>简单方便</h2><p>通常管理linux进程的时候,一般来说都需要自己编写一个能够实现进程start/stop/restart/reload功能的脚本,然后丢到/etc/init.d/下面。其实这么做有很多不好的地方:</p>
<ul>
<li>a) 编写这个脚本,耗时耗力。</li>
<li>b) 当这个进程挂掉的时候,linux不会自动重启它的,想要自动重启的话,还要自己另外写一个监控重启脚本。</li>
</ul>
<p>supervisor则可以完美的解决上面这那两个问题! 那么supervisor怎么解决呢?</p>
<ul>
<li>a) supervisor管理进程,就是通过fork/exec的方式把这些被管理的进程,当作supervisor的子进程来启动。这样的话,只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去就OK了。这样就省下了自己写脚本管理linux进程的麻烦了。</li>
<li>b) 被管理进程作为supervisor的子进程,当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,所以也就可以对挂掉的子进程进行自动重启了, 至于重启还是不重启,也要看配置文件里面有没有设置autostart=true。</li>
</ul>
<h2 id="精确"><a href="#精确" class="headerlink" title="精确"></a>精确</h2><p>linux对进程状态的反馈有时候不太准确, 也就是说linux进程通常很难获得准确的up/down状态, Pidfiles经常说谎! 而supervisor监控子进程,得到的子进程状态无疑是准确的。supervisord将进程作为子进程启动,所以它总是知道其子进程的正确的up/down状态,可以方便的对这些数据进行查询. </p>
<h2 id="进程分组"><a href="#进程分组" class="headerlink" title="进程分组"></a>进程分组</h2><p>进程支持分组启动和停止,也支持启动顺序,即‘优先级’,supervisor允许为进程分配优先级,并允许用户通过supervisorctl客户端发出命令,如“全部启动”和”重新启动所有“,它们以预先分配的优先级顺序启动。还可以将进程分为<strong>”进程组“</strong>,一组逻辑关联的进程可以作为一个单元停止或启动。进程组supervisor可以对进程组统一管理,也就是说我们可以把需要管理的进程写到一个组里面,然后把这个组作为一个对象进行管理,如启动,停止,重启等等操作。而linux系统则是没有这种功能的,想要停止一个进程,只能一个一个的去停止,要么就自己写个脚本去批量停止。</p>
<h2 id="集中式管理"><a href="#集中式管理" class="headerlink" title="集中式管理"></a>集中式管理</h2><p>supervisor管理的进程,进程组信息,全部都写在一个ini格式的文件里就OK了。管理supervisor时, 可以在本地进行管理,也可以远程管理,而且supervisor提供了一个web界面,可以在web界面上监控,管理进程。 当然了,本地,远程和web管理的时候,需要调用supervisor的xml_rpc接口。</p>
<h2 id="可扩展性"><a href="#可扩展性" class="headerlink" title="可扩展性"></a>可扩展性</h2><p>supervisor有一个简单的事件(event)通知协议,还有一个用于控制的XML-RPC接口,可以用Python开发人员来扩展构建。</p>
<h2 id="权限"><a href="#权限" class="headerlink" title="权限"></a>权限</h2><p>总所周知, linux的进程特别是侦听在1024端口之下的进程,一般用户大多数情况下,是不能对其进行控制的。想要控制的话,必须要有root权限。然而supervisor提供了一个功能,可以为supervisord或者每个子进程,设置一个非root的user,这个user就可以管理它对应的进程了。</p>
<h2 id="兼容性,稳定性"><a href="#兼容性,稳定性" class="headerlink" title="兼容性,稳定性"></a>兼容性,稳定性</h2><p>supervisor由Python编写,在除Windows操作系统以外基本都支持,如linux,Mac OS x,solaris,FreeBSD系统。</p>
<h1 id="Supervisor组成部分"><a href="#Supervisor组成部分" class="headerlink" title="Supervisor组成部分"></a>Supervisor组成部分</h1><h2 id="supervisord-服务守护进程"><a href="#supervisord-服务守护进程" class="headerlink" title="supervisord: 服务守护进程"></a>supervisord: 服务守护进程</h2><p>supervisor服务器的进程名是supervisord。它主要负责在自己的调用中启动子程序,响应客户端的命令,重新启动崩溃或退出的进程,记录其子进程stdout和stderr的输出,以及生成和处理对应于子进程生命周期中的”event”服务器进程使用的配置文件,通常路径存放在/etc/supervisord.confa中。此配置文件是INI格式的配置文件。</p>
<h2 id="supervisorctl:命令行客户端"><a href="#supervisorctl:命令行客户端" class="headerlink" title="supervisorctl:命令行客户端"></a>supervisorctl:命令行客户端</h2><p>supervisor命令行的客户端名称是supervisorctl。它为supervisord提供了一个类似于shell的交互界面。使用supervisorctl,用户可以查看不同的supervisord进程列表,获取控制子进程的状态,如停止和启动子进程</p>
<h2 id="Web-Server:提供与supervisorctl功能相当的WEB操作界面"><a href="#Web-Server:提供与supervisorctl功能相当的WEB操作界面" class="headerlink" title="Web Server:提供与supervisorctl功能相当的WEB操作界面"></a>Web Server:提供与supervisorctl功能相当的WEB操作界面</h2><p>一个可以通过Web界面来查看和控制进程的状态,默认监听在9091上。</p>
<h2 id="XML-RPC-Interface:XML-RPC接口"><a href="#XML-RPC-Interface:XML-RPC接口" class="headerlink" title="XML-RPC Interface:XML-RPC接口"></a>XML-RPC Interface:XML-RPC接口</h2><p>supervisor用于控制的XML-RPC接口</p>
<h1 id="Supervisor安装-YUM安装"><a href="#Supervisor安装-YUM安装" class="headerlink" title="Supervisor安装 (YUM安装)"></a>Supervisor安装 (YUM安装)</h1><p>centos系统下可以直接yum安装, 前提是需要下载epel源, 下载地址: <a href="http://dl.fedoraproject.org/pub/epel/">http://dl.fedoraproject.org/pub/epel/</a></p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">==================centos6版本系统==================</span><br><span class="line">[root@localhost ~]<span class="comment"># rpm -ivh epel-release-latest-6.noarch.rpm --force</span></span><br><span class="line">[root@localhost ~]<span class="comment"># yum install -y supervisor</span></span><br><span class="line"> </span><br><span class="line">开机启动</span><br><span class="line">[root@localhost ~]<span class="comment"># chkconfig supervisord on</span></span><br><span class="line"> </span><br><span class="line">启动/关闭/重启等操作</span><br><span class="line">[root@localhost ~]<span class="comment"># /etc/init.d/supervisord {start|stop|status|restart|reload|force-reload|condrestart}</span></span><br><span class="line"> </span><br><span class="line">==================centos7版本系统==================</span><br><span class="line">[root@localhost ~]<span class="comment"># rpm -ivh epel-release-latest-7.noarch.rpm --force</span></span><br><span class="line">or</span><br><span class="line">[root@localhost ~]<span class="comment"># yum install -y epel-release</span></span><br><span class="line">[root@localhost ~]<span class="comment"># yum install -y supervisor</span></span><br><span class="line"> </span><br><span class="line">开机启动</span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl enable supervisord</span></span><br><span class="line"> </span><br><span class="line">启动/关闭/重启等操作</span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl start/stop/restart supervisord</span></span><br></pre></td></tr></table></figure>
<p><strong>特别注意另一种安装方式</strong>: 除了 yum 安装方式以外, 我们通过会选用 pip 或 easy_install 方式安装 supervisor。 但是 supervisor 目前只有 python2 支持的版本, 目前不支持python3。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># centos7下安装</span></span><br><span class="line">[root@localhost ~]<span class="comment"># yum install -y python-setuptools</span></span><br><span class="line">[root@localhost ~]<span class="comment"># easy_install supervisor 或者 "pip install supervisor"</span></span><br></pre></td></tr></table></figure>
<p>配置及启动</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 配置文件, 将默认配置保存在/etc/supervisord.conf中</span></span><br><span class="line">[root@localhost ~]<span class="comment"># echo_supervisord_conf > /etc/supervisord.conf </span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">[root@localhost ~]<span class="comment"># supervisord -c /etc/supervisord.conf</span></span><br><span class="line">[root@localhost ~]<span class="comment"># ps -ef|grep /etc/supervisord</span></span><br><span class="line">root 26586 1 0 02:02 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf</span><br><span class="line">root 26588 26184 0 02:02 pts/0 00:00:00 grep --color=auto /etc/supervisord</span><br><span class="line"> </span><br><span class="line">[root@localhost ~]<span class="comment"># which supervisord</span></span><br><span class="line">/usr/bin/supervisord</span><br><span class="line">[root@localhost ~]<span class="comment"># which supervisorctl</span></span><br><span class="line">/usr/bin/supervisorctl</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 如果没有下面文件, 就手动创建</span></span><br><span class="line">[root@localhost ~]<span class="comment"># vim /usr/lib/systemd/system/supervisord.service</span></span><br><span class="line">[Unit]</span><br><span class="line">Description=Process Monitoring and Control Daemon</span><br><span class="line">After=rc-local.service nss-user-lookup.target</span><br><span class="line"> </span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">ExecStart=/usr/bin/supervisord -c /etc/supervisord.conf</span><br><span class="line">ExecReload=/usr/bin/supervisorctl reload </span><br><span class="line">ExecStop=/usr/bin/supervisorctl shutdown </span><br><span class="line"> </span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 设置755权限</span></span><br><span class="line">[root@localhost ~]<span class="comment"># chmod 755 /usr/lib/systemd/system/supervisord.service</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 重启服务(多测试几次)</span></span><br><span class="line">[root@localhost ~]<span class="comment"># ps -ef|grep /etc/supervisord</span></span><br><span class="line">root 26586 1 0 02:02 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf</span><br><span class="line">root 26933 26184 0 02:08 pts/0 00:00:00 grep --color=auto /etc/supervisord</span><br><span class="line">[root@localhost ~]<span class="comment"># /usr/bin/supervisorctl shutdown</span></span><br><span class="line">Shut down</span><br><span class="line">[root@localhost ~]<span class="comment"># ps -ef|grep /etc/supervisord </span></span><br><span class="line">root 26940 26184 0 02:08 pts/0 00:00:00 grep --color=auto /etc/supervisord</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 由于上面的supervisord进程是使用配置文件手动启动的, 首次要使用下面的命令关闭,然后使用"systemctl start/stop supervisord" 才会生效!</span></span><br><span class="line"><span class="comment"># 如果第一次不使用下面命令关闭, 而首次就使用"systemctl stop supervisord" 则关闭不了.</span></span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl start supervisord</span></span><br><span class="line">[root@localhost ~]<span class="comment"># ps -ef|grep /etc/supervisord</span></span><br><span class="line">root 27049 1 0 02:09 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf</span><br><span class="line">root 27052 26184 0 02:09 pts/0 00:00:00 grep --color=auto /etc/supervisord</span><br><span class="line"> </span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl stop supervisord </span></span><br><span class="line">[root@localhost ~]<span class="comment"># ps -ef|grep /etc/supervisord</span></span><br><span class="line">root 27068 26184 0 02:09 pts/0 00:00:00 grep --color=auto /etc/supervisord</span><br><span class="line"> </span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl start supervisord</span></span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl restart supervisord</span></span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl reload supervisord</span></span><br><span class="line">[root@localhost ~]<span class="comment"># ps -ef|grep /etc/supervisord</span></span><br><span class="line">root 27097 1 0 02:09 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf</span><br><span class="line">root 27100 26184 0 02:09 pts/0 00:00:00 grep --color=auto /etc/supervisord</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 开机启动</span></span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl enable supervisord</span></span><br><span class="line">Created symlink from /etc/systemd/system/multi-user.target.wants/supervisord.service to /usr/lib/systemd/system/supervisord.service.</span><br></pre></td></tr></table></figure>
<p>实例</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 如下简单配置实例</span></span><br><span class="line">[root@localhost ~]<span class="comment"># vim /etc/supervisord.conf</span></span><br><span class="line">.......</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 不采用sock连接方式</span></span><br><span class="line">;[unix_http_server]</span><br><span class="line">;file=/tmp/supervisor.sock ; the path to the socket file</span><br><span class="line">;<span class="built_in">chmod</span>=0700 ; socket file mode (default 0700)</span><br><span class="line">;<span class="built_in">chown</span>=nobody:nogroup ; socket file uid:gid owner</span><br><span class="line">;username=user ; default is no username (open server)</span><br><span class="line">;password=123 ; default is no password (open server)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 采用http连接方式, 并设置登录验证信息</span></span><br><span class="line">[inet_http_server] ; inet (TCP) server disabled by default</span><br><span class="line">port=192.168.10.10:9001 ; ip_address:port specifier, *:port <span class="keyword">for</span> all iface</span><br><span class="line">username=user ; default is no username (open server)</span><br><span class="line">password=123 ; default is no password (open server)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 添加被监控程序,被监控程序自身非后台运行</span></span><br><span class="line">[program:nginx]</span><br><span class="line"><span class="built_in">command</span>=/usr/local/nginx/sbin/nginx</span><br><span class="line">process_name=nginx</span><br><span class="line">autostart=<span class="literal">true</span></span><br><span class="line">autorestart=<span class="literal">true</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 使配置生效</span></span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl reload supervisord</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 需要注意的地方</span></span><br><span class="line"><span class="comment"># 1) 如果autostart设为true(默认),而被监控进程为单例模式运行,则在被监控进程已运行的情况下重启supervisord,</span></span><br><span class="line"><span class="comment"># supervisord会不停的尝试启动被监控进程,造成资源浪费。</span></span><br><span class="line"><span class="comment"># 2) 被监控进程的stdout和stderr输出默认被保存在单独的临时文件中,可根据需要进行配置。supervisord默认启动时会清除这些临时文件,</span></span><br><span class="line"><span class="comment"># 如需要可修改配置nocleanup=true。建议将被监控进程的输出手动重定向。</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 运行</span></span><br><span class="line">[root@localhost ~]<span class="comment"># systemctl start supervisord</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># supervisor日志文件: /var/log/supervisor/supervisord.log</span></span><br><span class="line"><span class="comment"># supervisor配置文件: /etc/supervisord.conf</span></span><br><span class="line"><span class="comment"># supervisor连接方式: http和sock 两种</span></span><br></pre></td></tr></table></figure>
<h1 id="supervisor监控管理"><a href="#supervisor监控管理" class="headerlink" title="supervisor监控管理"></a>supervisor监控管理</h1><h2 id="web界面管理"><a href="#web界面管理" class="headerlink" title="web界面管理"></a>web界面管理</h2><p>为了更方便的远程管理 Supervisor ,我们还可以开启其自带的 web 控制台。打开 /etc/supervisord.conf ,在文件末尾增加以下内容:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[root@kevin ~]<span class="comment"># vim /etc/supervisord.conf</span></span><br><span class="line">......</span><br><span class="line">[inet_http_server]</span><br><span class="line">port=*:5000</span><br><span class="line">username=user</span><br><span class="line">password=123456</span><br><span class="line"> </span><br><span class="line">配置说明:</span><br><span class="line">port 为监听端口,username 和 password 分别为帐号和密码。</span><br><span class="line">保存文件后需要重启supervisor</span><br><span class="line">然后访问<span class="string">"http://ip:5000"</span> , 输入上面设置的用户名和密码即可打开supervisor的web 控制台了。</span><br></pre></td></tr></table></figure>
<h2 id="客户端命令行操作"><a href="#客户端命令行操作" class="headerlink" title="客户端命令行操作"></a>客户端命令行操作</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[root@170~ ~]<span class="comment"># supervisorctl -s http://ip:port -u user -p passwd command</span></span><br></pre></td></tr></table></figure>
<h2 id="通过shell来管理supervisor-,可以使用以下命令:"><a href="#通过shell来管理supervisor-,可以使用以下命令:" class="headerlink" title="通过shell来管理supervisor ,可以使用以下命令:"></a>通过shell来管理supervisor ,可以使用以下命令:</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl start appname <span class="comment">#启动特定程序</span></span><br><span class="line">supervisorctl stop appname <span class="comment">#停止特定程序</span></span><br><span class="line">supervisorctl restart appname <span class="comment">#重启特定程序</span></span><br><span class="line">supervisorctl start all <span class="comment">#启动所有程序</span></span><br><span class="line">supervisorctl stop all <span class="comment">#停止所有程序</span></span><br><span class="line">supervisorctl restart all <span class="comment">#重启所有程序</span></span><br></pre></td></tr></table></figure>
<p>最后需要注意的是:</p>
<blockquote>
<p>如果使用 Supervisor 监控 shell 脚本,不能在脚本中完全使用 nohup, setsid 等后台运行命令,否则 supervisor 会误认为程序自动退出而不断重启脚本。</p>
</blockquote>
<p>Supervisord 安装完成后有两个可用的命令行: supervisor 和 supervisorctl</p>
<p><strong>常见的命令如下:</strong></p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisord <span class="comment"># 初始启动Supervisord,启动、管理配置中设置的进程</span></span><br><span class="line">supervisorctl stop programxxx <span class="comment"># 停止某一个进程(programxxx),programxxx为[program:chatdemon]里配置的值,这个示例就是 chatdemon</span></span><br><span class="line">supervisorctl start programxxx <span class="comment"># 启动某个进程</span></span><br><span class="line">supervisorctl restart programxxx <span class="comment"># 重启某个进程</span></span><br><span class="line">supervisorctl stop groupworker <span class="comment"># 重启所有属于名为 groupworker 这个分组的进程(start,restart同理)</span></span><br><span class="line">supervisorctl stop all <span class="comment"># 停止全部进程,注:start、restart、stop 都不会载入最新的配置文件</span></span><br><span class="line">supervisorctl reload <span class="comment"># 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程</span></span><br><span class="line">supervisorctl update <span class="comment"># 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启。注意:显示用stop停止掉的进程,用reload或者update都不会自动重启</span></span><br></pre></td></tr></table></figure>
<p><strong>常见命令</strong></p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl <span class="built_in">tail</span> programname <span class="comment"># 查看programname的日志</span></span><br><span class="line">supervisorctl <span class="built_in">tail</span> redis <span class="comment"># 查看日志</span></span><br></pre></td></tr></table></figure>
<p><strong>supervisor事件监听及通知机制</strong></p>
<ul>
<li>supervisor 向 listeners 发送和子进程或自身有关的 notification 。对于同一pool内的 listeners,supervisor 会选取任一可用的进行通知。</li>
<li>配置被监控进程 [program:x] 的日志 Capture Mode,被监控进程可向stdout输出业务数据,由 supervisod 捕获这些数据,发给listener。</li>
<li>配置 event-listener:监听 PROCESS_COMMUNICATION_STDOUT 事件</li>
<li>envent-listener 模块开发:使用 python 的 supervisor.childutils 模块。该模块可作为监控代理模块,和进程及网管服务通信。与网管服务可采用 redis 的 list 实现。</li>
</ul>
<h1 id="使用-supervisorctl-管理进程"><a href="#使用-supervisorctl-管理进程" class="headerlink" title="使用 supervisorctl 管理进程"></a>使用 supervisorctl 管理进程</h1><ul>
<li>停止某一个进程,program_name 为 [program:x] 里的 x:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl stop program_name</span><br></pre></td></tr></table></figure>
<ul>
<li>启动某个进程:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl start program_name</span><br></pre></td></tr></table></figure>
<ul>
<li>重启某个进程:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl restart program_name</span><br></pre></td></tr></table></figure>
<ul>
<li>停止全部进程,注:start、restart、stop 都不会载入最新的配置文件:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl stop all</span><br></pre></td></tr></table></figure>
<ul>
<li>载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl reload</span><br></pre></td></tr></table></figure>
<ul>
<li>根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">supervisorctl update</span><br></pre></td></tr></table></figure>
<h1 id="Supervisor配置文件说明"><a href="#Supervisor配置文件说明" class="headerlink" title="Supervisor配置文件说明"></a>Supervisor配置文件说明</h1><p>[program:x]中配置要监控的进程</p>
<figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="section">[unix_http_server]</span> </span><br><span class="line"><span class="attr">file</span>=/tmp/supervisor.sock <span class="comment">; socket文件的路径,supervisorctl用XML_RPC和supervisord通信就是通过它进行</span></span><br><span class="line"> 的。如果不设置的话,supervisorctl也就不能用了</span><br><span class="line"> 不设置的话,默认为none。 非必须设置 </span><br><span class="line"><span class="comment">;chmod=0700 ; 这个简单,就是修改上面的那个socket文件的权限为0700</span></span><br><span class="line"> 不设置的话,默认为0700。 非必须设置</span><br><span class="line"><span class="comment">;chown=nobody:nogroup ; 这个一样,修改上面的那个socket文件的属组为user.group</span></span><br><span class="line"> 不设置的话,默认为启动supervisord进程的用户及属组。非必须设置</span><br><span class="line"><span class="comment">;username=user ; 使用supervisorctl连接的时候,认证的用户</span></span><br><span class="line"> 不设置的话,默认为不需要用户。 非必须设置</span><br><span class="line"><span class="comment">;password=123 ; 和上面的用户名对应的密码,可以直接使用明码,也可以使用SHA加密</span></span><br><span class="line"> 如:{SHA}82ab876d1387bfafe46cc1c8a2ef074eae50cb1d</span><br><span class="line"> 默认不设置。。。非必须设置</span><br><span class="line"> </span><br><span class="line"><span class="comment">;[inet_http_server] ; 侦听在TCP上的socket,Web Server和远程的supervisorctl都要用到他</span></span><br><span class="line"> 不设置的话,默认为不开启。非必须设置</span><br><span class="line"><span class="comment">;port=127.0.0.1:9001 ; 这个是侦听的IP和端口,侦听所有IP用 :9001或*:9001。</span></span><br><span class="line"> 这个必须设置,只要上面的<span class="section">[inet_http_server]</span>开启了,就必须设置它</span><br><span class="line"><span class="comment">;username=user ; 这个和上面的uinx_http_server一个样。非必须设置</span></span><br><span class="line"><span class="comment">;password=123 ; 这个也一个样。非必须设置</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[supervisord]</span> <span class="comment">;这个主要是定义supervisord这个服务端进程的一些参数的</span></span><br><span class="line"> 这个必须设置,不设置,supervisor就不用干活了</span><br><span class="line"><span class="attr">logfile</span>=/tmp/supervisord.log <span class="comment">; 这个是supervisord这个主进程的日志路径,注意和子进程的日志不搭嘎。</span></span><br><span class="line"> 默认路径$CWD/supervisord.log,$CWD是当前目录。。非必须设置</span><br><span class="line"><span class="attr">logfile_maxbytes</span>=<span class="number">50</span>MB <span class="comment">; 这个是上面那个日志文件的最大的大小,当超过50M的时候,会生成一个新的日</span></span><br><span class="line"> 志文件。当设置为0时,表示不限制文件大小</span><br><span class="line"> 默认值是50M,非必须设置。 </span><br><span class="line"><span class="attr">logfile_backups</span>=<span class="number">10</span> <span class="comment">; 日志文件保持的数量,上面的日志文件大于50M时,就会生成一个新文件。文件</span></span><br><span class="line"> 数量大于10时,最初的老文件被新文件覆盖,文件数量将保持为10</span><br><span class="line"> 当设置为0时,表示不限制文件的数量。</span><br><span class="line"> 默认情况下为10。。。非必须设置</span><br><span class="line"><span class="attr">loglevel</span>=info <span class="comment">; 日志级别,有critical, error, warn, info, debug, trace, or blather等</span></span><br><span class="line"> 默认为info。。。非必须设置项</span><br><span class="line"><span class="attr">pidfile</span>=/tmp/supervisord.pid <span class="comment">; supervisord的pid文件路径。</span></span><br><span class="line"> 默认为$CWD/supervisord.pid。。。非必须设置</span><br><span class="line"><span class="attr">nodaemon</span>=<span class="literal">false</span> <span class="comment">; 如果是true,supervisord进程将在前台运行</span></span><br><span class="line"> 默认为false,也就是后台以守护进程运行。。。非必须设置</span><br><span class="line"><span class="attr">minfds</span>=<span class="number">1024</span> <span class="comment">; 这个是最少系统空闲的文件描述符,低于这个值supervisor将不会启动。</span></span><br><span class="line"> 系统的文件描述符在这里设置cat /proc/sys/fs/file-max</span><br><span class="line"> 默认情况下为1024。。。非必须设置</span><br><span class="line"><span class="attr">minprocs</span>=<span class="number">200</span> <span class="comment">; 最小可用的进程描述符,低于这个值supervisor也将不会正常启动。</span></span><br><span class="line"> ulimit -u这个命令,可以查看linux下面用户的最大进程数</span><br><span class="line"> 默认为200。。。非必须设置</span><br><span class="line"><span class="comment">;umask=022 ; 进程创建文件的掩码</span></span><br><span class="line"> 默认为022。。非必须设置项</span><br><span class="line"><span class="comment">;user=chrism ; 这个参数可以设置一个非root用户,当我们以root用户启动supervisord之后。</span></span><br><span class="line"> 我这里面设置的这个用户,也可以对supervisord进行管理</span><br><span class="line"> 默认情况是不设置。。。非必须设置项</span><br><span class="line"><span class="comment">;identifier=supervisor ; 这个参数是supervisord的标识符,主要是给XML_RPC用的。当你有多个</span></span><br><span class="line"> supervisor的时候,而且想调用XML_RPC统一管理,就需要为每个</span><br><span class="line"> supervisor设置不同的标识符了</span><br><span class="line"> 默认是supervisord。。。非必需设置</span><br><span class="line"><span class="comment">;directory=/tmp ; 这个参数是当supervisord作为守护进程运行的时候,设置这个参数的话,启动</span></span><br><span class="line"> supervisord进程之前,会先切换到这个目录</span><br><span class="line"> 默认不设置。。。非必须设置</span><br><span class="line"><span class="comment">;nocleanup=true ; 这个参数当为false的时候,会在supervisord进程启动的时候,把以前子进程</span></span><br><span class="line"> 产生的日志文件(路径为AUTO的情况下)清除掉。有时候咱们想要看历史日志,当</span><br><span class="line"> 然不想日志被清除了。所以可以设置为true</span><br><span class="line"> 默认是false,有调试需求的同学可以设置为true。。。非必须设置</span><br><span class="line"><span class="comment">;childlogdir=/tmp ; 当子进程日志路径为AUTO的时候,子进程日志文件的存放路径。</span></span><br><span class="line"> 默认路径是这个东西,执行下面的这个命令看看就OK了,处理的东西就默认路径</span><br><span class="line"> python -c "import tempfile<span class="comment">;print tempfile.gettempdir()"</span></span><br><span class="line"> 非必须设置</span><br><span class="line"><span class="comment">;environment=KEY="value" ; 这个是用来设置环境变量的,supervisord在linux中启动默认继承了linux的</span></span><br><span class="line"> 环境变量,在这里可以设置supervisord进程特有的其他环境变量。</span><br><span class="line"> supervisord启动子进程时,子进程会拷贝父进程的内存空间内容。 所以设置的</span><br><span class="line"> 这些环境变量也会被子进程继承。</span><br><span class="line"> 小例子:<span class="attr">environment</span>=name=<span class="string">"haha"</span>,age=<span class="string">"hehe"</span></span><br><span class="line"> 默认为不设置。。。非必须设置</span><br><span class="line"><span class="comment">;strip_ansi=false ; 这个选项如果设置为true,会清除子进程日志中的所有ANSI 序列。什么是ANSI</span></span><br><span class="line"> 序列呢?就是我们的\n,\t这些东西。</span><br><span class="line"> 默认为false。。。非必须设置</span><br><span class="line"> </span><br><span class="line"><span class="comment">; the below section must remain in the config file for RPC</span></span><br><span class="line"><span class="comment">; (supervisorctl/web interface) to work, additional interfaces may be</span></span><br><span class="line"><span class="comment">; added by defining them in separate rpcinterface: sections</span></span><br><span class="line"><span class="section">[rpcinterface:supervisor]</span> <span class="comment">;这个选项是给XML_RPC用的,当然你如果想使用supervisord或者web server 这</span></span><br><span class="line"> 个选项必须要开启的</span><br><span class="line"><span class="attr">supervisor.rpcinterface_factory</span> = supervisor.rpcinterface:make_main_rpcinterface</span><br><span class="line"> </span><br><span class="line"><span class="section">[supervisorctl]</span> <span class="comment">;这个主要是针对supervisorctl的一些配置</span></span><br><span class="line"><span class="attr">serverurl</span>=unix:///tmp/supervisor.sock <span class="comment">; 这个是supervisorctl本地连接supervisord的时候,本地UNIX socket</span></span><br><span class="line"> 路径,注意这个是和前面的<span class="section">[unix_http_server]</span>对应的</span><br><span class="line"> 默认值就是unix:///tmp/supervisor.sock。。非必须设置</span><br><span class="line"><span class="comment">;serverurl=http://127.0.0.1:9001 ; 这个是supervisorctl远程连接supervisord的时候,用到的TCP socket路径</span></span><br><span class="line"> 注意这个和前面的<span class="section">[inet_http_server]</span>对应</span><br><span class="line"> 默认就是http://127.0.0.1:9001。。。非必须项</span><br><span class="line"> </span><br><span class="line"><span class="comment">;username=chris ; 用户名</span></span><br><span class="line"> 默认空。。非必须设置</span><br><span class="line"><span class="comment">;password=123 ; 密码</span></span><br><span class="line"> 默认空。。非必须设置</span><br><span class="line"><span class="comment">;prompt=mysupervisor ; 输入用户名密码时候的提示符</span></span><br><span class="line"> 默认supervisor。。非必须设置</span><br><span class="line"><span class="comment">;history_file=~/.sc_history ; 这个参数和shell中的history类似,我们可以用上下键来查找前面执行过的命令</span></span><br><span class="line"> 默认是no file的。。所以我们想要有这种功能,必须指定一个文件。。。非</span><br><span class="line"> 必须设置</span><br><span class="line"> </span><br><span class="line"><span class="comment">; The below sample program section shows all possible program subsection values,</span></span><br><span class="line"><span class="comment">; create one or more 'real' program: sections to be able to control them under</span></span><br><span class="line"><span class="comment">; supervisor.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">;[program:theprogramname] ;这个就是咱们要管理的子进程了,":"后面的是名字,最好别乱写和实际进程</span></span><br><span class="line"> 有点关联最好。这样的program我们可以设置一个或多个,一个program就是</span><br><span class="line"> 要被管理的一个进程</span><br><span class="line"><span class="comment">;command=/bin/cat ; 这个就是我们的要启动进程的命令路径了,可以带参数</span></span><br><span class="line"> 例子:/home/test.py -a 'hehe'</span><br><span class="line"> 有一点需要注意的是,我们的command只能是那种在终端运行的进程,不能是</span><br><span class="line"> 守护进程。这个想想也知道了,比如说<span class="attr">command</span>=service httpd start。</span><br><span class="line"> httpd这个进程被linux的service管理了,我们的supervisor再去启动这个命令</span><br><span class="line"> 这已经不是严格意义的子进程了。</span><br><span class="line"> 这个是个必须设置的项</span><br><span class="line"><span class="comment">;process_name=%(program_name)s ; 这个是进程名,如果我们下面的numprocs参数为1的话,就不用管这个参数</span></span><br><span class="line"> 了,它默认值%(program_name)s也就是上面的那个program冒号后面的名字,</span><br><span class="line"> 但是如果numprocs为多个的话,那就不能这么干了。想想也知道,不可能每个</span><br><span class="line"> 进程都用同一个进程名吧。</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="comment">;numprocs=1 ; 启动进程的数目。当不为1时,就是进程池的概念,注意process_name的设置</span></span><br><span class="line"> 默认为1 。。非必须设置</span><br><span class="line"><span class="comment">;directory=/tmp ; 进程运行前,会前切换到这个目录</span></span><br><span class="line"> 默认不设置。。。非必须设置</span><br><span class="line"><span class="comment">;umask=022 ; 进程掩码,默认none,非必须</span></span><br><span class="line"><span class="comment">;priority=999 ; 子进程启动关闭优先级,优先级低的,最先启动,关闭的时候最后关闭</span></span><br><span class="line"> 默认值为999 。。非必须设置</span><br><span class="line"><span class="comment">;autostart=true ; 如果是true的话,子进程将在supervisord启动后被自动启动</span></span><br><span class="line"> 默认就是true 。。非必须设置</span><br><span class="line"><span class="comment">;autorestart=unexpected ; 这个是设置子进程挂掉后自动重启的情况,有三个选项,false,unexpected</span></span><br><span class="line"> 和true。如果为false的时候,无论什么情况下,都不会被重新启动,</span><br><span class="line"> 如果为unexpected,只有当进程的退出码不在下面的exitcodes里面定义的退</span><br><span class="line"> 出码的时候,才会被自动重启。当为true的时候,只要子进程挂掉,将会被无</span><br><span class="line"> 条件的重启</span><br><span class="line"><span class="comment">;startsecs=1 ; 这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启</span></span><br><span class="line"> 动成功了</span><br><span class="line"> 默认值为1 。。非必须设置</span><br><span class="line"><span class="comment">;startretries=3 ; 当进程启动失败后,最大尝试启动的次数。。当超过3次后,supervisor将把</span></span><br><span class="line"> 此进程的状态置为FAIL</span><br><span class="line"> 默认值为3 。。非必须设置</span><br><span class="line"><span class="comment">;exitcodes=0,2 ; 注意和上面的的autorestart=unexpected对应。。exitcodes里面的定义的</span></span><br><span class="line"> 退出码是expected的。</span><br><span class="line"><span class="comment">;stopsignal=QUIT ; 进程停止信号,可以为TERM, HUP, INT, QUIT, KILL, USR1, or USR2等信号</span></span><br><span class="line"> 默认为TERM 。。当用设定的信号去干掉进程,退出码会被认为是expected</span><br><span class="line"> 非必须设置</span><br><span class="line"><span class="comment">;stopwaitsecs=10 ; 这个是当我们向子进程发送stopsignal信号后,到系统返回信息</span></span><br><span class="line"> 给supervisord,所等待的最大时间。 超过这个时间,supervisord会向该</span><br><span class="line"> 子进程发送一个强制kill的信号。</span><br><span class="line"> 默认为10秒。。非必须设置</span><br><span class="line"><span class="comment">;stopasgroup=false ; 这个东西主要用于,supervisord管理的子进程,这个子进程本身还有</span></span><br><span class="line"> 子进程。那么我们如果仅仅干掉supervisord的子进程的话,子进程的子进程</span><br><span class="line"> 有可能会变成孤儿进程。所以咱们可以设置可个选项,把整个该子进程的</span><br><span class="line"> 整个进程组都干掉。 设置为true的话,一般killasgroup也会被设置为true。</span><br><span class="line"> 需要注意的是,该选项发送的是stop信号</span><br><span class="line"> 默认为false。。非必须设置。。</span><br><span class="line"><span class="comment">;killasgroup=false ; 这个和上面的stopasgroup类似,不过发送的是kill信号</span></span><br><span class="line"><span class="comment">;user=chrism ; 如果supervisord是root启动,我们在这里设置这个非root用户,可以用来</span></span><br><span class="line"> 管理该program</span><br><span class="line"> 默认不设置。。。非必须设置项</span><br><span class="line"><span class="comment">;redirect_stderr=true ; 如果为true,则stderr的日志会被写入stdout日志文件中</span></span><br><span class="line"> 默认为false,非必须设置</span><br><span class="line"><span class="comment">;stdout_logfile=/a/path ; 子进程的stdout的日志路径,可以指定路径,AUTO,none等三个选项。</span></span><br><span class="line"> 设置为none的话,将没有日志产生。设置为AUTO的话,将随机找一个地方</span><br><span class="line"> 生成日志文件,而且当supervisord重新启动的时候,以前的日志文件会被</span><br><span class="line"> 清空。当 <span class="attr">redirect_stderr</span>=<span class="literal">true</span>的时候,sterr也会写进这个日志文件</span><br><span class="line"><span class="comment">;stdout_logfile_maxbytes=1MB ; 日志文件最大大小,和[supervisord]中定义的一样。默认为50</span></span><br><span class="line"><span class="comment">;stdout_logfile_backups=10 ; 和[supervisord]定义的一样。默认10</span></span><br><span class="line"><span class="comment">;stdout_capture_maxbytes=1MB ; 这个东西是设定capture管道的大小,当值不为0的时候,子进程可以从stdout</span></span><br><span class="line"> 发送信息,而supervisor可以根据信息,发送相应的event。</span><br><span class="line"> 默认为0,为0的时候表达关闭管道。。。非必须项</span><br><span class="line"><span class="comment">;stdout_events_enabled=false ; 当设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候,将</span></span><br><span class="line"> 触发supervisord发送PROCESS_LOG_STDOUT类型的event</span><br><span class="line"> 默认为false。。。非必须设置</span><br><span class="line"><span class="comment">;stderr_logfile=/a/path ; 这个东西是设置stderr写的日志路径,当redirect_stderr=true。这个就不用</span></span><br><span class="line"> 设置了,设置了也是白搭。因为它会被写入stdout_logfile的同一个文件中</span><br><span class="line"> 默认为AUTO,也就是随便找个地存,supervisord重启被清空。。非必须设置</span><br><span class="line"><span class="comment">;stderr_logfile_maxbytes=1MB ; 这个出现好几次了,就不重复了</span></span><br><span class="line"><span class="comment">;stderr_logfile_backups=10 ; 这个也是</span></span><br><span class="line"><span class="comment">;stderr_capture_maxbytes=1MB ; 这个一样,和stdout_capture一样。 默认为0,关闭状态</span></span><br><span class="line"><span class="comment">;stderr_events_enabled=false ; 这个也是一样,默认为false</span></span><br><span class="line"><span class="comment">;environment=A="1",B="2" ; 这个是该子进程的环境变量,和别的子进程是不共享的</span></span><br><span class="line"><span class="comment">;serverurl=AUTO ;</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">; The below sample eventlistener section shows all possible</span></span><br><span class="line"><span class="comment">; eventlistener subsection values, create one or more 'real'</span></span><br><span class="line"><span class="comment">; eventlistener: sections to be able to handle event notifications</span></span><br><span class="line"><span class="comment">; sent by supervisor.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">;[eventlistener:theeventlistenername] ;这个东西其实和program的地位是一样的,也是suopervisor启动的子进</span></span><br><span class="line"> 程,不过它干的活是订阅supervisord发送的event。他的名字就叫</span><br><span class="line"> listener了。我们可以在listener里面做一系列处理,比如报警等等</span><br><span class="line"> 楼主这两天干的活,就是弄的这玩意</span><br><span class="line"><span class="comment">;command=/bin/eventlistener ; 这个和上面的program一样,表示listener的可执行文件的路径</span></span><br><span class="line"><span class="comment">;process_name=%(program_name)s ; 这个也一样,进程名,当下面的numprocs为多个的时候,才需要。否则默认就</span></span><br><span class="line"> OK了</span><br><span class="line"><span class="comment">;numprocs=1 ; 相同的listener启动的个数</span></span><br><span class="line"><span class="comment">;events=EVENT ; event事件的类型,也就是说,只有写在这个地方的事件类型。才会被发送</span></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="comment">;buffer_size=10 ; 这个是event队列缓存大小,单位不太清楚,楼主猜测应该是个吧。当buffer</span></span><br><span class="line"> 超过10的时候,最旧的event将会被清除,并把新的event放进去。</span><br><span class="line"> 默认值为10。。非必须选项</span><br><span class="line"><span class="comment">;directory=/tmp ; 进程执行前,会切换到这个目录下执行</span></span><br><span class="line"> 默认为不切换。。。非必须</span><br><span class="line"><span class="comment">;umask=022 ; 掩码,默认为none,不说了</span></span><br><span class="line"><span class="comment">;priority=-1 ; 启动优先级,默认-1,也不扯了</span></span><br><span class="line"><span class="comment">;autostart=true ; 是否随supervisord启动一起启动,默认true</span></span><br><span class="line"><span class="comment">;autorestart=unexpected ; 是否自动重启,和program一个样,分true,false,unexpected等,注意</span></span><br><span class="line"> unexpected和exitcodes的关系</span><br><span class="line"><span class="comment">;startsecs=1 ; 也是一样,进程启动后跑了几秒钟,才被认定为成功启动,默认1</span></span><br><span class="line"><span class="comment">;startretries=3 ; 失败最大尝试次数,默认3</span></span><br><span class="line"><span class="comment">;exitcodes=0,2 ; 期望或者说预料中的进程退出码,</span></span><br><span class="line"><span class="comment">;stopsignal=QUIT ; 干掉进程的信号,默认为TERM,比如设置为QUIT,那么如果QUIT来干这个进程</span></span><br><span class="line"> 那么会被认为是正常维护,退出码也被认为是expected中的</span><br><span class="line"><span class="comment">;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)</span></span><br><span class="line"><span class="comment">;stopasgroup=false ; send stop signal to the UNIX process group (default false)</span></span><br><span class="line"><span class="comment">;killasgroup=false ; SIGKILL the UNIX process group (def false)</span></span><br><span class="line"><span class="comment">;user=chrism ;设置普通用户,可以用来管理该listener进程。</span></span><br><span class="line"> 默认为空。。非必须设置</span><br><span class="line"><span class="comment">;redirect_stderr=true ; 为true的话,stderr的log会并入stdout的log里面</span></span><br><span class="line"> 默认为false。。。非必须设置</span><br><span class="line"><span class="comment">;stdout_logfile=/a/path ; 这个不说了,好几遍了</span></span><br><span class="line"><span class="comment">;stdout_logfile_maxbytes=1MB ; 这个也是</span></span><br><span class="line"><span class="comment">;stdout_logfile_backups=10 ; 这个也是</span></span><br><span class="line"><span class="comment">;stdout_events_enabled=false ; 这个其实是错的,listener是不能发送event</span></span><br><span class="line"><span class="comment">;stderr_logfile=/a/path ; 这个也是</span></span><br><span class="line"><span class="comment">;stderr_logfile_maxbytes=1MB ; 这个也是</span></span><br><span class="line"><span class="comment">;stderr_logfile_backups ; 这个不说了</span></span><br><span class="line"><span class="comment">;stderr_events_enabled=false ; 这个也是错的,listener不能发送event</span></span><br><span class="line"><span class="comment">;environment=A="1",B="2" ; 这个是该子进程的环境变量</span></span><br><span class="line"> 默认为空。。。非必须设置</span><br><span class="line"><span class="comment">;serverurl=AUTO ; override serverurl computation (childutils)</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">; The below sample group section shows all possible group values,</span></span><br><span class="line"><span class="comment">; create one or more 'real' group: sections to create "heterogeneous"</span></span><br><span class="line"><span class="comment">; process groups.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">;[group:thegroupname] ;这个东西就是给programs分组,划分到组里面的program。我们就不用一个一个去操作了</span></span><br><span class="line"> 我们可以对组名进行统一的操作。 注意:program被划分到组里面之后,就相当于原来</span><br><span class="line"> 的配置从supervisor的配置文件里消失了。。。supervisor只会对组进行管理,而不再</span><br><span class="line"> 会对组里面的单个program进行管理了</span><br><span class="line"><span class="comment">;programs=progname1,progname2 ; 组成员,用逗号分开</span></span><br><span class="line"> 这个是个必须的设置项</span><br><span class="line"><span class="comment">;priority=999 ; 优先级,相对于组和组之间说的</span></span><br><span class="line"> 默认999。。非必须选项</span><br><span class="line"> </span><br><span class="line"><span class="comment">; The [include] section can just contain the "files" setting. This</span></span><br><span class="line"><span class="comment">; setting can list multiple files (separated by whitespace or</span></span><br><span class="line"><span class="comment">; newlines). It can also contain wildcards. The filenames are</span></span><br><span class="line"><span class="comment">; interpreted as relative to this file. Included files *cannot*</span></span><br><span class="line"><span class="comment">; include files themselves.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">;[include] ;这个东西挺有用的,当我们要管理的进程很多的时候,写在一个文件里面</span></span><br><span class="line"> 就有点大了。我们可以把配置信息写到多个文件中,然后include过来</span><br><span class="line"><span class="comment">;files = relative/directory/*.ini</span></span><br></pre></td></tr></table></figure>
<h2 id="样例"><a href="#样例" class="headerlink" title="样例"></a>样例</h2><figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="section">[supervisord]</span></span><br><span class="line"><span class="attr">http_port</span>=/var/tmp/supervisor.sock <span class="comment">; (default is to run a UNIX domain socket server)</span></span><br><span class="line"><span class="comment">;http_port=127.0.0.1:9001 ; (alternately, ip_address:port specifies AF_INET)</span></span><br><span class="line"><span class="comment">;sockchmod=0700 ; AF_UNIX socketmode (AF_INET ignore, default 0700)</span></span><br><span class="line"><span class="comment">;sockchown=nobody.nogroup ; AF_UNIX socket uid.gid owner (AF_INET ignores)</span></span><br><span class="line"><span class="comment">;umask=022 ; (process file creation umask;default 022)</span></span><br><span class="line"><span class="attr">logfile</span>=/var/log/supervisor/supervisord.log <span class="comment">; (main log file;default $CWD/supervisord.log)</span></span><br><span class="line"><span class="attr">logfile_maxbytes</span>=<span class="number">50</span>MB <span class="comment">; (max main logfile bytes b4 rotation;default 50MB)</span></span><br><span class="line"><span class="attr">logfile_backups</span>=<span class="number">10</span> <span class="comment">; (num of main logfile rotation backups;default 10)</span></span><br><span class="line"><span class="attr">loglevel</span>=info <span class="comment">; (logging level;default info; others: debug,warn)</span></span><br><span class="line"><span class="attr">pidfile</span>=/var/run/supervisord.pid <span class="comment">; (supervisord pidfile;default supervisord.pid)</span></span><br><span class="line"><span class="attr">nodaemon</span>=<span class="literal">false</span> <span class="comment">; (start in foreground if true;default false)</span></span><br><span class="line"><span class="attr">minfds</span>=<span class="number">1024</span> <span class="comment">; (min. avail startup file descriptors;default 1024)</span></span><br><span class="line"><span class="attr">minprocs</span>=<span class="number">200</span> <span class="comment">; (min. avail process descriptors;default 200)</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">;nocleanup=true ; (don't clean up tempfiles at start;default false)</span></span><br><span class="line"><span class="comment">;http_username=user ; (default is no username (open system))</span></span><br><span class="line"><span class="comment">;http_password=123 ; (default is no password (open system))</span></span><br><span class="line"><span class="comment">;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP)</span></span><br><span class="line"><span class="comment">;user=chrism ; (default is current user, required if root)</span></span><br><span class="line"><span class="comment">;directory=/tmp ; (default is not to cd during start)</span></span><br><span class="line"><span class="comment">;environment=KEY=value ; (key value pairs to add to environment)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[supervisorctl]</span></span><br><span class="line"><span class="attr">serverurl</span>=unix:///var/tmp/supervisor.sock <span class="comment">; use a unix:// URL for a unix socket</span></span><br><span class="line"><span class="comment">;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket</span></span><br><span class="line"><span class="comment">;username=chris ; should be same as http_username if set</span></span><br><span class="line"><span class="comment">;password=123 ; should be same as http_password if set</span></span><br><span class="line"><span class="comment">;prompt=mysupervisor ; cmd line prompt (default "supervisor")</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">; The below sample program section shows all possible program subsection values,</span></span><br><span class="line"><span class="comment">; create one or more 'real' program: sections to be able to control them under</span></span><br><span class="line"><span class="comment">; supervisor.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">;[program:example]</span></span><br><span class="line"><span class="comment">;command=/bin/echo; the program (relative uses PATH, can take args)</span></span><br><span class="line"><span class="comment">;priority=999 ; the relative start priority (default 999)</span></span><br><span class="line"><span class="comment">;autostart=true ; start at supervisord start (default: true)</span></span><br><span class="line"><span class="comment">;autorestart=true ; retstart at unexpected quit (default: true)</span></span><br><span class="line"><span class="comment">;startsecs=10 ; number of secs prog must stay running (def. 10)</span></span><br><span class="line"><span class="comment">;startretries=3 ; max # of serial start failures (default 3)</span></span><br><span class="line"><span class="comment">;exitcodes=0,2 ; 'expected' exit codes for process (default 0,2)</span></span><br><span class="line"><span class="comment">;stopsignal=QUIT ; signal used to kill process (default TERM)</span></span><br><span class="line"><span class="comment">;stopwaitsecs=10 ; max num secs to wait before SIGKILL (default 10)</span></span><br><span class="line"><span class="comment">;user=chrism ; setuid to this UNIX account to run the program</span></span><br><span class="line"><span class="comment">;log_stdout=true ; if true, log program stdout (default true)</span></span><br><span class="line"><span class="comment">;log_stderr=true ; if true, log program stderr (def false)</span></span><br><span class="line"><span class="comment">;logfile=/var/log/supervisor.log ; child log path, use NONE for none; default AUTO</span></span><br><span class="line"><span class="comment">;logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)</span></span><br><span class="line"><span class="comment">;logfile_backups=10 ; # of logfile backups (default 10)</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>“;”为注释。各参数的含义都很明确。可以根据官方手册结合实验来进一步深入了解。重点说几个[program:example]中的参数</p>
</blockquote>
<figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="comment">;command=/bin/echo; supervisor启动时将要开启的进程。相对或绝对路径均可。若是相对路径则会从supervisord的$PATH变中查找。命令可带参数。</span></span><br><span class="line"><span class="comment">;priority=999 指明进程启动和关闭的顺序。低优先级表明进程启动时较先启动关闭时较后关闭。高优先级表明进程启动时启动时较后启动关闭时较先关闭。</span></span><br><span class="line"><span class="comment">;autostart=true 是否随supervisord启动而启动</span></span><br><span class="line"><span class="comment">;autorestart=true 进程意外退出后是否自动重启</span></span><br><span class="line"><span class="comment">;startsecs=10 进程持续运行多久才认为是启动成功</span></span><br><span class="line"><span class="comment">;startretries=3 重启失败的连续重试次数</span></span><br><span class="line"><span class="comment">;exitcodes=0,2 若autostart设置为unexpected且监控的进程并非因为supervisord停止而退出,那么如果进程的退出码不在exitcode列表中supervisord将重启进程</span></span><br><span class="line"><span class="comment">;stopsignal=QUIT 杀进程的信号</span></span><br><span class="line"><span class="comment">;stopwaitsecs=10 向进程发出stopsignal后等待OS向supervisord返回SIGCHILD 的时间。若超时则supervisord将使用SIGKILL杀进程</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>下面是一个使用supervisor监控的配置情况(配置中的其他默认内容在此省略)</p>
</blockquote>
<figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="section">[program:worker_for_summary]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/worker_for_summary.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">1</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:worker_for_detail_all]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/worker_for_detail_all.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">1</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:worker_for_detail_recent_list]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/worker_for_detail_recent_list.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">1</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:worker_for_detail_recent_sset]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/worker_for_detail_recent_sset.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">1</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:publisher_for_summary]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/publisher_for_summary.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">999</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:publisher_for_summary_nt]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/publisher_for_summary_nt.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">999</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:publisher_for_detail]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/publisher_for_detail.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">999</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br><span class="line"> </span><br><span class="line"><span class="section">[program:publisher_for_detail_nt]</span></span><br><span class="line"><span class="attr">command</span>=/home/op1/scripts/rabbitmqclient/publisher_for_detail_nt.py</span><br><span class="line"><span class="attr">priority</span>=<span class="number">999</span></span><br><span class="line"><span class="attr">log_stderr</span>=<span class="literal">true</span> <span class="comment">; if true, log program stderr (def false)</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>配置完成后启动supervisord</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[root@op-zhongkong ~]<span class="comment"># /etc/init.d/supervisord start</span></span><br><span class="line"> </span><br><span class="line">可以看到配置的各个进程在后台运行了起来。停掉某个进程后supervisor会马上自动重启该进程!!!</span><br><span class="line">[root@op-zhongkong ~]<span class="comment"># supervisorctl </span></span><br><span class="line">supervisor> status</span><br><span class="line">publisher_for_detail RUNNING pid 27557, <span class="built_in">uptime</span> 0:00:45</span><br><span class="line">publisher_for_detail_nt RUNNING pid 27567, <span class="built_in">uptime</span> 0:00:45</span><br><span class="line">publisher_for_summary RUNNING pid 27566, <span class="built_in">uptime</span> 0:00:45</span><br><span class="line">publisher_for_summary_nt RUNNING pid 27568, <span class="built_in">uptime</span> 0:00:45</span><br><span class="line">worker_for_detail_all RUNNING pid 27581, <span class="built_in">uptime</span> 0:00:45</span><br><span class="line">worker_for_detail_recent RUNNING pid 27582, <span class="built_in">uptime</span> 0:00:45</span><br><span class="line">worker_for_summary RUNNING pid 27559, <span class="built_in">uptime</span> 0:00:45</span><br></pre></td></tr></table></figure>
<blockquote>
<p>停止supervisor</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[root@op-zhongkong ~]<span class="comment"># /etc/init.d/supervisord stop</span></span><br></pre></td></tr></table></figure>
<blockquote>