-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
955 lines (680 loc) · 98 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>orochi</title>
<meta name="author" content="zhaowenlei">
<meta name="description" content="技术整理归档">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:site_name" content="orochi"/>
<meta property="og:image" content="undefined"/>
<link href="/favicon.ico" rel="icon" type="image/x-ico">
<link rel="alternate" href="http://zipperary.com/atom.xml" title="orochi" type="application/atom+xml">
<link rel="stylesheet" href="/css/style.css" media="screen" type="text/css">
<!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<script src="//libs.baidu.com/jquery/1.8.0/jquery.min.js"></script>
</head>
<body>
<header id="header" class="inner">
<div class="alignleft">
<h1><a href="/">orochi</a></h1>
<h2><a href="/">I'm a 博客</a></h2>
</div>
<nav id="main-nav" class="alignright">
<ul>
<li><a href="/">首页</a></li>
<li><a href="/archives">归档</a></li>
<li><a href="/about">关于</a></li>
<li><a href="/tools">工具</a></li>
<li> <a href="/atom.xml">RSS</a> </li>
<li> <a title="把这个链接拖到你的Chrome收藏夹工具栏中" href='javascript:(function() {
function c() {
var e = document.createElement("link");
e.setAttribute("type", "text/css");
e.setAttribute("rel", "stylesheet");
e.setAttribute("href", f);
e.setAttribute("class", l);
document.body.appendChild(e)
}
function h() {
var e = document.getElementsByClassName(l);
for (var t = 0; t < e.length; t++) {
document.body.removeChild(e[t])
}
}
function p() {
var e = document.createElement("div");
e.setAttribute("class", a);
document.body.appendChild(e);
setTimeout(function() {
document.body.removeChild(e)
}, 100)
}
function d(e) {
return {
height : e.offsetHeight,
width : e.offsetWidth
}
}
function v(i) {
var s = d(i);
return s.height > e && s.height < n && s.width > t && s.width < r
}
function m(e) {
var t = e;
var n = 0;
while (!!t) {
n += t.offsetTop;
t = t.offsetParent
}
return n
}
function g() {
var e = document.documentElement;
if (!!window.innerWidth) {
return window.innerHeight
} else if (e && !isNaN(e.clientHeight)) {
return e.clientHeight
}
return 0
}
function y() {
if (window.pageYOffset) {
return window.pageYOffset
}
return Math.max(document.documentElement.scrollTop, document.body.scrollTop)
}
function E(e) {
var t = m(e);
return t >= w && t <= b + w
}
function S() {
var e = document.createElement("audio");
e.setAttribute("class", l);
e.src = i;
e.loop = false;
e.addEventListener("canplay", function() {
setTimeout(function() {
x(k)
}, 500);
setTimeout(function() {
N();
p();
for (var e = 0; e < O.length; e++) {
T(O[e])
}
}, 15500)
}, true);
e.addEventListener("ended", function() {
N();
h()
}, true);
e.innerHTML = " <p>If you are reading this, it is because your browser does not support the audio element. We recommend that you get a new browser.</p> <p>";
document.body.appendChild(e);
e.play()
}
function x(e) {
e.className += " " + s + " " + o
}
function T(e) {
e.className += " " + s + " " + u[Math.floor(Math.random() * u.length)]
}
function N() {
var e = document.getElementsByClassName(s);
var t = new RegExp("\\b" + s + "\\b");
for (var n = 0; n < e.length; ) {
e[n].className = e[n].className.replace(t, "")
}
}
var e = 30;
var t = 30;
var n = 350;
var r = 350;
var i = "//s3.amazonaws.com/moovweb-marketing/playground/harlem-shake.mp3";
var s = "mw-harlem_shake_me";
var o = "im_first";
var u = ["im_drunk", "im_baked", "im_trippin", "im_blown"];
var a = "mw-strobe_light";
var f = "//s3.amazonaws.com/moovweb-marketing/playground/harlem-shake-style.css";
var l = "mw_added_css";
var b = g();
var w = y();
var C = document.getElementsByTagName("*");
var k = null;
for (var L = 0; L < C.length; L++) {
var A = C[L];
if (v(A)) {
if (E(A)) {
k = A;
break
}
}
}
if (A === null) {
console.warn("Could not find a node of the right size. Please try a different page.");
return
}
c();
S();
var O = [];
for (var L = 0; L < C.length; L++) {
var A = C[L];
if (v(A)) {
O.push(A)
}
}
})() '>High一下</a> </li>
</ul>
<div class="clearfix"></div>
</nav>
<div class="clearfix"></div></header>
<div id="content" class="inner">
<div id="main-col" class="alignleft"><div id="wrapper">
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2018-07-12T09:09:42.000Z"><a href="/2018/07/12/微服务使用cat监控-md/">2018-07-12</a></time>
<h1 class="title"><a href="/2018/07/12/微服务使用cat监控-md/">微服务使用cat监控.md</a></h1>
</header>
<div class="entry">
<h2 id="1-项目加入依赖的jar包"><a href="#1-项目加入依赖的jar包" class="headerlink" title="1. 项目加入依赖的jar包"></a>1. 项目加入依赖的jar包</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.aixuexi<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>thor-util<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.4.9.1-RELEASE<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure>
<h2 id="2-加入cat的AOP"><a href="#2-加入cat的AOP" class="headerlink" title="2. 加入cat的AOP:"></a>2. 加入cat的AOP:</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"catAop"</span> <span class="attr">class</span>=<span class="string">"com.aixuexi.thor.aop.CatAop"</span>></span><span class="tag"></<span class="name">bean</span>></span></div><div class="line"><span class="tag"><<span class="name">aop:config</span>></span></div><div class="line"> <span class="tag"><<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">"service"</span> <span class="attr">expression</span>=<span class="string">"execution(* com.aixuexi.underworld.service.*.*(..))"</span>/></span></div><div class="line"> <span class="tag"><<span class="name">aop:aspect</span> <span class="attr">ref</span>=<span class="string">"catAop"</span>></span></div><div class="line"> <span class="tag"><<span class="name">aop:around</span> <span class="attr">method</span>=<span class="string">"around"</span> <span class="attr">pointcut-ref</span>=<span class="string">"service"</span>/></span></div><div class="line"> <span class="tag"></<span class="name">aop:aspect</span>></span></div><div class="line"><span class="tag"></<span class="name">aop:config</span>></span></div></pre></td></tr></table></figure>
<h2 id="3-在resources资源文件META-INF下,注意是src-main-resources-META-INF-文件夹,-而不是webapps下的那个META-INF-添加app-properties,如:app-name-inception"><a href="#3-在resources资源文件META-INF下,注意是src-main-resources-META-INF-文件夹,-而不是webapps下的那个META-INF-添加app-properties,如:app-name-inception" class="headerlink" title="3. 在resources资源文件META-INF下,注意是src/main/resources/META-INF/文件夹, 而不是webapps下的那个META-INF,添加app.properties,如:app.name=inception"></a>3. 在resources资源文件META-INF下,注意是src/main/resources/META-INF/文件夹, 而不是webapps下的那个META-INF,添加app.properties,如:app.name=inception</h2><h2 id="4-在项目部署的服务器上创建-data-appdatas-cat-client-xml-此文件有OP控制-这里的Domain名字用来做开关,如果一台机器上部署了多个应用,可以指定把一个应用的监控关闭。注意这里的目录必须要有读写权限"><a href="#4-在项目部署的服务器上创建-data-appdatas-cat-client-xml-此文件有OP控制-这里的Domain名字用来做开关,如果一台机器上部署了多个应用,可以指定把一个应用的监控关闭。注意这里的目录必须要有读写权限" class="headerlink" title="4. 在项目部署的服务器上创建/data/appdatas/cat/client.xml,此文件有OP控制,这里的Domain名字用来做开关,如果一台机器上部署了多个应用,可以指定把一个应用的监控关闭。注意这里的目录必须要有读写权限"></a>4. 在项目部署的服务器上创建/data/appdatas/cat/client.xml,此文件有OP控制,这里的Domain名字用来做开关,如果一台机器上部署了多个应用,可以指定把一个应用的监控关闭。注意这里的目录必须要有读写权限</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">config</span> <span class="attr">mode</span>=<span class="string">"client"</span>></span></div><div class="line"> <span class="tag"><<span class="name">servers</span>></span></div><div class="line"> <span class="tag"><<span class="name">server</span> <span class="attr">ip</span>=<span class="string">"10.26.83.53"</span> <span class="attr">port</span>=<span class="string">"2280"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">servers</span>></span></div><div class="line"> <span class="tag"><<span class="name">domain</span> <span class="attr">id</span>=<span class="string">"inception"</span> <span class="attr">enabled</span>=<span class="string">"true"</span>/></span></div><div class="line"><span class="tag"></<span class="name">config</span>></span></div></pre></td></tr></table></figure>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-04-02T04:24:35.000Z"><a href="/2017/04/02/spring mvc項目问题/">2017-04-02</a></time>
<h1 class="title"><a href="/2017/04/02/spring mvc項目问题/">spring maven 依赖引发的问题</a></h1>
</header>
<div class="entry">
<p>今天做搞spring项目,因为要写jsp,所以maven引入依赖,结果报了一大堆错误,网上搜了好多资料都没搞定,后来干脆就不弄了,睡了一觉,然后又来弄了一下,结果无意中又看了一遍文章。奇迹般的搞定了,希望和一样遇到该错误的人不要走弯路。<br>错误如下:<br><img src="/imgs/301901220093456.png" alt=""><br>遗憾的是出现 <font color="red" size="3">color=Unable to compile class for JSP</font> 的错误.<br> 网上寻找资料后知晓, 该问题由maven引入的servlet-api和tomcat自带的servlet-api版本发生了冲突.<br> 解决方案如下:<br> maven的pom文件中, 把<font color="red" size="3">color=servlet-api</font>依赖项的<font color="red" size="3">color=scope</font>改为<font color="red" size="3">color=provided</font>(即参与compile和test, 但不参与package).</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jsp-api<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.0<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>servlet-api<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.5<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure>
<p>改为<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jsp-api<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.0<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"><<span class="name">scope</span>></span>provided<span class="tag"></<span class="name">scope</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>servlet-api<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.5<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"><<span class="name">scope</span>></span>provided<span class="tag"></<span class="name">scope</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure></p>
<p>参考文章:<a href="http://www.cnblogs.com/mumuxinfei/p/4611098.html" target="_blank" rel="external">http://www.cnblogs.com/mumuxinfei/p/4611098.html</a></p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/zookeeper-05/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/zookeeper-05/">Zookeeper client</a></h1>
</header>
<div class="entry">
<p>当我写完Zookeeper leader选举后,准备看看Zookeeper的存储和处理客户端请求的时候发现,如果能看看Zookeeper的API是不是在理解后面的过程更好些呢。</p>
<p>Zookeeper的client是通过Zookeeper类提供的。前面曾经说过,Zookeeper给使用者提供的是一个类似操作系统的文件结构,只不过这个结构是分布式的。可以理解为一个分布式的文件系统。我们可以通过Zookeeper来访问这个分布式的文件系统。</p>
<p>Zookeeper的client api给我们提供以下这些API:</p>
<ol>
<li>create</li>
</ol>
<p>在给定的path上创建节点,这个path就像文件系统的路径,比如/myapp/data/1,在创建节点的时候还可以指定节点的类型:是永久节点,永久顺序节点,临时节点,临时顺序节点。这个节点类型是非常强大的。永久节点一经创建就永久保留了,就像我们在文件系统上创建一个普通文件,这个文件的生命周期跟创建它的应用没有任何关系。而临时节点呢,当创建这个临时节点的应用与zookeeper之间的会话过期之后就会被zookeeper自动删除了。这个特性是实现很多功能的关键。比如我们做集群感知,我们的应用启动的时候将自己的ip地址作为临时节点创建在某个节点下面。当我们的应用因为某些原因,比如网络断掉或者宕机,它与zookeeper的会话就会过期了,过期后这个临时节点就删除了。这样我们就可以通过这个特性来感知到我们的服务的集群有哪些机器是活者的。那么顺序节点又是什么呢。一般,如果我们在指定的path上创建节点,如果这个节点已经被创建了,则会抛出一个NodeExistsException的异常。如果我们在指定的路径上创建顺序节点,则Zookeeper会自动的在我们给定的path上加上一个顺序编号。这个特性就是实现分布式锁的关键。假设我们有几个节点共享一个资源,我们这几个节点都想争用这个资源,那我们就都向某个路径创建临时顺序节点。然后顺序最小的那个就获得锁,然后如果某个节点释放了锁,那顺序第二小的那个就获得锁,以此类推,这样一个分布式的公平锁就实现了。</p>
<p>除此之外,每个节点上还可以保存一些数据。</p>
<ol>
<li><p>delete 删除给定节点。删除节点的时候还可以给定一个version,只有路径和version都匹配的时候节点才会被删除。有了这个version在分布式环境种我们就可以用乐观锁的方式来确保一致性。比如我们先读取一下节点,获得了节点的version,然后删除,如果删除成功了则说明在这之间没有人操作过这个节点,否则就是并发冲突了。</p>
</li>
<li><p>exists 这个节点会返回一个Stat对象,如果给定的path不存在的话则返回null。这个方法有一个关键参数,可以提供一个Watcher对象。Wathcer是Zookeeper强大功能的源泉。Watcher就是一个事件处理器,一个回调。比如这个exists方法,调用后,如果别人对这个path上的节点进行操作,比如创建,删除或设置数据,这个Wather都会接收到对应的通知。</p>
</li>
<li><p>setData/getData 设置或获取节点的数据,getData也可以设置Watcher</p>
</li>
<li><p>getChildren 获取子节点,可以设置Watcher</p>
</li>
<li><p>sync zookeeper是一个集群,创建节点的时候只要半数以上的节点确认就认为是创建成功了,但是如果读取的时候正好读取到一个落后的节点上,那就有可能读取到旧的数据,这个时候可以执行一个sync操作,这个操作可以确保读取到最新的数据。</p>
</li>
</ol>
<p>zookeeper的client api基本上介绍完了。zookeeper强大的功能都是通过这些API来实现的,zookeeper通过一个简单的文件系统数据模型对外提供服务。通过临时节点,Watcher等手段我们可以实现一些在分布式环境种很难做到的事情。</p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/jsp 问题/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/jsp 问题/">jsp 依赖问题</a></h1>
</header>
<div class="entry">
<p>今天搞了一下jsp,做了个例子,其中有一句out.write(),结果总是报红,百度了好长时间还是没有搞定,国内的网站真是坑啊,说什么的都有,总之没有说到点儿上的。<br>最后google了一下,搞定。<br>You’ll have this problem with out, pageContext and jspContext because they use classes provided with the JSP api (not the servlet API).</p>
<p>To use them (if you’re working with a maven project) add this dependency :<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jsp-api<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.0<span class="tag"></<span class="name">version</span>></span></div><div class="line"><span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure></p>
<p>If you have the problem with every implicit object (session, request, etc.) you should add the servlet api dependency too :<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>servlet-api<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.5<span class="tag"></<span class="name">version</span>></span></div><div class="line"><span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure></p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/zookeeper/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/zookeeper/">zookeeper是什么</a></h1>
</header>
<div class="entry">
<p>Google的三篇论文影响了很多很多人,也影响了很多很多系统。这三篇论文一直是分布式领域传阅的经典。根据MapReduce,于是我们有了Hadoop;根据GFS,于是我们有了HDFS;根据BigTable,于是我们有了HBase。而在这三篇论文里都提及Google的一个lock service—Chubby,哦,于是我们有了Zookeeper。</p>
<p>随着大数据的火热,Hxx们已经变得耳熟能详,现在作为一个开发人员如果都不知道这几个名词出门都好像不好意思跟人打招呼。但实际上对我们这些非大数据开发人员而言,Zookeeper是比Hxx们可能接触到更多的一个基础服务。但是,无奈的是它一直默默的位于二线,从来没有Hxx们那么耀眼。那么到底什么是Zookeeper呢?Zookeeper可以用来干什么?我们将如何使用Zookeeper?Zookeeper又是怎么实现的?</p>
<p>伴随着Zookeeper有两篇论文:一篇是Zab,就是介绍Zookeeper背后使用的一致性协议的(Zookeeper atomic broadcast protocol),还有一篇就是介绍Zookeeper本身的。在这两篇论文里都提到Zookeeper是一个分布式协调服务(a service for coordinating processes of distributed applications)。那分布式协调服务又是个什么东西呢?首先我们来看“协调”是什么意思。</p>
<p>说到协调,我首先想到的是北京很多十字路口的交通协管,他们手握着小红旗,指挥车辆和行人是不是可以通行。如果我们把车辆和行人比喻成运行在计算机中的单元(线程),那么这个协管是干什么的?很多人都会想到,这不就是锁么?对,在一个并发的环境里,我们为了避免多个运行单元对共享数据同时进行修改,造成数据损坏的情况出现,我们就必须依赖像锁这样的协调机制,让有的线程可以先操作这些资源,然后其他线程等待。对于进程内的锁来讲,我们使用的各种语言平台都已经给我们准备很多种选择。就拿Java来说,有最普通不过的同步方法或同步块:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">sharedMethod</span><span class="params">()</span></span>{</div><div class="line"> <span class="comment">//对共享数据进行操作</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>使用了这种方式后,多个线程对sharedMethod进行操作的时候,就会协调好步骤,不会对sharedMethod里的资源进行破坏,产生不一致的情况。这个最简单的协调方法,但有的时候我们可能需要更复杂的协调。比如我们常常为了提高性能,我们使用读写锁。因为大部分时候我们对资源是读取多而修改少,而如果不管三七二十一全部使用排他的写锁,那么性能有可能就会受到影响。还是用java举例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SharedSource</span></span>{</div><div class="line"> <span class="keyword">private</span> ReadWriteLock rwlock = <span class="keyword">new</span> ReentrantReadWriteLock();</div><div class="line"> <span class="keyword">private</span> Lock rlock = rwlock.readLock();</div><div class="line"> <span class="keyword">private</span> Lock wlock = rwlock.writeLock();</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">read</span><span class="params">()</span></span>{</div><div class="line"> rlock.lock();</div><div class="line"> <span class="keyword">try</span>{</div><div class="line"> <span class="comment">//读取资源</span></div><div class="line"> }<span class="keyword">finally</span>{</div><div class="line"> rlock.unlock();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">()</span></span>{</div><div class="line"> wlock.lock();</div><div class="line"> <span class="keyword">try</span>{</div><div class="line"> <span class="comment">//写资源</span></div><div class="line"> }<span class="keyword">finally</span>{</div><div class="line"> wlock.unlock();</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>我们在进程内还有各种各样的协调机制(一般我们称之为同步机制)。现在我们大概了解了什么是协调了,但是上面介绍的协调都是在进程内进行协调。在进程内进行协调我们可以使用语言,平台,操作系统等为我们提供的机制。那么如果我们在一个分布式环境中呢?也就是我们的程序运行在不同的机器上,这些机器可能位于同一个机架,同一个机房又或不同的数据中心。在这样的环境中,我们要实现协调该怎么办?那么这就是分布式协调服务要干的事情。</p>
<p>ok,可能有人会讲,这个好像也不难。无非是将原来在同一个进程内的一些原语通过网络实现在分布式环境中。是的,表面上是可以这么说。但分布式系统中,说往往比做容易得多。在分布式系统中,所有同一个进程内的任何假设都不存在:因为网络是不可靠的。</p>
<p>比如,在同一个进程内,你对一个方法的调用如果成功,那就是成功(当然,如果你的代码有bug那就另说了),如果调用失败,比如抛出异常那就是调用失败。在同一个进程内,如果这个方法先调用先执行,那就是先执行。但是在分布式环境中呢? 由于网络的不可靠,你对一个服务的调用失败了并不表示一定是失败的,可能是执行成功了,但是响应返回的时候失败了。还有,A和B都去调用C服务,在时间上A还先调用一些,B后调用,那么最后的结果是不是一定A的请求就先于B到达呢? 这些本来在同一个进程内的种种假设我们都要重新思考,我们还要思考这些问题给我们的设计和编码带来了哪些影响。还有,在分布式环境中为了提升可靠性,我们往往会部署多套服务,但是如何在多套服务中达到一致性,这在同一个进程内很容易解决的问题,但在分布式环境中确实一个大难题。</p>
<p>所以分布式协调远远比同一个进程里的协调复杂得多,所以类似Zookeeper这类基础服务就应运而生。这些系统都在各个系统久经考验,它的可靠性,可用性都是经过理论和实践的验证的。所以我们在构建一些分布式系统的时候,就可以以这类系统为起点来构建我们的系统,这将节省不少成本,而且bug也将更少。</p>
<p>本篇文章试图从外围介绍一下Zookeeper是一个什么样子的服务和我们为什么需要这样一种服务。在后面的文章中会介绍Zookeeper到底能干些什么。</p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/zookeeper-04/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/zookeeper-04/">Zookeeper leader选举</a></h1>
</header>
<div class="entry">
<p>在上一篇文章中我们大致浏览了zookeeper的启动过程,并且提到在Zookeeper的启动过程中leader选举是非常重要而且最复杂的一个环节。那么什么是leader选举呢?zookeeper为什么需要leader选举呢?zookeeper的leader选举的过程又是什么样子的?本文的目的就是解决这三个问题。</p>
<p>首先我们来看看什么是leader选举。其实这个很好理解,leader选举就像总统选举一样,每人一票,获得多数票的人就当选为总统了。在zookeeper集群中也是一样,每个节点都会投票,如果某个节点获得超过半数以上的节点的投票,则该节点就是leader节点了。</p>
<p>国家选举总统是为了选一个最高统帅,治理国家。那么zookeeper集群选举的目的又是什么呢?其实这个要清楚明白的解释还是挺复杂的。我们可以简单点想这个问题:我们有一个zookeeper集群,有好几个节点。每个节点都可以接收请求,处理请求。那么,如果这个时候分别有两个客户端向两个节点发起请求,请求的内容是修改同一个数据。比如客户端c1,请求节点n1,请求是set a = 1; 而客户端c2,请求节点n2,请求内容是set a = 2;</p>
<p>那么最后a是等于1还是等于2呢? 这在一个分布式环境里是很难确定的。解决这个问题有很多办法,而zookeeper的办法是,我们选一个总统出来,所有的这类决策都提交给总统一个人决策,那之前的问题不就没有了么。</p>
<p>那我们现在的问题就是怎么来选择这个总统呢? 在现实中,选择总统是需要宣讲拉选票的,那么在zookeeper的世界里这又如何处理呢?我们还是show code吧。</p>
<p>在QuorumPeer的startLeaderElection方法里包含leader选举的逻辑。Zookeeper默认提供了4种选举方式,默认是第4种: FastLeaderElection。</p>
<p>我们先假设我们这是一个崭新的集群,崭新的集群的选举和之前运行过一段时间的选举是有稍许不同的,后面会提及。</p>
<p>节点状态: 每个集群中的节点都有一个状态 LOOKING, FOLLOWING, LEADING, OBSERVING。都属于这4种,每个节点启动的时候都是LOOKING状态,如果这个节点参与选举但最后不是leader,则状态是FOLLOWING,如果不参与选举则是OBSERVING,leader的状态是LEADING。</p>
<p>开始这个选举算法前,每个节点都会在zoo.cfg上指定的监听端口启动监听(server.1=127.0.0.1:20881:20882),这里的20882就是这里用于选举的端口。</p>
<p>在FastLeaderElection里有一个Manager的内部类,这个类里有启动了两个线程:WorkerReceiver, WorkerSender。为什么说选举这部分复杂呢,我觉得就是这些线程就像左右互搏一样,非常难以理解。顾名思义,这两个线程一个是处理从别的节点接收消息的,一个是向外发送消息的。对于外面的逻辑接收和发送的逻辑都是异步的。</p>
<p>这里配置好了,QuorumPeer的run方法就开始执行了,这里实现的是一个简单的状态机。因为现在是LOOKING状态,所以进入LOOKING的分支,调用选举算法开始选举了:</p>
<p>setCurrentVote(makeLEStrategy().lookForLeader());<br>而在lookForLeader里主要是干什么呢?首先我们会更新一下一个叫逻辑时钟的东西,这也是在分布式算法里很重要的一个概念,但是在这里先不介绍,可以参考后面的论文。然后决定我要投票给谁。不过zookeeper这里的选举真直白,每个节点都选自己(汗),选我,选我,选我…… 然后向其他节点广播这个选举信息。这里实际上并没有真正的发送出去,只是将选举信息放到由WorkerSender管理的一个队列里。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">synchronized</span>(<span class="keyword">this</span>){</div><div class="line"> <span class="comment">//逻辑时钟 </span></div><div class="line"> logicalclock++;</div><div class="line"> <span class="comment">//getInitLastLoggedZxid(), getPeerEpoch()这里先不关心是什么,后面会讨论</span></div><div class="line"> updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//getInitId() 即是获取选谁,id就是myid里指定的那个数字,所以说一定要唯一</span></div><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">long</span> <span class="title">getInitId</span><span class="params">()</span></span>{</div><div class="line"> <span class="keyword">if</span>(self.getQuorumVerifier().getVotingMembers().containsKey(self.getId())) </div><div class="line"> <span class="keyword">return</span> self.getId();</div><div class="line"> <span class="keyword">else</span> <span class="keyword">return</span> Long.MIN_VALUE;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>//发送选举信息,异步发送<br>sendNotifications();<br>复制代码<br>现在我们去看看怎么把投票信息投递出去。这个逻辑在WorkerSender里,WorkerSender从sendqueue里取出投票,然后交给QuorumCnxManager发送。因为前面发送投票信息的时候是向集群所有节点发送,所以当然也包括自己这个节点,所以QuorumCnxManager的发送逻辑里会判断,如果这个要发送的投票信息是发送给自己的,则不发送了,直接进入接收队列。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">toSend</span><span class="params">(Long sid, ByteBuffer b)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (self.getId() == sid) {</div><div class="line"> b.position(<span class="number">0</span>);</div><div class="line"> addToRecvQueue(<span class="keyword">new</span> Message(b.duplicate(), sid));</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">//发送给别的节点,判断之前是不是发送过</span></div><div class="line"> <span class="keyword">if</span> (!queueSendMap.containsKey(sid)) {</div><div class="line"> <span class="comment">//这个SEND_CAPACITY的大小是1,所以如果之前已经有一个还在等待发送,则会把之前的一个删除掉,发送新的</span></div><div class="line"> ArrayBlockingQueue<ByteBuffer> bq = <span class="keyword">new</span> ArrayBlockingQueue<ByteBuffer>(SEND_CAPACITY);</div><div class="line"> queueSendMap.put(sid, bq);</div><div class="line"> addToSendQueue(bq, b);</div><div class="line"></div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> ArrayBlockingQueue<ByteBuffer> bq = queueSendMap.get(sid);</div><div class="line"> <span class="keyword">if</span>(bq != <span class="keyword">null</span>){</div><div class="line"> addToSendQueue(bq, b);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> LOG.error(<span class="string">"No queue for server "</span> + sid);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="comment">//这里是真正的发送逻辑了</span></div><div class="line"> connectOne(sid);</div><div class="line"> </div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>connectOne就是真正发送了。在发送之前会先把自己的id和选举地址发送过去。然后判断要发送节点的id是不是比自己的id大,如果大则不发送了。如果要发送又是启动两个线程:SendWorker,RecvWorker(这种一个进程内许多不同种类的线程,各自干活的状态真的很难理解)。发送逻辑还算简单,就是从刚才放到那个queueSendMap里取出,然后发送。并且发送的时候将发送出去的东西放到一个lastMessageSent的map里,如果queueSendMap里是空的,就发送lastMessageSent里的东西,确保对方一定收到了。</p>
<p>看完了SendWorker的逻辑,再来看看数据接收的逻辑吧。还记得前面提到的有个Listener在选举端口上启动了监听么,现在这里应该接收到数据了。我们可以看到receiveConnection方法。在这里,如果接收到的的信息里的id比自身的id小,则断开连接,并尝试发送消息给这个id对应的节点(当然,如果已经有SendWorker在往这个节点发送数据,则不用了)。</p>
<p>如果接收到的消息的id比当前的大,则会有RecvWorker接收数据,RecvWorker会将接收到的数据放到recvQueue里。</p>
<p>而FastLeaderElection的WorkerReceiver线程里会不断地从这个recvQueue里读取Message处理。在WorkerReceiver会处理一些协议上的事情,比如消息格式等。除此之外还会看看接收到的消息是不是来自投票成员。如果是投票成员,则会看看这个消息里的状态,如果是LOOKING状态并且当前的逻辑时钟比投票消息里的逻辑时钟要高,则会发个通知过去,告诉谁是leader。在这里,刚刚启动的崭新集群,所以逻辑时钟基本上都是相同的,所以这里还没判断出谁是leader。不过在这里我们注意到如果当前节点的状态是LOOKING的话,接收逻辑会将接收到的消息放到FastLeaderElection的recvqueue里。而在FastLeaderElection会从这个recvqueue里读取东西。</p>
<p>这里就是选举的主要逻辑了:totalOrderPredicate</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">totalOrderPredicate</span><span class="params">(<span class="keyword">long</span> newId, <span class="keyword">long</span> newZxid, <span class="keyword">long</span> newEpoch, <span class="keyword">long</span> curId, <span class="keyword">long</span> curZxid, <span class="keyword">long</span> curEpoch)</span> </span>{<span class="keyword">return</span> ((newEpoch > curEpoch) || </div><div class="line"> ((newEpoch == curEpoch) &&</div><div class="line"> ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));</div><div class="line"> }</div></pre></td></tr></table></figure>
<ol>
<li><p>判断消息里的epoch是不是比当前的大,如果大则消息里id对应的server我就承认它是leader</p>
</li>
<li><p>如果epoch相等则判断zxid,如果消息里的zxid比我的大我就承认它是leader</p>
</li>
<li><p>如果前面两个都相等那就比较一下server id吧,如果比我的大我就承认它是leader。</p>
</li>
</ol>
<p>关于前面两个东西暂时我们不去关心它,对于新启动的集群这两者都是相等的。</p>
<p>那这样看来server id的大小也是leader选举的一环啊(有的人生下来注定就不平凡,这都是命啊)。</p>
<p>最后我们来看看,很多文章所介绍的,如果超过一半的人说它是leader,那它就是leader的逻辑吧</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">termPredicate</span><span class="params">(</span></span></div><div class="line"> HashMap<Long, Vote> votes,</div><div class="line"> Vote vote) {</div><div class="line"></div><div class="line"> HashSet<Long> set = <span class="keyword">new</span> HashSet<Long>();</div><div class="line"> <span class="comment">//遍历已经收到的投票集合,将等于当前投票的集合取出放到set中</span></div><div class="line"> <span class="keyword">for</span> (Map.Entry<Long,Vote> entry : votes.entrySet()) {</div><div class="line"> <span class="keyword">if</span> (self.getQuorumVerifier().getVotingMembers().containsKey(entry.getKey())</div><div class="line"> && vote.equals(entry.getValue())){</div><div class="line"> set.add(entry.getKey());</div><div class="line"> }</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="comment">//统计set,也就是投某个id的票数是否超过一半</span></div><div class="line"> <span class="keyword">return</span> self.getQuorumVerifier().containsQuorum(set);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">containsQuorum</span><span class="params">(Set<Long> ackSet)</span> </span>{</div><div class="line"> <span class="keyword">return</span> (ackSet.size() > half);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>最后一关:如果选的是自己,则将自己的状态更新为LEADING,否则根据type,要么是FOLLOWING,要么是OBSERVING。</p>
<p>到这里选举就结束了。</p>
<p>这里介绍的是一个新集群启动时候的选举过程,启动的时候就是根据zoo.cfg里的配置,向各个节点广播投票,一般都是选投自己。然后收到投票后就会进行进行判断。如果某个节点收到的投票数超过一半,那么它就是leader了。 </p>
<p>了解了这个过程,我们来看看另外一个问题:</p>
<p>一个集群有3台机器,挂了一台后的影响是什么?挂了两台呢? </p>
<p>挂了一台:挂了一台后就是收不到其中一台的投票,但是有两台可以参与投票,按照上面的逻辑,它们开始都投给自己,后来按照选举的原则,两个人都投票给其中一个,那么就有一个节点获得的票等于2,2 > (3/2)=1 的,超过了半数,这个时候是能选出leader的。</p>
<p>挂了两台: 挂了两台后,怎么弄也只能获得一张票, 1 不大于 (3/2)=1的,这样就无法选出一个leader了。</p>
<p>在前面介绍时,为了简单我假设的是这是一个崭新的刚启动的集群,这样的集群与工作一段时间后的集群有什么不同呢?不同的就是epoch和zxid这两个参数。在新启动的集群里这两个一般是相等的,而工作一段时间后这两个参数有可能有的节点落后其他节点,至于是为什么,这个还要在后面的存储和处理额胡断请求的文章里介绍。</p>
<ul>
<li>关于逻辑时钟,我们的分布式大牛Leslie Lamport曾写过一篇论文:Time, Clocks, and the Ordering of Events in a Distributed System</li>
</ul>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/zookeeper-01/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/zookeeper-01/">zookeeper可以干什么</a></h1>
</header>
<div class="entry">
<p>在Zookeeper的官网上有这么一句话:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. </p>
<p>这大概描述了Zookeeper主要可以干哪些事情:配置管理,名字服务,提供分布式同步以及集群管理。那这些服务又到底是什么呢?我们为什么需要这样的服务?我们又为什么要使用Zookeeper来实现呢,使用Zookeeper有什么优势?接下来我会挨个介绍这些到底是什么,以及有哪些开源系统中使用了。</p>
<h3 id="配置管理"><a href="#配置管理" class="headerlink" title="配置管理"></a>配置管理</h3><p>在我们的应用中除了代码外,还有一些就是各种配置。比如数据库连接等。一般我们都是使用配置文件的方式,在代码中引入这些配置文件。但是当我们只有一种配置,只有一台服务器,并且不经常修改的时候,使用配置文件是一个很好的做法,但是如果我们配置非常多,有很多服务器都需要这个配置,而且还可能是动态的话使用配置文件就不是个好主意了。这个时候往往需要寻找一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。比如我们可以把配置放在数据库里,然后所有需要配置的服务都去这个数据库读取配置。但是,因为很多服务的正常运行都非常依赖这个配置,所以需要这个集中提供配置服务的服务具备很高的可靠性。一般我们可以用一个集群来提供这个配置服务,但是用集群提升可靠性,那如何保证配置在集群中的一致性呢? 这个时候就需要使用一种实现了一致性协议的服务了。Zookeeper就是这种服务,它使用Zab这种一致性协议来提供一致性。现在有很多开源项目使用Zookeeper来维护配置,比如在HBase中,客户端就是连接一个Zookeeper,获得必要的HBase集群的配置信息,然后才可以进一步操作。还有在开源的消息队列Kafka中,也使用Zookeeper来维护broker的信息。在Alibaba开源的SOA框架Dubbo中也广泛的使用Zookeeper管理一些配置来实现服务治理。</p>
<h3 id="名字服务"><a href="#名字服务" class="headerlink" title="名字服务"></a>名字服务</h3><p>名字服务这个就很好理解了。比如为了通过网络访问一个系统,我们得知道对方的IP地址,但是IP地址对人非常不友好,这个时候我们就需要使用域名来访问。但是计算机是不能是别域名的。怎么办呢?如果我们每台机器里都备有一份域名到IP地址的映射,这个倒是能解决一部分问题,但是如果域名对应的IP发生变化了又该怎么办呢?于是我们有了DNS这个东西。我们只需要访问一个大家熟知的(known)的点,它就会告诉你这个域名对应的IP是什么。在我们的应用中也会存在很多这类问题,特别是在我们的服务特别多的时候,如果我们在本地保存服务的地址的时候将非常不方便,但是如果我们只需要访问一个大家都熟知的访问点,这里提供统一的入口,那么维护起来将方便得多了。</p>
<h3 id="分布式锁"><a href="#分布式锁" class="headerlink" title="分布式锁"></a>分布式锁</h3><p>其实在第一篇文章中已经介绍了Zookeeper是一个分布式协调服务。这样我们就可以利用Zookeeper来协调多个分布式进程之间的活动。比如在一个分布式环境中,为了提高可靠性,我们的集群的每台服务器上都部署着同样的服务。但是,一件事情如果集群中的每个服务器都进行的话,那相互之间就要协调,编程起来将非常复杂。而如果我们只让一个服务进行操作,那又存在单点。通常还有一种做法就是使用分布式锁,在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举)。比如HBase的Master就是采用这种机制。但要注意的是分布式锁跟同一个进程的锁还是有区别的,所以使用的时候要比同一个进程里的锁更谨慎的使用。</p>
<h3 id="集群管理"><a href="#集群管理" class="headerlink" title="集群管理"></a>集群管理</h3><p>在分布式的集群中,经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出。有新的节点加入进来,也有老的节点退出集群。这个时候,集群中其他机器需要感知到这种变化,然后根据这种变化做出对应的决策。比如我们是一个分布式存储系统,有一个中央控制节点负责存储的分配,当有新的存储进来的时候我们要根据现在集群目前的状态来分配存储节点。这个时候我们就需要动态感知到集群目前的状态。还有,比如一个分布式的SOA架构中,服务是一个集群提供的,当消费者访问某个服务时,就需要采用某种机制发现现在有哪些节点可以提供该服务(这也称之为服务发现,比如Alibaba开源的SOA框架Dubbo就采用了Zookeeper作为服务发现的底层机制)。还有开源的Kafka队列就采用了Zookeeper作为Cosnumer的上下线管理。</p>
<p>后记</p>
<p>在这篇文章中,列出了一些Zookeeper可以提供的服务,并给出了一些开源系统里面的实例。后面我们从Zookeeper的安装配置开始,并用示例进一步介绍Zookeeper如何使用。</p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/zookeeper-03/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/zookeeper-03/">Zookeeper启动过程</a></h1>
</header>
<div class="entry">
<p>在上一篇,我们了解了zookeeper最基本的配置,也从中了解一些配置的作用,那么这篇文章中,我们将介绍Zookeeper的启动过程,我们在了解启动过程的时候还要回过头看看上一篇中各个配置参数在启动时的位置。</p>
<p>Zookeeper的启动入口在org.apache.zookeeper.server.quorum.QuorumPeerMain。</p>
<p>在这个类的main方法里进入了zookeeper的启动过程,首先我们会解析配置文件,即zoo.cfg和myid。</p>
<p>这样我们就知道了dataDir和dataLogDir指向哪儿了,然后就可以启动日志清理任务了(如果配置了的话)。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">DatadirCleanupManager purgeMgr = <span class="keyword">new</span> DatadirCleanupManager(config.getDataDir(), </div><div class="line"> config.getDataLogDir(), config.getSnapRetainCount(), config.getPurgeInterval()); </div><div class="line">purgeMgr.start();</div></pre></td></tr></table></figure>
<p>接下来会初始化ServerCnxnFactory,这个是用来接收来自客户端的连接的,也就是这里启动的是一个tcp server。在Zookeeper里提供两种tcp server的实现,一个是使用java原生NIO的方式,另外一个是使用Netty。默认是java nio的方式,一个典型的Reactor模型。因为java nio编程并不是本文的重点,所以在这里就只是简单的介绍一下。</p>
<p>//首先根据配置创建对应factory的实例:NIOServerCnxnFactory 或者 NettyServerCnxnFactory<br>ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();<br>//初始化配置<br>cnxnFactory.configure(config.getClientPortAddress(),config.getMaxClientCnxns());<br>创建几个SelectorThread处理具体的数据读取和写出。</p>
<p>先是创建ServerSocketChannel,bind等<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">this</span>.ss = ServerSocketChannel.open();</div><div class="line">ss.socket().setReuseAddress(<span class="keyword">true</span>);</div><div class="line">ss.socket().bind(addr);</div><div class="line">ss.configureBlocking(<span class="keyword">false</span>);</div></pre></td></tr></table></figure></p>
<p>然后创建一个AcceptThread线程来接收客户端的连接。</p>
<p>这一部分就是处理客户端请求的模块了,如果遇到有客户端请求的问题可以看看这部分。</p>
<p>接下来就进入初始化的主要部分了,首先会创建一个QuorumPeer实例,这个类就是表示zookeeper集群中的一个节点。初始化QuorumPeer的时候有这么几个关键点:</p>
<ol>
<li><p>初始化FileTxnSnapLog,这个类主要管理Zookeeper中的操作日志(WAL)和snapshot。</p>
</li>
<li><p>初始化ZKDatabase,这个类就是Zookeeper的目录结构在内存中的表示,所有的操作最后都会映射到这个类上面来。</p>
</li>
<li><p>初始化决议validator(QuorumVerifier->QuorumMaj) (其实这一步,是在配置)。这一步是从zoo.cfg的server.n这一部分初始化出集群的成员出来,有哪些需要参与投票(follower),有哪些只是observer。还有决定half是多少等,这些都是zookeeper的核心。在这一步,对于每个节点会初始化一个QuorumServer对象,并且放到allMembers,votingMembers,observingMembers这几个map里。而且这里也对参与者的个数进行了一些判断。</p>
</li>
<li><p>leader选举 这一步非常重要,也是zookeeper里最复杂而最精华的一部分。</p>
</li>
</ol>
<p>到这里,我们的zookeeper就启动完成了。后面我将会分三部分进一步深入理解zookeeper:</p>
<ol>
<li><p>leader选举</p>
</li>
<li><p>存储</p>
</li>
<li><p>处理客户端请求</p>
</li>
</ol>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2017-03-26T04:24:35.000Z"><a href="/2017/03/26/zookeeper-02/">2017-03-26</a></time>
<h1 class="title"><a href="/2017/03/26/zookeeper-02/">Zookeeper的配置</a></h1>
</header>
<div class="entry">
<p>前面两篇文章介绍了Zookeeper是什么和可以干什么,那么接下来我们就实际的接触一下Zookeeper这个东西,看看具体如何使用,有个大体的感受,后面再描述某些地方的时候也能在大脑中有具体的印象。本文只关注分布式模式的zookeeper,因为这也是在生产环境的唯一部署方式,单机的zookeeper可以在测试和开发环境使用,但是单机环境的zookeeper就不再是zookeeper了。</p>
<p>安装配置很简单,官网也有介绍,这里就只对后面的文章有提到的点说明下。</p>
<p>配置-zoo.cfg</p>
<p>这是zookeeper的主要配置文件,因为Zookeeper是一个集群服务,集群的每个节点都需要这个配置文件。为了避免出差错,zoo.cfg这个配置文件里没有跟特定节点相关的配置,所以每个节点上的这个zoo.cfg都是一模一样的配置。这样就非常便于管理了,比如我们可以把这个文件提交到版本控制里管理起来。其实这给我们设计集群系统的时候也是个提示:集群系统一般有很多配置,应该尽量将通用的配置和特定每个服务的配置(比如服务标识)分离,这样通用的配置在不同服务之间copy就ok了。ok,下面来介绍一些配置点:</p>
<pre><code>clientPort=2181
</code></pre><p>client port,顾名思义,就是客户端连接zookeeper服务的端口。这是一个TCP port。</p>
<pre><code>dataDir=/data
dataLogDir=/datalog
</code></pre><p>dataLogDir如果没提供的话使用的则是dataDir。zookeeper的持久化都存储在这两个目录里。dataLogDir里是放到的顺序日志(WAL)。而dataDir里放的是内存数据结构的snapshot,便于快速恢复。为了达到性能最大化,一般建议把dataDir和dataLogDir分到不同的磁盘上,这样就可以充分利用磁盘顺序写的特性。</p>
<p>下面是集群中服务的列表</p>
<pre><code>server.1=127.0.0.1:20881:30881
server.2=127.0.0.1:20882:30882
server.3=127.0.0.1:20883:30883
</code></pre><p>在上面的例子中,我把三个zookeeper服务放到同一台机器上。上面的配置中有两个TCP port。后面一个是用于Zookeeper选举用的,而前一个是Leader和Follower或Observer交换数据使用的。我们还注意到server.后面的数字。这个就是myid(关于myid是什么下一节会介绍)。</p>
<p>上面这几个是一些基本配置。</p>
<p>还有像 tickTime,这是个时间单位定量。比如tickTime=1000,这就表示在zookeeper里1 tick表示1000 ms,所有其他用到时间的地方都会用多少tick来表示。</p>
<p>比如 syncLimit = 2 就表示fowller与leader的心跳时间是2 tick。</p>
<p>maxClientCnxns – 对于一个客户端的连接数限制,默认是60,这在大部分时候是足够了。但是在我们实际使用中发现,在测试环境经常超过这个数,经过调查发现有的团队将几十个应用全部部署到一台机器上,以方便测试,于是这个数字就超过了。</p>
<p>minSessionTimeout, maxSessionTimeout – 一般,客户端连接zookeeper的时候,都会设置一个session timeout,如果超过这个时间client没有与zookeeper server有联系,则这个session会被设置为过期(如果这个session上有临时节点,则会被全部删除,这就是实现集群感知的基础,后面的文章会介绍这一点)。但是这个时间不是客户端可以无限制设置的,服务器可以设置这两个参数来限制客户端设置的范围。</p>
<p>autopurge.snapRetainCount,autopurge.purgeInterval – 客户端在与zookeeper交互过程中会产生非常多的日志,而且zookeeper也会将内存中的数据作为snapshot保存下来,这些数据是不会被自动删除的,这样磁盘中这样的数据就会越来越多。不过可以通过这两个参数来设置,让zookeeper自动删除数据。autopurge.purgeInterval就是设置多少小时清理一次。而autopurge.snapRetainCount是设置保留多少个snapshot,之前的则删除。</p>
<p>不过如果你的集群是一个非常繁忙的集群,然后又碰上这个删除操作,可能会影响zookeeper集群的性能,所以一般会让这个过程在访问低谷的时候进行,但是遗憾的是zookeeper并没有设置在哪个时间点运行的设置,所以有的时候我们会禁用这个自动删除的功能,而在服务器上配置一个cron,然后在凌晨来干这件事。</p>
<p>以上就是zoo.cfg里的一些配置了。下面就来介绍myid。</p>
<p>配置-myid</p>
<p>在dataDir里会放置一个myid文件,里面就一个数字,用来唯一标识这个服务。这个id是很重要的,一定要保证整个集群中唯一。zookeeper会根据这个id来取出server.x上的配置。比如当前id为1,则对应着zoo.cfg里的server.1的配置。</p>
<ol>
<li>而且在后面我们介绍leader选举的时候,这个id的大小也是有意义的。</li>
</ol>
<p>OK,上面就是配置的讲解了,现在我们可以启动zookeeper集群了。进入到bin目录,执行 ./zkServer.sh start即可。</p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<article class="post">
<div class="gallery">
<div class="photoset">
<img src="">
</div>
<div class="control">
<div class="prev"></div>
<div class="next"></div>
</div>
</div>
<div class="post-content">
<header>
<div class="icon"></div>
<time datetime="2016-05-22T04:20:38.000Z"><a href="/2016/05/22/Fabric入门/">2016-05-22</a></time>
<h1 class="title"><a href="/2016/05/22/Fabric入门/">Fabric入门</a></h1>
</header>
<div class="entry">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Fabric是一个用Python写的SSH连接工具,可用于应用开发、部署和系统管理。</p>
<p>我的项目需要使用SSH远程登录到服务器中执行某个命令,可以使用Fabric实现整个流程。但我只需要在一台服务器上执行命令,使用Fabric有些浪费,完全可以使用更轻的框架。好在Fabric很容易上手,就暂定用它执行命令。</p>
<p>以下内容来自Fabric的官方文档<a href="http://docs.fabfile.org/en/latest/tutorial.html" target="_blank" rel="external">《Overview and Tutorial》</a></p>
<h2 id="什么是Fabric"><a href="#什么是Fabric" class="headerlink" title="什么是Fabric"></a>什么是Fabric</h2><p>Fabric提供一套SSH组件,包括Python库和命令行工具,用于应用开发和系统管理。简单说,Fabric有下面两种工具:</p>
<p>通过命令行远程执行Python函数<br>使用Python函数通过ssh远程执行shell命令</p>
<h2 id="Hello-fab"><a href="#Hello-fab" class="headerlink" title="Hello, fab"></a>Hello, fab</h2><p>创建一个fabfile.py文件</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">hello</span><span class="params">()</span>:</span></div><div class="line"> print(<span class="string">"Hello world!"</span>)</div></pre></td></tr></table></figure>
<p>在命令行中使用fab命令运行</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab hello</div><div class="line">Hello world!</div><div class="line">Done.</div><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab hello</div><div class="line">Hello world!</div><div class="line"></div><div class="line">Done.</div></pre></td></tr></table></figure>
<h2 id="任务参数"><a href="#任务参数" class="headerlink" title="任务参数"></a>任务参数</h2><p>为上面函数添加参数</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">hello</span><span class="params">(name=<span class="string">"world"</span>)</span>:</span></div><div class="line"> print(<span class="string">"Hello %s!"</span> % name</div></pre></td></tr></table></figure>
<p>运行,参数格式 :,=,….<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab hello:name=windroc</div><div class="line">Hello windroc!</div><div class="line">Done.</div><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab hello:name=windroc</div><div class="line">Hello windroc!</div><div class="line">Done.</div></pre></td></tr></table></figure></p>
<p>下面方式具有同样效果<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab hello:windroc</div><div class="line">Hello windroc!</div><div class="line"></div><div class="line">Done.</div><div class="line"></div><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab hello:windroc</div><div class="line">Hello windroc!</div><div class="line"></div><div class="line">Done.</div></pre></td></tr></table></figure></p>
<p>本地命令<br>使用 <figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">fabfile.py</div><div class="line">```python</div><div class="line">from fabric.api import local</div><div class="line"></div><div class="line">def prepare_deploy():</div><div class="line"> local("date")</div><div class="line"> local("who")</div><div class="line"> local("uname -a")</div></pre></td></tr></table></figure></p>
<p>运行结果如下<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab prepare_deploy</div><div class="line">[localhost] local: date</div><div class="line">2015年 02月 15日 星期日 07:03:46 UTC</div><div class="line">[localhost] local: who</div><div class="line">vagrant pts/0 2015-02-15 01:15 (10.0.2.2)</div><div class="line">vagrant pts/1 2015-02-11 12:03 (10.0.2.2)</div><div class="line">vagrant pts/2 2015-02-14 13:11 (10.0.2.2)</div><div class="line">vagrant pts/4 2015-02-14 13:16 (10.0.2.2)</div><div class="line">vagrant pts/5 2015-02-15 02:56 (10.0.2.2)</div><div class="line">[localhost] local: uname -a</div><div class="line">Linux precise64 3.8.0-44-generic #66~precise1-Ubuntu SMP Tue Jul 15 04:01:04 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux</div><div class="line"> </div><div class="line">Done.</div></pre></td></tr></table></figure></p>
<h2 id="组织fabfile"><a href="#组织fabfile" class="headerlink" title="组织fabfile"></a>组织fabfile</h2><p>比如分成多个子任务<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> fabric.api <span class="keyword">import</span> local</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_date</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"date"</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_who</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"who"</span>)</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_uname</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"uname -a"</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">prepare_deploy</span><span class="params">()</span>:</span></div><div class="line"> print_date()</div><div class="line"> print_who()</div><div class="line"> print_uname()</div></pre></td></tr></table></figure></p>
<p>输出与之前一样</p>
<h2 id="故障"><a href="#故障" class="headerlink" title="故障"></a>故障</h2><p>Fabirc检查命令操作的返回值,如果没有正常退出则终止。</p>
<p>在fabfile.py中加入一个肯定会出错的命令。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> fabric.api <span class="keyword">import</span> local</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_date</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"date"</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_who</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"who"</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_uname</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"uname -a"</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">error_task</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"asdagasdg"</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">prepare_deploy</span><span class="params">()</span>:</span></div><div class="line"> print_date()</div><div class="line"> print_who()</div><div class="line"> error_task()</div><div class="line"> print_uname()</div></pre></td></tr></table></figure></p>
<p>运行出错<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab prepare_deploy</div><div class="line">[localhost] local: date</div><div class="line">2015年 02月 15日 星期日 07:06:55 UTC</div><div class="line">[localhost] local: who</div><div class="line">vagrant pts/0 2015-02-15 01:15 (10.0.2.2)</div><div class="line">vagrant pts/1 2015-02-11 12:03 (10.0.2.2)</div><div class="line">vagrant pts/2 2015-02-14 13:11 (10.0.2.2)</div><div class="line">vagrant pts/4 2015-02-14 13:16 (10.0.2.2)</div><div class="line">vagrant pts/5 2015-02-15 02:56 (10.0.2.2)</div><div class="line">[localhost] local: asdagasdg</div><div class="line">/bin/sh: 1: asdagasdg: not found</div><div class="line"> </div><div class="line">Fatal error: local() encountered an error (return code 127) while executing 'asdagasdg'</div><div class="line"> </div><div class="line">Aborting.</div><div class="line">local() encountered an error (return code 127) while executing 'asdagasdg'</div></pre></td></tr></table></figure></p>
<p>可以看到,运行出错后,不再运行后续任务</p>
<h2 id="故障处理"><a href="#故障处理" class="headerlink" title="故障处理"></a>故障处理</h2><p>环境变量warn_only,可以将abort转为warning,并提供灵活的处理方式</p>
<p>fabfile</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> fabric.api <span class="keyword">import</span> local, settings, abort</div><div class="line"><span class="keyword">from</span> fabric.contrib.console <span class="keyword">import</span> confirm</div><div class="line"> </div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_date</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"date"</span>)</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_who</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"who"</span>)</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_uname</span><span class="params">()</span>:</span></div><div class="line"> local(<span class="string">"uname -a"</span>)</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">error_task</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">with</span> settings(warn_only=<span class="keyword">True</span>):</div><div class="line"> result = local(<span class="string">"asdagasdg"</span>)</div><div class="line"> <span class="keyword">if</span> result.failed <span class="keyword">and</span> <span class="keyword">not</span> confirm(<span class="string">"Test failed. Continue anyway?"</span>):</div><div class="line"> abort(<span class="string">"Aborting at user request"</span>)</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">prepare_deploy</span><span class="params">()</span>:</span></div><div class="line"> print_date()</div><div class="line"> print_who()</div><div class="line"> error_task()</div><div class="line"> print_uname()</div></pre></td></tr></table></figure>
<p>出错时,会根据用户输入决定是否运行后面的程序</p>
<p>运行<br>vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab prepare_deploy<br>[localhost] local: date<br>2015年 02月 15日 星期日 07:10:39 UTC<br>[localhost] local: who<br>vagrant pts/0 2015-02-15 01:15 (10.0.2.2)<br>vagrant pts/1 2015-02-11 12:03 (10.0.2.2)<br>vagrant pts/2 2015-02-14 13:11 (10.0.2.2)<br>vagrant pts/4 2015-02-14 13:16 (10.0.2.2)<br>vagrant pts/5 2015-02-15 02:56 (10.0.2.2)<br>[localhost] local: asdagasdg<br>/bin/sh: 1: asdagasdg: not found</p>
<p>Warning: local() encountered an error (return code 127) while executing ‘asdagasdg’</p>
<p>Test failed. Continue anyway? [Y/n] Y<br>[localhost] local: uname -a<br>Linux precise64 3.8.0-44-generic #66~precise1-Ubuntu SMP Tue Jul 15 04:01:04 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux</p>
<p>Done.</p>
<h2 id="建立连接"><a href="#建立连接" class="headerlink" title="建立连接"></a>建立连接</h2><p>Fabric最强大的功能,通过SSH远程执行任务</p>
<p>修改任务</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_ls</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">with</span> cd(<span class="string">'/var/log'</span>):</div><div class="line"> run(<span class="string">"ls"</span>)</div></pre></td></tr></table></figure>
<p>执行任务,会提示输入host,使用ssh连接形式user@host:port<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab prepare_deploy</div><div class="line">No hosts found. Please specify (single) host string for connection: [email protected]</div><div class="line">[[email protected]] run: date</div><div class="line">[[email protected]] Passphrase for private key: </div><div class="line">[[email protected]] Login password for 'wangdp': </div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">[[email protected]] out: Sun Feb 15 07:19:01 GMT 2015</div><div class="line">[[email protected]] out: </div><div class="line"> </div><div class="line">[[email protected]] run: who</div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">…</div><div class="line">[[email protected]] out: nwp_bj pts/65 Feb 15 03:20 (localhost:24.0)</div><div class="line">[[email protected]] out: </div><div class="line"> </div><div class="line">[[email protected]] run: ls</div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">[[email protected]] out: aso sudo.log xcat</div><div class="line">[[email protected]] out: </div><div class="line"></div><div class="line"></div><div class="line">Done.</div><div class="line">Disconnecting from uranus.hpc.nmic.cn... done.</div></pre></td></tr></table></figure></p>
<p>也可以在命令行中给出host。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> fabric.api <span class="keyword">import</span> run</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">host_type</span><span class="params">()</span>:</span></div><div class="line"> run(<span class="string">"uname -s"</span>)</div></pre></td></tr></table></figure></p>
<p>运行结果<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab -H localhost host_type</div><div class="line">[localhost] Executing task 'host_type'</div><div class="line">[localhost] run: uname -s</div><div class="line">[localhost] Passphrase for private key: </div><div class="line">Sorry, you can't enter an empty password. Please try again.</div><div class="line">[localhost] Passphrase for private key: </div><div class="line">[localhost] Login password for 'vagrant': </div><div class="line">[localhost] out: Linux</div><div class="line">[localhost] out: </div><div class="line"> </div><div class="line"> </div><div class="line">Done.</div><div class="line">Disconnecting from localhost... done.</div></pre></td></tr></table></figure></p>
<h2 id="远程交互"><a href="#远程交互" class="headerlink" title="远程交互"></a>远程交互</h2><p>Fabric可以与远端进行对话,看下面的例子</p>
<p>fabfile.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">rm_file</span><span class="params">()</span>:</span></div><div class="line"> tmp_dir = <span class="string">"/cma/g3/wangdp/tmp"</span></div><div class="line"> </div><div class="line"> <span class="keyword">with</span> cd(tmp_dir):</div><div class="line"> run(<span class="string">"touch 1.txt"</span>)</div><div class="line"> run(<span class="string">"rm -i 1.txt"</span>)</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">deploy</span><span class="params">()</span>:</span></div><div class="line"> rm_file()</div></pre></td></tr></table></figure></p>
<p>运行<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab deploy</div><div class="line">No hosts found. Please specify (single) host string for connection: [email protected]</div><div class="line">[[email protected]] run: touch 1.txt</div><div class="line">[[email protected]] Passphrase for private key: </div><div class="line">[[email protected]] Login password for 'wangdp': </div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">[[email protected]] out: </div><div class="line"> </div><div class="line">[[email protected]] run: rm -i 1.txt</div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">[[email protected]] out: rm: remove 1.txt? y</div><div class="line">[[email protected]] out: </div><div class="line"> </div><div class="line"> </div><div class="line">Done.</div><div class="line">Disconnecting from uranus.hpc.nmic.cn... done.</div></pre></td></tr></table></figure></p>
<p>提前定义连接<br>最常见的方法,设置env.hosts列表</p>
<p>在fabfile模块头部设置env.hosts</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">env.hosts = [<span class="string">'[email protected]'</span>]</div></pre></td></tr></table></figure>
<p>运行<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">vagrant@precise64:/vagrant/sms_log_agent_monitor$ fab deploy</div><div class="line">[[email protected]] Executing task 'deploy'</div><div class="line">[[email protected]] run: touch 1.txt</div><div class="line">[[email protected]] Passphrase for private key: </div><div class="line">[[email protected]] Login password for 'wangdp': </div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">[[email protected]] out: </div><div class="line"> </div><div class="line">[[email protected]] run: rm -i 1.txt</div><div class="line">[[email protected]] out: welcome login</div><div class="line">[[email protected]] out: [YOU HAVE NEW MAIL]</div><div class="line">[[email protected]] out: rm: remove 1.txt? y</div><div class="line">[[email protected]] out: </div><div class="line"> </div><div class="line"> </div><div class="line">Done.</div><div class="line">Disconnecting from [email protected]... done.</div></pre></td></tr></table></figure></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文介绍了Fabric的基本功能:</p>
<p>定义任务,并用fab命令执行<br>使用local运行本地命令<br>使用settings修改环境变量<br>处理命令出错,提醒用户,手动中止<br>定义host列表,使用run远程执行命令。</p>
</div>
<footer>
<div class="clearfix"></div>
</footer>
</div>
</article>
<nav id="pagination">
<a href="/page/2/" class="alignright next">下一页</a>
<div class="clearfix"></div>
</nav></div></div>
<aside id="sidebar" class="alignright">
<div class="search">
<form action="//google.com/search" method="get" accept-charset="utf-8">
<input type="search" name="q" results="0" placeholder="搜索">
<input type="hidden" name="q" value="site:www.orochi.cc">
</form>
</div>
<div class="widget tag">
<h3 class="title">分类</h3>
<ul class="entry">
<li><a href="/categories/java技术/">java技术</a><small>3</small></li>
<li><a href="/categories/jsp/">jsp</a><small>1</small></li>
<li><a href="/categories/music/">music</a><small>3</small></li>
<li><a href="/categories/python/">python</a><small>1</small></li>
<li><a href="/categories/spring-maven/">spring maven</a><small>1</small></li>
<li><a href="/categories/zookeeper/">zookeeper</a><small>6</small></li>
<li><a href="/categories/个人日记/">个人日记</a><small>1</small></li>
<li><a href="/categories/技术总结/">技术总结</a><small>4</small></li>
</ul>
</div>
<div class="widget tag">
<h3 class="title">简介</h3>
<ul class="entry">
<li>本体:钊文磊</li>
<li>现状:帝都java攻城狮</li>
<li>Theme: <a href="https://github.com/zhaowenlei/lightum">Lightum</a>
<li>想交友的朋友请<a href="http://zipperary.com/about">联系我</a>!</li>
<li>QQ 号:496945276</li>
<font color="red">Hexo 交流群:287306637</font>
</ul>
</div>
<iframe width="100%" height="550" class="share_self" frameborder="0" scrolling="no" src="http://widget.weibo.com/weiboshow/index.php?language=&width=0&height=550&fansRow=1&ptype=0&speed=0&skin=1&isTitle=1&noborder=1&isWeibo=1&isFans=0&uid=2421828583&verifier=34ccaeb5&dpc=1"></iframe>
</aside>
<div class="clearfix"></div>
</div>
<footer id="footer" class="inner"><section>
Theme of <a href="https://github.com/zippera/lightum">Lightum</a>, Improved from <a href="https://github.com/hexojs/hexo-theme-light">Light</a>, by <a href="/">zhaowenlei</a>
</section>
<div class="clearfix"></div>
</footer>
<script src="//libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
(function($){
$('.fancybox').fancybox();
})(jQuery);
</script>
<!-- mathjax config similar to math.stackexchange -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
processEscapes: true
}
});
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
}
});
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Queue(function() {
var all = MathJax.Hub.getAllJax(), i;
for(i=0; i < all.length; i += 1) {
all[i].SourceElement().parentNode.className += ' has-jax';
}
});
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
</body>
</html>
<a href="https://github.com/zhaowenlei" target="_blank"><img style="position: absolute; top: 0; left: 0; border: 0;" src="/imgs/forkme_left_green_007200.png" alt="Fork me on GitHub"></a>