-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
2243 lines (2005 loc) · 136 KB
/
Copy pathindex.html
File metadata and controls
2243 lines (2005 loc) · 136 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Life Career</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="Life Career">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Life Career">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Life Career">
<link rel="alternate" href="/atom.xml" title="Life Career" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">Life Career</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" results="0" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-常用地址" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/03/19/常用地址/" class="article-date">
<time datetime="2019-03-19T07:05:00.000Z" itemprop="datePublished">2019-03-19</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/03/19/常用地址/">go语言入门</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="go语言入门汇总"><a href="#go语言入门汇总" class="headerlink" title="go语言入门汇总"></a>go语言入门汇总</h2><ul>
<li><a href="https://studygolang.com/" target="_blank" rel="external">go中文社区</a></li>
<li><a href="https://golang.google.cn/" target="_blank" rel="external">go中文官网</a></li>
</ul>
<h2 id="解决方案汇总"><a href="#解决方案汇总" class="headerlink" title="解决方案汇总"></a>解决方案汇总</h2><h3 id="go-basic"><a href="#go-basic" class="headerlink" title="go basic"></a>go basic</h3><ul>
<li>基础数据类型<ul>
<li>有符号整数类型:int8(-128 到 127), int16(-32768 到 32767), int32(-2147483648 到 2147483647), int64(±9.2e18)</li>
<li>无符号整数类型:uint8, uint16, uint32, uint64. <font color="blue">一般情况下不会使用无符号数,在减法运算时可能出问题</font></li>
<li>浮点数:float32(math.MaxFloat32=3.4e38, math.MinFloat32=1.4e-45), float64(math.MaxFloat64=1.8e308, math.MinFloat64=4.9e-324)</li>
<li>boolean: true/false</li>
<li>字符串:不可变字符串序列,java中字符串也是不可变量</li>
<li>常量:const</li>
</ul>
</li>
<li>集合类型<ul>
<li>数组:长度不可变<pre><code><br>var a [3]int<br>var a [3]int = [3]int{1,2,3}<br>var a := […]int{1,2,3,4}<br></code></pre></li>
<li>slice: 相同类型元素的可变长度序列,底层用数组来表示 ,长度变化时可能涉及底层元素的迁移复制(就是典型的java数组扩容复制)<pre><code><br>var s := make([]int, len, cap)<br>var s := make([]int, len)<br>s = append(s, 1)<br></code></pre></li>
<li>map:散列表的引用<pre><code><br>ages := make(map[string]int) // name-age map<br>ages := map[string]int{<br> “alice”: 20,<br> “collins”: 19,<br>}<br></code></pre></li>
<li>结构体struct</li>
</ul>
</li>
<li>指针和地址。go全部使用传值参数,被调用的函数收到的是主调函数复制的变量的副本,修改这个副本不会对主调函数中的值产生任何影响,因此如果需要修改参数的值或因为参数过大想要避免复制,就可以在参数定义中使用指针参数<ul>
<li>*ptr定义一个指针变量</li>
<li>&var获取一个变量的地址,可以复制给一个指针变量<pre><code><br>val := 1<br>ptr := &val<br></code></pre></li>
</ul>
</li>
<li>并发:go routine和channel<ul>
<li>与线程的区别包括:①栈长度可变(2K-1G)②M:N调度 </li>
<li>Concurrency is not parallism</li>
</ul>
</li>
<li>包管理:import语句和govenor<ul>
<li>不需要再git clone,直接用go get来完成源码的拉取和安装执行</li>
<li>一种内部惯例:仔细维护vendor/目录,将所有的vendor相关变更也提交到代码库</li>
<li>这种模式下:<pre><code><br>// 不使用选项,下载后会自动完成编译和打包<br>go get xxx.gitlab.xxx/yyy/zzz<br>// -d选项只下载源码,不执行安装<br>go get -d xxx.gitlab.xxx/yyy/zzz<br></code></pre></li>
</ul>
</li>
<li>常用包及功能<ul>
<li>fmt包 打印输出控制</li>
<li>strings/bytes/strconv/unicode包 用于字符串操作</li>
<li>math包 封装基础数学函数</li>
<li>sync包 用于并发锁控制</li>
<li>json包 用于json序列化/反序列化等操作</li>
<li>io/bufio包 用于管理输入输出流Reader/Writer</li>
<li>sql/driver包 数据库访问</li>
</ul>
</li>
</ul>
<h3 id="web解决方案"><a href="#web解决方案" class="headerlink" title="web解决方案"></a>web解决方案</h3><ul>
<li>web框架:gin</li>
<li>数据库连接</li>
<li>连接池管理</li>
<li>缓存解决方案 redis<ul>
<li>goredis</li>
</ul>
</li>
<li>服务治理 kite</li>
<li>web服务器</li>
<li>序列化 <ul>
<li>protobuf</li>
<li>thrift</li>
</ul>
</li>
<li>日志组件 logger</li>
</ul>
<h2 id="深入思考"><a href="#深入思考" class="headerlink" title="深入思考"></a>深入思考</h2><h3 id="go是垃圾语言?"><a href="#go是垃圾语言?" class="headerlink" title="go是垃圾语言?"></a>go是垃圾语言?</h3><ul>
<li><a href="http://blog.jobbole.com/102766/" target="_blank" rel="external">中文翻译</a></li>
<li><a href="https://medium.com/@tucnak/why-go-is-a-poorly-designed-language-1cc04e5daf2" target="_blank" rel="external">原文</a></li>
<li>分析</li>
</ul>
<h3 id="concurrency-is-not-parallism"><a href="#concurrency-is-not-parallism" class="headerlink" title="concurrency is not parallism"></a>concurrency is not parallism</h3><ul>
<li><a href="https://vimeo.com/49718712" target="_blank" rel="external">Rob Pike演讲地址</a></li>
<li><a href="https://talks.golang.org/2012/waza.slide#1" target="_blank" rel="external">ppt地址</a></li>
</ul>
<h3 id="go优势和劣势比较"><a href="#go优势和劣势比较" class="headerlink" title="go优势和劣势比较"></a>go优势和劣势比较</h3><ul>
<li><a href="https://blog.csdn.net/jiang7701037/article/details/82794315" target="_blank" rel="external">go的优劣比较</a></li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/03/19/常用地址/" data-id="cjtfttllm0000vaqhq7jhxjhi" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/go/">go</a></li></ul>
</footer>
</div>
</article>
<article id="post-翻译:成为多语种的程序员(Being a Polyglot Programmer)" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/03/06/翻译:成为多语种的程序员(Being a Polyglot Programmer)/" class="article-date">
<time datetime="2019-03-06T07:05:00.000Z" itemprop="datePublished">2019-03-06</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/03/06/翻译:成为多语种的程序员(Being a Polyglot Programmer)/">翻译:成为多语种的程序员(Being a Polyglot Programmer)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="翻译:成为多语种的程序员(Being-a-Polyglot-Programmer)"><a href="#翻译:成为多语种的程序员(Being-a-Polyglot-Programmer)" class="headerlink" title="翻译:成为多语种的程序员(Being a Polyglot Programmer)"></a>翻译:成为多语种的程序员(Being a Polyglot Programmer)</h2><h3 id="译者注"><a href="#译者注" class="headerlink" title="译者注"></a>译者注</h3><ul>
<li><a href="https://www.infoq.com/news/2017/05/being-polyglot-programmer" target="_blank" rel="external">原文地址</a></li>
<li><a href="https://polyconf.com/" target="_blank" rel="external">polyconf网站地址</a>,可以通过各个演讲者(speaker)的<a href="https://eventil.com" target="_blank" rel="external">eventil</a>找到他们在polyconf上的演讲视频,了解更多多语言(polyglot)方面的内容</li>
<li>本文是Qcon对PolyConf创建者Zaiste的采访,主要阐述Zaiste对polyglot的基本目标、方法的介绍,帮助读者理解polyglot</li>
</ul>
<h3 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h3><ul>
<li>年轻的IT开发者会学习多种不同的编程语言,这其中甚至包括Fortran这样的上古时代的语言。但当他们步入职场之后,使用的语言往往会走向单一(公司内部统一)</li>
<li>Zaiste在语言的使用上另辟蹊径,他每年都会学习新的语言,并在实际项目中使用这些语言。他非常重视多语言的实践,为此创建了大型的会议组织<a href="https://polyconf.com/" target="_blank" rel="external">Polyglotism in IT:PolyConf</a>来探讨这个议题。PolyConf将在2017年的6月7日-6月9日在巴黎La Géode举行。InfoQ将全程会议的新闻、Q&A、文章等内容</li>
<li>在InfoQ FR对Zaiste的采访中,他谈到了自己探索使用多种语言编程的实践经历、动机、不同语言基础范式上的差异,以及今年PolyConf的一些公开的内容</li>
<li><font color="blue">InfoQ FR:</font>Zaiste,麻烦您做一下自我介绍</li>
<li><font color="red">Zaiste: </font>大家好,我是Zaiste,我在巴黎经营一家软件公司,业余爱好会组织国际性的技术事件,其中最大的就是PolyConf。我从12年前开始从事编程工作,当时在银行系统使用java相关的技术,包括Struts 1和Spring等。2005年我开始使用Ruby & Rails,两年后组织了Ruby & Python方面的会议(RuPy)。目前我主要享受JavaScript编程,尤其是最新版本。另外,我对Clojure、OCaml和Reason社区也都保持关注,期望能找到时间进行更深入的了解</li>
<li><font color="blue">InfoQ FR:</font>为什么你认为PolyGlotism这么重要,你为此专门创建了PolyConf会议</li>
<li><font color="red">Zaiste: </font>多语言编程不是单纯为了使用一次语言,或在单一项目中使用某一语言。核心的目标是为了在软件开发艺术中获取通用性的认知和技能,鼓励程序员跨越语言边界,从深度和广度两方面更加深入的理解编程。在这基础上进一步提升和拓展编程技能。在这一点上,米开朗基罗和达芬奇是我们的榜样,他们不仅是发明家、导师、建筑师,同样也是画家和雕塑家。拥有匠心的程序员,不应该局限于一两门编程语言的使用。</li>
<li>就业市场上,雇主一般都要求在专一领域有多年实践经验积累。熟练掌握Angular通常意味着没有React相关技能,作为js对同一个问题的两种解决方案,程序员一般都是选择其中一个方案深入使用和实践。从维护成本上考虑,企业一般都会使用统一的技术栈,从多种语言/框架中选择其中一个,这就决定了企业会选择技术栈不那么广,但是在企业所在的技术栈上有足够的深度和经验的程序员。</li>
<li><font color="blue">InfoQ FR:</font>你在日产工作中怎么时间多语言编程?</li>
<li><font color="red">Zaiste: </font>PolyConf帮助我从不同的视角全面深入的理解我所使用的语言和工具。我在这个过程中,见证了一些上古时期的技术或者不流行的技术变得流行,也见证了已经被遗忘的技术又重新被采用。这种“多语言编程实践”让我对编程有了一个全面的视角。通过对不同技术的使用和对比,我可以更快的了解新技术的侧重点和应用场景</li>
<li><font color="blue">InfoQ FR:</font>最近在探索哪些新兴的语言?</li>
<li><font color="red">Zaiste: </font>我特别喜欢Rust和OCaml,这两个语言都带来了独特的特性。Rust是一个非常非常快的系统编程语言(System Programming Language, 区别于Application Programming Language)。OCaml是一个多范式(multi-paradigm)语言,支持函数式编程、命令式编程和面向对象编程(OOP)</li>
<li><font color="blue">InfoQ FR:</font>业界有一些其他的多语言编程相关的会议(conference),你创立PolyConf的目的是什么?</li>
<li><font color="red">Zaiste: </font>是的,我乐见业界出现更多关于多语言编程的会议。大家的目标是统一的,都是要促进编程语言和相关社区的交流。PolyConf希望能成为其中的一支重要力量</li>
<li><font color="blue">InfoQ FR:</font>如果我参加PolyConf能看到什么内容</li>
<li><font color="red">Zaiste: </font>今年虚拟机是会议的焦点,Chris Seaton会介绍Oracle基于Ruby的高性能动态编译器和解释器Graal。Maxime Chevalier-Boisvert将会介绍她们正在建设中的动态语言平台。Jack Moffitt将介绍Servo,这是用Rust写的浏览器引擎原型。Dalcol将会讨论Lua和LuaJIT。这些是我们会议的主要内容。</li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/03/06/翻译:成为多语种的程序员(Being a Polyglot Programmer)/" data-id="cjsx38q49000090fy5envak53" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Polyglot/">Polyglot</a></li></ul>
</footer>
</div>
</article>
<article id="post-java classloader" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/02/10/java classloader/" class="article-date">
<time datetime="2019-02-10T12:28:00.000Z" itemprop="datePublished">2019-02-10</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/02/10/java classloader/">java classloader详解</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><ul>
<li>classloader是用来加载 Class 的。它负责<font color="red">将Class 的字节码形式转换成内存形式的 Class 对象</font>。字节码可以来自于磁盘文件 <em>.class,也可以是 jar 包里的 </em>.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte,它有特定的复杂的内部格式。</li>
<li>JVM 运行并不是一次性加载所需要的全部类的,它是按需加载,也就是<font color="red">延迟加载</font>。程序在运行的过程中会逐渐遇到很多不认识的新类,这时候就会调用 ClassLoader 来加载这些类。加载完成后就会将 Class 对象存在 ClassLoader 里面,下次就不需要重新加载了。</li>
<li><font color="red">虚拟机使用调用者 Class 对象的 ClassLoader 来加载当前未知的类。</font></li>
<li>类名和classloader都相同,jvm才会视为同一个类(同一个Class对象)</li>
<li>classLoader功能:<img src="../images/classloader.png" alt="image"></li>
<li>核心方法<pre><code><br>package java.lang;<br>public abstract class ClassLoader {<br> public Class loadClass(String name);<br> protected Class defineClass(byte[] b);<br> public URL getResource(String name);<br> public Enumeration getResources(String name);<br> public ClassLoader getParent();<br>}<br></code></pre></li>
</ul>
<h2 id="classloader设计思路"><a href="#classloader设计思路" class="headerlink" title="classloader设计思路"></a>classloader设计思路</h2><h3 id="jvm-classloader"><a href="#jvm-classloader" class="headerlink" title="jvm classloader"></a>jvm classloader</h3><ul>
<li>Bootstrap Classloader<ul>
<li>Bootstrap Classloader本身没有parent加载器,但是可以用作其它Classloader实例的parent classloader</li>
<li>加载jvm运行时核心类库JAVA_HOME/lib/rt.jar中的java.util,java.nio,java.lang等</li>
</ul>
</li>
<li>ExtClassLoader<ul>
<li>负责加载jvm扩展类,位于 JAVA_HOME/lib/ext/*.jar中的类</li>
<li>parent是Bootstrap Classloader</li>
</ul>
</li>
<li>AppClassLoader<ul>
<li>直接面向用户端的classloader,加载Classpath环境变量里定义的路径中的jar包和目录。我们自己的代码及二方/三方jar包通常都是这个类加载器加载</li>
<li>parent是ExtClassLoader</li>
</ul>
</li>
<li>类图结构 <img src="../images/jvm_classloader.jpg" alt="image"></li>
</ul>
<h3 id="双亲委派模式"><a href="#双亲委派模式" class="headerlink" title="双亲委派模式"></a>双亲委派模式</h3><ul>
<li>什么是双亲委派<ul>
<li>当一个ClassLoader实例需要加载某个类时,<ul>
<li><ol>
<li>首先由最顶层的类加载器Bootstrap ClassLoader试图加载</li>
</ol>
</li>
<li><ol>
<li>如果没加载到,则把任务转交给Extension ClassLoader试图加载</li>
</ol>
</li>
<li><ol>
<li>如果也没加载到,则转交给App ClassLoader 进行加载</li>
</ol>
</li>
<li><ol>
<li>如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类</li>
</ol>
</li>
<li><ol>
<li>如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常</li>
</ol>
</li>
</ul>
</li>
<li>委派关系示意图<img src="../images/classloader_parent_delegate.png" alt="image"></li>
</ul>
</li>
<li>为什么使用双亲委派<ul>
<li>避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次</li>
<li>安全性考虑,避免核心类库功能被随意替代</li>
</ul>
</li>
</ul>
<h3 id="Thread-contextClassLoader"><a href="#Thread-contextClassLoader" class="headerlink" title="Thread.contextClassLoader"></a>Thread.contextClassLoader</h3><ul>
<li>SPI 的接口由 Java 核心库来提供,而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进类路径(CLASSPATH)里。SPI接口中的代码经常需要加载具体的实现类。那么问题来了,SPI的接口是Java核心库的一部分,是由<strong>启动类加载器(Bootstrap Classloader)来加载的;SPI的实现类是由系统类加载器(System ClassLoader)</strong>来加载的。</li>
<li>Bootstrap Classloader是无法找到 SPI 的实现类的,因为依照双亲委派模型,BootstrapClassloader无法委派AppClassLoader来加载类。</li>
<li>而线程上下文类加载器破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。</li>
<li>两种使用场景<ul>
<li><ol>
<li>SPI。当高层提供了统一接口让低层去实现,同时又要是在高层加载(或实例化)低层的类时,必须通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。</li>
</ol>
</li>
<li><ol>
<li>当使用本类托管类加载,然而加载本类的ClassLoader未知时,为了隔离不同的调用者,可以取调用者各自的线程上下文类加载器代为托管。</li>
</ol>
</li>
</ul>
</li>
</ul>
<h2 id="classloader应用"><a href="#classloader应用" class="headerlink" title="classloader应用"></a>classloader应用</h2><h3 id="自定义从远程文件创建class对象"><a href="#自定义从远程文件创建class对象" class="headerlink" title="自定义从远程文件创建class对象"></a>自定义从远程文件创建class对象</h3><ul>
<li><pre><code>
public class NetworkClassLoader extends ClassLoader {
private String rootUrl;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;//this.findLoadedClass(name); // 父类已加载
//if (clazz == null) { //检查该类是否已被加载过
byte[] classData = getClassData(name); //根据类的二进制名称,获得该class文件的字节码数组
if (classData == null) {
throw new ClassNotFoundException();
}
clazz = defineClass(name, classData, 0, classData.length); //将class的字节码数组转换成Class类的实例
//}
return clazz;
}
private byte[] getClassData(String name) {
InputStream is = null;
String path = classNameToPath(name);
URL url = new URL(path);
byte[] buff = new byte[1024*4];
int len = -1;
is = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((len = is.read(buff)) != -1) {
baos.write(buff,0,len);
}
return baos.toByteArray();
}
private String classNameToPath(String name) {
return rootUrl + "/" + name.replace(".", "/") + ".class";
}
}
</code></pre>
</li>
</ul>
<h3 id="dubbo-中的classloader"><a href="#dubbo-中的classloader" class="headerlink" title="dubbo 中的classloader"></a>dubbo 中的classloader</h3><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul>
<li><a href="https://www.jianshu.com/p/c3a49a4b2efb" target="_blank" rel="external">Java ClassLoader 透析</a></li>
<li><a href="https://www.jianshu.com/p/6e5c2b463b2a" target="_blank" rel="external">深入分析Java ClassLoader原理</a></li>
<li><a href="https://blog.csdn.net/yangcheng33/article/details/52631940" target="_blank" rel="external">真正理解线程上下文类加载器(多案例分析)</a></li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/02/10/java classloader/" data-id="cjs1hnfbt0000sxfylkrp1d0t" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/classloader/">classloader</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/jvm/">jvm</a></li></ul>
</footer>
</div>
</article>
<article id="post-redis实践" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/02/06/redis实践/" class="article-date">
<time datetime="2019-02-05T16:28:00.000Z" itemprop="datePublished">2019-02-06</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/02/06/redis实践/">redis实践</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="redis实践case"><a href="#redis实践case" class="headerlink" title="redis实践case"></a>redis实践case</h2><h3 id="阻塞式队列"><a href="#阻塞式队列" class="headerlink" title="阻塞式队列"></a>阻塞式队列</h3><ul>
<li>命令<ul>
<li>lpop/rpop</li>
<li>弹出列表key(可以指定多个)的头元素</li>
<li>lpop [key…]</li>
</ul>
</li>
<li><p>blpop/brpop</p>
<ul>
<li>弹出列表key(可以指定多个)的头元素,若队列为空则阻塞一定时间或到队列有元素位置。多个阻塞的请求使用FIFO的方式响应。timeout=0则一直阻塞</li>
<li>blpop [key…] timeout</li>
<li>实现原理:redis服务器是文件事件和时间事件的双重事件处理器,所以虽然单线程处理文件事件,但是队列为空需要阻塞时,key会被push到名为blocking_keys的dict结构中,当有push操作时,会查找blocking_keys有无对应的key,若有则返回到第一个被阻塞的client</li>
</ul>
</li>
<li><p>rpush/lpush</p>
<ul>
<li>将一个或多个值插入队列尾端</li>
</ul>
</li>
<li><p>rpoplpush </p>
<ul>
<li>将队列任务直接转移到processing队列,等到process完成再移除processing队列,从而实现at least once保证</li>
<li>rpoplpush taskqueue processingTaskqueue</li>
</ul>
</li>
<li><p>目标:</p>
<ul>
<li>可靠性,是否需要支持分布式事务</li>
<li>支持什么样的一致性保证(at least once/at most once)</li>
<li>吞吐量保证</li>
<li>参考<a href="http://blog.bronto.com/engineering/reliable-queueing-in-redis-part-1/" target="_blank" rel="external">http://blog.bronto.com/engineering/reliable-queueing-in-redis-part-1/</a></li>
</ul>
</li>
<li>我们实际实现时,没有用redis的bpop命令,而是在客户端进行status缓存,每隔一段时间去刷新status,减小阻塞在redis端的线程。使用bpop应该问题也不大</li>
</ul>
<h3 id="分布式锁,实现不同时间单位的流量控制"><a href="#分布式锁,实现不同时间单位的流量控制" class="headerlink" title="分布式锁,实现不同时间单位的流量控制"></a>分布式锁,实现不同时间单位的流量控制</h3><ul>
<li>命令<ul>
<li>incr/incrby</li>
<li>decr/decrby</li>
<li>原子的对一个key进行加减操作</li>
<li>incr/decr key</li>
<li>incrby/decrby key amount</li>
</ul>
</li>
</ul>
<h2 id="redis实践问题"><a href="#redis实践问题" class="headerlink" title="redis实践问题"></a>redis实践问题</h2><h3 id="缓存倾斜"><a href="#缓存倾斜" class="headerlink" title="缓存倾斜"></a>缓存倾斜</h3><ul>
<li>hot key造成的集群访问量倾斜<ul>
<li>hot key是指一段时间内,某些访问量远远高于其它redis key的key,导致大部分的访问流量都落在hot key所在的slot/redis节点,常见的hot key场景有:</li>
<li><ol>
<li>库存系统/秒杀系统中,最热门的商品信息或库存</li>
</ol>
</li>
<li><ol>
<li>新闻应用之中的热点新闻</li>
</ol>
</li>
</ul>
</li>
<li><p>方案:</p>
<ul>
<li>一、本地缓存,考虑两个问题:<ul>
<li>hot key过多时,本地缓存是否会过大</li>
<li>本地缓存同redis数据的一致性</li>
</ul>
</li>
<li>二、利用分片算法特性,将key进行打散处理<ul>
<li>在hot key上增加后缀/前缀,一般数量是集群节点数量N <em> 指定倍数M个tmp key,这N</em> M个tmp key分摊到不同的实例上,将访问量均摊到所有实例。一份数据变成了N*M+1份,多分数据之间的一致性保证就是一件高成本的事情,因此写多读少的场景可能不适合</li>
<li>N*M个tmp key备份数据要随机使用不同的缓存时间(随机),防止tmp key同时失效造成缓存雪崩,这是一种通过坡度过期的方式避免雪崩的思路</li>
<li>tmp key的前缀后缀策略,注意twemproxy的计算分片时,越靠前的字符权重越大,越靠后的字符权重越小,因此当key特别长时,加后缀有可能起不到分散到不同节点的作用</li>
</ul>
</li>
</ul>
</li>
<li><p>big key造成集群的数据量倾斜</p>
<ul>
<li>big key是指数据大小远大于其它key,导致分片之后,big key所在(量大)的节点内存使用量远大于其它实例,造成内存不足,拖累集群。常见的big key场景由:</li>
<li><ol>
<li>论坛中大型持久盖楼活动</li>
</ol>
</li>
<li><ol>
<li>聊天系统中热门聊天室的消息列表</li>
</ol>
</li>
</ul>
</li>
<li><p>方案:对数据进行拆分(数据必须是可拆分的列表、set等)</p>
<ul>
<li>一、hset,将key的内容打散到不同的实例中</li>
<li>二、list,将list拆分成子list</li>
</ul>
</li>
<li><p>既是big key也是hot key</p>
<ul>
<li><ol>
<li>考虑是否适合redis存储</li>
</ol>
</li>
<li><ol>
<li>可否迁出集群,采用master+replication/读写分离的方式来存储</li>
</ol>
</li>
</ul>
</li>
<li><p>如何发现hot key和big key</p>
<ul>
<li>一、基于业务预判</li>
<li>二、监控<ul>
<li><ol>
<li>在应用程序端进行收集</li>
</ol>
</li>
<li><ol>
<li>在proxy层进行redis请求的收集(推荐)</li>
</ol>
</li>
<li><ol>
<li>在redis实例上使用monitor统计热点key(高并发时产生的overhead过高)</li>
</ol>
</li>
<li><ol>
<li>对redis端口进行数据抓包</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
<li><p>自动处理</p>
<ul>
<li><ol>
<li>发现big key和hot key,进行报警</li>
</ol>
</li>
<li><ol>
<li>自动处理big key和hot key,例如建立一套中间层,升级为big key或hot key的key访问,转为hot key/big key方式的访问</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="缓存雪崩问题"><a href="#缓存雪崩问题" class="headerlink" title="缓存雪崩问题"></a>缓存雪崩问题</h3><ul>
<li>第一种:大量缓存几种在某一个时间段失效</li>
<li>解决方案:<ul>
<li><ol>
<li>缓存访问流控(只允许一个线程穿透到数据库,通过JVM同步或者分布式同步实现),</li>
</ol>
</li>
<li><ol>
<li>缓存坡度过期策略(不同的key使用不同的缓存过期时间,例如在基础有效时长上加一个随机数,这个随机数越大,以为着坡度越大,缓冲器越长,压力就会越小)</li>
</ol>
</li>
<li><ol>
<li>在缓存更新时间与缓存过期时间之间增加容忍期,相当于缓存永远不会失效,那当然不会有雪崩问题</li>
</ol>
</li>
</ul>
</li>
<li>第二种:或者缓存服务器宕机/hot key失效,导致并发访问在短时间内几种到数据库后端系统<ul>
<li><ol>
<li>通过HA架构提高缓存服务的可靠性</li>
</ol>
</li>
<li><ol>
<li>数据库保护性访问流控(保护了数据库和依赖数据库的其它业务,但是当前业务的可用性收到影响),</li>
</ol>
</li>
<li><ol>
<li>当遇到缓存服务器宕机/hot key失效这些场景,只能进行保护性降级,熔断->限流->隔离</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="缓存穿透问题"><a href="#缓存穿透问题" class="headerlink" title="缓存穿透问题"></a>缓存穿透问题</h3><ul>
<li>并发很高的场景下,某个key没有在缓存中,短时间内对该key的大量并发访问就会全部穿透到数据库中,或者大量非法/无效的key被同时访问,对后端造成很大的压力,被称为缓存穿透</li>
<li><p>解决方案</p>
<ul>
<li><ol>
<li>布隆过滤器,空key缓存/key existing判断(bitmap存储),或者直接怼查询过的空key加入到缓存</li>
</ol>
</li>
<li><ol>
<li>对key做按规则的过滤</li>
</ol>
</li>
</ul>
</li>
<li><p>缓存与db数据的一致性保证</p>
<ul>
<li><ol>
<li>数据库更新时同步更新,监听binlog异步更新</li>
</ol>
</li>
<li><ol>
<li>以库存为例,直接使用缓存做更新,redis可以通过incr和decr操作判断库存是否足够,并直接完成库存扣减。无论是db扣减还是缓存扣减,都要使用增量方式完成。</li>
</ol>
</li>
</ul>
</li>
</ul>
<h2 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h2><ul>
<li><a href="https://segmentfault.com/a/1190000017387491" target="_blank" rel="external">link1</a></li>
<li><a href="https://zhangxh20.github.io/2017/09/04/redis/cacheCollapse/" target="_blank" rel="external">link2</a></li>
<li><a href="https://yq.aliyun.com/articles/540337" target="_blank" rel="external">link3</a></li>
<li><a href="https://www.jishuwen.com/d/2Hnm" target="_blank" rel="external">link4</a></li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/02/06/redis实践/" data-id="cjrrzufsh00009vfycyo6gobn" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/redis/">redis</a></li></ul>
</footer>
</div>
</article>
<article id="post-SOA方法论" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/01/27/SOA方法论/" class="article-date">
<time datetime="2019-01-27T14:00:00.000Z" itemprop="datePublished">2019-01-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/01/27/SOA方法论/">SOA架构总结</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="SOA方法论"><a href="#SOA方法论" class="headerlink" title="SOA方法论"></a>SOA方法论</h2><ul>
<li>SOA原则:自治和管制</li>
<li>服务间调用使用消息替代RPC调用,实现依赖剥离</li>
<li>服务都是无连接的</li>
<li>SOA目标和优势:<ul>
<li>松耦合。服务自治,各系统之间划定服务边界,服务调用通过接口发布。调用方无需关心服务实现细节,利于团队工作划分和业务发展</li>
<li>位置透明。消费者无需关心服务位置</li>
<li>可在异构平台之间使用</li>
<li>便于测试和并行开发</li>
</ul>
</li>
</ul>
<h2 id="RPC核心组件"><a href="#RPC核心组件" class="headerlink" title="RPC核心组件"></a>RPC核心组件</h2><h3 id="服务暴露"><a href="#服务暴露" class="headerlink" title="服务暴露"></a>服务暴露</h3><ul>
<li>provider需要以某种形式提供服务调用的相关信息,一般用IDL文件来暴露其服务信息。dubbo使用xml文件注册其提供的服务内容,并通过zookeeper来实现服务的负载均衡和容灾</li>
<li>同一语言平台的RPC通过共享接口定义来实现</li>
</ul>
<h3 id="远程代理对象"><a href="#远程代理对象" class="headerlink" title="远程代理对象"></a>远程代理对象</h3><ul>
<li>jdk动态代理,突出可读性。实现代理对象一样的接口,并实现代理功能。如统一封装发送远程调用请求</li>
<li>java动态代理简介<pre><code><br>public interface InvocationHandler{<br> public Object invoke(Object proxy, Method method, Object[] args);<br>}<br>public class DynamicProxy implements InvocationHandler{<br> private Object proxy;<br> public Object invoke(Object proxy, Method method, Object[] args){<pre><code>preProcess();
Object rs = method.invoke(proxy, args);
proProcess();
return rs;
</code></pre> }<br>}<br></code></pre></li>
<li>字节码生成,突出性能</li>
</ul>
<h3 id="通信"><a href="#通信" class="headerlink" title="通信"></a>通信</h3><ul>
<li>协议:TCP、HTTP<ul>
<li>传输层协议TCP性能更好</li>
<li>WebService 就是基于HTTP的RPC,具有良好的跨平台性,性能不如TCP</li>
</ul>
</li>
<li>IO方式,使用NIO</li>
</ul>
<h3 id="序列化"><a href="#序列化" class="headerlink" title="序列化"></a>序列化</h3><ul>
<li>java<ul>
<li>Java序列化是对对象的结构和内容的完全描述,所以数据会比较大,但是安全可靠。</li>
<li>性能差,必须要手动管理serializationUID</li>
</ul>
</li>
<li>protobuf<ul>
<li>只针对数据进行序列化,数据小,性能高</li>
<li>通过额外的protobuf配置文件,在序列化和反序列化两方约定数据的格式</li>
<li>protobuf在数据表示方面也进行了一些技巧来减小数据的大小<ul>
<li>varint 越小的数字占用的存储越小</li>
<li>sint 由于有符号负数在计算机中需要用一个很大的数值来表示,这样用上述varint来表示就一定需要5个byte(比使用正常的32bit整型还大) - optional字段不序列化</li>
<li>数据类型也用一个WiredType来映射</li>
</ul>
</li>
<li>封包/解包速度<ul>
<li>XML需要进行复杂的文法词法分析,而protobuf只要简单的将二进制序列按照指定的格式读取到C++对应的结构类型中 </li>
</ul>
</li>
</ul>
</li>
<li>Hessian<ul>
<li>hessian相当于一个java和protobuf平衡的方案,省去protobuf的配置文件,在程序逻辑中进行一些数据大小的优化</li>
<li>着重数据的序列化,简单类型信息会直接附带;复杂类型序列化成Map,包含基本类型描述和数据内容。</li>
<li>在序列化过程中,如果一个对象之前出现过,hessian会直接插入一个R index这样的块来表示一个引用位置,从而省去再次序列化和反序列化的时间。</li>
</ul>
</li>
<li>一些最佳实践原则<ul>
<li>子类的field不应该跟父类冲突(相同)</li>
<li>不论是父类/子类,java序列化必须显示声明serializationUID否则可能会随着代码和环境的变化,产生版本冲到导致的反序列化错误</li>
<li>引用其它对象时,也要遵从响应的原则</li>
</ul>
</li>
</ul>
<h3 id="负载均衡(dubbo)"><a href="#负载均衡(dubbo)" class="headerlink" title="负载均衡(dubbo)"></a>负载均衡(dubbo)</h3><ul>
<li>服务列表。使用zk进行管理,通过zk保证分布式数据的一致性,当服务地址发生变更时,zk要主动通知各client,保证列表的实时性和有效性</li>
<li>支持手动下线</li>
<li>选择算法。部分算法支持手动权重调节<ul>
<li>random</li>
<li>round robin</li>
<li>consistent hash,相同参数的请求总是被发送到同一个provider</li>
<li>least active loadbalance,为provider维护一个未完成的调用数量统计,这样可以使慢的服务得到少的请求</li>
</ul>
</li>
<li>注意路由/负载均衡/failover的区别<ul>
<li>路由,是根据路由配置选出调用子集(如指定调用最近的机房的provider列表)</li>
<li>负载均衡,在路由选出来的provider列表中按上述的算法选出一台服务器进行调用</li>
<li>failover,负载均衡到某台机器,但是这台机器失败时,需要换一个机器</li>
</ul>
</li>
<li>dubbo支持客户使用SPI机制对负载均衡进行定制扩展。</li>
</ul>
<h2 id="常见RPC组件"><a href="#常见RPC组件" class="headerlink" title="常见RPC组件"></a>常见RPC组件</h2><h3 id="google-GRPC"><a href="#google-GRPC" class="headerlink" title="google GRPC"></a>google GRPC</h3><h3 id="阿里-dubbo-核心代码"><a href="#阿里-dubbo-核心代码" class="headerlink" title="阿里 dubbo 核心代码"></a>阿里 dubbo 核心代码</h3><ul>
<li>BeanDefinition。从xml配置文件加载bean定义,用BeanDefinition对象表示,spring先转化得到BeanDefinition列表,再用BeanDefinition对象初始化所有的Bean</li>
<li>DubboBeanDefinitionParser/BeanDefinitionParser(spring)。DubboBeanDefinitionParser将dubbo配置文件转化为Dubbo相关的BeanDefinition定义</li>
<li>ReferenceBean/ReferenceConfig。DubboBeanDefinitionParser将bean定义用ReferenceBean和ReferenceConfig来表示,包含了服务接口名、服务器地址url、方法、客户端类型、Invoker等远程调用信息。</li>
<li>Invoker。封装了远程调用的逻辑,管理具体的Client来向远程服务器发送请求。Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。</li>
<li>Invocation。Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数,同步异步,isoneway等。</li>
<li>ProxyFactory/JdkProxyFactory/JavaassistProxyFactory。代理对象生成逻辑,JdkProxyFactory使用java动态代理,JavaassistProxyFactory使用字节码生成技术。</li>
<li>ExchangeClient/Client/EndPoint。执行具体的transport层功能。</li>
<li>LoadBalance。实现上述具体的负载均衡策略。</li>
</ul>
<h3 id="苹果thrift"><a href="#苹果thrift" class="headerlink" title="苹果thrift"></a>苹果thrift</h3><h2 id="其它服务组织"><a href="#其它服务组织" class="headerlink" title="其它服务组织"></a>其它服务组织</h2><h3 id="RESTful"><a href="#RESTful" class="headerlink" title="RESTful"></a>RESTful</h3><ul>
<li>REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。<ul>
<li>客户端和服务器间的请求无状态</li>
<li>分层系统,这表示组件无法了解它与之交互的中间层以外的组件。通过将系统知识限制在单个层,可以限制整个系统的复杂性,促进了底层的独立性。</li>
<li>资源都使用 URI (Universal Resource Identifier) 得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE。</li>
</ul>
</li>
</ul>
<h3 id="REST架构和RPC架构比较"><a href="#REST架构和RPC架构比较" class="headerlink" title="REST架构和RPC架构比较"></a>REST架构和RPC架构比较</h3><ul>
<li>在 RPC 样式的架构中,关注点在于方法,而在 REST 样式的架构中,关注点在于资源 —— 将使用标准方法检索并操作信息片段(使用表示的形式)</li>
<li>RESTful的关键是定义可表示流程元素/资源的对象。在REST中,每一个对象都是通过URL来表示的,对象用户负责将状态信息打包进每一条消息内,以便对象的处理总是无状态的。</li>
<li>RESTful的第二大问题是组合管理及流程绑定。企业对正规的(基于SOAP)SOA最大的反对声之一便是,这种等级的发现和绑定灵活性不足以适应复杂性。 </li>
</ul>
<h3 id="microservice"><a href="#microservice" class="headerlink" title="microservice"></a>microservice</h3><h2 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h2><ul>
<li><a href="https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html" target="_blank" rel="external">google protocol buffer使用和原理</a></li>
<li><a href="https://www.ibm.com/developerworks/community/blogs/3302cc3b-074e-44da-90b1-5055f1dc0d9c/entry/%E5%AE%B9%E5%99%A8%E6%8A%80%E6%9C%AF%E5%92%8C%E5%BE%AE%E6%9C%8D%E5%8A%A1_%E7%B3%BB%E5%88%97%E8%AF%BE%E7%A8%8B%E6%9D%A5%E4%BA%86?lang=zh" target="_blank" rel="external">IBM “容器技术和微服务”系列课程 </a> </li>
<li><a href="http://dubbo.apache.org/zh-cn/blog/dubbo-loadbalance.html" target="_blank" rel="external">dubbo官方负载均衡文档</a></li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/01/27/SOA方法论/" data-id="cjrrz76p4000171fyyd4xaor5" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/SOA/">SOA</a></li></ul>
</footer>
</div>
</article>
<article id="post-笔记——《从PAXOS到Zookeeper——分布式一致性原理与实践》" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/01/27/笔记——《从PAXOS到Zookeeper——分布式一致性原理与实践》/" class="article-date">
<time datetime="2019-01-27T13:25:00.000Z" itemprop="datePublished">2019-01-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/01/27/笔记——《从PAXOS到Zookeeper——分布式一致性原理与实践》/">笔记——《从PAXOS到Zookeeper——分布式一致性原理与实践》</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="分布式架构"><a href="#分布式架构" class="headerlink" title="分布式架构"></a>分布式架构</h2><h3 id="CAP理论"><a href="#CAP理论" class="headerlink" title="CAP理论"></a>CAP理论</h3><ul>
<li>CAP定理:一个分布式系统不可能同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这三个基本需求,最多只能同时满足其中两个</li>
<li>一致性:数据在多个版本之间能否保持一致。注意有强一致性和最终一致性之分</li>
<li>可用性:分布式系统要在有限时间内返回有效结果</li>
<li>分区容错性:遇到部分网络分区故障时,仍然能对外提供满足一致性和可用性的服务</li>
<li>因为网络是必然可能出现问题,因此分区容错性是分布式系统必须要面对和重点解决的问题,因此架构设计师往往把精力花在C和A之间寻求平衡</li>
</ul>
<h3 id="BASE理论"><a href="#BASE理论" class="headerlink" title="BASE理论"></a>BASE理论</h3><ul>
<li>BASE是CAP对一致性和可用性权衡的结果(基于CAP理论中提到的P必须要保证的前提)</li>
<li>其核心思想是,即使不能做到强一致性,但每个应用都可以根据自己的业务特点,采用适当的方式达到最终一致性</li>
<li>BA:Basically Available,S:Soft state,E:Eventually consistent</li>
<li>基本可用:①损失响应速度②容许部分功能被降级(主动/被动)</li>
<li>弱状态:允许不同节点数据副本之间存在同步延迟(短时间内读到的数据可能不一致)</li>
<li>最终一致性:经过一段时间可以达成一致,而不需要实时保证系统数据一致性(强一致性)</li>
<li>最终一致性的五种体现形式:<ul>
<li>因果一致性:A进程更新后,B进程要更新必须基于A进程更新后的最新之,不能发生丢失更新的情况</li>
<li>读己之所写:单个进程,读到的数据一定不能比自己上次写入的旧</li>
<li>会话一致性:单个会话中实现的读己之所写</li>
<li>单调读一致性:一个进程从系统中读到的数据必须是单调变新的</li>
<li>单调写一致性:一个系统要能够保证同一个进程的写操作顺序的执行</li>
</ul>
</li>
</ul>
<h2 id="一致性协议"><a href="#一致性协议" class="headerlink" title="一致性协议"></a>一致性协议</h2><h3 id="一致性问题描述"><a href="#一致性问题描述" class="headerlink" title="一致性问题描述"></a>一致性问题描述</h3><ul>
<li>分布式系统中,每个机器节点可以明确知道自己的事务状态,但无法直接获知其它分布式节点的操作结果。</li>
<li>当一个事务操作需要跨越多个分布式事务节点的时候,为了保持事务的ACID特性,需要引入一个叫做Coordinator协调者的组件来统一调度所有分布式节点的执行逻辑。被调度的分布式节点被称为参与者Participant。</li>
<li>协调者负责调度参与者的行为,并最终决定参与者是否要把事务真正提交。基于这个衍生出了2PC和3PC两种协议</li>
</ul>
<h3 id="分布式系统一致性的核心需求:"><a href="#分布式系统一致性的核心需求:" class="headerlink" title="分布式系统一致性的核心需求:"></a>分布式系统一致性的核心需求:</h3><ul>
<li>确保分区容错性(P),在一致性(C)和可用性(A)之间做平衡。两种方案:①在保证可用性时,使用最终一致性(E)②在保证强一致性时,牺牲部分可用性(BA)</li>
<li>使用Qorum(半数以上)机制保证系统不会产生split brain</li>
</ul>
<h3 id="2PC"><a href="#2PC" class="headerlink" title="2PC"></a>2PC</h3><ul>
<li>提交事务请求阶段/投票阶段<ul>
<li>事务询问。协调者向所有的参与者发送事务内容</li>
<li>执行事务。各参与者执行事务操作,并记录undo、redo日志</li>
<li>参与者向协调者反馈事务执行结果</li>
</ul>
</li>
<li>执行事务提交<ul>
<li>协调者发出commit请求</li>
<li>参与者收到commit请求后,正式commit事务</li>
<li>参与者发送ACK</li>
<li>协调者收到所有的ACK,完成事务commit</li>
</ul>
</li>
<li><p>回滚事务</p>
<ul>
<li>发送rollback请求</li>
<li>用Undo日志回滚事务</li>
<li>发送rollback ACK</li>
<li>协调者收到所有的ACK,完成事务中断</li>
</ul>
</li>
<li><p>优点:原理简单,实现方便</p>
</li>
<li>缺点:<ul>
<li>同步阻塞,二阶段执行过程中所有参与事务的逻辑都处于阻塞状态,参与者无法进行其它操作,极大的限制分布式系统的性能</li>
<li>协调者单点问题</li>
<li>数据不一致。某个参与者的网络在投票阶段过后中断,无法收到协调者发送的执行事务提交的请求,就会导致数据不一致的情况</li>
<li>没有容错机制,任何节点失败都会导致失败</li>
</ul>
</li>
</ul>
<h3 id="3PC"><a href="#3PC" class="headerlink" title="3PC"></a>3PC</h3><ul>
<li>2PC的改进版,将事务提交阶段一分为2,行程canCommit、preCommit、doCommit三个阶段</li>
<li>canCommit<ul>
<li>事务询问。协调者向所有参与者发送事务内容</li>
<li>参与者响应执行结果</li>
</ul>
</li>
<li>preCommit-commit<ul>
<li>发送预提交请求。</li>
<li>事务预提交。执行事务,并记录Redo、undo日志</li>
<li>参与者反馈执行响应</li>
</ul>
</li>
<li>preCommit-rollback<ul>
<li>发送中断请求</li>
<li>中断事务</li>
</ul>
</li>
<li>doCommit-commit<ul>
<li>发送提交请求</li>
<li>执行提交</li>
<li>反馈执行结果</li>
<li>完成提交事务</li>
</ul>
</li>
<li>doCommit-rollback<ul>
<li>发送中断请求</li>
<li>事务回滚</li>
<li>反馈回滚结果</li>
<li>完成中断事务</li>
</ul>
</li>
<li>优点:<ul>
<li>降低了参与者的阻塞范围</li>
<li>能够在出现单点故障后达成一致(超时事务会提交)</li>
</ul>
</li>
<li>缺点:网络故障默认提交事务,会导致数据不一致</li>
</ul>
<h3 id="Paxos算法"><a href="#Paxos算法" class="headerlink" title="Paxos算法"></a>Paxos算法</h3><ul>
<li>概述:<ul>
<li>基于消息传递且具有高度容错性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法</li>
<li>Paxos目的是,如何在一个易发生机器宕机或网络异常的分布式环境中,快速且正确的在集群内部对某个数据的值达成一致</li>
</ul>
</li>
<li>优点<ul>
<li>引入了过半原则</li>
<li>支持节点角色转换</li>
<li>解决无限期等待问题</li>
</ul>
</li>
</ul>
<h3 id="Paxos的工程实践——Chubby"><a href="#Paxos的工程实践——Chubby" class="headerlink" title="Paxos的工程实践——Chubby"></a>Paxos的工程实践——Chubby</h3><ul>
<li><p>一些实现特性:</p>
<ul>
<li>设计成了类似标准文件系统的结构,并支持相同的访问方式(ZK用类似的实现)</li>
<li>客户端缓存+服务端通知机制,减少服务端的压力</li>
<li>缓存租期+加会话延迟机制,给客户端闪断恢复的机会</li>
</ul>
</li>
<li><p>Chubby对Paxos算法的实现:</p>
<ul>
<li>Paxos算法在Chubby中的作用在于保证集群内各个副本节点的日志能够保持一致性</li>
<li>保证集群副本日志一致性</li>
<li>保证master故障切换时副本之间的一致性</li>
</ul>
</li>
<li>三层结构:<ul>
<li>底层容错日志(顺序日志)</li>
<li>K-V容错数据库</li>
<li>上层分布式锁服务和小文件存储服务</li>
</ul>
</li>
</ul>
<h3 id="ZK与Paxos"><a href="#ZK与Paxos" class="headerlink" title="ZK与Paxos"></a>ZK与Paxos</h3><ul>
<li>Zookeeper是一个开源的分布式协调服务,有Yahoo创建,是Google Chubby的开源实现。Zookeeper的目的是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。</li>
<li><p>Zookeeper是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现以下功能:</p>
<ul>
<li>数据发布/订阅</li>
<li>负载均衡</li>
<li>命名服务</li>
<li>分布式协调/通知</li>
<li>集群管理</li>
<li>Master选举</li>
<li>分布式锁和分布式队列</li>
</ul>
</li>
<li><p>ZK保证的分布式一致性特性:</p>
<ul>
<li>顺序一致性。同一个客户端发起的事务请求,最终将严格地按照其发起的顺序被应用到Zookeeper中</li>
<li>原子性。要么所有机器都成功应用了某一个事务,要么都没有应用</li>
<li>单一视图。无论客户端连接的是哪个服务器,看到的服务端数据模型都是一致的</li>
<li>可靠性/Duration。事务完成后不丢失</li>
<li>实时性。ZK保证的是BASE中的Basically Available</li>
</ul>
</li>
<li><p>ZK设计目标</p>
<ul>
<li>简单的数据模型:树形结构的名字空间,由一系列的ZNode数据节点组成,也是用的类似文件系统的目录结构,为了高效实用,全部由内存提供服务</li>
<li>可以构建集群。</li>
<li>顺序访问。实用全局唯一的事务编号,来反应事务操作的先后顺序。</li>
<li>高性能。主要面向读多写少的场景,性能非常高。</li>
</ul>
</li>
</ul>
<h3 id="ZK基本概念"><a href="#ZK基本概念" class="headerlink" title="ZK基本概念"></a>ZK基本概念</h3><ul>
<li>集群角色。<ul>
<li>Leader。提供读写服务,协调Qorum写机制</li>
<li>Follower提供读服务,参与Leader选举,参与Qorum写</li>
<li>Observer,仅提供读服务</li>
</ul>
</li>
<li>会话。客户端与服务端的TCP长连接</li>
<li>数据节点ZNode。树形结构,类似/foo/path1</li>
<li>版本。每个ZNode都有一个Stat数据结构,记录了version(当前ZNode版本)、cversion(当前ZNode子节点版本)、aversion(当前ZNode的ACL版本)</li>
<li>Watcher。客户端通知机制</li>
<li>Access Control Lists。数据权限控制,类似文件系统的权限</li>
</ul>
<h3 id="ZAB协议(Zookeeper-Atomic-Broadcast)"><a href="#ZAB协议(Zookeeper-Atomic-Broadcast)" class="headerlink" title="ZAB协议(Zookeeper Atomic Broadcast)"></a>ZAB协议(Zookeeper Atomic Broadcast)</h3><ul>
<li>核心:定义了对于那些会改变ZooKeeper服务器数据状态的事务请求处理方式。</li>
<li>所有事务请求必须由一个全局唯一的服务器来协调处理,即leader服务器</li>
<li>Leader服务器将一个事务请求转换为一个事务Proposal,并分发给所有Follower</li>
<li>Follower对proposal进行响应,若超过半数Follower,就进行事务commit</li>