-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
4888 lines (4349 loc) · 855 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>5 分钟搭建 Git 服务器-Gogs</title>
<url>/2016/09/06/gogs-installation-introduction/</url>
<content><![CDATA[<blockquote>
<p>Gogs 基于 Go 语言的自助 Git 服务。它具有易安装、跨平台、轻量级、开源化等特性…</p>
</blockquote>
<!-- ![][1] -->
<img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/gogs-logo.png" class="" width="150">
<p>最近新到一家公司,发现在使用 Gogs 搭建 Git 服务,遂研究了一下,和前段时间研究的 GitLab 做了一个简单的对比,虽然 Gogs 相对与 GitLab 还比较年轻,也许没有 GitLab 强大和稳健,但 Gogs 更加简单易用,而且能够满足正常的工作使用。</p>
<p>Gogs 是轻量级的 Git 服务,正如官方介绍的:一个廉价的树莓派的配置足以满足 Gogs 的最低系统硬件要求。最大程度上节省您的服务器资源!关键的一点是免费开源的,所有的代码都开源在 GitHub 上。下面结合官方的介绍,总结一下在 Linux 系统下的安装方法,真的是相当的简单,5 分钟足矣!</p>
<a id="more"></a>
<h1 id="一、安装"><a href="#一、安装" class="headerlink" title="一、安装"></a>一、安装</h1><h2 id="1-1-下载"><a href="#1-1-下载" class="headerlink" title="1.1 下载"></a>1.1 下载</h2><p>下载对应系统版本的二进制安装包,并上传至 Linux 系统,或通过以下命令下载:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ wget https://github.com/gogits/gogs/releases/download/v0.9.97/linux_amd64.tar.gz</span><br></pre></td></tr></table></figure>
<p><em><a href="https://github.com/gogits/gogs/releases" target="_blank" rel="noopener">Gogs发布版本</a></em></p>
<h2 id="1-2-解压安装包"><a href="#1-2-解压安装包" class="headerlink" title="1.2 解压安装包"></a>1.2 解压安装包</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ tar -xzvf gogs_v0.9.97_linux_amd64.tar.gz</span><br></pre></td></tr></table></figure>
<h2 id="1-3-安装"><a href="#1-3-安装" class="headerlink" title="1.3 安装"></a>1.3 安装</h2><p>进入到刚刚解压后的目录执行命令 <code>./gogs web</code>,出现以下信息:</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/687148dbly1fo7tcw90n2j20u307cab3.jpg" alt=""></p>
<h2 id="1-4-配置"><a href="#1-4-配置" class="headerlink" title="1.4 配置"></a>1.4 配置</h2><p>打开浏览器输地址入:<code>http://ip:3000</code>,第一次会出现以下的配置界面,根据实际情况选择即可。</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/Gogs-install.png" alt=""></p>
<h2 id="1-5-完成安装"><a href="#1-5-完成安装" class="headerlink" title="1.5 完成安装"></a>1.5 完成安装</h2><p>之后进入以下界面,表明安装已经完成。</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/687148dbly1fo7tdl9kvrj20xk0r60ub.jpg" alt=""></p>
<h2 id="1-6-后台运行"><a href="#1-6-后台运行" class="headerlink" title="1.6 后台运行"></a>1.6 后台运行</h2><p>后台运行可参考以下命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ nohup ./gogs web > nohup.out 2>&1 &</span><br></pre></td></tr></table></figure>
<h1 id="升级"><a href="#升级" class="headerlink" title="升级"></a>升级</h1><p>引用自官方<a href="https://gogs.io/docs/upgrade/upgrade_from_binary" target="_blank" rel="noopener">二级制升级文档</a></p>
<h2 id="2-1-首先,确认当前安装的位置:"><a href="#2-1-首先,确认当前安装的位置:" class="headerlink" title="2.1 首先,确认当前安装的位置:"></a>2.1 首先,确认当前安装的位置:</h2> <figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 默认位置在 git 用户下的家目录</span></span><br><span class="line">$ sudo su - git</span><br><span class="line">$ <span class="built_in">cd</span> ~</span><br><span class="line">$ <span class="built_in">pwd</span></span><br><span class="line">/home/git</span><br><span class="line">$ ls</span><br><span class="line">gogs gogs-repositories</span><br></pre></td></tr></table></figure>
<h2 id="2-2-然后将当前目录移动到另一个临时的位置,但不是删除!"><a href="#2-2-然后将当前目录移动到另一个临时的位置,但不是删除!" class="headerlink" title="2.2 然后将当前目录移动到另一个临时的位置,但不是删除!"></a>2.2 然后将当前目录移动到另一个临时的位置,但不是删除!</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ mv gogs gogs_old</span><br></pre></td></tr></table></figure>
<h2 id="2-3-下载并解压新的二进制:"><a href="#2-3-下载并解压新的二进制:" class="headerlink" title="2.3 下载并解压新的二进制:"></a>2.3 下载并解压新的二进制:</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 请根据系统和类型获取相应的二进制版本</span></span><br><span class="line">$ wget https://dl.gogs.io/gogs_v<span class="variable">$VERSION_</span><span class="variable">$OS_</span><span class="variable">$ARCH</span>.tar.gz</span><br><span class="line">$ tar -zxvf gogs_v<span class="variable">$VERSION_</span><span class="variable">$OS_</span><span class="variable">$ARCH</span>.tar.gz</span><br><span class="line">$ ls</span><br><span class="line">gogs gogs_old gogs-repositories gogs_v<span class="variable">$VERSION_</span><span class="variable">$OS_</span><span class="variable">$ARCH</span>.tar.gz</span><br></pre></td></tr></table></figure>
<h2 id="2-4-复制-custom、data-和-log-目录到新解压的目录中:"><a href="#2-4-复制-custom、data-和-log-目录到新解压的目录中:" class="headerlink" title="2.4 复制 custom、data 和 log 目录到新解压的目录中:"></a>2.4 复制 <code>custom</code>、<code>data</code> 和 <code>log</code> 目录到新解压的目录中:</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ cp -R gogs_old/custom gogs</span><br><span class="line">$ cp -R gogs_old/data gogs</span><br><span class="line">$ cp -R gogs_old/<span class="built_in">log</span> gogs</span><br></pre></td></tr></table></figure>
<h2 id="2-5-最后,运行并打开浏览器进行测试:"><a href="#2-5-最后,运行并打开浏览器进行测试:" class="headerlink" title="2.5 最后,运行并打开浏览器进行测试:"></a>2.5 最后,运行并打开浏览器进行测试:</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> gogs</span><br><span class="line">$ ./gogs web</span><br></pre></td></tr></table></figure>
<hr>
]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Gogs</tag>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title>CentOS 7 安装 Node.js</title>
<url>/2016/07/30/CentOS-7-%E5%AE%89%E8%A3%85-Node-js/</url>
<content><![CDATA[<blockquote>
<p><a href="https://nodejs.org/en/" target="_blank" rel="noopener">Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world.</a></p>
</blockquote>
<h1 id="1-下载源码安装文件"><a href="#1-下载源码安装文件" class="headerlink" title="1. 下载源码安装文件"></a>1. 下载源码安装文件</h1><h2 id="1-1-在线安装"><a href="#1-1-在线安装" class="headerlink" title="1.1 在线安装"></a>1.1 在线安装</h2><p>通过以下命令下载源文件:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">wget https://nodejs.org/dist/v4.4.7/node-v4.4.7.tar.gz</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="1-2-离线安装"><a href="#1-2-离线安装" class="headerlink" title="1.2 离线安装"></a>1.2 离线安装</h2><p>如果网络不好,可以通过<a href="https://nodejs.org/en/download/" target="_blank" rel="noopener">官方网站</a>下载,然后上传到 CentOS 系统中,下载如图所示:</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/20160730163835.png" alt=""></p>
<p>我这儿官方网站是无法正常访问的,需要一些手段(大家懂得),所以在网盘备份一份,地址:<a href="http://pan.baidu.com/s/1bpIAUAz" target="_blank" rel="noopener">http://pan.baidu.com/s/1bpIAUAz</a></p>
<h1 id="2-编译安装"><a href="#2-编译安装" class="headerlink" title="2. 编译安装"></a>2. 编译安装</h1><h2 id="2-1-解压文件到指定的目录"><a href="#2-1-解压文件到指定的目录" class="headerlink" title="2.1 解压文件到指定的目录"></a>2.1 解压文件到指定的目录</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">tar -xzvf node-v4.4.7.tar.gz -C app/</span><br></pre></td></tr></table></figure>
<h2 id="2-2-安装依赖包"><a href="#2-2-安装依赖包" class="headerlink" title="2.2 安装依赖包"></a>2.2 安装依赖包</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yum install gcc gcc-c++</span><br></pre></td></tr></table></figure>
<h2 id="2-3-配置安装"><a href="#2-3-配置安装" class="headerlink" title="2.3 配置安装"></a>2.3 配置安装</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">./configure</span><br></pre></td></tr></table></figure>
<p>./configure 是源代码安装的第一步,主要的作用是对即将安装的软件进行配置,检查当前的环境是否满足要安装软件的依赖关系,生成 makefile文件,以便你可以用 make 和 make install 来编译和安装程序。</p>
<h2 id="2-4-编译安装"><a href="#2-4-编译安装" class="headerlink" title="2.4 编译安装"></a>2.4 编译安装</h2><p>编译命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">make</span><br></pre></td></tr></table></figure>
<p>编译的过程会花很长一段时间,等编译完成再执行安装命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">make install</span><br></pre></td></tr></table></figure>
<h1 id="3-检查安装"><a href="#3-检查安装" class="headerlink" title="3. 检查安装"></a>3. 检查安装</h1><p>运行以下命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">node --version</span><br></pre></td></tr></table></figure>
<p>若输出对应的版本号,则安装成功。</p>
<h1 id="4-设置-npm-淘宝镜像"><a href="#4-设置-npm-淘宝镜像" class="headerlink" title="4. 设置 npm 淘宝镜像"></a>4. 设置 npm 淘宝镜像</h1><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm config set registry https://registry.npm.taobao.org</span><br><span class="line"></span><br><span class="line">npm info underscore</span><br></pre></td></tr></table></figure>
<p>如果配置成功,第二步会有字符串返回</p>
<hr>
]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>Linux</tag>
<tag>Node.js</tag>
<tag>CentOS</tag>
</tags>
</entry>
<entry>
<title>CentOS 7 安装Python3、pip3</title>
<url>/2017/01/07/CentOS-7-%E5%AE%89%E8%A3%85-Python3%E3%80%81pip3/</url>
<content><![CDATA[<p>CentOS 7 默认安装了 Python 2,当需要使用 Python 3 的时候,可以手动下载 Python 源码后编译安装。</p>
<h1 id="一、安装-Python-3"><a href="#一、安装-Python-3" class="headerlink" title="一、安装 Python 3"></a>一、安装 Python 3</h1><h2 id="1-1-安装准备"><a href="#1-1-安装准备" class="headerlink" title="1.1 安装准备"></a>1.1 安装准备</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo mkdir /usr/<span class="built_in">local</span>/python3 <span class="comment"># 创建安装目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载 Python 源文件</span></span><br><span class="line">$ wget --no-check-certificate https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tgz</span><br><span class="line"><span class="comment"># 注意:wget获取https的时候要加上:--no-check-certificate</span></span><br><span class="line"></span><br><span class="line">$ tar -xzvf Python-3.6.0.tgz <span class="comment"># 解压缩包</span></span><br><span class="line"></span><br><span class="line">$ <span class="built_in">cd</span> Python-3.6.0 <span class="comment"># 进入解压目录</span></span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="1-2-编译安装"><a href="#1-2-编译安装" class="headerlink" title="1.2 编译安装"></a>1.2 编译安装</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo ./configure --prefix=/usr/<span class="built_in">local</span>/python3 <span class="comment"># 指定创建的目录</span></span><br><span class="line"></span><br><span class="line">$ sudo make</span><br><span class="line"></span><br><span class="line">$ sudo make install</span><br></pre></td></tr></table></figure>
<h2 id="1-3-配置"><a href="#1-3-配置" class="headerlink" title="1.3 配置"></a>1.3 配置</h2><h3 id="1-3-1-两个版本共存"><a href="#1-3-1-两个版本共存" class="headerlink" title="1.3.1 两个版本共存"></a>1.3.1 两个版本共存</h3><p>创建 python3 的软链接:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo ln -s /usr/<span class="built_in">local</span>/python3/bin/python3 /usr/bin/python3</span><br></pre></td></tr></table></figure>
<p>这样就可以通过 <code>python</code> 命令使用 Python 2,<code>python3</code> 来使用 Python 3。</p>
<h3 id="1-3-2-修改默认为-Python-3"><a href="#1-3-2-修改默认为-Python-3" class="headerlink" title="1.3.2 修改默认为 Python 3"></a>1.3.2 修改默认为 Python 3</h3><p>将 <code>/usr/bin</code> 中的 <code>python</code> 备份</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo mv python python.bak</span><br></pre></td></tr></table></figure>
<p>然后创建 python3 的软链接</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo ln -s /usr/<span class="built_in">local</span>/python3/bin/python3 /usr/bin/python</span><br></pre></td></tr></table></figure>
<p>这样默认的 Python 版本就替换为 Python 3 了。</p>
<p>因为 yum 使用 Python 2,因此替换为 Python 3 后可能无法正常工作,因此修改 yum 配置文件</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo vi /usr/bin/yum</span><br></pre></td></tr></table></figure>
<p>将第一行指定的 python 版本改为 python2.7(<code>#!/usr/bin/python</code> 改为 <code>#!/usr/bin/python2.7</code>)</p>
<h1 id="二、安装-pip"><a href="#二、安装-pip" class="headerlink" title="二、安装 pip"></a>二、安装 pip</h1><h2 id="2-1-yum-安装"><a href="#2-1-yum-安装" class="headerlink" title="2.1 yum 安装"></a>2.1 yum 安装</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 首先安装 epel 扩展源</span></span><br><span class="line">$ sudo yum -y install epel-release</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 python-pip</span></span><br><span class="line">$ sudo yum -y install python-pip</span><br><span class="line"></span><br><span class="line"><span class="comment"># 清除 cache</span></span><br><span class="line">$ sudo yum clean all</span><br></pre></td></tr></table></figure>
<p>通过这种方式貌似只能安装 pip2,想要安装 Python 3 的 pip,可以通过以下的源代码安装方式。</p>
<h2 id="2-2-源码安装"><a href="#2-2-源码安装" class="headerlink" title="2.2 源码安装"></a>2.2 源码安装</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 下载源代码</span></span><br><span class="line">$ wget --no-check-certificate https://github.com/pypa/pip/archive/9.0.1.tar.gz</span><br><span class="line"></span><br><span class="line">$ tar -zvxf 9.0.1 -C pip-9.0.1 <span class="comment"># 解压文件</span></span><br><span class="line"></span><br><span class="line">$ <span class="built_in">cd</span> pip-9.0.1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 Python 3 安装</span></span><br><span class="line">$ python3 setup.py install</span><br></pre></td></tr></table></figure>
<p>创建链接:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo ln -s /usr/<span class="built_in">local</span>/python3/bin/pip /usr/bin/pip3</span><br></pre></td></tr></table></figure>
<h2 id="2-3-升级-pip"><a href="#2-3-升级-pip" class="headerlink" title="2.3 升级 pip"></a>2.3 升级 pip</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pip install --upgrade pip</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>Linux</tag>
<tag>CentOS</tag>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>CentOS 7 安装最新的 Git</title>
<url>/2016/07/30/CentOS-7-%E5%AE%89%E8%A3%85%E6%9C%80%E6%96%B0%E7%9A%84-Git/</url>
<content><![CDATA[<blockquote>
<p>yum 源仓库里的 Git 版本更新不及时,最新版本的 Git 是 1.8.3.1,但是官方最新版本已经到了 2.9.2。想要安装最新版本的的 Git,只能下载源码进行安装。</p>
</blockquote>
<h1 id="1-查看-yum-源仓库的-Git-信息:"><a href="#1-查看-yum-源仓库的-Git-信息:" class="headerlink" title="1. 查看 yum 源仓库的 Git 信息:"></a>1. 查看 yum 源仓库的 Git 信息:</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># yum info git</span></span><br></pre></td></tr></table></figure>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/20160730222608.png" alt=""></p>
<p>可以看出,截至目前,yum 源仓库中最新的 Git 版本才 1.8.3.1,而查看最<a href="https://github.com/git/git/releases" target="_blank" rel="noopener">新的 Git 发布版本</a>,已经 2.9.2 了。</p>
<a id="more"></a>
<h1 id="2-依赖库安装"><a href="#2-依赖库安装" class="headerlink" title="2. 依赖库安装"></a>2. 依赖库安装</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel</span></span><br><span class="line"><span class="comment"># yum install gcc perl-ExtUtils-MakeMaker</span></span><br></pre></td></tr></table></figure>
<h1 id="3-卸载低版本的-Git"><a href="#3-卸载低版本的-Git" class="headerlink" title="3. 卸载低版本的 Git"></a>3. 卸载低版本的 Git</h1><p>通过命令:<code>git –-version</code> 查看系统带的版本,Git 版本是: <code>1.8.3.1</code>,所以先要卸载低版本的 Git,命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># yum remove git</span></span><br></pre></td></tr></table></figure>
<h1 id="4-下载新版的-Git-源码包"><a href="#4-下载新版的-Git-源码包" class="headerlink" title="4. 下载新版的 Git 源码包"></a>4. 下载新版的 Git 源码包</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># wget https://github.com/git/git/archive/v2.9.2.tar.gz</span></span><br></pre></td></tr></table></figure>
<p>也可以离线下载,然后传到 CentOS 系统中指定的目录下。</p>
<h1 id="5-解压到指定目录"><a href="#5-解压到指定目录" class="headerlink" title="5. 解压到指定目录"></a>5. 解压到指定目录</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># tar -xzvf v2.9.2.tar.gz -C ~/app/</span></span><br></pre></td></tr></table></figure>
<h1 id="6-安装-Git"><a href="#6-安装-Git" class="headerlink" title="6. 安装 Git"></a>6. 安装 Git</h1><p>分别执行以下命令进行编译安装,编译过程可能比较漫长,请耐心等待完成。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># cd git-2.9.2</span></span><br><span class="line"><span class="comment"># make prefix=/usr/local/git all</span></span><br><span class="line"><span class="comment"># make prefix=/usr/local/git install</span></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"><span class="comment"># echo "export PATH=$PATH:/usr/local/git/bin" >> /etc/bashrc</span></span><br><span class="line"><span class="comment"># source /etc/bashrc # 实时生效</span></span><br></pre></td></tr></table></figure>
<h1 id="8-查看版本号"><a href="#8-查看版本号" class="headerlink" title="8. 查看版本号"></a>8. 查看版本号</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># git --version</span></span><br><span class="line">git version 2.9.2</span><br></pre></td></tr></table></figure>
<p>至此,CentOS 就安装上了最新版本的 Git。</p>
<hr>
]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>Git</tag>
<tag>Linux</tag>
<tag>CentOS</tag>
</tags>
</entry>
<entry>
<title>CentOS 中配置 Git 命令自动补全</title>
<url>/2016/09/04/CentOS-%E4%B8%AD%E9%85%8D%E7%BD%AE-Git-%E5%91%BD%E4%BB%A4%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8/</url>
<content><![CDATA[<h1 id="1-Step-1"><a href="#1-Step-1" class="headerlink" title="1. Step 1"></a>1. Step 1</h1><p>保存以下文件的内容为:<code>git-completion.bash</code></p>
<p><a href="https://github.com/git/git/blob/master/contrib/completion/git-completion.bash" target="_blank" rel="noopener">git-completion.bash</a></p>
<h1 id="2-Step-2"><a href="#2-Step-2" class="headerlink" title="2. Step 2"></a>2. Step 2</h1><p>将上述文件 <code>git-completion.bash</code> copy 至个人 <code>home</code> 目录,可设为隐藏文件以免后续被误删。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ cp git-completion.bash ~/.git-completion.bash</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h1 id="Step-3"><a href="#Step-3" class="headerlink" title="Step 3"></a>Step 3</h1><p>编辑环境变量文件</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ vi ~/.bashrc</span><br></pre></td></tr></table></figure>
<p>在最后加入下面内容</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.git-completion.bash</span><br></pre></td></tr></table></figure>
<p>完成以上步骤后,重启 <code>shell</code>,就可以通过 <code>tab</code> 键自动补全 <code>Git</code> 命令了。</p>
<hr>
]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
<tag>CentOS</tag>
</tags>
</entry>
<entry>
<title>CentOS 安装 Nginx</title>
<url>/2016/12/23/CentOS-%E5%AE%89%E8%A3%85-Nginx/</url>
<content><![CDATA[<h1 id="一、安装准备"><a href="#一、安装准备" class="headerlink" title="一、安装准备"></a>一、安装准备</h1><p>首先由于 Nginx 的一些模块依赖一些 lib 库,所以在安装 Nginx 之前,必须先安装这些 lib 库,这些依赖库主要有 g++、gcc、openssl-devel、pcre-devel 和 zlib-devel,执行如下命令安装:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yum install gcc-c++</span><br><span class="line">$ yum install pcre pcre-devel</span><br><span class="line">$ yum install zlib zlib-devel</span><br><span class="line">$ yum install openssl openssl--devel</span><br></pre></td></tr></table></figure>
<h1 id="二、安装-Nginx"><a href="#二、安装-Nginx" class="headerlink" title="二、安装 Nginx"></a>二、安装 Nginx</h1><p>安装之前,最好检查一下是否已经安装有 Nginx</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ find -name nginx</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>如果系统已经安装了 Nginx,那么就先卸载</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yum remove nginx</span><br></pre></td></tr></table></figure>
<p>首先进入 /usr/local 目录</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> /usr/<span class="built_in">local</span></span><br></pre></td></tr></table></figure>
<p>从官网下载最新版的 Nginx</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ wget http://nginx.org/download/nginx-1.9.6.tar.gz</span><br><span class="line">$ tar -zxvf nginx-1.9.6.tar.gz</span><br><span class="line">$ <span class="built_in">cd</span> nginx-1.9.6</span><br></pre></td></tr></table></figure>
<p>接下来安装,使用 <code>--prefix</code> 参数指定 Nginx 安装的目录</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ ./configure --prefix=/usr/<span class="built_in">local</span>/nginx <span class="comment"># 指定 Nginx 安装的目录 /usr/local/nginx</span></span><br><span class="line">$ make prefix=/usr/<span class="built_in">local</span>/nginx</span><br><span class="line">$ make install prefix=/usr/<span class="built_in">local</span>/nginx</span><br></pre></td></tr></table></figure>
<p>如果没有报错,顺利完成后,最好看一下 nginx 的安装目录</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ whereis nginx</span><br></pre></td></tr></table></figure>
<p>安装完毕后,进入安装后目录(/usr/local/nginx)便可以启动或停止它了。</p>
<h1 id="二、基本操作命令"><a href="#二、基本操作命令" class="headerlink" title="二、基本操作命令"></a>二、基本操作命令</h1><h2 id="2-1-启动命令"><a href="#2-1-启动命令" class="headerlink" title="2.1 启动命令"></a>2.1 启动命令</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">/usr/<span class="built_in">local</span>/nginx/sbin/nginx -c /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf</span><br></pre></td></tr></table></figure>
<h2 id="2-2-重启命令"><a href="#2-2-重启命令" class="headerlink" title="2.2 重启命令"></a>2.2 重启命令</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">/usr/<span class="built_in">local</span>/nginx/sbin/nginx -c /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf -s reload</span><br></pre></td></tr></table></figure>
<h2 id="2-3-停止命令"><a href="#2-3-停止命令" class="headerlink" title="2.3 停止命令"></a>2.3 停止命令</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">/usr/<span class="built_in">local</span>/nginx/sbin/nginx -c /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf -s stop</span><br></pre></td></tr></table></figure>
<p><code>-c</code> 制定配置文件的路径,如果不加 Nginx 会自动加载默认路径的配置文件。</p>
]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>Linux</tag>
<tag>CentOS</tag>
<tag>Nginx</tag>
</tags>
</entry>
<entry>
<title>CentOS 安装 rar、zip 解压缩</title>
<url>/2016/12/22/CentOS-%E5%AE%89%E8%A3%85-rar%E3%80%81zip-%E8%A7%A3%E5%8E%8B%E7%BC%A9/</url>
<content><![CDATA[<p>Windows 系统压缩的 rar 和 zip 文件,在 Linux 系统下是无法通过 tar 命令解压缩的,需要使用 rar 和 zip 命令来解压缩。下面记录一下 rar 和 zip 安装和简单的使用。</p>
<h1 id="一、rar-安装使用"><a href="#一、rar-安装使用" class="headerlink" title="一、rar 安装使用"></a>一、rar 安装使用</h1><p>Linux 系统下使用 rarlinux 解压缩 rar 压缩文件,下载页面:<a href="http://www.rarsoft.com/download.htm。" target="_blank" rel="noopener">http://www.rarsoft.com/download.htm。</a></p>
<h2 id="1-1-下载系统对应的版本"><a href="#1-1-下载系统对应的版本" class="headerlink" title="1.1 下载系统对应的版本"></a>1.1 下载系统对应的版本</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ wget http://www.rarsoft.com/rar/rarlinux-x64-5.4.0.tar.gz</span><br></pre></td></tr></table></figure>
<h2 id="1-2-解压、安装"><a href="#1-2-解压、安装" class="headerlink" title="1.2 解压、安装"></a>1.2 解压、安装</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ tar -zxvf rarlinux-x64-5.4.0.tar.gz</span><br><span class="line">$ <span class="built_in">cd</span> rar</span><br><span class="line">$ make</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>看见下面这些信息就是安装成功了:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">mkdir -p /usr/<span class="built_in">local</span>/bin</span><br><span class="line">mkdir -p /usr/<span class="built_in">local</span>/lib</span><br><span class="line">cp rar unrar /usr/<span class="built_in">local</span>/bin</span><br><span class="line">cp rarfiles.lst /etc</span><br><span class="line">cp default.sfx /usr/<span class="built_in">local</span>/lib</span><br></pre></td></tr></table></figure>
<h2 id="1-3-常用-rar-命令"><a href="#1-3-常用-rar-命令" class="headerlink" title="1.3 常用 rar 命令"></a>1.3 常用 rar 命令</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ rar x centos.rar <span class="comment"># 解压 centos.rar 到当前目录</span></span><br><span class="line">$ rar centos.rar ./piaoyi.org/ <span class="comment"># 将 piaoyi.org 目录打包为 centos.rar</span></span><br></pre></td></tr></table></figure>
<h2 id="1-4-常见错误原因分析"><a href="#1-4-常见错误原因分析" class="headerlink" title="1.4 常见错误原因分析"></a>1.4 常见错误原因分析</h2><h3 id="1-4-1-如果在运行命令-rar-时-出现下面这个问题"><a href="#1-4-1-如果在运行命令-rar-时-出现下面这个问题" class="headerlink" title="1.4.1 如果在运行命令 rar 时,出现下面这个问题"></a>1.4.1 如果在运行命令 rar 时,出现下面这个问题</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">rar: /lib/i686/nosegneg/libc.so.6: version <span class="string">'GLIBC_2.7'</span> not found (required by rar)</span><br></pre></td></tr></table></figure>
<p>解决办法:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ cp rar_static /usr/<span class="built_in">local</span>/bin/rar</span><br></pre></td></tr></table></figure>
<h3 id="1-4-2-使用-rar-的时候出现错误"><a href="#1-4-2-使用-rar-的时候出现错误" class="headerlink" title="1.4.2 使用 rar 的时候出现错误"></a>1.4.2 使用 rar 的时候出现错误</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">bash: /usr/<span class="built_in">local</span>/bin/rar: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory</span><br></pre></td></tr></table></figure>
<p>因为 64 位系统中安装了 32 位程序,解决方法:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yum install glibc.i686</span><br></pre></td></tr></table></figure>
<h3 id="1-4-3-重新安装-glibc-i686-以后还有如下类似错误"><a href="#1-4-3-重新安装-glibc-i686-以后还有如下类似错误" class="headerlink" title="1.4.3 重新安装 glibc.i686 以后还有如下类似错误"></a>1.4.3 重新安装 glibc.i686 以后还有如下类似错误</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">error <span class="keyword">while</span> loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory</span><br></pre></td></tr></table></figure>
<p>再继续安装包:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yum install libstdc++.so.6</span><br></pre></td></tr></table></figure>
<h1 id="二、zip-unzip-安装使用"><a href="#二、zip-unzip-安装使用" class="headerlink" title="二、zip/unzip 安装使用"></a>二、zip/unzip 安装使用</h1><h2 id="2-1-检查是否有包含-zip(unzip)-的软件包"><a href="#2-1-检查是否有包含-zip(unzip)-的软件包" class="headerlink" title="2.1 检查是否有包含 zip(unzip) 的软件包"></a>2.1 检查是否有包含 zip(unzip) 的软件包</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yum provides zip</span><br><span class="line">Loaded plugins: fastestmirror</span><br><span class="line">Loading mirror speeds from cached hostfile</span><br><span class="line"> * base: mirrors.cqu.edu.cn</span><br><span class="line"> * extras: mirrors.cqu.edu.cn</span><br><span class="line"> * updates: mirrors.tuna.tsinghua.edu.cn</span><br><span class="line">zip-3.0-11.el7.x86_64 : A file compression and packaging utility compatible with PKZIP</span><br><span class="line">Repo : base</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">zip-3.0-11.el7.x86_64 : A file compression and packaging utility compatible with PKZIP</span><br><span class="line">Repo : @base</span><br></pre></td></tr></table></figure>
<h2 id="2-2-安装-zip、unzip"><a href="#2-2-安装-zip、unzip" class="headerlink" title="2.2 安装 zip、unzip"></a>2.2 安装 zip、unzip</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yum install zip</span><br><span class="line">$ yum install unzip</span><br></pre></td></tr></table></figure>
<h2 id="2-3-常用命令"><a href="#2-3-常用命令" class="headerlink" title="2.3 常用命令"></a>2.3 常用命令</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ zip -r myfile.zip ./*</span><br><span class="line"><span class="comment"># 将当前目录下的所有文件和文件夹全部压缩成 myfile.zip 文件,-r 表示递归压缩子目录下所有文件.</span></span><br><span class="line"></span><br><span class="line">$ unzip -o -d /home/sunny myfile.zip</span><br><span class="line"><span class="comment"># 把 myfile.zip 文件解压到 /home/sunny/</span></span><br><span class="line"><span class="comment"># -o :不提示的情况下覆盖文件</span></span><br><span class="line"><span class="comment"># -d:将文件解压缩到指定目录下</span></span><br><span class="line"></span><br><span class="line">$ zip -d myfile.zip smart.txt</span><br><span class="line"><span class="comment"># 删除压缩文件中 smart.txt 文件</span></span><br><span class="line"></span><br><span class="line">$ zip -m myfile.zip ./rpm_info.txt</span><br><span class="line"><span class="comment"># 向压缩文件中 myfile.zip 中添加 rpm_info.txt 文件</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>Linux</tag>
<tag>CentOS</tag>
<tag>rar</tag>
<tag>zip</tag>
</tags>
</entry>
<entry>
<title>CentOS 系统下 GitLab 搭建与基本配置</title>
<url>/2016/07/31/CentOS-%E7%B3%BB%E7%BB%9F%E4%B8%8B-GitLab-%E6%90%AD%E5%BB%BA%E4%B8%8E%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE/</url>
<content><![CDATA[<blockquote>
<p><a href="https://about.gitlab.com/" target="_blank" rel="noopener">GitLab 是一个开源的版本管理系统,提供了类似于 GitHub 的源代码浏览,管理缺陷和注释等功能,你可以将代码免费托管到 GitLab.com,而且不限项目数量和成员数。最吸引人的一点是,可以在自己的服务器上搭建 GitLab CE (社区免费版)版本,方便内部团队协作开发和代码管理。</a></p>
</blockquote>
<p>下面介绍如何在 CentOS 服务器上搭建 GitLab CE 版本,以及一些基本的配置。</p>
<h1 id="1-安装"><a href="#1-安装" class="headerlink" title="1. 安装"></a>1. 安装</h1><p>GitLab 提供了两种安装方式:源码手动编译安装和软件包管理安装。</p>
<p>源码手动编译安装虽然配置灵活,但过程比较麻烦,不容易安装成功,所以我这里选择软件包管理安装的形式。</p>
<h2 id="1-1-使用-GitLab-提供仓库在线安装"><a href="#1-1-使用-GitLab-提供仓库在线安装" class="headerlink" title="1.1 使用 GitLab 提供仓库在线安装"></a>1.1 使用 GitLab 提供仓库在线安装</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash</span><br><span class="line">yum install gitlab-ce</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>国外的 GitLab 仓库访问速度较慢,可以使用国内的站点:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">curl -sS http://packages.gitlab.cc/install/gitlab-ce/script.rpm.sh | sudo bash</span><br><span class="line">yum install gitlab-ce</span><br></pre></td></tr></table></figure>
<h2 id="1-2-下载离线软件包安装"><a href="#1-2-下载离线软件包安装" class="headerlink" title="1.2 下载离线软件包安装"></a>1.2 下载离线软件包安装</h2><p>如果网络速度不理想,可以使用离线软件包 rpm 的方式进行安装,下面提供了几个站点的下载地址。</p>
<ul>
<li>GitLab 官方:<a href="https://packages.gitlab.com/gitlab/gitlab-ce?filter=rpms" target="_blank" rel="noopener">https://packages.gitlab.com/gitlab/gitlab-ce?filter=rpms</a></li>
<li>清华大学TUNA开源镜像站:<a href="https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/</a></li>
<li>浙大开源镜像站:<a href="http://mirrors.lifetoy.org/gitlab-ce/yum/el7/" target="_blank" rel="noopener">http://mirrors.lifetoy.org/gitlab-ce/yum/el7/</a></li>
</ul>
<p>下载好 rpm 软件安装包后上传到服务器指定的目录下,通过以下命令进行安装:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">rpm -ivh gitlab-ce-8.9.6-ce.0.el7.x86_64.rpm</span><br></pre></td></tr></table></figure>
<p>记录一下 rpm 卸载软件安装包命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">rpm -e --nodeps gitlab-ce-8.9.6-ce.0.el7.x86_64</span><br></pre></td></tr></table></figure>
<h1 id="2-启动-GitLab"><a href="#2-启动-GitLab" class="headerlink" title="2. 启动 GitLab"></a>2. 启动 GitLab</h1><p>安装完成之后,打开配置文件 <code>/etc/gitlab/gitlab.rb</code> 将 <code>external_url = 'http://git.example.com'</code> 修改为自己的 IP 地址:<code>external_url 'http://ip_address'</code> ,然后执行下面的命令,对 GitLab 进行编译:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab-ctl reconfigure</span><br></pre></td></tr></table></figure>
<p>完成后,使用浏览器访问:<a href="http://ip_address" target="_blank" rel="noopener">http://ip_address</a> 可进入 GitLab 登录页面,首次访问系统会让你重新设置管理员的密码,默认的管理员账号是 root,如果你想更改默认管理员账号,登录系统后可以修改帐号名。</p>
<h1 id="3-GitLab-基本配置"><a href="#3-GitLab-基本配置" class="headerlink" title="3. GitLab 基本配置"></a>3. GitLab 基本配置</h1><p>GitLab 的相关参数配置都存在 <code>/etc/gitlab/gitlab.rb</code> 文件里。自 GitLab 7.6 开始的新安装包, 已经默认将所有的参数写入到 <code>/etc/gitlab/gitlab.rb</code> 配置文件中。</p>
<h2 id="3-1-配置端口"><a href="#3-1-配置端口" class="headerlink" title="3.1 配置端口"></a>3.1 配置端口</h2><p>GitLab 默认使用 80 端口对外提供服务,因为 80 端口被其他服务占用,所以需要更改。打开 <code>/etc/gitlab/gitlab.rb</code> 配置文件,修改 <code>external_url 'http://ip_address'</code> 为 <code>external_url 'http://ip_address:new-port'</code>,</p>
<p>重新编译配置:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab-ctl reconfigure</span><br></pre></td></tr></table></figure>
<p>这时候就可以通更改后的 IP + 端口号码进行访问了。</p>
<h2 id="3-2-邮箱配置"><a href="#3-2-邮箱配置" class="headerlink" title="3.2 邮箱配置"></a>3.2 邮箱配置</h2><p>以下是 163 邮箱的配置参考,打开 <code>/etc/gitlab/gitlab.rb</code> 配置文件,添加以下内容:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab_rails[<span class="string">'smtp_enable'</span>] = <span class="literal">true</span></span><br><span class="line">gitlab_rails[<span class="string">'smtp_address'</span>] = <span class="string">"smtp.163.com"</span></span><br><span class="line">gitlab_rails[<span class="string">'smtp_port'</span>] = 25</span><br><span class="line">gitlab_rails[<span class="string">'smtp_user_name'</span>] = <span class="string">"test@163.com"</span></span><br><span class="line">gitlab_rails[<span class="string">'smtp_password'</span>] = <span class="string">"password"</span></span><br><span class="line">gitlab_rails[<span class="string">'smtp_authentication'</span>] = <span class="string">"login"</span></span><br><span class="line">gitlab_rails[<span class="string">'smtp_enable_starttls_auto'</span>] = <span class="literal">true</span></span><br><span class="line">gitlab_rails[<span class="string">'gitlab_email_from'</span>] = <span class="string">"test@163.com"</span></span><br></pre></td></tr></table></figure>
<p><strong>注意:</strong> <code>test@163.com</code> 和 <code>password</code> 更新为自己邮箱地址和密码;邮箱需要开启 SMTP 协议。</p>
<p>重新编译配置即可生效:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab-ctl reconfigure</span><br></pre></td></tr></table></figure>
<p>其它邮箱的配置可参考:<a href="https://doc.gitlab.cc/omnibus/settings/smtp.html" target="_blank" rel="noopener">https://doc.gitlab.cc/omnibus/settings/smtp.html</a></p>
<h2 id="3-3-头像配置"><a href="#3-3-头像配置" class="headerlink" title="3.3 头像配置"></a>3.3 头像配置</h2><p>GitLab 默认使用的是 Gravatar 头像服务,不过现在貌似 Gravatar 国内好像访问不了,导致 GitLab 默认头像破裂,无法显示,可以替换为多说 Gravatar 服务器。打开 <code>/etc/gitlab/gitlab.rb</code> 配置文件,增加下面这一行:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab_rails[<span class="string">'gravatar_plain_url'</span>] = <span class="string">'http://gravatar.duoshuo.com/avatar/%{hash}?s=%{size}&d=identicon'</span></span><br></pre></td></tr></table></figure>
<p>再分别执行以下命令即可</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab-ctl reconfigure</span><br><span class="line">gitlab-rake cache:clear RAILS_ENV=production</span><br></pre></td></tr></table></figure>
<p>也可以关闭 Gravatar 头像显示配置,登录 GitLab 管理员账户,进入设置界面(路径地址:<code>http://ip:port/admin/application_settings</code> ),取消以下选项即可。</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/20160731201717.png" alt=""></p>
<h2 id="3-4-用户注册配置"><a href="#3-4-用户注册配置" class="headerlink" title="3.4 用户注册配置"></a>3.4 用户注册配置</h2><p>管理员设置界面(路径地址:<code>http://ip:port/admin/application_settings</code> )以下选项可以控制用户注册配置,包括是否允许登录、注册和注册邮箱验证等选项。</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/20160731202214.png" alt=""></p>
<h2 id="3-5-常用命令"><a href="#3-5-常用命令" class="headerlink" title="3.5 常用命令"></a>3.5 常用命令</h2><p>GitLab 服务启动、停止、状态查询、修改配置生效等命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab-ctl start/stop/status/reconfigure <span class="comment"># 服务启动、停止、状态查询、修改配置生效</span></span><br></pre></td></tr></table></figure>
<p>也可以查看帮助文档获取更多命令信息:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gitlab-ctl --<span class="built_in">help</span></span><br></pre></td></tr></table></figure>
<hr>
]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
<tag>CentOS</tag>
<tag>GitLab</tag>
</tags>
</entry>
<entry>
<title>Docker 利用数据卷容器来备份、恢复、迁移数据</title>
<url>/2017/01/09/Docker-%E5%88%A9%E7%94%A8%E6%95%B0%E6%8D%AE%E5%8D%B7%E5%AE%B9%E5%99%A8%E6%9D%A5%E5%A4%87%E4%BB%BD%E3%80%81%E6%81%A2%E5%A4%8D%E3%80%81%E8%BF%81%E7%A7%BB%E6%95%B0%E6%8D%AE/</url>
<content><![CDATA[<blockquote>
<p>在 Docker 容器之间如果需要共享数据,可以创建一个数据卷容器来实现,并且可以方便的通过数据卷容器来备份、恢复、迁移数据。</p>
</blockquote>
<h1 id="创建数据卷容器"><a href="#创建数据卷容器" class="headerlink" title="创建数据卷容器"></a>创建数据卷容器</h1><p>创建一个名为:dbdata 的数据卷容器;设置挂载点为 <code>/vdata</code>。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo docker run -d -v /vdata --name dbdata alpine sh</span><br></pre></td></tr></table></figure>
<p>数据卷容器是一个普通的 Docker 容器,可以不需要启动。</p>
<a id="more"></a>
<h1 id="使用数据卷容器"><a href="#使用数据卷容器" class="headerlink" title="使用数据卷容器"></a>使用数据卷容器</h1><p>使用命令 <code>--volumes-from</code> 创建挂载数据卷容器 dbdata 的容器:db1、db2。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo docker run -it --volumes-from dbdata --name db1 alpine sh</span><br><span class="line"></span><br><span class="line">$ sudo docker run -it --volumes-from dbdata --name db1 alpine sh</span><br></pre></td></tr></table></figure>
<p>在容器 db1 的挂载目录 <code>/vdata</code> 目录下,创建文件 1.txt 等测试数据,查看容器 db2 的挂载目录 <code>/vdata</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">$ sudo docker run --rm --volumes-from dbdata -v $(<span class="built_in">pwd</span>):/backup alpine tar cvf /backup/vdata-bak.tar /vdata</span><br></pre></td></tr></table></figure>
<p>使用 <code>tar cvf</code> 命令,备份数据卷容器 dbdata 中的目录 <code>/vdata</code> 为 <code>vdata-bak.tar</code>,并挂载到宿主机的当前目录下。</p>
<h1 id="恢复数据卷数据"><a href="#恢复数据卷数据" class="headerlink" title="恢复数据卷数据"></a>恢复数据卷数据</h1><p>创建数据卷容器:dbdata2</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo docker run -d -v /vdata --name dbdata2 alpine sh</span><br></pre></td></tr></table></figure>
<p>使用一个临时容器,恢复备份数据 <code>vdata-bak.tar</code> 到 dbdata2</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo docker run --rm --volumes-from dbdata2 -v $(<span class="built_in">pwd</span>):/backup alpine tar xvf /backup/vdata-bak.tar</span><br></pre></td></tr></table></figure>
<p>使用一个临时容器,查看 dbdata2 恢复的数据:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ sudo docker run --rm --volumes-from dbdata2 alpine /bin/ls /vdata</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Docker</category>
</categories>
<tags>
<tag>Docker</tag>
</tags>
</entry>
<entry>
<title>Docker 安装 Gitea/Gogs 与主机共享 22 端口</title>
<url>/2021/01/06/docker-gitea-share-port-22-with-host/</url>
<content><![CDATA[<p>如果主机的 22 端口已被使用,使用 <code>Docker</code> 安装 <code>Gitea</code> 时只能把容器的 22 端口映射到主机的其它端口(如:10022),这是没有任何问题的。但是以 <code>SSH</code> 方式 <code>clone</code> 项目时,<code>URL</code> 长这样<br><code>ssh://git@git.example.com:10022:username/project.git</code></p>
<p>如果我们想要类似以下这样的 <code>URL</code> 时就需要把 <code>Gitea</code> 容器的和主机共享 22 端口<br><code>git@git.example.com:username/project.git</code></p>
<p>下面总结一下使用 <code>Docker</code> 安装 <code>Gitea</code> 共享主机 22 端口的主要步骤,<code>Gogs</code> 应该是同理。</p>
<h3 id="创建-git-用户"><a href="#创建-git-用户" class="headerlink" title="创建 git 用户"></a>创建 git 用户</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Create git user</span></span><br><span class="line">adduser git</span><br><span class="line"></span><br><span class="line"><span class="comment"># Make sure user has UID and GID 1000</span></span><br><span class="line">usermod -u 1000 -g 1000 git</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create docker group</span></span><br><span class="line">groupadd docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add git user to docker group</span></span><br><span class="line">usermod -aG docker git</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the gitea data directory</span></span><br><span class="line"></span><br><span class="line">mkdir -p /home/git/gitea/data</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="安装-Gitea"><a href="#安装-Gitea" class="headerlink" title="安装 Gitea"></a>安装 Gitea</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run -d --name=gitea -p 10022:22 -p 10080:3000 -v /home/git/gitea/data:/data --restart=always gitea/gitea:latest</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create a symlink between the container authorized_keys and the host git user authorized_keys</span></span><br><span class="line">ln -s /home/git/gitea/data/git/.ssh /home/git/</span><br></pre></td></tr></table></figure>
<h3 id="生成-SSH-key"><a href="#生成-SSH-key" class="headerlink" title="生成 SSH key"></a>生成 SSH key</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sudo -u git ssh-keygen -t rsa -b 4096 -C <span class="string">"Gitea Host Key"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty <span class="variable">$(cat /home/git/.ssh/id_rsa.pub)</span>"</span> >> /home/git/.ssh/authorized_keys</span><br><span class="line"></span><br><span class="line">chmod 600 /home/git/.ssh/authorized_keys</span><br></pre></td></tr></table></figure>
<h3 id="配置-SSH-passthrough"><a href="#配置-SSH-passthrough" class="headerlink" title="配置 SSH passthrough"></a>配置 SSH passthrough</h3><p>配置 <code>passthrough</code> 连接到 <code>Gitea</code> 容器的 <code>SSH</code> 映射端口 10022</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">mkdir -p /app/gitea/</span><br><span class="line"></span><br><span class="line">cat >/app/gitea/gitea <<<span class="string">'END'</span></span><br><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line">ssh -p 10022 -o StrictHostKeyChecking=no git@127.0.0.1 \</span><br><span class="line"><span class="string">"SSH_ORIGINAL_COMMAND=\"<span class="variable">$SSH_ORIGINAL_COMMAND</span>\" <span class="variable">$0</span> <span class="variable">$@</span>"</span></span><br><span class="line">END</span><br><span class="line"></span><br><span class="line">chmod +x /app/gitea/gitea</span><br></pre></td></tr></table></figure>
<h3 id="Caddy-反向代理配置"><a href="#Caddy-反向代理配置" class="headerlink" title="Caddy 反向代理配置"></a>Caddy 反向代理配置</h3><p>这里使用 <code>Caddy</code> 反向代理配置域名,<code>Caddyfile</code> 配置信息如下:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">git.example.com {</span><br><span class="line"> encode zstd gzip</span><br><span class="line"></span><br><span class="line"> reverse_proxy localhost:10080</span><br><span class="line"> header / Strict-Transport-Security "max-age=31536000;"</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>配置完域名之后,输入域名进行安装,现在就可以修改 【SSH 服务域名】 为 <code>git.example.com</code>,【Gitea 基本 URL】 为 <code>https://git.example.com/</code>,也可以后通过 <code>/home/git/gogs/data/gogs/conf/app.ini</code> 配置文件修改相关配置。</p>
<h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ul>
<li><p>由于 <code>docker</code> 启动容器的默认 <code>uid</code> 和 <code>gid</code> 是 1000,所以 <code>git</code> 用户的 <code>uid</code>、<code>gid</code> 必须为 1000,如果 <code>git</code> 用户的 <code>uid</code> 和 <code>gid</code> 不是 1000(比如:1002),尝试通过 <code>docker run --user 1002:1002</code>、 <code>docker run -e "PUID=1002" -e "PGID=1002"</code> 等方式启动 <code>docker</code> 容器都不管用。</p>
</li>
<li><p>保证 <code>git</code> 用户下的所有文件都属于 <code>git</code> 用户和 <code>git</code> 组</p>
</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[git]$ ls -la /home/git/gitea/data</span><br><span class="line">total 20</span><br><span class="line">drwxrwxr-x 5 git git 4096 Jan 5 14:14 .</span><br><span class="line">drwxrwxr-x 3 git git 4096 Jan 5 13:56 ..</span><br><span class="line">drwxr-xr-x 5 git git 4096 Jan 5 14:20 git</span><br><span class="line">drwxr-xr-x 10 git git 4096 Jan 5 14:40 gitea</span><br><span class="line">drwx------ 2 git git 4096 Jan 5 14:14 ssh</span><br></pre></td></tr></table></figure>
<h3 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h3><ul>
<li><a href="https://raincal.com/post/gogs-share-22-port" target="_blank" rel="noopener">Gogs 与主机共享 22 端口</a></li>
<li><a href="https://gitea.com/jwobith/docker-gitea#additional-steps" target="_blank" rel="noopener">docker-gitea</a></li>
</ul>
]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Gogs</tag>
<tag>Git</tag>
<tag>Docker</tag>
<tag>Gitea</tag>
</tags>
</entry>
<entry>
<title>Docker 搭建 Keepalived 实现 Nginx 双机热备</title>
<url>/2022/07/10/docker-keepalived-nginx-ha/</url>
<content><![CDATA[<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/keepalived-nginx-ha.jpg" alt="keepalived Nginx HA"></p>
<p>docker 搭建 keepalived 实现 nginx 双机热备</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">❯ docker run --privileged -d --name node1 debian:11 top -b</span><br><span class="line">❯ docker run --privileged -d --name node2 debian:11 top -b</span><br></pre></td></tr></table></figure>
<p><code>–-privileged</code> 是指以特权模式启动容器,否则 keepalived 无法成功生成虚拟 IP</p>
<p>分别进入 node1、node2 容器节点(<code>docker exec -it node1 /bin/bash</code> 和 <code>docker exec -it node2 /bin/bash</code> )</p>
<p>安装以下软件 <code>apt update && apt install curl vim iproute2 inetutils-ping psmisc net-tools systemctl nginx keepalived -y</code></p>
<a id="more"></a>
<p>创建以下两个文件:</p>
<blockquote>
<p><code>vi /etc/keepalived/chk_nginx.sh</code></p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">!/bin/bash</span></span><br><span class="line">echo $(date) "start check nginx..." >> /etc/keepalived/check_nginx.log</span><br><span class="line"></span><br><span class="line">counter=$(systemctl status nginx | grep running | wc -l)</span><br><span class="line">if [ "${counter}" = "0" ]; then</span><br><span class="line"> echo $(date) "nginx is not running, restarting..." >> /etc/keepalived/check_nginx.log</span><br><span class="line"> systemctl start nginx</span><br><span class="line"> sleep 2</span><br><span class="line"> counter=$(systemctl status nginx | grep running | wc -l)</span><br><span class="line"> if [ "${counter}" = "0" ]; then</span><br><span class="line"> echo $(date) "nginx is down, kill all keepalived..." >> /etc/keepalived/check_nginx.log</span><br><span class="line"> killall keepalived</span><br><span class="line"> fi</span><br><span class="line">fi</span><br></pre></td></tr></table></figure>
<p>脚本作用是查看是否存在 nginx 进程,不存在就重启 nginx,然后再查看一次,还不存在就杀掉 keepalived 进程(这样备既就会自动上线)<br>脚本权限需设置为 755(<code>chmod 755 /etc/keepalived/chk_nginx.sh</code>),否则 keepalived 会认为它不安全<br>注意首行一定是 <code>#!/bin/bash</code>,而不是 <code>#/bin/bash</code>,否则脚本不会被 keepalived 执行</p>
<p>node1 容器节点为虚拟 IP <code>172.17.0.201</code> 的主节点、虚拟IP <code>172.17.0.202</code> 的备用节点, keepalived 配置文件如下:</p>
<blockquote>
<p><code>vi /etc/keepalived/keepalived.conf</code></p>
</blockquote>
<figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="comment">! Configuration File for keepalived</span></span><br><span class="line"></span><br><span class="line"><span class="attr">global_defs</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">router_id</span> <span class="string">localhost</span></span><br><span class="line"> <span class="attr">script_user</span> <span class="string">root</span></span><br><span class="line"> <span class="attr">enable_script_security</span></span><br><span class="line"><span class="attr">}</span></span><br><span class="line"></span><br><span class="line"><span class="attr">vrrp_script</span> <span class="string">chk_http_port {</span></span><br><span class="line"> <span class="attr">script</span> <span class="string">/etc/keepalived/chk_nginx.sh</span></span><br><span class="line"> <span class="attr">interval</span> <span class="string">10 # 间隔几秒执行脚本(注意最好大于脚本的执行时间)</span></span><br><span class="line"> <span class="attr">weight</span> <span class="string">-20</span></span><br><span class="line"><span class="attr">}</span></span><br><span class="line"></span><br><span class="line"><span class="attr">vrrp_instance</span> <span class="string">VI_1 {</span></span><br><span class="line"> <span class="attr">state</span> <span class="string">MASTER # 1、备机改为:BACKUP</span></span><br><span class="line"> <span class="attr">interface</span> <span class="string">eth0 # 2、替换为实际网卡名称,可使用 ifconfig 或 ip addr 查看</span></span><br><span class="line"> <span class="attr">virtual_router_id</span> <span class="string">51</span></span><br><span class="line"> <span class="attr">priority</span> <span class="string">100 # 3、备机优先级设置小一点,例如:90</span></span><br><span class="line"> <span class="attr">advert_int</span> <span class="string">1</span></span><br><span class="line"> <span class="attr">authentication</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">auth_type</span> <span class="string">PASS</span></span><br><span class="line"> <span class="attr">auth_pass</span> <span class="string">1111</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">track_script</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">chk_http_port</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">virtual_ipaddress</span> <span class="string">{</span></span><br><span class="line"> <span class="meta">172.17.0.201</span> <span class="string"># 虚拟 IP(和 node1、node2 容器节点在同一网段的任意闲置 IP 即可)</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"><span class="attr">}</span></span><br><span class="line"></span><br><span class="line"><span class="attr">vrrp_instance</span> <span class="string">VI_2 {</span></span><br><span class="line"> <span class="attr">state</span> <span class="string">BACKUP</span></span><br><span class="line"> <span class="attr">interface</span> <span class="string">eth0 #</span></span><br><span class="line"> <span class="attr">virtual_router_id</span> <span class="string">52</span></span><br><span class="line"> <span class="attr">priority</span> <span class="string">90</span></span><br><span class="line"> <span class="attr">advert_int</span> <span class="string">1</span></span><br><span class="line"> <span class="attr">authentication</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">auth_type</span> <span class="string">PASS</span></span><br><span class="line"> <span class="attr">auth_pass</span> <span class="string">1111</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">track_script</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">chk_http_port</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">virtual_ipaddress</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">172.17.0.202</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"><span class="attr">}</span></span><br></pre></td></tr></table></figure>
<p>node2 容器节点为虚拟 IP <code>172.17.0.202</code> 的主节点、虚拟IP <code>172.17.0.201</code> 的备用节点, keepalived 配置文件如下:</p>
<blockquote>
<p><code>vi /etc/keepalived/keepalived.conf</code></p>
</blockquote>
<figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="comment">! Configuration File for keepalived</span></span><br><span class="line"></span><br><span class="line"><span class="attr">global_defs</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">router_id</span> <span class="string">localhost</span></span><br><span class="line"> <span class="attr">script_user</span> <span class="string">root</span></span><br><span class="line"> <span class="attr">enable_script_security</span></span><br><span class="line"><span class="attr">}</span></span><br><span class="line"></span><br><span class="line"><span class="attr">vrrp_script</span> <span class="string">chk_http_port {</span></span><br><span class="line"> <span class="attr">script</span> <span class="string">/etc/keepalived/chk_nginx.sh</span></span><br><span class="line"> <span class="attr">interval</span> <span class="string">10</span></span><br><span class="line"> <span class="attr">weight</span> <span class="string">-20</span></span><br><span class="line"><span class="attr">}</span></span><br><span class="line"></span><br><span class="line"><span class="attr">vrrp_instance</span> <span class="string">VI_1 {</span></span><br><span class="line"> <span class="attr">state</span> <span class="string">BACKUP</span></span><br><span class="line"> <span class="attr">interface</span> <span class="string">eth0</span></span><br><span class="line"> <span class="attr">virtual_router_id</span> <span class="string">51</span></span><br><span class="line"> <span class="attr">priority</span> <span class="string">90</span></span><br><span class="line"> <span class="attr">advert_int</span> <span class="string">1</span></span><br><span class="line"> <span class="attr">authentication</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">auth_type</span> <span class="string">PASS</span></span><br><span class="line"> <span class="attr">auth_pass</span> <span class="string">1111</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">track_script</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">chk_http_port</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">virtual_ipaddress</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">172.17.0.201</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"><span class="attr">}</span></span><br><span class="line"></span><br><span class="line"><span class="attr">vrrp_instance</span> <span class="string">VI_2 {</span></span><br><span class="line"> <span class="attr">state</span> <span class="string">MASTER</span></span><br><span class="line"> <span class="attr">interface</span> <span class="string">eth0</span></span><br><span class="line"> <span class="attr">virtual_router_id</span> <span class="string">52</span></span><br><span class="line"> <span class="attr">priority</span> <span class="string">100</span></span><br><span class="line"> <span class="attr">advert_int</span> <span class="string">1</span></span><br><span class="line"> <span class="attr">authentication</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">auth_type</span> <span class="string">PASS</span></span><br><span class="line"> <span class="attr">auth_pass</span> <span class="string">1111</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">track_script</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">chk_http_port</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"> <span class="attr">virtual_ipaddress</span> <span class="string">{</span></span><br><span class="line"> <span class="attr">172.17.0.202</span></span><br><span class="line"> <span class="attr">}</span></span><br><span class="line"><span class="attr">}</span></span><br></pre></td></tr></table></figure>
<p>node1 和 node2 容器节点互为主备, keepalived 配置文件除过注释标注的 3 处不同外,其余配置项保持一致即可<br>可使用 <code>keepalived -t</code> 检测配置文件是否有误</p>
<p>为了验证主备切换效果,可分别在主备 nginx 默认界面添加不同信息加以区分(<code>vi /var/www/html/index.nginx-debian.html</code>)</p>
<p>1、分别启动 node1 和 node2 容器节点的 keepalived(<code>systemctl start keepalived</code>)</p>
<p>2、在 node1 节点容器使用 ifconfig 或 ip addr 命令可看到绑定的虚拟 IP <code>172.17.0.201</code>,此时通过访问虚拟 IP:<code>curl http://172.17.0.201</code> 展示的是 node1 节点的 nginx 信息</p>
<p>3、在 node2 节点容器使用 ifconfig 或 ip addr 命令可看到绑定的虚拟 IP <code>172.17.0.202</code>,此时通过访问虚拟 IP:<code>curl http://172.17.0.202</code> 展示的是 node2 节点的 nginx 信息</p>
<p>4、故意改错 node1 上的 nginx 配置文件让其无法启动,并且停止 nginx(<code>systemctl stop nginx</code>)(模拟宕机情况),node1 节点的 keepalived 检测到 nginx 进程不存在,然后尝试启动 nginx,发现启动失败,所以会杀掉 keepalived 进程,释放虚拟 IP <code>172.17.0.201</code></p>
<p>5、node2 节点 keepalived 感知到 node1 节点下线后会绑定虚拟 IP <code>172.17.0.201</code> 接管请求,完成主备切换。此时在 node2 节点容器使用 ifconfig 或 ip addr 命令可看到绑定的虚拟 IP <code>172.17.0.201</code> 和 <code>172.17.0.202</code>,此时通过访问 <code>curl http://172.17.0.201</code> 和 <code>curl http://172.17.0.201</code> 展示的都是 node2 节点的 nginx 信息</p>
<p>6、当恢复 node1 节点 nginx 配置文件并重新加载,然后启动 keepalived,在 node1 容器节点使用 ifconfig 或 ip addr 命令可再次看到绑定的虚拟 IP <code>172.17.0.201</code>,此时通过访问 <code>curl http://172.17.0.201</code> 展示的又是 node1 节点的 nginx 信息,访问 <code>curl http://172.17.0.201</code> 展示的是 node2 节点的 nginx 信息</p>
<hr>
]]></content>
<categories>
<category>架构</category>
</categories>
<tags>
<tag>Nginx</tag>
<tag>Docker</tag>
<tag>Keepalived</tag>
<tag>HA</tag>
</tags>
</entry>
<entry>
<title>Git 同时 push 到多个远程仓库</title>
<url>/2016/07/24/Git-%E5%90%8C%E6%97%B6-push-%E5%88%B0%E5%A4%9A%E4%B8%AA%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93/</url>
<content><![CDATA[<h3 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h3><p>如果一个本地仓库添加多个远程仓库,不想 git push 多次,可以修改 .git/config 文件</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">vim .git/config</span><br></pre></td></tr></table></figure>
<p>比如以下信息表示在 git@OSC 和 GitHub 两个远程托管</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/20160724193534.png" alt=""></p>
<a id="more"></a>
<p>修改为以下信息</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/20160724190056.png" alt=""></p>
<p>则可同时 push 到两个远程仓库</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git push origin master</span><br><span class="line">Everything up-to-date</span><br><span class="line">Everything up-to-date</span><br></pre></td></tr></table></figure>
<h3 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h3><p>添加第二个远程地址时使用以下命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git remote <span class="built_in">set</span>-url --add origin git@github.com:ehlxr/ehlxr-Hexo.git</span><br></pre></td></tr></table></figure>
<p>查看远程分支 origin:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git remote -v</span><br><span class="line">origin git@git.oschina.net:ehlxr/ehlxr-Hexo.git (fetch)</span><br><span class="line">origin git@git.oschina.net:ehlxr/ehlxr-Hexo.git (push)</span><br><span class="line">origin git@github.com:ehlxr/ehlxr-Hexo.git (push)</span><br></pre></td></tr></table></figure>
<p>也可以同时 push 到多个远程地址</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git push origin master</span><br><span class="line">Everything up-to-date</span><br><span class="line">Everything up-to-date</span><br></pre></td></tr></table></figure>
<hr>
]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title>GitHub 更新已经 fork 的项目</title>
<url>/2016/07/28/update-from-github-fork/</url>
<content><![CDATA[<blockquote>
<p>GitHub 上有个很方便的功能叫 fork,将别人的工程一键复制到自己账号下。这个功能很方便,但有点不足的是,当源项目更新后,你 fork 的分支并不会一起更新,需要自己手动去更新,下面记录下网上找到的更新的开发方法。</p>
</blockquote>
<h3 id="1-在本地装好-GitHub-客户端,或者-Git-客户端"><a href="#1-在本地装好-GitHub-客户端,或者-Git-客户端" class="headerlink" title="1. 在本地装好 GitHub 客户端,或者 Git 客户端"></a>1. 在本地装好 GitHub 客户端,或者 Git 客户端</h3><h3 id="2-clone-自己的-fork-分支到本地"><a href="#2-clone-自己的-fork-分支到本地" class="headerlink" title="2. clone 自己的 fork 分支到本地"></a>2. clone 自己的 fork 分支到本地</h3><p>可以直接使用 GitHub 客户端,clone 到本地,如果使用命令行,命令为:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git <span class="built_in">clone</span> git@github.com:ehlxr/strman-java.git</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="3-增加源分支地址到你项目远程分支列表中"><a href="#3-增加源分支地址到你项目远程分支列表中" class="headerlink" title="3. 增加源分支地址到你项目远程分支列表中"></a>3. 增加源分支地址到你项目远程分支列表中</h3><p>此处是关键,先得将原来的仓库指定为 upstream,命令为:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git remote add upstream git@github.com:shekhargulati/strman-java.git</span><br></pre></td></tr></table></figure>
<p>此处可使用 git remote -v 查看远程分支列表</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git remote -v</span><br><span class="line">origin git@github.com:ehlxr/strman-java.git (fetch)</span><br><span class="line">origin git@github.com:ehlxr/strman-java.git (push)</span><br><span class="line">upstream git@github.com:shekhargulati/strman-java.git (fetch)</span><br><span class="line">upstream git@github.com:shekhargulati/strman-java.git (push)</span><br></pre></td></tr></table></figure>
<h3 id="4-fetch-源分支的新版本到本地"><a href="#4-fetch-源分支的新版本到本地" class="headerlink" title="4. fetch 源分支的新版本到本地"></a>4. fetch 源分支的新版本到本地</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git fetch upstream</span><br></pre></td></tr></table></figure>
<h3 id="5-合并两个版本的代码"><a href="#5-合并两个版本的代码" class="headerlink" title="5. 合并两个版本的代码"></a>5. 合并两个版本的代码</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git merge upstream/master</span><br></pre></td></tr></table></figure>
<h3 id="6-将合并后的代码-push-到-GitHub-上去"><a href="#6-将合并后的代码-push-到-GitHub-上去" class="headerlink" title="6. 将合并后的代码 push 到 GitHub 上去"></a>6. 将合并后的代码 push 到 GitHub 上去</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git push origin master</span><br></pre></td></tr></table></figure>
<hr>
<p>参考网址:</p>
<p><a href="https://help.github.com/articles/fork-a-repo" target="_blank" rel="noopener">https://help.github.com/articles/fork-a-repo</a></p>
<p><a href="http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E7%89%9B/14167.shtml" target="_blank" rel="noopener">原文出处</a></p>
]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
<tag>GitHub</tag>
</tags>
</entry>
<entry>
<title>Go 常用代码块</title>
<url>/2017/12/06/go-commons-code-snippets/</url>
<content><![CDATA[<blockquote>
<p>总结备忘一下常用的的 Go 代码片段</p>
</blockquote>
<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">getFilelist</span><span class="params">(r <span class="keyword">string</span>)</span></span> {</span><br><span class="line"> err := filepath.Walk(r, <span class="function"><span class="keyword">func</span><span class="params">(p <span class="keyword">string</span>, f os.FileInfo, err error)</span> <span class="title">error</span></span> {</span><br><span class="line"> <span class="keyword">if</span> f == <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> p == r || f.IsDir() {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Println(p)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></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"> fmt.Printf(<span class="string">"filepath.Walk() returned %v\n"</span>, err)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="定时器"><a href="#定时器" class="headerlink" title="定时器"></a>定时器</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">duration := <span class="number">2</span> * time.Second</span><br><span class="line">timer := time.NewTimer(duration)</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"> <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> <-timer.C:</span><br><span class="line"> fmt.Println(<span class="string">"here"</span>)</span><br><span class="line"> timer.Reset(duration)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}()</span><br></pre></td></tr></table></figure>
<h2 id="定时执行任务"><a href="#定时执行任务" class="headerlink" title="定时执行任务"></a>定时执行任务</h2><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="keyword">const</span> INTERVAL_PERIOD time.Duration = <span class="number">24</span> * time.Hour</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> HOUR_TO_TICK <span class="keyword">int</span> = <span class="number">23</span></span><br><span class="line"><span class="keyword">const</span> MINUTE_TO_TICK <span class="keyword">int</span> = <span class="number">00</span></span><br><span class="line"><span class="keyword">const</span> SECOND_TO_TICK <span class="keyword">int</span> = <span class="number">03</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"> ticker := updateTicker()</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <-ticker.C</span><br><span class="line"> fmt.Println(time.Now(), <span class="string">"- just ticked"</span>)</span><br><span class="line"> ticker = updateTicker()</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">updateTicker</span><span class="params">()</span> *<span class="title">time</span>.<span class="title">Ticker</span></span> {</span><br><span class="line"> n := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(),</span><br><span class="line"> HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, <span class="number">0</span>, time.Local)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> !n.After(time.Now()) {</span><br><span class="line"> n = n.Add(INTERVAL_PERIOD)</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(n, <span class="string">"- next tick"</span>)</span><br><span class="line"> diff := n.Sub(time.Now())</span><br><span class="line"> <span class="keyword">return</span> time.NewTicker(diff)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="读取文件"><a href="#读取文件" class="headerlink" title="读取文件"></a>读取文件</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">f, err := os.Open(<span class="string">"/test.txt"</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">defer</span> f.Close()</span><br><span class="line">fd, err := ioutil.ReadAll(f)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line">}</span><br><span class="line">fmt.Println(<span class="keyword">string</span>(fd))</span><br></pre></td></tr></table></figure>
<h2 id="map-转-json"><a href="#map-转-json" class="headerlink" title="map 转 json"></a>map 转 json</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{})</span><br><span class="line">m[<span class="string">"show"</span>] = <span class="string">"1"</span></span><br><span class="line">m[<span class="string">"content"</span>] = <span class="string">"test"</span></span><br><span class="line">j, err := json.Marshal(m)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line">}</span><br><span class="line">fmt.Println(<span class="keyword">string</span>(j))</span><br></pre></td></tr></table></figure>
<h2 id="json-转-map"><a href="#json-转-map" class="headerlink" title="json 转 map"></a>json 转 map</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> result = []<span class="keyword">byte</span>(<span class="string">`</span></span><br><span class="line"><span class="string">{</span></span><br><span class="line"><span class="string"> "show": 1,</span></span><br><span class="line"><span class="string"> "content": "test"</span></span><br><span class="line"><span class="string">}`</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> r <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">interface</span>{}</span><br><span class="line"><span class="keyword">if</span> err := json.Unmarshal(result, &r); err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line">}</span><br><span class="line">fmt.Println(r)</span><br></pre></td></tr></table></figure>
<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">writeFile</span><span class="params">(path <span class="keyword">string</span>, b []<span class="keyword">byte</span>)</span></span> {</span><br><span class="line"> file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, <span class="number">0777</span>)</span><br><span class="line"> <span class="keyword">defer</span> file.Close()</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="built_in">panic</span>(err)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> file.Write(b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="简单的获取-IP"><a href="#简单的获取-IP" class="headerlink" title="简单的获取 IP"></a>简单的获取 IP</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">GetPulicIP</span><span class="params">()</span> <span class="title">string</span></span> {</span><br><span class="line"> conn, _ := net.Dial(<span class="string">"udp"</span>, <span class="string">"8.8.8.8:80"</span>)</span><br><span class="line"> <span class="keyword">defer</span> conn.Close()</span><br><span class="line"> localAddr := conn.LocalAddr().String()</span><br><span class="line"> idx := strings.LastIndex(localAddr, <span class="string">":"</span>)</span><br><span class="line"> <span class="keyword">return</span> localAddr[<span class="number">0</span>:idx]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Golang</category>
</categories>
<tags>
<tag>Golang</tag>
<tag>Go</tag>
</tags>
</entry>
<entry>
<title>Golang 反射使用总结</title>
<url>/2018/01/26/golang-reflect/</url>
<content><![CDATA[<p>Go 语言中反射的操作主要定义在标准库 <a href="https://golang.org/pkg/reflect/" target="_blank" rel="noopener"><code>reflect</code></a> 中,在标准库中定义了两种类型来表现运行时的对象信息,分别是:<a href="https://golang.org/pkg/reflect/#Value" target="_blank" rel="noopener"><code>reflect.Value</code></a>(反射对象的类型)和 <a href="https://golang.org/pkg/reflect/#Type" target="_blank" rel="noopener"><code>reflect.Type</code></a>(反射对象的值),Go 语言中所有反射操作都是基于这两个类型进行的。</p>
<img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/golang-reflect.jpg" class="" width="350">
<!-- ![golang-reflect][1] -->
<p>为了方便演示操作(<a href="https://github.com/ehlxr/go-utils/blob/master/common/reflect/main.go" target="_blank" rel="noopener">完整代码示例</a>),首先定义以下结构体以及字段、方法:</p>
<a id="more"></a>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> User <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="keyword">string</span> <span class="string">`json:"name"`</span></span><br><span class="line"> Age <span class="keyword">int</span> <span class="string">`json:"age" default:"18"`</span></span><br><span class="line"> addr <span class="keyword">string</span> <span class="string">`json:"addr"`</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">(u User)</span> <span class="title">Do</span><span class="params">(in <span class="keyword">string</span>)</span> <span class="params">(<span class="keyword">string</span>, <span class="keyword">int</span>)</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"%s Name is %s, Age is %d \n"</span>, in, u.Name, u.Age)</span><br><span class="line"> <span class="keyword">return</span> u.Name, u.Age</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="一、反射对象-Value-和-Type"><a href="#一、反射对象-Value-和-Type" class="headerlink" title="一、反射对象 Value 和 Type"></a>一、反射对象 Value 和 Type</h2><p>既然 Go 语言中所有反射操作都是基于 <code>Value</code> 和 <code>Type</code> 进行的,那么想要进行反射操作,首先就要获取到反射对象的这两个类型对象才可以。</p>
<p><code>reflect</code> 包提供了两个函数:<code>reflect.ValueOf()</code> 和 <code>reflect.TypeOf()</code>,通过这两个函数就可以方便的获取到任意类型(用 <code>interface{}</code> 表示任意类型)的 <code>Value</code> 对象和 <code>Type</code> 对象。例如:</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line">u := User{<span class="string">"tom"</span>, <span class="number">27</span>, <span class="string">"beijing"</span>}</span><br><span class="line"></span><br><span class="line">v := reflect.ValueOf(u)</span><br><span class="line">fmt.Println(v)</span><br><span class="line"></span><br><span class="line">t := reflect.TypeOf(u)</span><br><span class="line">fmt.Println(t)</span><br></pre></td></tr></table></figure>
<p>知道 <code>Value</code> 对象后,也可以通过 <code>Value.Type()</code> 方法获取到 <code>Type</code> 对象。例如:</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line">t1 := v.Type()</span><br><span class="line">fmt.Println(t == t1)</span><br></pre></td></tr></table></figure>
<p>可以看到输出结果为 <code>true</code>。</p>
<p>通过 <code>Type</code> 类型对象也可以获取到 <code>Value</code> 类型对象,不过是零值的指针。例如:</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line">v1 := reflect.New(t)</span><br><span class="line">fmt.Println(v1)</span><br></pre></td></tr></table></figure>
<p>结果为:<code>&{ 0}</code></p>
<h2 id="二、反射对象的-Kind"><a href="#二、反射对象的-Kind" class="headerlink" title="二、反射对象的 Kind"></a>二、反射对象的 Kind</h2><p><code>Kind</code> 表示反射对象的类型 <code>Type</code> 所代表的具体类型,零值表示无效的类型,具体有以下类型值:</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> Kind <span class="keyword">uint</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> Invalid Kind = <span class="literal">iota</span></span><br><span class="line"> Bool</span><br><span class="line"> Int</span><br><span class="line"> Int8</span><br><span class="line"> Int16</span><br><span class="line"> Int32</span><br><span class="line"> Int64</span><br><span class="line"> Uint</span><br><span class="line"> Uint8</span><br><span class="line"> Uint16</span><br><span class="line"> Uint32</span><br><span class="line"> Uint64</span><br><span class="line"> Uintptr</span><br><span class="line"> Float32</span><br><span class="line"> Float64</span><br><span class="line"> Complex64</span><br><span class="line"> Complex128</span><br><span class="line"> Array</span><br><span class="line"> Chan</span><br><span class="line"> Func</span><br><span class="line"> Interface</span><br><span class="line"> Map</span><br><span class="line"> Ptr</span><br><span class="line"> Slice</span><br><span class="line"> String</span><br><span class="line"> Struct</span><br><span class="line"> UnsafePointer</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>可以通过 <code>Value.Kind()</code> 或者 <code>Type.Kind()</code> 函数获得。例如:</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取 Kind 类型</span></span><br><span class="line">k := t.Kind()</span><br><span class="line">fmt.Println(k)</span><br><span class="line">k1 := v.Kind()</span><br><span class="line">fmt.Println(k1)</span><br><span class="line">fmt.Println(k == k1)</span><br><span class="line">fmt.Println()</span><br></pre></td></tr></table></figure>
<p>可以看到两种方式获取的结果是一样的,都是 <code>struct</code>。</p>
<h2 id="三、反射对象的字段"><a href="#三、反射对象的字段" class="headerlink" title="三、反射对象的字段"></a>三、反射对象的字段</h2><p>反射能够操作的字段和方法必须是可导出(首字母大写)的。</p>
<p>反射对象的字段值修改要通过调用 <code>Value</code> 类型的方法 <code>Elem()</code> 后返回的 <code>Value</code> 对象值来操作。</p>
<p><code>Elem()</code> 方法定义:<code>func (v Value) Elem() Value</code>,返回 <code>v</code> 包含的值或指针 <code>v</code> 指向的值,<code>v</code> 的 <code>Kind</code> 如果不是 <code>Interface</code> 或 <code>Ptr</code>,则会 panic。</p>
<p><code>reflect.Indirect()</code> 函数的如果参数是指针的 <code>Value</code>,则相当于调用了 <code>Elem()</code> 方法返回的值,否则返回 <code>Value</code> 自身值。</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 修改反射对象的值</span></span><br><span class="line">i := <span class="number">20</span></span><br><span class="line">fmt.Println(<span class="string">"before i ="</span>, i)</span><br><span class="line">e := reflect.Indirect(reflect.ValueOf(&i))</span><br><span class="line"><span class="comment">// e := reflect.ValueOf(&i).Elem()</span></span><br><span class="line"><span class="keyword">if</span> e.CanSet() {</span><br><span class="line"> e.SetInt(<span class="number">22</span>)</span><br><span class="line">}</span><br><span class="line">fmt.Println(<span class="string">"after i ="</span>, i)</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">// elem := reflect.Indirect(reflect.ValueOf(&u))</span></span><br><span class="line">elem := reflect.ValueOf(&u).Elem()</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < t.NumField(); i++ {</span><br><span class="line"> <span class="comment">// 反射获取字段的元信息,例如:名称、Tag 等</span></span><br><span class="line"> ft := t.Field(i)</span><br><span class="line"> fmt.Println(<span class="string">"field name:"</span>, ft.Name)</span><br><span class="line"> tag := ft.Tag</span><br><span class="line"> fmt.Println(<span class="string">"Tag:"</span>, tag)</span><br><span class="line"> fmt.Println(<span class="string">"Tag json:"</span>, tag.Get(<span class="string">"json"</span>))</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 反射修改字段的值</span></span><br><span class="line"> fv := elem.Field(i)</span><br><span class="line"> <span class="keyword">if</span> fv.CanSet() {</span><br><span class="line"> <span class="keyword">if</span> fv.Kind() == reflect.Int {</span><br><span class="line"> fmt.Println(<span class="string">"change age to 30"</span>)</span><br><span class="line"> fv.SetInt(<span class="number">30</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> fv.Kind() == reflect.String {</span><br><span class="line"> fmt.Println(<span class="string">"change name to jerry"</span>)</span><br><span class="line"> fv.SetString(<span class="string">"jerry"</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> fmt.Println()</span><br><span class="line">}</span><br><span class="line">fmt.Println(<span class="string">"after user:"</span>, u)</span><br></pre></td></tr></table></figure>
<h2 id="四、反射对象的方法"><a href="#四、反射对象的方法" class="headerlink" title="四、反射对象的方法"></a>四、反射对象的方法</h2><p>可以通过 <code>Value</code> 的 <code>Method()</code> 方法或 <code>Type</code> 的 <code>Method()</code> 方法,两种形式获取对象方法信息进行反射调用,略有不同,示例如下:</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 反射方法操作</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < v.NumMethod(); i++ {</span><br><span class="line"> method := t.Method(i) <span class="comment">// 获取方法信息对象,方法 1</span></span><br><span class="line"> mt := method.Type <span class="comment">// 获取方法信息 Type 对象,方法 1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// m := v.Method(i) // 获取方法信息对象,方法 2</span></span><br><span class="line"> <span class="comment">// mt := m.Type() // 获取方法信息 Type 对象,方法 2</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"method name:"</span>, method.Name)</span><br><span class="line"></span><br><span class="line"> in := []reflect.Value{}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取方法入参类型</span></span><br><span class="line"> <span class="keyword">for</span> j := <span class="number">0</span>; j < mt.NumIn(); j++ {</span><br><span class="line"> fmt.Println(<span class="string">"method in type:"</span>, mt.In(j))</span><br><span class="line"> <span class="keyword">if</span> mt.In(j).Kind() == reflect.String {</span><br><span class="line"> in = <span class="built_in">append</span>(in, reflect.ValueOf(<span class="string">"welcome"</span>))</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 方法 1 获取的方法信息对象会把方法的接受者也当着入参之一</span></span><br><span class="line"> <span class="keyword">if</span> mt.In(j).Name() == t.Name() {</span><br><span class="line"> in = <span class="built_in">append</span>(in, v)</span><br><span class="line"> }</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="keyword">for</span> j := <span class="number">0</span>; j < mt.NumOut(); j++ {</span><br><span class="line"> fmt.Println(<span class="string">"method out type:"</span>, mt.Out(j))</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">// out := m.Call(in) // 方法 1 获取的 Method 对象反射调用方式</span></span><br><span class="line"> out := method.Func.Call(in) <span class="comment">// 方法 1 获取的 Method 对象反射调用方式</span></span><br><span class="line"> <span class="keyword">for</span> _, o := <span class="keyword">range</span> out {</span><br><span class="line"> fmt.Println(<span class="string">"out:"</span>, o)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="五、反射对象-Value-还原"><a href="#五、反射对象-Value-还原" class="headerlink" title="五、反射对象 Value 还原"></a>五、反射对象 Value 还原</h2><p>通过 <code>reflect.ValueOf()</code> 可以把任意类型对象转换为 <code>Value</code> 类型对象,也可以通过 <code>Value</code> 类型的方法 <code>Interface()</code> 把 <code>Value</code> 类型对象还原为原始数据类型对象。</p>
<figure class="highlight golang"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Value 转原始类型</span></span><br><span class="line"><span class="keyword">if</span> u1, ok := v.Interface().(User); ok {</span><br><span class="line"> fmt.Println(<span class="string">"after:"</span>, u1.Name, u1.Age)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>Golang</category>
</categories>
<tags>
<tag>Golang</tag>
<tag>Go</tag>
</tags>
</entry>
<entry>
<title>Good bye 2016...</title>
<url>/2016/12/31/Good-bye-2016/</url>
<content><![CDATA[<p>时光飞逝,转眼间,2016 年已经在今天画上句号,结束了,再提起 2016 年就已经是过往了…</p>
<p>印象中,十年应该要算是很长的一段时间吧,但仔细一琢磨十年前也就才 2006 年…那时候是高中,每天除了上课就是写作业,学习很枯燥总感觉时间很多,憧憬着美好的未来,过着也算是无忧无虑生活。每天放学,下晚自习,总是很快的骑着自行车冲出校门口,因为晚了学生会很多,那时候骑自行车一个比一个快。周五不用上晚自习,三五成群去网吧通宵,包宿八块钱八个小时,从晚上十点到第二天早上六点,六点从网吧出来总会有种恍如隔世的感觉,大街上几乎没有人,回出租屋睡上一天,感觉就是一周最美好的时光了!看着别的同学拿着小灵通、MP3 ,很羡慕,自已也想要有一个,都不敢奢望能有一台电脑…一切仿佛也就是昨天而已,但是已经十年之前了,不禁一颤,人生能有几个十年…</p>
<a id="more"></a>
<p>总结 2016,收获,知足,感恩…眼前的要珍惜,来之不易的拥有更要珍惜。</p>
<!-- <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" height= "90%" width="100%" src="https://music.daoapp.io/iframe?song=35307971&qssl=1&qlrc=1&qnarrow=0&autoplay=1"></iframe> -->
<hr>
]]></content>
<categories>
<category>年末总结</category>
</categories>
<tags>
<tag>年末总结</tag>
</tags>
</entry>
<entry>
<title>HTTPS 笔记</title>
<url>/2020/03/11/https-note/</url>
<content><![CDATA[<p>随着互联网的迅速发展,网络安全问题日益凸显,现在 Chrome 浏览器已经开始阻止非 https 网站的访问了。对于 https 的流程一直不是十分清晰,借着还没有完全复工有时间,大概画了个图总结一下。</p>
<p>想要了解 https 流程,CA 的相关知识,加密方式(对称加密、非对称加密),以及哈希计算(例如:MD5、sha256)等技术必须得掌握,这里先不做介绍,后续有时间再进行归纳总结。</p>
<p>https 是在 http 的基础上加入了 SSL 协议,SSL 依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密。</p>
<a id="more"></a>
<h2 id="HTTPS-的验证流程"><a href="#HTTPS-的验证流程" class="headerlink" title="HTTPS 的验证流程"></a>HTTPS 的验证流程</h2><p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/https.jpg" alt="https"></p>
<ol>
<li>客户端发起 https 请求</li>
<li>服务端返回数字证书文件(X.509 格式)</li>
<li>客户端验证数字证书,并且提取服务端公钥</li>
<li>如果客户端验证数字证书通过,则随机生成一个对称加密的 key,并使用服务器公钥对 key 加密</li>
<li>客户端发送加密后的 key 到服务端</li>
<li>服务端使用私钥解密拿到 key</li>
<li>客户端与服务端使用该 key 对称加解密通讯信息</li>
</ol>
<h2 id="客户端验证数字证书"><a href="#客户端验证数字证书" class="headerlink" title="客户端验证数字证书"></a>客户端验证数字证书</h2><ol>
<li>浏览器安装后会自带一些权威 CA 公钥</li>
<li>使用相匹配的 CA 公钥对数字证书中的数字签名解密,如果能够解密则得到数字证书的摘要,由此证明数字证书是可信的</li>
<li>根据数字证书中的散列算法对网站信息进行哈希运算,将得到的结果与上一步得到的摘要对比,如果两者一致,就证明证书未被修改过</li>
</ol>
<h2 id="数字证书的签发过程"><a href="#数字证书的签发过程" class="headerlink" title="数字证书的签发过程"></a>数字证书的签发过程</h2><p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/digital_signature.jpg" alt="digital signature"></p>
<hr>
]]></content>
<categories>
<category>Java开发技术</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title>JVM TLAB</title>
<url>/2021/07/27/jvm-tlab/</url>
<content><![CDATA[<p><strong>TLAB(Thread Local Allocation Buffer)</strong> 线程本地分配缓存区</p>
<ol>
<li>由于对象一般分配在堆上,而堆是线程共用的,因此可能会有多个线程在堆上申请空间,而每一次的<strong>对象分配都必须加锁保证线程同步</strong>,会使分配的效率下降。考虑到对象分配几乎是 <code>Java</code> 中最常用的操作,因此 <code>JVM</code> 使用了 <code>TLAB</code> 这样的线程专有区域来避免多线程冲突,提高对象分配的效率。</li>
<li>我们说 <code>TLAB</code> 是线程独享的,但是只是在 <strong>“分配”</strong> 这个动作上是线程独享的,至于在读取、垃圾回收等动作上都是线程共享的。而且在使用上也没有什么区别</li>
<li><code>JVM</code> 为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间 <code>TLAB</code>,其大小由 <code>JVM</code> 根据运行的情况计算而得,在 <code>TLAB</code> 上分配对象时不需要加锁,因此 <strong>JVM 在给线程的对象分配内存时会尽量的在 TLAB 上分配</strong>,在这种情况下 JVM 中分配对象内存的性能和 <code>C</code> 基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配</li>
<li>在 <code>TLAB</code> 分配之后,并不影响对象的移动和回收,也就是说,虽然对象刚开始可能通过 <code>TLAB</code> 分配内存,存放在 <code>Eden</code> 区,但是还是会被垃圾回收或者被移到 <code>Survivor Space、Old Gen</code> 等。</li>
<li><strong>“堆是线程共享的内存区域” 这句话并不完全正确</strong>,因为 <code>TLAB</code> 是堆内存的一部分,它在读取上确实是线程共享的,但是在内存分配上,是线程独享的。</li>
<li><code>TLAB</code> 的空间其实并不大(默认是 <code>eden</code> 区空间的 <code>1%</code>),所以大对象还是可能需要在堆内存中直接分配。那么,对象的内存分配步骤就是先尝试 <code>TLAB</code> 分配,空间不足之后,再判断是否应该直接进入老年代,然后再确定是再 <code>eden</code> 分配还是在老年代分配。<a id="more"></a>
</li>
</ol>
<p><code>TLAB</code> 对象分配过程</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/92dcfb688fa1414c9480e423efdd27d5.png" alt=""></p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/41eb453d8c00439aa719d6c5396db3ad.png" alt=""></p>
<p>参考链接</p>
<ul>
<li><a href="http://www.atguigu.com/download_detail.shtml?v=279" target="_blank" rel="noopener">尚硅谷-宋红康-详解 Java 虚拟机</a></li>
<li><a href="https://www.cnblogs.com/myseries/p/12884249.html" target="_blank" rel="noopener">JVM 关于对象分配在堆、栈、TLAB 的理解</a></li>
<li><a href="https://blog.csdn.net/QGhurt/article/details/107289843" target="_blank" rel="noopener">TLAB(Thread Local Allocation Buffer)</a></li>
</ul>
<hr>
]]></content>
<categories>
<category>JVM</category>
</categories>
<tags>
<tag>JVM</tag>
<tag>TLAB</tag>
<tag>JAVA</tag>
</tags>
</entry>
<entry>
<title>JVM-垃圾回收(二)</title>
<url>/2018/08/23/jvm-gc2/</url>
<content><![CDATA[<blockquote>
<p>接着上次 JVM 中 GC 机制的总结,这次主要复习一下垃圾收集的常用算法和 Minor GC、Full GC 相关的一些知识点。</p>
</blockquote>
<h2 id="一、垃圾收集算法"><a href="#一、垃圾收集算法" class="headerlink" title="一、垃圾收集算法"></a>一、垃圾收集算法</h2><h3 id="1-1-标记-清除(Mark-Sweep)"><a href="#1-1-标记-清除(Mark-Sweep)" class="headerlink" title="1.1 标记 - 清除(Mark-Sweep)"></a>1.1 标记 - 清除(Mark-Sweep)</h3><p>算法分成 “标记”、“清除” 两个阶段:首先标记出所有需要回收的对象(两次标记),在标记完成后统一回收所有被标记的对象。如下图所示:</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/687148dbly1ftwah4c7cxj20gv07b0sz.jpg" alt=""></p>
<a id="more"></a>
<p>标记-清除算法的不足主要有以下两点:</p>
<ul>
<li><strong>空间问题</strong>,会产生大量不连续的内存碎片,导致无法给大对象分配内存。</li>
<li><strong>效率问题</strong>,因为内存碎片的存在,操作会变得更加费时,因为查找下一个可用空闲块已不再是一个简单操作。</li>
</ul>
<h3 id="1-2-标记-整理(Mark-Compact)"><a href="#1-2-标记-整理(Mark-Compact)" class="headerlink" title="1.2 标记 - 整理(Mark-Compact)"></a>1.2 标记 - 整理(Mark-Compact)</h3><p>此算法的标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。具体示意图如下所示:</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/687148dbly1ftwamib399j20hq06f0t0.jpg" alt=""></p>
<h3 id="1-3-复制(Copying)"><a href="#1-3-复制(Copying)" class="headerlink" title="1.3 复制(Copying)"></a>1.3 复制(Copying)</h3><p>将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。主要不足是只使用了内存的一半。</p>
<p>复制算法过程:</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/687148dbly1ftwao1yh4wj20j00670t3.jpg" alt=""></p>
<h3 id="1-4-分代收集"><a href="#1-4-分代收集" class="headerlink" title="1.4 分代收集"></a>1.4 分代收集</h3><p>JVM 采用分代收集(Generational Collection)算法,此算法相较于前几种没有什么新的特征,主要思想为:根据对象存活周期的不同将内存划分为几块,一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法:</p>
<ul>
<li><strong>新生代使用复制算法</strong> 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。</li>
<li><strong>老年代使用标记 - 清理 或者 标记 - 整理 算法</strong> 在老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用 “标记 - 清除” 或 “标记 - 整理” 算法来进行回收。</li>
</ul>
<h2 id="二、Minor-GC-和-Full-GC"><a href="#二、Minor-GC-和-Full-GC" class="headerlink" title="二、Minor GC 和 Full GC"></a>二、Minor GC 和 Full GC</h2><h3 id="2-1-Minor-GC"><a href="#2-1-Minor-GC" class="headerlink" title="2.1 Minor GC"></a>2.1 Minor GC</h3><p>发生在新生代上,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。</p>
<p>Minor GC 会使用复制收集算法进行垃圾回收,但是并不是将内存划分为大小相等的两块,而是分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和使用过的那一块 Survivor。HotSpot 虚拟机的 Eden 和 Survivor 的大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时需要依赖于老年代进行分配担保,也就是借用老年代的空间存储放不下的对象。</p>
<h3 id="2-2-Full-GC"><a href="#2-2-Full-GC" class="headerlink" title="2.2 Full GC"></a>2.2 Full GC</h3><p>发生在老年代上,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。</p>
<h3 id="2-3-Full-GC-的触发条件"><a href="#2-3-Full-GC-的触发条件" class="headerlink" title="2.3 Full GC 的触发条件"></a>2.3 Full GC 的触发条件</h3><p>对于 Minor GC,其触发条件非常简单,当 Eden 区空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件:</p>
<h4 id="2-3-1-调用-System-gc"><a href="#2-3-1-调用-System-gc" class="headerlink" title="2.3.1 调用 System.gc()"></a>2.3.1 调用 <code>System.gc()</code></h4><p>此方法的调用是建议 JVM 进行 Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加 Full GC 的频率,也即增加了间歇性停顿的次数。因此强烈建议能不使用此方法就不要使用,让虚拟机自己去管理它的内存,可通过 <code>-XX:+ DisableExplicitGC</code> 虚拟机参数来禁止 RMI 调用 <code>System.gc()</code>。</p>
<h4 id="2-3-2-老年代空间不足"><a href="#2-3-2-老年代空间不足" class="headerlink" title="2.3.2 老年代空间不足"></a>2.3.2 老年代空间不足</h4><p>老年代空间不足的常见场景为大对象直接进入老年代、长期存活的对象进入老年代等。</p>
<p>为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 <code>-Xmn</code> 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 <code>-XX:MaxTenuringThreshold</code> 虚拟机参数调大对象进入老年代的年龄,让对象在新生代多存活一段时间。</p>
<h4 id="2-3-3-空间分配担保失败"><a href="#2-3-3-空间分配担保失败" class="headerlink" title="2.3.3 空间分配担保失败"></a>2.3.3 空间分配担保失败</h4><p>使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。</p>
<h4 id="2-3-4-JDK-1-7-及以前的永久代空间不足"><a href="#2-3-4-JDK-1-7-及以前的永久代空间不足" class="headerlink" title="2.3.4 JDK 1.7 及以前的永久代空间不足"></a>2.3.4 JDK 1.7 及以前的永久代空间不足</h4><p>在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS 垃圾收集器的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 <code>java.lang.OutOfMemoryError</code>。</p>
<p>为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS 垃圾收集器。</p>
<p>在 JDK 1.8 中用元空间替换了永久代作为方法区的实现,元空间是本地内存,因此减少了一种 Full GC 触发的可能性。</p>
<h4 id="2-3-5-Concurrent-Mode-Failure"><a href="#2-3-5-Concurrent-Mode-Failure" class="headerlink" title="2.3.5 Concurrent Mode Failure"></a>2.3.5 Concurrent Mode Failure</h4><p>使用 CMS 垃圾收集器执行的过程中,同时有对象要放入老年代,而此时老年代空间不足(有时候 “<strong>空间不足</strong>” 是指 CMS GC 当前的浮动垃圾过多导致暂时性的空间不足),便会报 <code>Concurrent Mode Failure</code> 错误,并触发 Full GC。</p>
<hr>
]]></content>
<categories>
<category>Java开发技术</category>
</categories>
<tags>
<tag>JVM</tag>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title>Java List与数组之间的转换</title>
<url>/2016/07/20/Java-List%E4%B8%8E%E6%95%B0%E7%BB%84%E4%B9%8B%E9%97%B4%E7%9A%84%E8%BD%AC%E6%8D%A2/</url>
<content><![CDATA[<h3 id="1-数组转换为List"><a href="#1-数组转换为List" class="headerlink" title="1. 数组转换为List"></a>1. 数组转换为List</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">String[] arr = <span class="keyword">new</span> String[] {<span class="string">"str1"</span>, <span class="string">"str2"</span>};</span><br><span class="line">List<String> list = Arrays.asList(arr);</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="2-List转换为数组"><a href="#2-List转换为数组" class="headerlink" title="2 .List转换为数组"></a>2 .List转换为数组</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<String> list = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">list.add(<span class="string">"str1"</span>);</span><br><span class="line">list.add(<span class="string">"str2"</span>);</span><br><span class="line"><span class="keyword">int</span> size = list.size();</span><br><span class="line">String[] arr = list.toArray(<span class="keyword">new</span> String[size]);</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java开发技术</category>
</categories>
<tags>
<tag>Java</tag>
<tag>List</tag>
</tags>
</entry>
<entry>
<title>Java 位运算笔记</title>
<url>/2018/08/02/java-positional-operator/</url>
<content><![CDATA[<p>一些零碎的知识点总是似懂非懂,用法老是模棱两可,每次都要去网络上查询,长时间不用又忘记了。比如 Java 中的位运算。今天抽空归纳总结一下,加强一下记忆。</p>
<h2 id="一、原码、反码和补码"><a href="#一、原码、反码和补码" class="headerlink" title="一、原码、反码和补码"></a>一、原码、反码和补码</h2><h3 id="1-1-原码"><a href="#1-1-原码" class="headerlink" title="1.1 原码"></a>1.1 原码</h3><p>一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号,正数为 0, 负数为 1。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。</p>
<p>原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。比如 8 位二进制:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[+1] 原 = 0000 0001</span><br><span class="line">[-1] 原 = 1000 0001</span><br></pre></td></tr></table></figure>
<p>第一位是符号位,因为第一位是符号位,所以 8 位二进制数的取值范围就是:[1111 1111 , 0111 1111],即:[-127 , 127]</p>
<a id="more"></a>
<h3 id="1-2-反码"><a href="#1-2-反码" class="headerlink" title="1.2 反码"></a>1.2 反码</h3><p>反码的表示方法是:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[+1] = [00000001] 原 = [00000001] 反</span><br><span class="line">[-1] = [10000001] 原 = [11111110] 反</span><br></pre></td></tr></table></figure>
<h3 id="1-3-补码"><a href="#1-3-补码" class="headerlink" title="1.3 补码"></a>1.3 补码</h3><p>补码的表示方法是:正数的补码就是其本身,负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后 + 1。(即在反码的基础上 + 1)</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[+1] = [00000001] 原 = [00000001] 反 = [00000001] 补</span><br><span class="line">[-1] = [10000001] 原 = [11111110] 反 = [11111111] 补</span><br></pre></td></tr></table></figure>
<h2 id="二、左移运算(-lt-lt-)"><a href="#二、左移运算(-lt-lt-)" class="headerlink" title="二、左移运算(<<)"></a>二、左移运算(<<)</h2><p><code>value << num</code></p>
<blockquote>
<p>num 指定要移位值;value 移动的位数。</p>
</blockquote>
<p>将左操作数(value)转为二进制数后向左边移动 num 位,并且在低位补 0,高位丢弃。</p>
<p>例如:<code>5 << 2</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0000 0000 0000 0000 0000 0000 0000 0101 5 的补码(同原码)</span><br><span class="line">0000 0000 0000 0000 0000 0000 0001 0100 左移 2 位后,低位补 0。换算成 10 进制为 20</span><br></pre></td></tr></table></figure>
<p>如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模。如:对 int 类型(最大位数 32)的数值移动 33 位,实际上只移动了 <code>33 % 32 = 1</code> 位。</p>
<blockquote>
<p>注:n 位二进制,最高位为符号位,因此表示的数值范围:$ -2^{(n-1)} $ —— $ 2^{(n-1)}-1 $,所以模为:$ 2^{(n-1)} $。</p>
</blockquote>
<p>在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以 2 的 1 次方,左移 n 位就相当于乘以 2 的 n 次方。如:<code>5 << 2</code> 相当于 $ 5 * 2^2 = 20 $。</p>
<p>如果移进高阶位(int 31 或 long 63 位),那么该值将变为负值。如:<code>1 << 31 = -2147483648</code></p>
<h2 id="三、右移运算(-gt-gt-)"><a href="#三、右移运算(-gt-gt-)" class="headerlink" title="三、右移运算(>>)"></a>三、右移运算(>>)</h2><p><code>value >> num</code></p>
<blockquote>
<p>num 指定要移位值;value 移动的位数。</p>
</blockquote>
<p>将左操作数(value)转为二进制数后向右边移动 num 位,符号位不变,高位补上符号位(若左操作数是正数,则高位补 0,若左操作数是负数,则高位补 1),低位丢弃。</p>
<p>右移时,被移走的最高位(最左边的位)由原来最高位的数字补充,这叫做符号位扩展(保留符号位)(sign extension),在进行右移操作时用来保持负数的符号。</p>
<p>例如:<code>7 >> 2</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0000 0000 0000 0000 0000 0000 0000 0111 7 的补码(同原码)</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0001 右移 2 位后,高位补 0。换算成 10 进制为 1</span><br></pre></td></tr></table></figure>
<p>例如:<code>-7 >> 2</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">1000 0000 0000 0000 0000 0000 0000 0111 -7 的原码</span><br><span class="line">1111 1111 1111 1111 1111 1111 1111 1000 -7 的反码</span><br><span class="line">1111 1111 1111 1111 1111 1111 1111 1001 -7 的补码</span><br><span class="line">1111 1111 1111 1111 1111 1111 1111 1110 右移 2 位后,高位补 1</span><br><span class="line">1000 0000 0000 0000 0000 0000 0000 0010 补码转原码。换算成 10 进制为 -2</span><br></pre></td></tr></table></figure>
<p>正数右移 n 位相当于除以 2 的 n 次方并且舍弃了余数。如:<code>7 >> 2</code> 相当于: $ 7 / 2^2 = 1 $。</p>
<p>负数右移 n 位相当于除以 2 的 n 次方,如果有余数 -1。如:<code>-7 >> 2</code> 相当于: $ 7 * 2^2 -1= -2 $。</p>
<h2 id="四、无符号右移(-gt-gt-gt-)"><a href="#四、无符号右移(-gt-gt-gt-)" class="headerlink" title="四、无符号右移(>>>)"></a>四、无符号右移(>>>)</h2><p><code>value >>> num</code></p>
<blockquote>
<p>num 指定要移位值;value 移动的位数。</p>
</blockquote>
<p>将左操作数(value)转为二进制数后向右边移动 num 位,0 补最高位(忽略了符号位扩展)。</p>
<p>无符号右移运算只是对 32 位和 64 位的值有意义。</p>
<p>例如:<code>-7 >>> 2</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">1000 0000 0000 0000 0000 0000 0000 0111 -7 的原码</span><br><span class="line">1111 1111 1111 1111 1111 1111 1111 1001 -7 的补码</span><br><span class="line">0011 1111 1111 1111 1111 1111 1111 1110 右移 2 位后,高位补 0。换算成 10 进制为 1073741822</span><br></pre></td></tr></table></figure>
<h2 id="五、位逻辑运算符"><a href="#五、位逻辑运算符" class="headerlink" title="五、位逻辑运算符"></a>五、位逻辑运算符</h2><h3 id="5-1-与运算(-amp-)"><a href="#5-1-与运算(-amp-)" class="headerlink" title="5.1 与运算(&)"></a>5.1 与运算(<strong>&</strong>)</h3><p>与运算:两个运算数比较位都是 1,则结果为 1,否则为 0。例如:<code>5 & 3 = 1</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0000 0000 0000 0000 0000 0000 0000 0101 5 转换为二进制</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0011 3 转换为二进制</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0001 换算成 10 进制为 1</span><br></pre></td></tr></table></figure>
<h3 id="5-2-或运算(-)"><a href="#5-2-或运算(-)" class="headerlink" title="5.2 或运算(|)"></a>5.2 或运算(<strong>|</strong>)</h3><p>或运算:两个运算数比较位有一个为 1,则结果为 1,否则为 0。例如:<code>5 | 3 = 7</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0000 0000 0000 0000 0000 0000 0000 0101 5 转换为二进制</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0011 3 转换为二进制</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0111 换算成 10 进制为 7</span><br></pre></td></tr></table></figure>
<h3 id="5-3-异或运算(-)"><a href="#5-3-异或运算(-)" class="headerlink" title="5.3 异或运算(^)"></a>5.3 异或运算(<strong>^</strong>)</h3><p>异或运算:两个运算数比较位不同时,其结果是 1,否则为 0。例如:<code>5 ^ 3 = 6</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0000 0000 0000 0000 0000 0000 0000 0101 5 转换为二进制</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0011 3 转换为二进制</span><br><span class="line">0000 0000 0000 0000 0000 0000 0000 0110 换算成 10 进制为 6</span><br></pre></td></tr></table></figure>
<h3 id="5-4-非运算(-)"><a href="#5-4-非运算(-)" class="headerlink" title="5.4 非运算(~)"></a>5.4 非运算(<strong>~</strong>)</h3><p>非运算:也叫做补,一元运算符,对其运算数的每一位取反。例如:<code>~5 = -6</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0000 0000 0000 0000 0000 0000 0000 0101 5 转换为二进制</span><br><span class="line">1111 1111 1111 1111 1111 1111 1111 1010 取非后的原码</span><br><span class="line">1000 0000 0000 0000 0000 0000 0000 0110 转换补码,换算成 10 进制为 -6</span><br></pre></td></tr></table></figure>
<h2 id="六、其它"><a href="#六、其它" class="headerlink" title="六、其它"></a>六、其它</h2><ul>
<li><p>Java 中整数类型(byte、short、int 和 long)在内存中是以有符号的二进制补码表示。所以位运算时,首先要转换为原码。</p>
</li>
<li><p>补码转原码:补码转原码和原码转补码的方法是一样的,取反 + 1(补码的补码是原码)。</p>
</li>
<li><p>当位运算数是 byte 和 short 类型时,将自动把这些类型扩大为 int 型(32 位)。</p>
</li>
<li><p>计算出 n 位二进制数所能表示的最大十进制数位移算法:<code>-1L ^ (-1L << n)</code> 或 <code>~(-1L << n)</code>。</p>
</li>
<li><p>byte 和 int 相互转换</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> i = <span class="number">234</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">byte</span> b = (<span class="keyword">byte</span>) i; <span class="comment">// 结果:b = -22</span></span><br><span class="line"><span class="comment">// 转换过程:</span></span><br><span class="line"><span class="comment">// 0000 0000 0000 0000 0000 0000 1110 1010 # int 234 的补码(与原码相等)</span></span><br><span class="line"><span class="comment">// 1110 1010 # byte 低位截取</span></span><br><span class="line"><span class="comment">// 1001 0110 # 求得补码,转为 10 进制为 -22</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> x = b ; <span class="comment">// 结果为:x = -22;8 位 byte 的转 32 的 int,值不变。</span></span><br><span class="line"><span class="keyword">int</span> y = b & <span class="number">0xff</span>; <span class="comment">// 结果为:x = 234; 可以通过将其和 0xff 进行位与(&)得到它的无符值</span></span><br><span class="line"><span class="comment">// 转换过程:</span></span><br><span class="line"><span class="comment">// 1001 0110 # byte -22 的原码</span></span><br><span class="line"><span class="comment">// 1000 0000 0000 0000 0000 0000 0001 0110 # int -22 的原码</span></span><br><span class="line"><span class="comment">// 1111 1111 1111 1111 1111 1111 1110 1010 # int -22 补码</span></span><br><span class="line"><span class="comment">// 0000 0000 0000 0000 0000 0000 1111 1111 # 0xff 的二进制数</span></span><br><span class="line"><span class="comment">// 0000 0000 0000 0000 0000 0000 1110 1010 # 和 0xff 进与操作的结果,转换为 10 进制为 234</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java开发技术</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title>Java 获取系统的配置信息</title>
<url>/2017/03/29/Java-%E8%8E%B7%E5%8F%96%E7%B3%BB%E7%BB%9F%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AF/</url>
<content><![CDATA[<p><code>System.getProperty()</code> 可以获取系统的配置信息,最近项目开发中要用到临时文件,所以想到了使用系统临时文件目录,最后得知可以通过 <code>System.getProperty("java.io.tmpdir")</code> 可以获取不同操作系统平台下的临时目录。比如:</p>
<p>在 <code>windows</code> 中的目录是:<code>C:\Users\登录用户~1\AppData\Local\Temp\</code></p>
<p>在 <code>linux</code> 下的目录是:<code>/tmp</code></p>
<p>在 <code>Mac</code> 下目录是 <code>/var/folders/c8/2c9rf0ss2w9c8tdtfcgvg9kh0000gn/T/</code> (我感觉是不同电脑应该不一样)</p>
<a id="more"></a>
<p>借此机会总结一下 <code>System.getProperty()</code> 可以获取那些系统信息:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">java.version <span class="comment">// Java运行时环境版本</span></span><br><span class="line">java.vendor <span class="comment">// Java运行时环境供应商</span></span><br><span class="line">java.vendor.url <span class="comment">// Java供应商的 URL</span></span><br><span class="line">java.home <span class="comment">// Java安装目录</span></span><br><span class="line">java.vm.specification.version <span class="comment">// Java虚拟机规范版本</span></span><br><span class="line">java.vm.specification.vendor <span class="comment">// Java虚拟机规范供应商</span></span><br><span class="line">java.vm.specification.name <span class="comment">// Java虚拟机规范名称</span></span><br><span class="line">java.vm.version <span class="comment">// Java虚拟机实现版本</span></span><br><span class="line">java.vm.vendor <span class="comment">// Java虚拟机实现供应商</span></span><br><span class="line">java.vm.name <span class="comment">// Java虚拟机实现名称</span></span><br><span class="line">java.specification.version <span class="comment">// Java运行时环境规范版本</span></span><br><span class="line">java.specification.vendor <span class="comment">// Java运行时环境规范供应商</span></span><br><span class="line">java.specification.name <span class="comment">// Java运行时环境规范名称</span></span><br><span class="line">java<span class="class">.<span class="keyword">class</span>.<span class="title">version</span> // <span class="title">Java</span>类格式版本号</span></span><br><span class="line"><span class="class"><span class="title">java</span>.<span class="title">class</span>.<span class="title">path</span> // <span class="title">Java</span>类路径</span></span><br><span class="line"><span class="class"><span class="title">java</span>.<span class="title">library</span>.<span class="title">path</span> // 加载库时搜索的路径列表</span></span><br><span class="line"><span class="class"><span class="title">java</span>.<span class="title">io</span>.<span class="title">tmpdir</span> // 默认的临时文件路径</span></span><br><span class="line"><span class="class"><span class="title">java</span>.<span class="title">compiler</span> // 要使用的 <span class="title">JIT</span> 编译器的名称</span></span><br><span class="line"><span class="class"><span class="title">java</span>.<span class="title">ext</span>.<span class="title">dirs</span> // 一个或多个扩展目录的路径</span></span><br><span class="line"><span class="class"><span class="title">os</span>.<span class="title">name</span> // 操作系统的名称</span></span><br><span class="line"><span class="class"><span class="title">os</span>.<span class="title">arch</span> // 操作系统的架构</span></span><br><span class="line"><span class="class"><span class="title">os</span>.<span class="title">version</span> // 操作系统的版本</span></span><br><span class="line"><span class="class"><span class="title">file</span>.<span class="title">separator</span> // 文件分隔符(在 <span class="title">UNIX</span> 系统中是“/”)</span></span><br><span class="line">path.separator // 路径分隔符(在 UNIX 系统中是“:”)</span><br><span class="line">line.separator <span class="comment">// 行分隔符(在 UNIX 系统中是“/n”)</span></span><br><span class="line">user.name <span class="comment">// 用户的账户名称</span></span><br><span class="line">user.home <span class="comment">// 用户的主目录</span></span><br><span class="line">user.dir <span class="comment">// 用户的当前工作目录</span></span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>Java开发技术</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title>Kubernetes 学习笔记之 MiniKube 安装</title>
<url>/2018/01/12/kubernetes-minikube-installation/</url>
<content><![CDATA[<p>Kubernetes 集群的搭建是有一定难度的,尤其是对于初学者来说,好多概念和原理不懂,即使有现成的教程也会出现很多不可预知的问题,很容易打击学习的积极性,就此弃坑。好在 Kubernetes 社区提供了可以在本地开发和体验的极简集群安装 MiniKube,对于入门学习来说很方便。</p>
<img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/687148dbly1fo7n00rkl6j20b40b4goj.jpg" class="" width="300">
<!-- ![minikube][1] -->
<p>MiniKube 官方安装介绍已经非常详细了,可以参考 <a href="https://github.com/kubernetes/minikube#installation" target="_blank" rel="noopener">installation</a>。但是在国内由于网络访问原因(懂的),即使有梯子也很折腾,所以记录一下阿里修改后的 MiniKube 安装。使用阿里修改后的 MiniKube 就可以从阿里云的镜像地址来获取所需 Docker 镜像和配置,其它的并没有差异,下文着重介绍。</p>
<a id="more"></a>
<h2 id="一、kubectl-安装"><a href="#一、kubectl-安装" class="headerlink" title="一、kubectl 安装"></a>一、kubectl 安装</h2><p>MiniKube 的安装需要先安装 kubectl 及相关驱动,这没什么好说的,参考<a href="https://github.com/kubernetes/minikube#requirements" target="_blank" rel="noopener">官方介绍</a>。</p>
<p>另 kubectl 也可通过源代码编译安装,编译源码需要有 Git、Golang 环境的支撑。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ git <span class="built_in">clone</span> https://github.com/kubernetes/kubernetes.git</span><br><span class="line">➜ <span class="built_in">cd</span> kubernetes</span><br><span class="line">➜ make</span><br><span class="line">➜ sudo cp _output/bin/kubectl /usr/<span class="built_in">local</span>/bin/</span><br><span class="line">➜ sudo chmod +x /usr/<span class="built_in">local</span>/bin/kubectl</span><br></pre></td></tr></table></figure>
<h2 id="二、MiniKube-安装"><a href="#二、MiniKube-安装" class="headerlink" title="二、MiniKube 安装"></a>二、MiniKube 安装</h2><p>MiniKube 是使用 Go 语言开发的,所以安装其实很方便,一是通过下载基于不同平台早已编译好的二级制文件安装,二是可以编译源文件安装。</p>
<h3 id="2-1-二级制文件安装"><a href="#2-1-二级制文件安装" class="headerlink" title="2.1 二级制文件安装"></a>2.1 二级制文件安装</h3><ul>
<li>Mac OSX 平台</li>
</ul>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">➜ curl -Lo minikube http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v0.24.1/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/</span><br></pre></td></tr></table></figure>
<ul>
<li>Linux 平台</li>
</ul>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">➜ curl -Lo minikube http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v0.24.1/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/</span><br></pre></td></tr></table></figure>
<ul>
<li>Windows 平台</li>
</ul>
<p>下载 <a href="http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v0.24.1/minikube-windows-amd64.exe" target="_blank" rel="noopener">minikube-windows-amd64.exe</a> 文件,并重命名为 <code>minikube.exe</code>,然后加入到环境变量路径下即可。</p>
<h3 id="2-2-源码编译安装"><a href="#2-2-源码编译安装" class="headerlink" title="2.2 源码编译安装"></a>2.2 源码编译安装</h3><p>编译源码需要有 Git、Golang 环境的支撑。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ git <span class="built_in">clone</span> https://github.com/AliyunContainerService/minikube</span><br><span class="line">➜ <span class="built_in">cd</span> minikube</span><br><span class="line">➜ git checkout aliyun-v0.24.1</span><br><span class="line">➜ make</span><br><span class="line">➜ sudo cp out/minikube /usr/<span class="built_in">local</span>/bin/</span><br><span class="line">➜ sudo chmod +x /usr/<span class="built_in">local</span>/bin/minikube</span><br></pre></td></tr></table></figure>
<blockquote>
<p>示例版本是 <code>v0.24.1</code>,可更改为<a href="https://github.com/AliyunContainerService/minikube/branches/all" target="_blank" rel="noopener">其它版本</a>。</p>
</blockquote>
<h2 id="三、简单使用"><a href="#三、简单使用" class="headerlink" title="三、简单使用"></a>三、简单使用</h2><h3 id="3-1-启动"><a href="#3-1-启动" class="headerlink" title="3.1 启动"></a>3.1 启动</h3><p>默认启动使用的是 VirtualBox 驱动,使用 <code>--vm-driver</code> 参数可以指定其它驱动。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 设置 docker 加速镜像</span></span><br><span class="line">➜ minikube start --registry-mirror=https://registry.docker-cn.com</span><br><span class="line">Starting <span class="built_in">local</span> Kubernetes v1.8.0 cluster...</span><br><span class="line">Starting VM...</span><br><span class="line">Getting VM IP address...</span><br><span class="line">Moving files into cluster...</span><br><span class="line">Setting up certs...</span><br><span class="line">Connecting to cluster...</span><br><span class="line">Setting up kubeconfig...</span><br><span class="line">Starting cluster components...</span><br><span class="line">Kubectl is now configured to use the cluster.</span><br><span class="line">Loading cached images from config file.</span><br></pre></td></tr></table></figure>
<h3 id="3-2-检测状态"><a href="#3-2-检测状态" class="headerlink" title="3.2 检测状态"></a>3.2 检测状态</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ minikube status</span><br><span class="line">minikube: Running</span><br><span class="line">cluster: Running</span><br><span class="line">kubectl: Correctly Configured: pointing to minikube-vm at 192.168.99.100</span><br></pre></td></tr></table></figure>
<h3 id="3-3-启动-kubernetes-dashboard"><a href="#3-3-启动-kubernetes-dashboard" class="headerlink" title="3.3 启动 kubernetes dashboard"></a>3.3 启动 kubernetes dashboard</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ minikube dashboard</span><br><span class="line">Opening kubernetes dashboard <span class="keyword">in</span> default browser...</span><br></pre></td></tr></table></figure>
<p>输入以上命令,浏览器中应该就会显示以下界面:</p>
<p><img src="https://cdn.jsdelivr.net/gh/0vo/oss/images/minikube-dashboard.jpg" alt="minikube-dashboard"></p>
<h3 id="3-4-测试"><a href="#3-4-测试" class="headerlink" title="3.4 测试"></a>3.4 测试</h3><ul>
<li>运行一个 nginx 的 pod</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ kubectl run hello --image=nginx --port=80</span><br><span class="line">deployment <span class="string">"hello"</span> created</span><br></pre></td></tr></table></figure>
<ul>
<li>导出运行的 nginx 服务</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ kubectl expose deployment hello --<span class="built_in">type</span>=NodePort</span><br><span class="line">service <span class="string">"hello"</span> exposed</span><br></pre></td></tr></table></figure>
<ul>
<li>查看一下运行情况,需要等一会才会显示状态为 <code>Running</code></li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ kubectl get pod</span><br><span class="line">NAME READY STATUS RESTARTS AGE</span><br><span class="line">hello-6dbbbb95d-4cqwz 1/1 Running 0 2m</span><br></pre></td></tr></table></figure>
<ul>
<li>curl 访问测试</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">➜ curl $(minikube service hello --url)</span><br><span class="line"><!DOCTYPE html></span><br><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"><title>Welcome to nginx!</title></span><br><span class="line"><style></span><br><span class="line"> body {</span><br><span class="line"> width: 35em;</span><br><span class="line"> margin: 0 auto;</span><br><span class="line"> font-family: Tahoma, Verdana, Arial, sans-serif;</span><br><span class="line"> }</span><br><span class="line"></style></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"><h1>Welcome to nginx!</h1></span><br><span class="line"><p>If you see this page, the nginx web server is successfully installed and</span><br><span class="line">working. Further configuration is required.</p></span><br><span class="line"></span><br><span class="line"><p>For online documentation and support please refer to</span><br><span class="line"><a href=<span class="string">"http://nginx.org/"</span>>nginx.org</a>.<br/></span><br><span class="line">Commercial support is available at</span><br><span class="line"><a href=<span class="string">"http://nginx.com/"</span>>nginx.com</a>.</p></span><br><span class="line"></span><br><span class="line"><p><em>Thank you <span class="keyword">for</span> using nginx.</em></p></span><br><span class="line"></body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure>
<h3 id="3-5-其它"><a href="#3-5-其它" class="headerlink" title="3.5 其它"></a>3.5 其它</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 查看集群的所有资源</span></span><br><span class="line">➜ kubectl get all</span><br><span class="line">➜ kubectl get all -o wide</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进入节点服务器</span></span><br><span class="line">➜ minikube ssh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行节点服务器命令,例如查看节点 docker info</span></span><br><span class="line">➜ minikube ssh -- docker info</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除集群</span></span><br><span class="line">➜ minikube delete</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭集群</span></span><br><span class="line">➜ minikube stop</span><br></pre></td></tr></table></figure>
<h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><ul>
<li><a href="https://yq.aliyun.com/articles/221687" target="_blank" rel="noopener">Minikube - Kubernetes本地实验环境</a></li>
</ul>
<hr>
]]></content>
<categories>
<category>kubernetes</category>
</categories>
<tags>
<tag>minikube</tag>
<tag>kubernetes</tag>
<tag>k8s</tag>
</tags>
</entry>
<entry>
<title>Linux 中 fg、bg、jobs 等指令</title>
<url>/2017/01/18/Linux-%E4%B8%AD-fg%E3%80%81bg%E3%80%81jobs%E3%80%81-%E6%8C%87%E4%BB%A4/</url>
<content><![CDATA[<blockquote>
<p>记录总结一下 <code>Linux</code> 中 <code>fg</code>、<code>bg</code>、<code>jobs</code>、<code>&</code>、<code>ctrl + z</code> 等相关指令对任务进程的操作。</p>
</blockquote>
<h1 id="一、基本用法"><a href="#一、基本用法" class="headerlink" title="一、基本用法"></a>一、基本用法</h1><h2 id="1-1-amp-和-jobs-指令"><a href="#1-1-amp-和-jobs-指令" class="headerlink" title="1.1 & 和 jobs 指令"></a>1.1 <code>&</code> 和 <code>jobs</code> 指令</h2><p><code>&</code> 用在一个命令的最后,可以把这个命令转换为后台运行的任务进程。</p>
<p><code>jobs</code> 查看当前终端有多少在后台运行的进程。</p>
<ul>
<li><p><code>jobs</code> 命令执行的结果,<code>+</code> 表示是一个当前的作业,<code>-</code> 减号表示是一个当前作业之后的一个作业。</p>
</li>
<li><p><code>jobs -l</code> 选项可显示所有任务的进程号 <code>pid</code></p>
</li>
<li><p><code>jobs</code> 的状态可以是 <code>running</code>,<code>stopped</code>,<code>terminated</code>。但是如果任务进程被终止了(<code>kill</code>),当前的终端环境中也就删除了任务的进程标识;也就是说 <strong>jobs 命令显示的是当前 shell 环境中后台正在运行或者被挂起的任务进程信息</strong></p>
</li>
</ul>
<a id="more"></a>
<h2 id="1-3-fg-和-bg-指令"><a href="#1-3-fg-和-bg-指令" class="headerlink" title="1.3 fg 和 bg 指令"></a>1.3 <code>fg</code> 和 <code>bg</code> 指令</h2><p><code>fg</code> 将后台任务进程调至前台继续运行,如果后台中有多个任务进程,可以用 <code>fg %num</code> 将选中的任务进程调至前台。</p>
<p><code>bg</code> 将挂起的任务进程重新启动运行,如果有多个暂停的任务进程,可以用 <code>bg %num</code> 将选中的任务进程启动运行。</p>
<blockquote>
<p><code>%num</code> 是通过 <code>jobs</code> 命令查到的后台正在执行的任务的序号(不是 <code>pid</code>)</p>
</blockquote>
<h1 id="二、进程的挂起"><a href="#二、进程的挂起" class="headerlink" title="二、进程的挂起"></a>二、进程的挂起</h1><h2 id="2-1-后台进程的挂起"><a href="#2-1-后台进程的挂起" class="headerlink" title="2.1 后台进程的挂起"></a>2.1 后台进程的挂起</h2><ul>
<li><p>在 <code>solaris</code> 中通过 <code>stop</code> 命令执行,通过 <code>jobs</code> 命令查看任务号(假设为 <code>num</code>),然后执行:<code>stop %num</code></p>
</li>
<li><p>在 <code>redhat</code> 中,不存在 <code>stop</code> 命令,可通过执行命令 <code>kill -stop PID</code>,将进程挂起</p>
</li>
</ul>
<h2 id="2-2-前台进程的挂起"><a href="#2-2-前台进程的挂起" class="headerlink" title="2.2 前台进程的挂起"></a>2.2 前台进程的挂起</h2><p><code>ctrl + z</code>:可以将一个正在前台执行的任务放到后台运行,并且挂起</p>
<h1 id="三、挂起进程重新运行"><a href="#三、挂起进程重新运行" class="headerlink" title="三、挂起进程重新运行"></a>三、挂起进程重新运行</h1><ul>
<li><p>通过 <code>bg %num</code> 即可将挂起的任务进程的状态由 <code>stopped</code> 改为 <code>running</code>,仍在后台运行</p>
</li>
<li><p>通过 <code>fg %num</code> 即可将挂起的任务进程转为前台执行</p>
</li>