-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
1439 lines (1326 loc) · 200 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Q.Y Zuo Believes]]></title>
<subtitle><![CDATA[Qiyang Zuo's Blog]]></subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://zuoqy.com/"/>
<updated>2018-11-27T13:59:06.034Z</updated>
<id>http://zuoqy.com/</id>
<author>
<name><![CDATA[Qiyang Zuo]]></name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title><![CDATA[Go语言内存模型]]></title>
<link href="http://zuoqy.com/2018/11/26/Golang-Memory-Model/"/>
<id>http://zuoqy.com/2018/11/26/Golang-Memory-Model/</id>
<published>2018-11-26T15:11:58.000Z</published>
<updated>2018-11-27T13:59:06.034Z</updated>
<content type="html"><![CDATA[<h2 id="Introduction">Introduction</h2><p>Go内存模型指定了一系列条件,在该条件下,可以保证在一个goroutine中对某个变量的读取操作可以观察(observe)到其他goroutine对这个变量写入的值。(内存可见性)</p>
<h2 id="Happens-before">Happens-before</h2><p>指令重排序对goroutine内部的读写顺序可能有影响,但不会影响代码定义的当前goroutine整体的行为。<br>例如在goroutine1中有代码<code>a = 1; b = 2;</code>,在其他goroutine中可能观察到b先于a赋值,但对于goroutine1,其行为不会因为指令重排序发生变化。</p>
<p>Go语言中的Happens-Before:Happens-Before定义了内存操作的顺序。如果e1 happens-before e2, 则 e2 一定在e2发生之后才发生。如果e1 e2 没有happens-before的关系限制,则e1 e2是并发发生的。在单个goroutine内部, happens-before 次序就是程序定义的顺序。</p>
<p>读取操作r对变量v 可以观察到写入操作w对变量v的写入,必须同时满足如下两个条件:</p>
<ol>
<li>r不能先于w发生</li>
<li>没有其他的对变量v的写入操作w‘ 晚于 w 但 先于r发生</li>
</ol>
<p>读取操作保证能观察到写入操作w对变量v的写入,必须同时满足如下两个条件:</p>
<ol>
<li>w 先于 r发生</li>
<li>任何其他对变量v的写入操作,要么先于w发生,要么晚于r发生。</li>
</ol>
<a id="more"></a>
<p>初始化变量v为其对应类型的零值,在此内存模型视为写入操作。读取或者写入长于一个机器字长的值,会被分为多个机器字长、执行顺序不固定的读取或者写入操作.</p>
<p>常见的Happens-before:</p>
<ul>
<li>The go statement that starts a new goroutine <strong>happens before</strong> the goroutine’s execution begins.<br>例如:</li>
</ul>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a string</span><br><span class="line"></span><br><span class="line"><span class="func"><span class="keyword">func</span> <span class="title">f</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="built_in">print</span>(a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="func"><span class="keyword">func</span> <span class="title">hello</span><span class="params">()</span></span> {</span><br><span class="line"> a = <span class="string">"hello, world"</span></span><br><span class="line"> go f() <span class="comment">// 在未来的某个时刻执行f() 可能是在hello() 返回之后</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>A send on a channel <strong>happens before</strong> the corresponding receive from that channel completes.<pre><code>向channel中写入数据先于从对应channel中接收数据
</code></pre></li>
<li><p>The closing of a channel <strong>happens before</strong> a receive that returns a zero value because the channel is closed.</p>
<pre><code>关闭channel先于从对应channel中接收数据
</code></pre><p>下面的程序保证能打印a的值</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> c = <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="typename">int</span>, <span class="number">10</span>)</span><br><span class="line"><span class="keyword">var</span> a <span class="typename">string</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> f() {</span><br><span class="line"> a = <span class="string">"hello, world"</span></span><br><span class="line"> c <- <span class="number">0</span> <span class="comment">// or close(c)</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> main() {</span><br><span class="line"> <span class="keyword">go</span> f()</span><br><span class="line"> <-c</span><br><span class="line"> <span class="built_in">print</span>(a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>A receive from an unbuffered channel <strong>happens before</strong> the send on that channel completes.<br>从非缓冲channel中接收数据先于向channel发送数据发生<br>违背直觉,但下面的程序会保证输出hello world</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> c = <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="typename">int</span>)</span><br><span class="line"><span class="keyword">var</span> a <span class="typename">string</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> f() {</span><br><span class="line"> a = <span class="string">"hello, world"</span></span><br><span class="line"> <-c</span><br><span class="line">}</span><br><span class="line"><span class="keyword">func</span> main() {</span><br><span class="line"> <span class="keyword">go</span> f()</span><br><span class="line"> c <- <span class="number">0</span></span><br><span class="line"> <span class="built_in">print</span>(a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>The kth receive on a channel with capacity C <strong>happens before</strong> the k+Cth send from that channel completes.</p>
</li>
</ul>
<p>下面这个程序定义了信号量为3,因此同时最多只有3个worker在工作</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> limit = <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="typename">int</span>, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> main() {</span><br><span class="line"> <span class="keyword">for</span> _, w := <span class="keyword">range</span> work {</span><br><span class="line"> <span class="keyword">go</span> <span class="keyword">func</span>(w <span class="keyword">func</span>()) {</span><br><span class="line"> limit <- <span class="number">1</span></span><br><span class="line"> w()</span><br><span class="line"> <-limit</span><br><span class="line"> }(w)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">select</span>{}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li><p>For any <code>sync.Mutex</code> or <code>sync.RWMutex</code> variable <code>l</code> and <code>n < m</code>, call n of <code>l.Unlock()</code> happens before call m of <code>l.Lock()</code> returns.<br>下面的程序中,f()函数中的unlock(1)先于Lock(2)执行</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> l sync.Mutex</span><br><span class="line"><span class="keyword">var</span> a <span class="typename">string</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> f() {</span><br><span class="line"> a = <span class="string">"hello, world"</span></span><br><span class="line"> l.Unlock() <span class="comment">//1 </span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> main() {</span><br><span class="line"> l.Lock()</span><br><span class="line"> <span class="keyword">go</span> f()</span><br><span class="line"> l.Lock() <span class="comment">// 2</span></span><br><span class="line"> <span class="built_in">print</span>(a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>For any call to <code>l.RLock</code> on a <code>sync.RWMutex</code> variable <code>l</code>, there is an n such that the <code>l.RLock</code> <strong>happens (returns) after</strong> call n to <code>l.Unlock</code> and the matching <code>l.RUnlock</code> <strong>happens before</strong> call n+1 to <code>l.Lock</code>.</p>
</li>
<li><p>A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns.<br><code>f()</code>函数先于<code>once.Do()</code>返回<br>下面的程序只会执行setup函数一次</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a string</span><br><span class="line"><span class="keyword">var</span> once sync.<span class="type">Once</span></span><br><span class="line"></span><br><span class="line"><span class="func"><span class="keyword">func</span> <span class="title">setup</span><span class="params">()</span></span> {</span><br><span class="line"> a = <span class="string">"hello, world"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="func"><span class="keyword">func</span> <span class="title">doprint</span><span class="params">()</span></span> {</span><br><span class="line"> once.<span class="type">Do</span>(setup)</span><br><span class="line"> <span class="built_in">print</span>(a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="func"><span class="keyword">func</span> <span class="title">twoprint</span><span class="params">()</span></span> {</span><br><span class="line"> go doprint()</span><br><span class="line"> go doprint()</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="Reference">Reference</h2><ul>
<li><a href="https://golang.org/ref/mem" target="_blank" rel="external">https://golang.org/ref/mem</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="Introduction">Introduction</h2><p>Go内存模型指定了一系列条件,在该条件下,可以保证在一个goroutine中对某个变量的读取操作可以观察(observe)到其他goroutine对这个变量写入的值。(内存可见性)</p>
<h2 id="Happens-before">Happens-before</h2><p>指令重排序对goroutine内部的读写顺序可能有影响,但不会影响代码定义的当前goroutine整体的行为。<br>例如在goroutine1中有代码<code>a = 1; b = 2;</code>,在其他goroutine中可能观察到b先于a赋值,但对于goroutine1,其行为不会因为指令重排序发生变化。</p>
<p>Go语言中的Happens-Before:Happens-Before定义了内存操作的顺序。如果e1 happens-before e2, 则 e2 一定在e2发生之后才发生。如果e1 e2 没有happens-before的关系限制,则e1 e2是并发发生的。在单个goroutine内部, happens-before 次序就是程序定义的顺序。</p>
<p>读取操作r对变量v 可以观察到写入操作w对变量v的写入,必须同时满足如下两个条件:</p>
<ol>
<li>r不能先于w发生</li>
<li>没有其他的对变量v的写入操作w‘ 晚于 w 但 先于r发生</li>
</ol>
<p>读取操作保证能观察到写入操作w对变量v的写入,必须同时满足如下两个条件:</p>
<ol>
<li>w 先于 r发生</li>
<li>任何其他对变量v的写入操作,要么先于w发生,要么晚于r发生。</li>
</ol>]]>
</summary>
<category term="golang" scheme="http://zuoqy.com/tags/golang/"/>
<category term="memory model" scheme="http://zuoqy.com/tags/memory-model/"/>
<category term="golang" scheme="http://zuoqy.com/categories/golang/"/>
</entry>
<entry>
<title><![CDATA[Kubernetes3 为什么需要Pod]]></title>
<link href="http://zuoqy.com/2018/11/06/Kubernates3-why-pod/"/>
<id>http://zuoqy.com/2018/11/06/Kubernates3-why-pod/</id>
<published>2018-11-06T15:01:09.000Z</published>
<updated>2018-11-26T15:09:54.608Z</updated>
<content type="html"><![CDATA[<h2 id="为什么需要Pod">为什么需要Pod</h2><ol>
<li>在工业实际部署中,经常需要多个进程部署在同一个节点上。类似于Linux操作系统中的进程组的概念(<code>pstree -g</code> 命令查看进程组),他们之间有着”超亲密关系”,例如相互之间会发生直接的文件交换、使用localhost或者Socket文件进行本地通信、共享某些Linux Namespace等。</li>
<li>容器的”单进程模型”,PID=1的进程往往是应用本身,无法像正常操作系统中的init进程那样拥有进程管理的功能。</li>
<li>存在”超亲密关系”的进程,需要按照严格的拓扑顺序启动。也就是需要对容器成组进行调度(gang scheduling)。<br>基于上述原因,Kubernetes提供了Pod这个逻辑概念,将需要在同一节点上,可能共享Namespace以及其他本地资源的容器进行成组调度。</li>
</ol>
<h2 id="Pod的实现机制">Pod的实现机制</h2><p>Pod是逻辑概念,其实是一组共享了某些资源(Network Namespace)的容器组。在没有成组调度时,如要实现容器A和容器B共享网络和Volume,可以通过如下命令实现:<br><a id="more"></a><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ docker run --net=B --volumes-from=B --name=A image-A ...</span><br></pre></td></tr></table></figure></p>
<p>但容器B就必须先于容器A启动,改变了容器A和容器B的对等关系。</p>
<p>因此,在Kubernetes项目中,Pod的是基于一个中间容器——<strong>Infra容器</strong>来实现的。</p>
<p><img src="http://zuoqy.com/images/2018-11-06/1.png" alt="1"><br><code>k8s.gcr.io/pause</code>这个镜像,是用汇编写的永远暂停的很小的镜像。用户容器通过加入Infra容器的Network Namespace实现资源的本地共享。</p>
<p>在Pod中,对于AB两个容器:</p>
<ul>
<li>可以直接通过localhost进行网络通信</li>
<li>看到的网络设备跟Infra容器看到的完全一样,网络资源被Pod中的所有容器共享</li>
<li>一个Pod对应一个IP地址</li>
<li>Pod的生命周期只和Infra容器有关,与AB容器无关。</li>
<li>Volume只需要在Pod中挂载一次,Pod内的所有容器即可共享。</li>
</ul>
<h2 id="容器设计模式"><a href="https://www.usenix.org/conference/hotcloud16/workshop-program/presentation/burns" target="_blank" rel="external">容器设计模式</a></h2><p>Kubernetes 希望当用户想在同一个容器中跑多个不相关的应用时,优先考虑使用Pod模式,看它们是否可以被描述为一个Pod中的多个容器。</p>
<p>典型的例子:</p>
<ol>
<li>war包和Web服务器</li>
</ol>
<p>在这种模型下,我们可以将war包和web服务器分别制作成两个容器镜像(包含war包的镜像可以做的非常小和简单,方便应用更新和部署),并且共享同一个Volume,包含war包的容器先于web服务器容器启动。</p>
<p>例如:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apiVersion: v1 kind: Pod metadata: name: javaweb-2 spec: initContainers: - image: app:v2 name: war command: ["cp", "/app.war", "/app"] volumeMounts: - mountPath: /app name: app-volume containers: - image: tomcat:7.0 name: tomcat command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"] volumeMounts: - mountPath: /root/apache-tomcat-7.0/webapps name: app-volume ports: - containerPort: 8080 hostPort: 8001 volumes: - name: app-volume emptyDir: {}</span><br></pre></td></tr></table></figure>
<p>所有<code>spec.initContainers</code>定义的容器,都会比<code>spec.containers</code> 定义的用户容器先启动。并且,<code>spec.initContainers</code> 容器会按顺序逐一启动,而直到它们都启动并且退出了,用户容器才会启动。</p>
<ol>
<li>日志收集</li>
</ol>
<p>应用进程容器不断的输出日志到特定的Volume路径,日志收集进程容器共享日志Volume进行收集。</p>
<p>其实,上述Pod组合模式,就是容器设计模式中的<strong>Sidecar模式</strong>,更多细节,可参见之前的文章:<a href="http://zuoqy.com/2018/10/07/Pattern-Service-Mesh/" target="_blank" rel="external">Pattern-Service-Mesh</a></p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="为什么需要Pod">为什么需要Pod</h2><ol>
<li>在工业实际部署中,经常需要多个进程部署在同一个节点上。类似于Linux操作系统中的进程组的概念(<code>pstree -g</code> 命令查看进程组),他们之间有着”超亲密关系”,例如相互之间会发生直接的文件交换、使用localhost或者Socket文件进行本地通信、共享某些Linux Namespace等。</li>
<li>容器的”单进程模型”,PID=1的进程往往是应用本身,无法像正常操作系统中的init进程那样拥有进程管理的功能。</li>
<li>存在”超亲密关系”的进程,需要按照严格的拓扑顺序启动。也就是需要对容器成组进行调度(gang scheduling)。<br>基于上述原因,Kubernetes提供了Pod这个逻辑概念,将需要在同一节点上,可能共享Namespace以及其他本地资源的容器进行成组调度。</li>
</ol>
<h2 id="Pod的实现机制">Pod的实现机制</h2><p>Pod是逻辑概念,其实是一组共享了某些资源(Network Namespace)的容器组。在没有成组调度时,如要实现容器A和容器B共享网络和Volume,可以通过如下命令实现:<br>]]>
</summary>
<category term="kubernetes" scheme="http://zuoqy.com/tags/kubernetes/"/>
<category term="Cloud Compute" scheme="http://zuoqy.com/categories/Cloud-Compute/"/>
</entry>
<entry>
<title><![CDATA[Kubernetes2 基本概念]]></title>
<link href="http://zuoqy.com/2018/10/29/Kubernates2-essential-concept/"/>
<id>http://zuoqy.com/2018/10/29/Kubernates2-essential-concept/</id>
<published>2018-10-29T14:39:15.000Z</published>
<updated>2018-11-26T15:09:54.607Z</updated>
<content type="html"><![CDATA[<h2 id="要解决什么问题">要解决什么问题</h2><p>可以方便的将用户应用的镜像部署到集群,并提供路由网关、水平扩展、监控、备份灾难恢复等一系列运维能力。这些问题,其实一个PAAS平台都可以解决。</p>
<p>除了解决以上问题,Kubernetes项目区别于其他PAAS平台,重点要解决的问题,来自于Borg项目的研究人员在论文中的一个重要观点:</p>
<blockquote>
<p>运行在大规模集群中的各个任务之间,存在着各种各样的关系。这些关系的处理,才是作业编排和管理系统最困难的地方。</p>
</blockquote>
<h2 id="需要什么样的架构">需要什么样的架构</h2><ul>
<li>节点分为控制节点Master和计算节点Node</li>
<li>Master节点包含kube-controller, kube-api-server 和 kube-scheduler三个组件</li>
<li>Node节点中的核心组件kubelet:<ul>
<li>通过CNI(Container Networking Interface)与网络插件交互,配置容器网络;</li>
<li>通过CSI(Container Storage Interface)与存储插件交互,配置持久化存储;</li>
<li>通过CRI(Container Runtime Interface)与容器运行时交互,定义容器运行时的各项操作。</li>
<li>容器运行时通过OCI容器运行时规范,与Linux操作系统交互。<a id="more"></a>
</li>
</ul>
</li>
</ul>
<p><img src="http://zuoqy.com/images/2018-10-29/1.png" alt="1"></p>
<p>其控制节点的架构理论,参考Google内部系统Borg</p>
<p><img src="http://zuoqy.com/images/2018-10-29/2.png" alt="2"></p>
<p>Kubernetes项目的设计思想,就是从更宏观的角度,以统一的方式来定义任务之间的各种关系,并未将来支持更多类型的关系留有余地。</p>
<p>如果应用内部各个子服务之间的依赖关系不复杂,则用Swarm+Compose的方式完全可以解决。在Compose文件中,经常会出现类似这样的编排配置:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">DB_NAME=/web/db</span><br><span class="line">DB_PORT=tcp://<span class="number">172.17</span>.<span class="number">0.5</span>:<span class="number">5432</span></span><br><span class="line">DB_PORT_5432_TCP=tcp://<span class="number">172.17</span>.<span class="number">0.5</span>:<span class="number">5432</span></span><br><span class="line">DB_PORT_5432_TCP_PROTO=tcp</span><br><span class="line">DB_PORT_5432_TCP_PORT=<span class="number">5432</span></span><br><span class="line">DB_PORT_5432_TCP_ADDR=<span class="number">172.17</span>.<span class="number">0.5</span></span><br></pre></td></tr></table></figure>
<p>对应子系统之间依赖负责的应用,例如:有的子服务需要部署在同一台机器上(例如需要本地进程间通信,如<a href="http://zuoqy.com/2018/10/07/Pattern-Service-Mesh/" target="_blank" rel="external">Service Mesh</a>),有的需要安排在不同的机器上(如web服务和DB)。</p>
<p>此时,Kubernetes的设计理念就派上了用场。Kubernetes以Pod为核心,抽象出了处理容器间关系的各种上层对象,如下图所示:</p>
<p><img src="http://zuoqy.com/images/2018-10-29/3.png" alt="3"></p>
<p>例如,容器间需要紧密协作,则需要Pod;容器间需要访问授权,则由Secret对象来完成等等。</p>
<h2 id="参考">参考</h2><ul>
<li><a href="http://malteschwarzkopf.de/research/assets/google-stack.pdf" target="_blank" rel="external">http://malteschwarzkopf.de/research/assets/google-stack.pdf</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="要解决什么问题">要解决什么问题</h2><p>可以方便的将用户应用的镜像部署到集群,并提供路由网关、水平扩展、监控、备份灾难恢复等一系列运维能力。这些问题,其实一个PAAS平台都可以解决。</p>
<p>除了解决以上问题,Kubernetes项目区别于其他PAAS平台,重点要解决的问题,来自于Borg项目的研究人员在论文中的一个重要观点:</p>
<blockquote>
<p>运行在大规模集群中的各个任务之间,存在着各种各样的关系。这些关系的处理,才是作业编排和管理系统最困难的地方。</p>
</blockquote>
<h2 id="需要什么样的架构">需要什么样的架构</h2><ul>
<li>节点分为控制节点Master和计算节点Node</li>
<li>Master节点包含kube-controller, kube-api-server 和 kube-scheduler三个组件</li>
<li>Node节点中的核心组件kubelet:<ul>
<li>通过CNI(Container Networking Interface)与网络插件交互,配置容器网络;</li>
<li>通过CSI(Container Storage Interface)与存储插件交互,配置持久化存储;</li>
<li>通过CRI(Container Runtime Interface)与容器运行时交互,定义容器运行时的各项操作。</li>
<li>容器运行时通过OCI容器运行时规范,与Linux操作系统交互。]]>
</summary>
<category term="kubernetes" scheme="http://zuoqy.com/tags/kubernetes/"/>
<category term="Cloud Compute" scheme="http://zuoqy.com/categories/Cloud-Compute/"/>
</entry>
<entry>
<title><![CDATA[如何有效地沟通]]></title>
<link href="http://zuoqy.com/2018/10/17/communicate-effectively/"/>
<id>http://zuoqy.com/2018/10/17/communicate-effectively/</id>
<published>2018-10-17T14:28:03.000Z</published>
<updated>2018-10-17T15:03:43.027Z</updated>
<content type="html"><![CDATA[<p>在吴军老师新书《态度》写给其女儿一封信中,提到了如何有效地沟通:</p>
<p><strong>第一,有效的沟通要以对方确认为准,不要以为话说出去,别人就一定能接收了你传递的信息。</strong><br>(那么反过来,自己要做到对别人来讲”靠谱”,就是”凡事有交代,件件有着落,事事有回应。”)</p>
<p><strong>第二,要以对方听得懂的话来沟通,不要把简单的问题讲复杂了。</strong> 这里作者举了一个很好的例子:</p>
<blockquote>
<p>中国的顾维钧先生是一个优秀的外交家,他在1919年的巴黎和会上向西方国家的代表讲述山东省对中国的重要性时,是这样说的:<br>孔子对中国人来说,相当于耶稣对西方人一样重要。西方人一直把耶路撒冷作为圣地,并且上千年一直要夺回那个地方。<br>山东是孔子的出生地,它在中国人心中的地位,相当于耶路撒冷在西方人心中的地位。</p>
</blockquote>
<p>简单的几句话,就把意思说明白了。对方能听懂,不是因为对山东和孔子有多么熟悉,而是因为熟知耶路撒冷和耶稣。</p>
<p><strong>第三,沟通要简洁,切中要害。对不同的人,表达方式也不一样。</strong></p>
<p><strong>第四,善辩不等于好的沟通,沟通的目的是让对方接受自己的想法,而非把对方驳得哑口无言。</strong></p>
]]></content>
<summary type="html">
<![CDATA[<p>在吴军老师新书《态度》写给其女儿一封信中,提到了如何有效地沟通:</p>
<p><strong>第一,有效的沟通要以对方确认为准,不要以为话说出去,别人就一定能接收了你传递的信息。</strong><br>(那么反过来,自己要做到对别人来讲”靠谱”,就是”凡事有交代,件件有]]>
</summary>
<category term="Reading" scheme="http://zuoqy.com/tags/Reading/"/>
<category term="态度" scheme="http://zuoqy.com/tags/%E6%80%81%E5%BA%A6/"/>
<category term="沟通" scheme="http://zuoqy.com/tags/%E6%B2%9F%E9%80%9A/"/>
<category term="Reading" scheme="http://zuoqy.com/categories/Reading/"/>
</entry>
<entry>
<title><![CDATA[Pattern Service Mesh]]></title>
<link href="http://zuoqy.com/2018/10/07/Pattern-Service-Mesh/"/>
<id>http://zuoqy.com/2018/10/07/Pattern-Service-Mesh/</id>
<published>2018-10-07T06:25:57.000Z</published>
<updated>2018-11-26T15:09:54.607Z</updated>
<content type="html"><![CDATA[<h2 id="当网络刚刚出现">当网络刚刚出现</h2><p>当人们刚想到让两台计算机通信,模型是这样的:</p>
<p><img src="http://zuoqy.com/images/2018-10-07/1.png" alt="1"></p>
<p>但上面过于简单的建模,无法使任意两台计算机之间的通信变得通用。分层的网络协议,使应用之间的通信时,应用本身不用关心网络的底层细节,例如如何拆包粘包,如何将字节序列转化成电信号等等。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/2.png" alt="2"></p>
<p>上面的模型依然存在问题,因为应用A在向B发送请求和数据包时,并不知道B是否能处理过来,如果B不能及时处理网络包,则会丢失数据。因此应用除了业务逻辑之外,还需要有专门的模块控制数据包的发送速度。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/3.png" alt="3"></p>
<p>幸运的是TCP/IP传输层协议的出现,从底层解决了流量控制和拥塞避免的问题,实现了可靠传输。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/4.png" alt="4"></p>
<p>上面的模型也成功的沿用了很长一段时间。</p>
<h2 id="微服务出现">微服务出现</h2><p>随后计算机逐渐变得便宜,出现了更多的节点和更可靠的网络连接。业界开始使用各种类型的网络系统,出现了更细粒度的分布式agent和面向服务的体系架构(SOA,Service Oriented Architectures)但依旧较重的服务组件。</p>
<p>90年代,Peter Deutsch和他的同事共同提出了<em>有关分布式系统的8条错误假设</em>(<a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing" target="_blank" rel="external">The 8 Fallacies of Distributed Computing</a>):</p>
<ul>
<li>网络是可靠的</li>
<li>没有延迟</li>
<li>带宽无限大</li>
<li>网络是安全的</li>
<li>网络拓扑不会改变</li>
<li>只有一个管理员</li>
<li>传输成本是0</li>
<li>网络是同构的</li>
</ul>
<a id="more"></a>
<p>这8条谬误,是为了提醒分布式系统的工程师们,不能简单忽视上面的问题,需要显式的处理它们。在分布式系统或者说微服务框架体系下,我们至少需要做以下事情:</p>
<ul>
<li>快速配置计算资源</li>
<li>基本的监控系统</li>
<li>快速的部署</li>
<li>易于配置的存储系统</li>
<li>轻松的访问边缘节点</li>
<li>权限验证和授权</li>
<li>标准RPC协议</li>
</ul>
<p>虽然TCP/IP协议和和通用的网络模型,仍然起到了很大的作用,但现如今,更智能的微服务架构模型需要新的服务层。例如服务发现(Service Discovery)和断路器(Circuit Breaker)。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/5.png" alt="5"></p>
<p><a href="https://martinfowler.com/bliki/CircuitBreaker.html" target="_blank" rel="external">Martin Fowler这样概括断路器</a>:</p>
<blockquote>
<p>The basic idea behind the circuit breaker is very simple. You wrap a protected function call in a circuit breaker object, which monitors for failures. Once the failures reach a certain threshold, the circuit breaker trips, and all further calls to the circuit breaker return with an error, without the protected call being made at all. Usually you’ll also want some kind of monitor alert if the circuit breaker trips.</p>
</blockquote>
<p>像断路器这样的简单装置,可以为服务之间的交互提供可靠性保证。(个人使用过Netflix公司的<a href="https://github.com/Netflix/Hystrix" target="_blank" rel="external">Hystrix</a>)即便如此,随着分布式系统规模的增长,某个组件出问题的可能性也成指数级增加。在一个庞大系统中的某个组件出问题,可能导致其客户端以及客户端的客户端相继发生断路。</p>
<p><strong>在过去需要几行代码搞定的东西,现在需要在各个client端重复模板代码来处理断路逻辑</strong></p>
<p>如果你使用过Hystrix,则你一定知道,你的代码中将大量包含类似:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CommandMayFailure</span> <span class="keyword">extends</span> <span class="title">HystrixCommand</span><<span class="title">String</span>> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">CommandMayFailure</span><span class="params">(String name)</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>(Setter</span><br><span class="line"> .withGroupKey(HystrixCommandGroupKey.Factory.asKey(<span class="string">"SystemX"</span>))</span><br><span class="line"> .andCommandKey(HystrixCommandKey.Factory.asKey(<span class="string">"SecondaryCommand"</span>))</span><br><span class="line"> .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(<span class="string">"SecondaryCommand"</span>))</span><br><span class="line"> .andCommandPropertiesDefaults(</span><br><span class="line"> <span class="comment">// we default to a 100ms timeout for secondary</span></span><br><span class="line"> HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(<span class="number">100</span>)));</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> String <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"this command always fails"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> String <span class="title">getFallback</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Hello Failure "</span> + name + <span class="string">"!"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这些样板代码往往需要设置线程池大小,断路器的失败阈值,超时时间,失败时的fallback逻辑等等。他们会遍布整个分布式系统的每个服务,他们与主要业务逻辑无关,大部分都是相同的。是不是很烦?</p>
<p>实际上,Facebook的<a href="https://code.fb.com/networking-traffic/introducing-proxygen-facebook-s-c-http-framework/" target="_blank" rel="external">Proxygen</a>和Twitter的<a href="https://finagle.github.io/blog/" target="_blank" rel="external">Finagle</a>这样的类库,意在避免这样的模板代码。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/6.png" alt="6"></p>
<p>上图描述了像Netflix、SoundCloud、Twitter这样的微服务先驱的微服务架构。</p>
<p>把断路器和服务发现下沉到类库中,有以下几个缺点:</p>
<ul>
<li>团队需要有专门的人力和时间,为适配这种类库构建生态系统</li>
<li>类库限制了你在微服务中使用什么样的工具、运行时环境和开发语言</li>
<li>在庞大分布式系统中,使用上述类库模型,类库本身也需要维护,版本适配将非常复杂。</li>
</ul>
<h2 id="Sidecar">Sidecar</h2><p>对比分层的网络协议栈,我们更希望断路和服务发现作为一个服务层(如下图所示),屏蔽底层的逻辑,对应用透明。但更改网络协议栈短期内是没有可能的事情。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/7.png" alt="7"></p>
<p>有些将这个服务层实现成一个代理应用(Proxy),服务之间不直接相互调用,而是将流量直接打到代理上,由代理做服务发现和路由。</p>
<p>这里衍生出的一个重要概念,就是<strong>Sidecars</strong>。<strong>Sidecar</strong>是和主应用进程相伴运行的进程,用来为主应用进程提供额外的功能特性。这里可以参见<a href="https://medium.com/airbnb-engineering/smartstack-service-discovery-in-the-cloud-4b8a080de619" target="_blank" rel="external">AirBnb</a>和<a href="https://medium.com/netflix-techblog/prana-a-sidecar-for-your-netflix-paas-based-applications-and-services-258a5790a015" target="_blank" rel="external">Netflix</a>的两篇有关Sidecar的文章。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/8.png" alt="8"></p>
<h2 id="Service_Mesh">Service Mesh</h2><p>如果你的微服务系统中各个应用间通过Sidecar proxy相互调用,则你的部署图可能是下图这样的:</p>
<p><img src="http://zuoqy.com/images/2018-10-07/9.png" alt="9"></p>
<p>2017年William在文章中这样定义<strong>Service Mesh</strong>:</p>
<blockquote>
<p>A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.</p>
</blockquote>
<p>而这套机制,已经在先进的PaaS运行时环境Kubernetes和Mesos完美支持:</p>
<p><img src="http://zuoqy.com/images/2018-10-07/10.png" alt="10"></p>
<p>最近火爆的项目<em><a href="https://istio.io/" target="_blank" rel="external">Istio project</a></em>则是Service Mesh系统的重要实例。</p>
<h2 id="参考">参考</h2><ul>
<li><a href="http://philcalcado.com/2017/08/03/pattern_service_mesh.html" target="_blank" rel="external">http://philcalcado.com/2017/08/03/pattern_service_mesh.html</a></li>
<li><a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing" target="_blank" rel="external">https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing</a></li>
<li><a href="https://martinfowler.com/bliki/CircuitBreaker.html" target="_blank" rel="external">https://martinfowler.com/bliki/CircuitBreaker.html</a></li>
<li><a href="https://github.com/Netflix/Hystrix" target="_blank" rel="external">https://github.com/Netflix/Hystrix</a></li>
<li><a href="https://code.fb.com/networking-traffic/introducing-proxygen-facebook-s-c-http-framework/" target="_blank" rel="external">https://code.fb.com/networking-traffic/introducing-proxygen-facebook-s-c-http-framework/</a></li>
<li><a href="https://finagle.github.io/blog/" target="_blank" rel="external">https://finagle.github.io/blog/</a></li>
<li><a href="https://medium.com/airbnb-engineering/smartstack-service-discovery-in-the-cloud-4b8a080de619" target="_blank" rel="external">https://medium.com/airbnb-engineering/smartstack-service-discovery-in-the-cloud-4b8a080de619</a></li>
<li><a href="https://medium.com/netflix-techblog/prana-a-sidecar-for-your-netflix-paas-based-applications-and-services-258a5790a015" target="_blank" rel="external">https://medium.com/netflix-techblog/prana-a-sidecar-for-your-netflix-paas-based-applications-and-services-258a5790a015</a></li>
<li><a href="https://istio.io/" target="_blank" rel="external">https://istio.io/</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="当网络刚刚出现">当网络刚刚出现</h2><p>当人们刚想到让两台计算机通信,模型是这样的:</p>
<p><img src="http://zuoqy.com/images/2018-10-07/1.png" alt="1"></p>
<p>但上面过于简单的建模,无法使任意两台计算机之间的通信变得通用。分层的网络协议,使应用之间的通信时,应用本身不用关心网络的底层细节,例如如何拆包粘包,如何将字节序列转化成电信号等等。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/2.png" alt="2"></p>
<p>上面的模型依然存在问题,因为应用A在向B发送请求和数据包时,并不知道B是否能处理过来,如果B不能及时处理网络包,则会丢失数据。因此应用除了业务逻辑之外,还需要有专门的模块控制数据包的发送速度。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/3.png" alt="3"></p>
<p>幸运的是TCP/IP传输层协议的出现,从底层解决了流量控制和拥塞避免的问题,实现了可靠传输。</p>
<p><img src="http://zuoqy.com/images/2018-10-07/4.png" alt="4"></p>
<p>上面的模型也成功的沿用了很长一段时间。</p>
<h2 id="微服务出现">微服务出现</h2><p>随后计算机逐渐变得便宜,出现了更多的节点和更可靠的网络连接。业界开始使用各种类型的网络系统,出现了更细粒度的分布式agent和面向服务的体系架构(SOA,Service Oriented Architectures)但依旧较重的服务组件。</p>
<p>90年代,Peter Deutsch和他的同事共同提出了<em>有关分布式系统的8条错误假设</em>(<a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing">The 8 Fallacies of Distributed Computing</a>):</p>
<ul>
<li>网络是可靠的</li>
<li>没有延迟</li>
<li>带宽无限大</li>
<li>网络是安全的</li>
<li>网络拓扑不会改变</li>
<li>只有一个管理员</li>
<li>传输成本是0</li>
<li>网络是同构的</li>
</ul>]]>
</summary>
<category term="Cloud Compute" scheme="http://zuoqy.com/tags/Cloud-Compute/"/>
<category term="Distributing System" scheme="http://zuoqy.com/tags/Distributing-System/"/>
<category term="Service Mesh" scheme="http://zuoqy.com/tags/Service-Mesh/"/>
<category term="Distributing System, Service Mesh" scheme="http://zuoqy.com/categories/Distributing-System-Service-Mesh/"/>
</entry>
<entry>
<title><![CDATA[软件设计的哲学(John Ousterhout分享)]]></title>
<link href="http://zuoqy.com/2018/10/06/A-Philosophy-of-Software-Design/"/>
<id>http://zuoqy.com/2018/10/06/A-Philosophy-of-Software-Design/</id>
<published>2018-10-06T14:37:57.000Z</published>
<updated>2018-10-09T16:47:41.024Z</updated>
<content type="html"><![CDATA[<h2 id="软件设计的目标">软件设计的目标</h2><ol>
<li><p>软件设计最大的目标是降低软件的复杂性(Complexity),复杂性使软件难于理解和维护。</p>
</li>
<li><p>复杂性的来源:</p>
<ul>
<li>含义模糊(Obscurity): 重要信息不突出。</li>
<li>相互依赖:模块无法独立于其他模块而被理解。</li>
</ul>
</li>
<li><p>复杂性的危害</p>
</li>
</ol>
<p>复杂性会逐渐递增,前面埋的坑,会导致后面的设计越来越复杂。</p>
<h2 id="设计原则">设计原则</h2><h3 id="暴露简单通用的接口,隐藏复杂的实现。">暴露简单通用的接口,隐藏复杂的实现。</h3><a id="more"></a>
<p><img src="http://zuoqy.com/images/2018-10-06/1.png" alt="Class should go deep"> </p>
<p>正例:Unix 文件I/O 接口</p>
<ul>
<li>Unix文件操作逻辑极为复杂,但前端模块只暴露的5个简单的接口。</li>
<li>隐藏了文件在磁盘上的表现形式、块分配、目录管理、权限管理、磁盘调度、块缓存和设备信息等复杂的底层内容。</li>
</ul>
<p><img src="http://zuoqy.com/images/2018-10-06/2.png" alt="A deep interface"> </p>
<p>反例:Java文件操作接口,读写一个文件要客户端感知很多类和细节:</p>
<p><img src="http://zuoqy.com/images/2018-10-06/3.png" alt="A deep interface cont'd"> </p>
<h3 id="模块外部需要感知和必须处理的异常越少越好:">模块外部需要感知和必须处理的异常越少越好:</h3><p><img src="http://zuoqy.com/images/2018-10-06/4.png" alt="minimize exceptions outside"> </p>
<h3 id="注重设计_vs-_注重速度">注重设计 vs. 注重速度</h3><p>如果目标是尽快把feature作完,把bug都fix掉,最终导致的结果就是设计缺陷,越来越复杂。</p>
<p><img src="http://zuoqy.com/images/2018-10-06/5.png" alt="tactical"> </p>
<p>如果初始有一个良好的设计,则在后面的迭代中形成良性循环,最小化复杂性。长远看来则节省了很多时间。</p>
<p><img src="http://zuoqy.com/images/2018-10-06/6.png" alt="strategy"> </p>
<p>Facebook由最初的口号”Move quickly and break things”转变为”Move quickly with solid infrastructure”<br>Google和VMWare由于注重设计而成功,吸引了大量顶级工程师。</p>
<p><img src="http://zuoqy.com/images/2018-10-06/7.png" alt="invest"> </p>
<p>持续的在设计上的小投入,最终换来巨大回报。</p>
<p><img src="http://zuoqy.com/images/2018-10-06/8.png" alt="invest"> </p>
<h2 id="参考">参考</h2><ul>
<li><a href="https://www.youtube.com/watch?v=bmSAYlu0NcY" target="_blank" rel="external">https://www.youtube.com/watch?v=bmSAYlu0NcY</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="软件设计的目标">软件设计的目标</h2><ol>
<li><p>软件设计最大的目标是降低软件的复杂性(Complexity),复杂性使软件难于理解和维护。</p>
</li>
<li><p>复杂性的来源:</p>
<ul>
<li>含义模糊(Obscurity): 重要信息不突出。</li>
<li>相互依赖:模块无法独立于其他模块而被理解。</li>
</ul>
</li>
<li><p>复杂性的危害</p>
</li>
</ol>
<p>复杂性会逐渐递增,前面埋的坑,会导致后面的设计越来越复杂。</p>
<h2 id="设计原则">设计原则</h2><h3 id="暴露简单通用的接口,隐藏复杂的实现。">暴露简单通用的接口,隐藏复杂的实现。</h3>]]>
</summary>
<category term="Software Design" scheme="http://zuoqy.com/tags/Software-Design/"/>
<category term="Software Design" scheme="http://zuoqy.com/categories/Software-Design/"/>
</entry>
<entry>
<title><![CDATA[Kubernetes学习笔记01: 一点历史]]></title>
<link href="http://zuoqy.com/2018/10/02/Kubernates1-Some-History/"/>
<id>http://zuoqy.com/2018/10/02/Kubernates1-Some-History/</id>
<published>2018-10-02T14:02:34.000Z</published>
<updated>2018-11-26T15:09:54.607Z</updated>
<content type="html"><![CDATA[<h2 id="1">1</h2><p>2013年,作为云计算PaaS热潮中的一份子,dotCloud相比于OpenStack、 Cloud Foundry、Heroku、Pivotal、RedHat似乎有些微不足道。</p>
<p>Cloud Foundry的开源PaaS项目,代表了当时PaaS技术的事实标准,被广泛接纳。它提供了一种”应用托管”能力,通过提供简单的命令,使开发者可以方便的将应用”上云”, 例如:<code>cf push "my_app"</code>其核心是应用的打包和分发。Cloud Foundry为每种主流语言都定义了一种打包格式, <code>cf push</code>的作用,就是将应用包、启动脚本上传到云端,通过调度器选择一批应用的虚拟机,<br>将脚本和应用包分发到机器上并启动执行。一个虚拟机上,往往启动多个来自不同用户的应用,PaaS平台会调用操作系统的Cgroups和Namespace机制,为每个应用单独创建隔离环境。<br>实现多个租户之间互不干涉的在虚拟机中部署和执行应用。整个过程就是PaaS项目最核心的能力。所谓的隔离环境,就是现在的容器技术。</p>
<p>在dotCloud公司开源自己的Docker项目之前,容器技术作为PaaS底层机制,并没有太多人关注。可就在开源后的短短几个月时间,Docker项目就迅速崛起,席卷了整个PaaS社区,改变了整个云计算领域的发展历程。</p>
<p>Docker项目区别于传统PaaS项目的秘密武器就是Docker镜像。如前面所述,当时PaaS项目的痛点就是用用的打包分发,用户需要为每一种语言、每一个应用维护一个打好的包,很难保持本地环境和云端环境的一致性。</p>
<p>Docker镜像的出现,从根本上解决了应用打包分发的问题:相比PaaS应用的压缩包只包含启动脚本加应用执行文件,Docker镜像则直接包含一个完整的操作系统所有文件和目录和应用启动的所有文件和脚本。轻松的保持了本地和云端环境的高度一致。<br>使用<code>docker build</code>、 <code>docker push</code> 和 <code>docker run</code> 即可将应用本地环境完整一致的打包推送到云端运行。</p>
<p>另外,Docker项目还对开发者有者天然的亲和力,开发者无需精通Linux内核原理和太多其他知识,就可以打包定制自己的镜像。简单的几个命令,就可以构建一个网站镜像,一个Nginx集群。深受开发者的欢迎。</p>
<p>Docker项目,重新定义了PaaS,使其演变为以Docker容器为核心,以Docker镜像为打包标准的”容器化”的PaaS。</p>
<h2 id="2">2</h2><p>随着Docker项目的崛起,dotCloud公司也将自己的公司名字改为Docker,并且不甘于仅仅提供创建和启停容器的小工具,希望提供更多平台层的能力,向PaaS进化。</p>
<p>2014年底,Docker公司发布Swarm,展示了Docker公司PaaS方向的野心。Swarm项目的最大亮点,就是它基本保持了单机部署和集群部署API的一致性,操作方式简单明了,受到众多开发者的热捧。(当时在公司的容器控制项目依赖Swarm也是基于这个原因)。</p>
<p>随后,Docker公司收购了率先提出”容器编排”(Container Orchestration)概念的<a href="http://www.fig.sh/" target="_blank" rel="external">Fig项目</a>(后来改名为Compose)。一时间,容器生态相关的项目层出不穷:容器网络处理SocketPlane项目(后被Docker公司收购),容器存储Flocker项目(后被EMC公司收购), 集群图形化管理Tutum项目(被Docker公司收购)等。</p>
<p>2014年6月,Google开源名为Kubernetes的项目,再一次改变了容器市场的格局。</p>
<h2 id="3">3</h2><p>为了限制Docker公司在Docker开源项目中的绝对话语权和强势态度,以及Docker项目在告诉迭代中表现出的不稳定和频繁变更问题,2015年6月,Docker公司、CoreOS、Google、RedHat共同宣布将Libcontainer(Containerd)项目捐出,改名为RunC,交由完全中立的基金会管理,并以其为依据,共同制定标准和规范,即OCI(Open Container Initiative). </p>
<p>然而OCI的成立,并没有改变Docker公司一家独大的现状。随后,Google、RedHat牵头发起名为CNCF(Cloud Native Computing Foundation)的基金会。希望以Kubernetes项目为基础,建立开源基础设施领域厂商主导的、按照独立基金会方式运营的平台级社区。</p>
<p>在容器编排方面,RedHat与Google结盟,打造出了一个与众不同的容器编排和管理生态。并在整个社区推进民主化架构。与Docker社区和Mesos社区形成三足鼎立局面。</p>
<p>2017年10月,DOcker公司在企业版Docker中内置Kubernetes,标志编排之争落下帷幕。</p>
<p>次年3月,Docker公司CTO Solomon Hykes宣布辞职,5年容器纷争尘埃落定。</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="1">1</h2><p>2013年,作为云计算PaaS热潮中的一份子,dotCloud相比于OpenStack、 Cloud Foundry、Heroku、Pivotal、RedHat似乎有些微不足道。</p>
<p>Cloud Foundry的开源PaaS项目,]]>
</summary>
<category term="kubernetes" scheme="http://zuoqy.com/tags/kubernetes/"/>
<category term="Cloud Compute" scheme="http://zuoqy.com/categories/Cloud-Compute/"/>
</entry>
<entry>
<title><![CDATA[关于回溯算法的几点心得]]></title>
<link href="http://zuoqy.com/2018/09/26/About-Backtracking/"/>
<id>http://zuoqy.com/2018/09/26/About-Backtracking/</id>
<published>2018-09-26T15:23:54.000Z</published>
<updated>2018-10-09T16:58:18.371Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:关于回溯算法的几点心得<br><strong>Abstract</strong>: Some inspirations about backtracking algorithms.</p>
<h2 id="回溯(Backtracking)算法思路:">回溯(Backtracking)算法思路:</h2><p>在当前局面下,你有若干种选择。逐一尝试每一种选择。<br>如果发现某种选择行不通(违反了某些限定条件)就返回;<br>如果某种选择试到最后发现是正确解,就将其加入解集。</p>
<blockquote>
<p>这里需要注意的是,为了能够回溯,多个选择都是从相同起点出发的,注意在同一层次下的多个选择结果之间不要相互影响。</p>
</blockquote>
<p>使用递归解决问题需要明确以下三点:<strong>选择 (Options)</strong>、<strong>限制 (Restraints)</strong> 和 <strong>结束条件 (Termination)</strong>。即“ORT原则”。</p>
<a id="more"></a>
<h2 id="例题">例题</h2><h3 id="例1:generate_parentheses">例1:<a href="https://leetcode.com/problems/generate-parentheses/" target="_blank" rel="external">generate parentheses</a></h3><p>Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.</p>
<p>For example, given n = 3, a solution set is:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> <span class="string">"((()))"</span>,</span><br><span class="line"> <span class="string">"(()())"</span>,</span><br><span class="line"> <span class="string">"(())()"</span>,</span><br><span class="line"> <span class="string">"()(())"</span>,</span><br><span class="line"> <span class="string">"()()()"</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>思路:</p>
<ul>
<li>选择:有两种选择:</li>
</ul>
<p>加左括号。<br>加右括号。</p>
<ul>
<li>限制:同时有以下限制:</li>
</ul>
<p>如果左括号已经用完了,则不能再加左括号了。<br>如果已经出现的右括号和左括号一样多,则不能再加右括号了。因为那样的话新加入的右括号一定无法匹配。</p>
<ul>
<li>结束条件: 左右括号都已经用完。</li>
</ul>
<p>结束后的正确性: 左右括号用完以后,一定是正确解。因为</p>
<ol>
<li>左右括号一样多</li>
<li>每个右括号都一定有与之配对的左括号。因此一旦结束就可以加入解集(有时也可能出现结束以后不一定是正确解的情况,这时要多一步判断)。</li>
</ol>
<p>递归函数传入参数: 限制和结束条件中有“用完”和“一样多”字样,因此你需要知道左右括号的数目,还有参数记录解集。</p>
<p>因此,把上面的思路拼起来就是代码:</p>
<figure class="highlight gcode"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="comment">(左右括号都已用完)</span> {</span><br><span class="line"> 加入解集,返回</span><br><span class="line">}</span><br><span class="line"><span class="comment">//否则开始试各种选择</span></span><br><span class="line"><span class="keyword">if</span> <span class="comment">(还有左括号可以用)</span> {</span><br><span class="line"> 加一个左括号,继续递归</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> <span class="comment">(右括号小于左括号)</span> {</span><br><span class="line"> 加一个右括号,继续递归</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Java代码如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> List<String> <span class="title">generateParenthesis</span><span class="params">(<span class="keyword">int</span> n)</span> </span>{</span><br><span class="line"> List<String> list = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> gen(list, <span class="string">""</span>, n, n);</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">gen</span><span class="params">(List<String> list, String s, <span class="keyword">int</span> leftCount, <span class="keyword">int</span> rightCount)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (leftCount == <span class="number">0</span> && rightCount == <span class="number">0</span>) {</span><br><span class="line"> list.add(s);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (leftCount > <span class="number">0</span>) {</span><br><span class="line"> gen(list, s + <span class="string">"("</span>, leftCount - <span class="number">1</span>, rightCount);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (rightCount > leftCount) {</span><br><span class="line"> gen(list, s + <span class="string">")"</span>, leftCount, rightCount - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="例2:_Letter_Combinations_of_a_Phone_Number">例2: <a href="https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/" target="_blank" rel="external">Letter Combinations of a Phone Number</a></h3><p>Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.</p>
<p>A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.</p>
<p>Input: “23”<br>Output: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].</p>
<p>思路:</p>
<ul>
<li>选择:当前数字上的所有letters,每个letter都是一个选择</li>
<li>限制:只能使用当前数字键上的letters,每次只能选一个</li>
<li>结束条件: 数字串结束</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] MAPPER = {<span class="string">"0"</span>, <span class="string">"1"</span>, <span class="string">"abc"</span>, <span class="string">"def"</span>, <span class="string">"ghi"</span>, <span class="string">"jkl"</span>, <span class="string">"mno"</span>, <span class="string">"qprs"</span>, <span class="string">"tuv"</span>, <span class="string">"wxyz"</span>};</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> List<String> <span class="title">letterCombinations</span><span class="params">(String digits)</span> </span>{</span><br><span class="line"> List<String> list = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> <span class="keyword">if</span> (digits == <span class="keyword">null</span> || digits.length() == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> }</span><br><span class="line"> combine(list, <span class="string">""</span>, digits, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">combine</span><span class="params">(List<String> list, String value, String digits, <span class="keyword">int</span> n)</span> </span>{</span><br><span class="line"> <span class="comment">// 结束条件</span></span><br><span class="line"> <span class="keyword">if</span> (n >= digits.length()) {</span><br><span class="line"> list.add(value);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 选择</span></span><br><span class="line"> String letters = MAPPER[digits.charAt(n) - <span class="string">'0'</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < letters.length(); i++) {</span><br><span class="line"> <span class="comment">// 限制</span></span><br><span class="line"> combine(list, value + letters.charAt(i), digits, n+<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="例3:_Combination_Sum">例3: <a href="https://leetcode.com/problems/combination-sum/description/" target="_blank" rel="external">Combination Sum</a></h3><p>Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.</p>
<p>The same repeated number may be chosen from candidates unlimited number of times.</p>
<p>Note:</p>
<p>All numbers (including target) will be positive integers.<br>The solution set must not contain duplicate combinations.<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Example 1: Input: candidates = [2,3,6,7], target = 7, A solution set is: [ [7], [2,2,3] ] Example 2: Input: candidates = [2,3,5], target = 8, A solution set is: [ [2,2,2,2], [2,3,3], [3,5] ]</span><br></pre></td></tr></table></figure></p>
<p>思路:</p>
<ul>
<li>选择:所有candidates中的一个</li>
<li>限制: 当前任何情况下,list中元素的和需要小于target</li>
<li>结束条件: 当前list中的元素和等于target</li>
</ul>
<p>需要注意: </p>
<ul>
<li>结果去重, 引入index解决</li>
<li>list副本在回溯过程中不应该相互影响</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> List<List<Integer>> combinationSum(<span class="keyword">int</span>[] candidates, <span class="keyword">int</span> target) {</span><br><span class="line"> List<List<Integer>> results = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> <span class="keyword">if</span> (candidates == <span class="keyword">null</span> || candidates.length == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> results;</span><br><span class="line"> }</span><br><span class="line"> Arrays.sort(candidates);</span><br><span class="line"> combinations(results, <span class="keyword">new</span> ArrayList<>(), candidates, target, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> results;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">combinations</span><span class="params">(List<List<Integer>> results, List<Integer> list, <span class="keyword">int</span>[] candidates, <span class="keyword">int</span> target, <span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> s = sum(list);</span><br><span class="line"> <span class="keyword">if</span> (s == target) {</span><br><span class="line"> results.add(copyOf(list));</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (s > target) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = index; i < candidates.length; i++) {</span><br><span class="line"> list.add(candidates[i]);</span><br><span class="line"> combinations(results, list, candidates, target, i);</span><br><span class="line"> list.remove(list.size() - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">sum</span><span class="params">(List<Integer> list)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (list == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (Integer i : list) {</span><br><span class="line"> sum += i;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> List<Integer> <span class="title">copyOf</span><span class="params">(List<Integer> list)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ArrayList(list);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="例4_Permutations">例4 <a href="https://leetcode.com/problems/permutations/description/" target="_blank" rel="external">Permutations</a></h3><p>Given a collection of distinct integers, return all possible permutations.</p>
<p>Example:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Input: [1,2,3] Output: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]</span><br></pre></td></tr></table></figure>
<p>思路:</p>
<ul>
<li>选择:余下的nums中的一个</li>
<li>限制:不重复的使用nums中的数字</li>
<li>结束条件:当前list长度等于nums个数</li>
</ul>
<p>Java代码如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> List<List<Integer>> permute(<span class="keyword">int</span>[] nums) {</span><br><span class="line"> List<List<Integer>> resultList = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> permute(resultList, nums, <span class="keyword">new</span> LinkedList(), <span class="keyword">new</span> <span class="keyword">boolean</span>[nums.length]);</span><br><span class="line"> <span class="keyword">return</span> resultList;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">permute</span><span class="params">(List<List<Integer>> resultList, <span class="keyword">int</span>[] nums, List<Integer> current, <span class="keyword">boolean</span>[] visited)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (current.size() == nums.length) {</span><br><span class="line"> resultList.add(copyOf(current));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < nums.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (visited[i]) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> current.add(nums[i]);</span><br><span class="line"> visited[i] = <span class="keyword">true</span>;</span><br><span class="line"> permute(resultList, nums, current, visited);</span><br><span class="line"> current.remove(current.size() -<span class="number">1</span>);</span><br><span class="line"> visited[i] = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> List<Integer> <span class="title">copyOf</span><span class="params">(List<Integer> list)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ArrayList<>(list);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="例5_N-Queens">例5 <a href="https://leetcode.com/problems/n-queens/description/" target="_blank" rel="external">N-Queens</a></h3><p>The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.</p>
<p>Given an integer n, return all distinct solutions to the n-queens puzzle.</p>
<p>Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space respectively.</p>
<p>Example:<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Input: 4 Output: [ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ] Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.</span><br></pre></td></tr></table></figure></p>
<p>思路:</p>
<ul>
<li>选择: 当前行中的任意一个不与前面所有行上皇后冲突的列</li>
<li>限制:横、竖、斜 都不能有两个皇后在同一线上 条件:x1 != x2 && y1 != y2 && |x1 - x2| != |y1 - y2|</li>
<li>结束条件:所有行上都放了皇后</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> List<List<String>> solveNQueens(<span class="keyword">int</span> n) {</span><br><span class="line"> <span class="keyword">boolean</span>[][] board = <span class="keyword">new</span> <span class="keyword">boolean</span>[n][n];</span><br><span class="line"> List<List<String>> results= <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> solve(results, board, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> results;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">solve</span><span class="params">(List<List<String>> results, <span class="keyword">boolean</span>[][] board, <span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (index == board.length) {</span><br><span class="line"> results.add(build(board));</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < board.length; i++) { <span class="comment">// 对于当前行</span></span><br><span class="line"> <span class="keyword">if</span> (valid(i, index, board)) { <span class="comment">// 可放皇后则继续搜索</span></span><br><span class="line"> board[index][i] = <span class="keyword">true</span>;</span><br><span class="line"> solve(results, board, index + <span class="number">1</span>);</span><br><span class="line"> board[index][i] = <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">valid</span><span class="params">(<span class="keyword">int</span> x1, <span class="keyword">int</span> y1, <span class="keyword">boolean</span>[][] board)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> y2 = <span class="number">0</span>; y2 < y1; y2++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> x2 = <span class="number">0</span>; x2 < board.length; x2++) {</span><br><span class="line"> <span class="keyword">if</span> (board[y2][x2] && (x1 == x2 || y1 == y2 || Math.abs(x1-x2) == Math.abs(y1 - y2))) { <span class="comment">// heng</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> List<String> <span class="title">build</span><span class="params">(<span class="keyword">boolean</span>[][] board)</span> </span>{</span><br><span class="line"> List<String> list = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < board.length; i++) {</span><br><span class="line"> StringBuilder sb = <span class="keyword">new</span> StringBuilder();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < board.length; j++) {</span><br><span class="line"> <span class="keyword">if</span> (board[i][j]) {</span><br><span class="line"> sb.append(<span class="string">"Q"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> sb.append(<span class="string">"."</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> list.add(sb.toString());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> list;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="参考:">参考:</h2><ul>
<li><a href="http://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=172641&page=1#pid2237150" target="_blank" rel="external">http://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=172641&page=1#pid2237150</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:关于回溯算法的几点心得<br><strong>Abstract</strong>: Some inspirations about backtracking algorithms.</p>
<h2 id="回溯(Backtracking)算法思路:">回溯(Backtracking)算法思路:</h2><p>在当前局面下,你有若干种选择。逐一尝试每一种选择。<br>如果发现某种选择行不通(违反了某些限定条件)就返回;<br>如果某种选择试到最后发现是正确解,就将其加入解集。</p>
<blockquote>
<p>这里需要注意的是,为了能够回溯,多个选择都是从相同起点出发的,注意在同一层次下的多个选择结果之间不要相互影响。</p>
</blockquote>
<p>使用递归解决问题需要明确以下三点:<strong>选择 (Options)</strong>、<strong>限制 (Restraints)</strong> 和 <strong>结束条件 (Termination)</strong>。即“ORT原则”。</p>]]>
</summary>
<category term="algorithm" scheme="http://zuoqy.com/tags/algorithm/"/>
<category term="backtracking" scheme="http://zuoqy.com/tags/backtracking/"/>
</entry>
<entry>
<title><![CDATA[kotlin01 基本语法]]></title>
<link href="http://zuoqy.com/2018/04/19/kotlin01-%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95/"/>
<id>http://zuoqy.com/2018/04/19/kotlin01-基本语法/</id>
<published>2018-04-19T13:57:52.000Z</published>
<updated>2018-04-19T14:13:32.082Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:kotlin基本语法<br><strong>Abstract</strong>: kotlin basic syntax</p>
<h2 id="定义package">定义package</h2><p>包定义需要在文件开头,不需要和文件系统路径一致。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> my.demo</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.*</span><br></pre></td></tr></table></figure>
<h2 id="定义function">定义function</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span>: Int {</span></span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>函数可以是一个表达式,返回类型由编译器推断:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span> = a + b</span></span><br></pre></td></tr></table></figure>
<p>无返回值的函数返回<code>Unit</code>对象。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">printSum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span>: Unit {</span></span><br><span class="line"> println(<span class="string">"sum of $a and $b is ${a + b}"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Unit可以被省略。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">printSum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span> {</span></span><br><span class="line"> println(<span class="string">"sum of $a and $b is ${a + b}"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="定义变量(Variables)">定义变量(Variables)</h2><p>只读变量:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable"><span class="keyword">val</span> a</span>: <span class="typename">Int</span> = <span class="number">1</span> <span class="comment">// 定义变量时复制</span></span><br><span class="line"><span class="variable"><span class="keyword">val</span> b</span> = <span class="number">2</span> <span class="comment">// 类型推断:`Int`</span></span><br><span class="line"><span class="variable"><span class="keyword">val</span> c</span>: <span class="typename">Int</span> <span class="comment">// 没有立即赋值需要显式指定变量类型</span></span><br><span class="line">c = <span class="number">3</span> <span class="comment">// deferred assignment</span></span><br></pre></td></tr></table></figure>
<p>可变变量:</p>
<figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="reserved">var</span> x = <span class="number">5</span> // `<span class="javascript">Int</span>` type <span class="keyword">is</span> inferred</span><br><span class="line">x += <span class="number">1</span></span><br></pre></td></tr></table></figure>
<h2 id="注释(Comments)">注释(Comments)</h2><p>kotlin的注释可以嵌套</p>
<figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 单行注释</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 块注释,</span><br><span class="line">可以包含多行 */</span></span><br></pre></td></tr></table></figure>
<h2 id="字符串模板(string_templates)">字符串模板(string templates)</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable"><span class="keyword">var</span> a</span> = <span class="number">1</span></span><br><span class="line"><span class="comment">// 简单变量模板:</span></span><br><span class="line"><span class="variable"><span class="keyword">val</span> s1</span> = <span class="string">"a is $a"</span> </span><br><span class="line"></span><br><span class="line">a = <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 模板中包含表达式</span></span><br><span class="line"><span class="variable"><span class="keyword">val</span> s2</span> = <span class="string">"${s1.replace("</span><span class="keyword">is</span><span class="string">", "</span>was<span class="string">")}, but now is $a"</span></span><br></pre></td></tr></table></figure>
<h2 id="条件表达式(conditional_expressions)">条件表达式(conditional expressions)</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">maxOf</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span>: Int {</span></span><br><span class="line"> <span class="keyword">if</span> (a > b) {</span><br><span class="line"> <span class="keyword">return</span> a</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> b</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fun <span class="function"><span class="title">maxOf</span><span class="params">(a: Int, b: Int)</span></span> = <span class="keyword">if</span> (<span class="tag">a</span> > b) <span class="tag">a</span> <span class="keyword">else</span> b</span><br></pre></td></tr></table></figure>
<h2 id="检查null">检查null</h2><p>如果函数返回null,必须显式在返回值类型后面加上<code>?</code></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">parseInt</span><span class="params">(str: <span class="typename">String</span>)</span>: Int? {</span></span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">printProduct</span><span class="params">(arg1: <span class="typename">String, arg2: String</span>)</span> {</span></span><br><span class="line"> <span class="variable"><span class="keyword">val</span> x</span> = parseInt(arg1)</span><br><span class="line"> <span class="variable"><span class="keyword">val</span> y</span> = parseInt(arg2)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 直接计算 `x * y` 会产生编译错误,因为x和y可能是null</span></span><br><span class="line"> <span class="keyword">if</span> (x != <span class="literal">null</span> && y != <span class="literal">null</span>) {</span><br><span class="line"> <span class="comment">// x 和 y 检查null之后会自动转型成非空类型(non-nullable)</span></span><br><span class="line"> println(x * y)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> println(<span class="string">"either '$arg1' or '$arg2' is not a number"</span>)</span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="类型检查和自动转型">类型检查和自动转型</h2><p><code>is</code>操作符可以用来检查对象是否属于某种类型,非可变本地变量或者属性在做完类型检查之后,会在分支内自动转成目标类型。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getStringLength</span><span class="params">(obj: <span class="typename">Any</span>)</span>: Int? {</span></span><br><span class="line"> <span class="keyword">if</span> (obj <span class="keyword">is</span> String) {</span><br><span class="line"> <span class="comment">// `obj` 自动转型成 `String`</span></span><br><span class="line"> <span class="keyword">return</span> obj.length</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// `obj` 类型在这里是`Any`</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>或者</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getStringLength</span><span class="params">(obj: <span class="typename">Any</span>)</span>: Int? {</span></span><br><span class="line"> <span class="keyword">if</span> (obj !<span class="keyword">is</span> String) <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// `obj` 自动转型成 `String`</span></span><br><span class="line"> <span class="keyword">return</span> obj.length</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>甚至</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getStringLength</span><span class="params">(obj: <span class="typename">Any</span>)</span>: Int? {</span></span><br><span class="line"> <span class="comment">// `obj` 在 `&&` 操作符的右侧被自动转型成 `String`类型</span></span><br><span class="line"> <span class="keyword">if</span> (obj <span class="keyword">is</span> String && obj.length > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> obj.length</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="for循环">for循环</h2><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">val <span class="keyword">items</span> = listOf(<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"kiwifruit"</span>)</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">item</span> <span class="operator">in</span> <span class="keyword">items</span>) {</span><br><span class="line"> println(<span class="keyword">item</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>或者</p>
<figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">val items = listOf(<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"kiwifruit"</span>)</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">index</span> in items.indices) {</span><br><span class="line"> println(<span class="string">"item at <span class="variable">$index</span> is <span class="subst">${items[<span class="keyword">index</span>]}</span>"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="while循环">while循环</h2><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">val items = listOf(<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"kiwifruit"</span>)</span><br><span class="line">var <span class="keyword">index</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> (<span class="keyword">index</span> < items.size) {</span><br><span class="line"> println(<span class="string">"item at <span class="variable">$index</span> is <span class="subst">${items[<span class="keyword">index</span>]}</span>"</span>)</span><br><span class="line"> <span class="keyword">index</span>++</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="when_表达式">when 表达式</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">describe</span><span class="params">(obj: <span class="typename">Any</span>)</span>: String =</span></span><br><span class="line"><span class="keyword">when</span> (obj) {</span><br><span class="line"> <span class="number">1</span> -> <span class="string">"One"</span></span><br><span class="line"> <span class="string">"Hello"</span> -> <span class="string">"Greeting"</span></span><br><span class="line"> <span class="keyword">is</span> <span class="typename">Long</span> -> <span class="string">"Long"</span></span><br><span class="line"> !<span class="keyword">is</span> String -> <span class="string">"Not a string"</span></span><br><span class="line"> <span class="keyword">else</span> -> <span class="string">"Unknown"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="range">range</h2><p>使用<code>in</code>操作符来判断变量是否在范围内, <code>!in</code>判断是否在范围外</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable"><span class="keyword">val</span> x</span> = <span class="number">10</span></span><br><span class="line"><span class="variable"><span class="keyword">val</span> y</span> = <span class="number">9</span></span><br><span class="line"><span class="keyword">if</span> (x <span class="keyword">in</span> <span class="number">1.</span>.y+<span class="number">1</span>) {</span><br><span class="line"> println(<span class="string">"fits in range"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> <span class="built_in">list</span> = listOf(<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (-<span class="number">1</span> !<span class="keyword">in</span> <span class="number">0.</span>.<span class="built_in">list</span>.lastIndex) {</span><br><span class="line"> println(<span class="string">"-1 is out of range"</span>)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">list</span>.size !<span class="keyword">in</span> <span class="built_in">list</span>.indices) {</span><br><span class="line"> println(<span class="string">"list size is out of valid list indices range too"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>迭代</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (x <span class="keyword">in</span> <span class="number">1</span>..<span class="number">5</span>) {</span><br><span class="line"> <span class="built_in">print</span>(x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>设置迭代步长</p>
<p>for (x in 1..10 step 2) {<br> print(x)<br>}<br>println()<br>for (x in 9 downTo 0 step 3) {<br> print(x)<br>}</p>
<h2 id="使用集合类">使用集合类</h2><p>迭代</p>
<figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">item</span> <span class="operator">in</span> <span class="keyword">items</span>) {</span><br><span class="line"> println(<span class="keyword">item</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>判断元素是否存在于集合中<br><figure class="highlight elixir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">when</span> {</span><br><span class="line"> <span class="string">"orange"</span> <span class="keyword">in</span> items -> println(<span class="string">"juicy"</span>)</span><br><span class="line"> <span class="string">"apple"</span> <span class="keyword">in</span> items -> println(<span class="string">"apple is fine too"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>fitler and map</p>
<figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">fruits</span><br><span class="line">.<span class="built_in">filter</span> { <span class="keyword">it</span>.startsWith(<span class="string">"a"</span>) }</span><br><span class="line">.sortedBy { <span class="keyword">it</span> }</span><br><span class="line">.map { <span class="keyword">it</span>.toUpperCase() }</span><br><span class="line">.forEach { println(<span class="keyword">it</span>) }</span><br></pre></td></tr></table></figure>
<h2 id="参考">参考</h2><ol>
<li><a href="https://kotlinlang.org/docs/reference/basic-syntax.html" target="_blank" rel="external">https://kotlinlang.org/docs/reference/basic-syntax.html</a></li>
</ol>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:kotlin基本语法<br><strong>Abstract</strong>: kotlin basic syntax</p>
<h2 id="定义package">定义package</h2><p>包定义需要在文件开头,不需要和文件系统路径一致。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> my.demo</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.*</span><br></pre></td></tr></table></figure>
<h2 id="定义function">定义function</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span>: Int {</span></span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>函数可以是一个表达式,返回类型由编译器推断:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span> = a + b</span></span><br></pre></td></tr></table></figure>
<p>无返回值的函数返回<code>Unit</code>对象。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">printSum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span>: Unit {</span></span><br><span class="line"> println(<span class="string">"sum of $a and $b is ${a + b}"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Unit可以被省略。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">printSum</span><span class="params">(a: <span class="typename">Int, b: Int</span>)</span> {</span></span><br><span class="line"> println(<span class="string">"sum of $a and $b is ${a + b}"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]>
</summary>
<category term="kotlin" scheme="http://zuoqy.com/tags/kotlin/"/>
</entry>
<entry>
<title><![CDATA[matplotlib unicode]]></title>
<link href="http://zuoqy.com/2017/03/21/matplotlib-unicode/"/>
<id>http://zuoqy.com/2017/03/21/matplotlib-unicode/</id>
<published>2017-03-21T07:05:11.000Z</published>
<updated>2018-09-26T15:22:33.368Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:解决matplotlib中文乱码的问题<br><strong>Abstract</strong>:How to plot unicode using matplotlib<br><a id="more"></a></p>
<h2 id="方法一">方法一</h2><p>在plot之前加上如下代码:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pylab <span class="keyword">import</span> mpl</span><br><span class="line">mpl.rcParams[<span class="string">'font.sans-serif'</span>] = [<span class="string">'FangSong'</span>] <span class="comment"># 指定默认字体</span></span><br><span class="line">mpl.rcParams[<span class="string">'axes.unicode_minus'</span>] = <span class="keyword">False</span> <span class="comment"># 解决保存图像是负号'-'显示为方块的问题</span></span><br></pre></td></tr></table></figure>
<p>方法的优点是不需要改动任何配置文件,缺点是每次都要加上这样的额外代码。</p>
<h2 id="方法二">方法二</h2><p>找到matplotlib 所在的安装目录,在mac下,使用anaconda2时,所在路径为:<code>~/anaconda2/pkgs/matplotlib-2.0.0-np111py36_0/lib/python3.6/site-packages/matplotlib/mpl-data</code><br>然后copy其中的matplotlibrc 文件到用户目录下:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp matplotlibrc ~/.matplotlib/</span><br></pre></td></tr></table></figure>
<p>修改其中的:<code>font.sans-serif</code> 加上支持中文的字体,修改<code>axes.unicode_minus</code>为<code>False</code><br>保存之后,删除<code>~/matplotlib/</code>下的<code>fontList.py3k.cache</code>字体缓存,重新加载程序即可。如果系统没有所选的字体,需要把字体文件拷贝到<code>~/anaconda2/pkgs/matplotlib-2.0.0-np111py36_0/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf</code>。</p>
<p>此方法的有点就是全局生效,对所有依赖该类库的程序都可以在plot中显示unicode字符。</p>
<p>验证:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">x = np.linspace(<span class="number">0</span>, <span class="number">3</span>*np.pi, <span class="number">500</span>)</span><br><span class="line">plt.plot(x, np.sin(x**<span class="number">2</span>))</span><br><span class="line">plt.title(<span class="string">'中文'</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure>
<p><img src="http://zuoqy.com/images/2017-03-21/1.png"></p>
<blockquote>
<p>以上方法可以解决包括nltk在内的所有用到matplotlib而不能打印中文的问题。</p>
</blockquote>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:解决matplotlib中文乱码的问题<br><strong>Abstract</strong>:How to plot unicode using matplotlib<br>]]>
</summary>
<category term="matplotlib" scheme="http://zuoqy.com/tags/matplotlib/"/>
<category term="nltk" scheme="http://zuoqy.com/tags/nltk/"/>
</entry>
<entry>
<title><![CDATA[2016——和时间赛跑的我]]></title>
<link href="http://zuoqy.com/2016/12/31/oh-my-2016/"/>
<id>http://zuoqy.com/2016/12/31/oh-my-2016/</id>
<published>2016-12-31T09:38:07.000Z</published>
<updated>2017-03-21T07:05:40.000Z</updated>
<content type="html"><![CDATA[<p>新年临近,转眼间已经是2016年的最后一天了。抓紧这最后的几个小时,跟风写个总结。</p>
<p>如果说要我2016年的关键词,那我想就想题目那样,和时间赛跑。从2011年来到北京,到现在已经有五年时间了。想起来北京上学之前,和长辈们、朋友们聊天,总会说到,北京的生活节奏很快blabla,我一直没有什么深刻的体会。如今我挤在早班地铁里,或者9点多下班打车回家的路上,有时想起那些话,心里琢磨难道这就是所谓的”生活节奏快吗”?只是我不知不觉地习惯了这样的”节奏”,很少有时间静下心来回顾和反思。李笑来在其公众号文章中曾说”慢即是快,快即是无”。我认为不光是读书是这样,一味的”快”,缺乏精益和反思,和懒惰一样可怕,整个人很容易被这样的节奏淹没。<br>2016年,我的最大敌人就是时间。繁忙的工作让我更加珍惜时间,深感时间流逝太快。人的年龄越大,越感到时间变快,可能是因为对于一个3岁小孩,他的一天占到他人生的一千分之一,一年占到他人生的三分之一,而对于一个70岁的老人,他的一天占到人生的两万分之一<code>[1]</code>。和时间赛跑,就是在和这样的节奏抗争,让我能继续保持进取心、继续保持好奇心、继续我的业余爱好,从看似重复的工作和生活中获取新鲜的东西。<br><a id="more"></a></p>
<ol>
<li>工作</li>
</ol>
<p>毕业之后便加入阿里巴巴,算上实习,已经有三年半的时间。虽然平时工作很忙,但很庆幸当时选择了互联网这个行业,没错过这个飞速发展时代。科技发展日新月异,互联网的发展最为抢眼,人们的生活方式也正在被互联网改变:人工智能、互联网汽车、共享单车、直播、VR正在影响千千万万普通的家庭。互联网改变了人们的出行方式、支付方式、娱乐方式,时常因为自己身处浪潮之中并有幸能贡献微薄之力而感到兴奋。用一个词去概括过去一年的工作,我想就是”变化”。互联网行业发展瞬息万变,要做102年的阿里巴巴,有一个很重要的理念,就是”拥抱变化”。在阿里巴巴的1000多天中,我已经经历了很多次业务变化。每一次变化都有”涅槃重生”的感觉,每经历一次都更加强。从开始的迷茫和愤愤,到现在能快速的调整和适应,内心更加强大,专注力更强,对业务的理解更快速。变化让我接触到更多项目的初创和死亡,遇到了更多在大公司中业务发展各阶段的各种问题和困难。有句话说的一点都不错,过的不那么”舒服”的时候,才是进步最快的时候。2016年经历了两次大的业务调整和变化,有幸接触到智能交互服务领域。2016年,各大互联网公司争相推出自己的智能语音交互服务,Google发布了Google Home、亚马逊发布了Echo、微软也推出小冰……身在其中,才感受到了这个领域所面临的各种挑战,而我又这么喜欢挑战。</p>
<ol>
<li>阅读</li>
</ol>
<p>读书占去了我大部分的业余时间。从2015年底开始到现在这一年多的时间里,我总共读过22本书。还剩着一堆进度为40%-50%的烂尾书没有读完(实在惭愧)。完成的书单按照时间顺序排列分别是:《重来》、《曾国藩的正面与侧面》、《极客与团队》、《HBase in Action》、《支付战争》、《文学回忆录》、《量子理论:爱因斯坦与波尔关于世界本质的伟大论战》、《一万小时天才理论》、《把你的英语用起来》、《失控》、《七周七并发模型》、《巴菲特之道》、《黑客与画家》、《第一本Docker书》、《囚徒健身》、《Java 8函数式编程》、《精进》、《Java性能优化权威指南》、《我最想要的记忆魔法书》、《神奇的眼脑直映快读法》、《激荡三十年(上)中国企业1978-2008》、《刻意练习》。书单还算比较多样,什么类型的都有,甚至还有文学类<code>- - !</code>。现在看着这个书单就像是一个日历摆在我面前,回忆书中的内容,总会想到写看书时所经历的事情。比如其中的技术书完全依据业务的需要,有些书则是在杭州出差期间,深夜的宾馆里静静看完的。读书的感觉很好,可以让浮躁的心安静下来,如同在时空中畅游,了解自己未知的领域、和作者对话。</p>
<ol>
<li>旅行</li>
</ol>
<p>读万卷书,行万里路。年初难得的两周假期,我和妻子去了美国旅行,期间从东岸飞到西岸,从纽约到旧金山,还去了夏威夷。在美国的两周多时间,我的身心的到了彻底的放松,在纽约时去了大都会博物馆、自然历史博物馆、帝国大厦、洛克菲勒、时代广场、中央公园和自由女神。被一个短短几百年时间建设起来的国家的文化积淀深深的震撼到了。自然环境、人文环境和文化气息,每每想起都感觉那么不真实,感觉我们的祖国还有很长很长的路要走啊。在旧金山,我们拜访了我向往的斯坦福大学,在金门大桥上徒步走过,迟到了渔人码头的海鲜大餐(感谢小伙伴在旧金山的招待)。在夏威夷,我们去过Maui和Honolulu两个岛,干净的沙滩和海水让我们流连忘返。</p>
<ol>
<li>写作</li>
</ol>
<p>2016年,我总共写了7篇博客。博客和微信公众号改变了我对写作的看法。原来越觉得一个人的能力在很大程度上体现在其阅读理解和写作上。同样的文字,不同人的理解程度千差万别,读过不一定全部理解,全部理解不一定能用自己的话写出来,能写出来,不一定能在Presentation中流畅的讲出来。而很多重要的机会都是看演讲和写作中能表达多少,你比别人多读了多少,多理解了多少。个人认为自己在写作方面有待提高,而且没有太多时间投入到写作上。这可能是和我在中学时代严重偏向理科导致的。其实,无论身处什么行业,偏爱什么学科,都离不开阅读理解和写作。</p>
<ol>
<li>业余爱好</li>
</ol>
<p>还好,我还有个业余爱好一直没有放弃。虽然能弹吉他的时间原来越少,但总有那么一些时间练习。音乐和阅读一样美妙,让大脑得到另一种形式的锻炼和休息。学习一样乐器的乐趣很多,吉他让我有机会真正去想了解音乐以及一些乐理知识。在中学时代,音乐课其实教授了不少有用的知识,知识当时不够重视。而有些能力比如音阶调式的听力、乐感等如果幼年时没有得到足够的锻炼,一旦成年是很难再去补救的<code>[2]</code>。在阅读《一万小时天才理论》和《精进》等书时从中获得的学习方法用在音乐中同样得到了良好的效果,避免无效练习之后,我节省了更多的时间获得真正的进步,也有了更多的成就感和乐趣。</p>
<ol>
<li>健身</li>
</ol>
<p>身体是一切的前提,工作后运动减少,让我开始了解健身知识,增加在健身方面投入。每周有一天少加半小时一小时班跑去健身房,回报就是身体状态和工作效率的提高。除了让大脑时刻保持良好的状态,让身体得到足够的锻炼是保持健康状态的不错选择。<em>种一棵树最好的时间是十年前,其次是现在。</em>不要等身体出了问题再去补救。从办健身卡到现在,坚持的还算不错,基本保持着一周去两次的状态。坚持健身也增强了我的意志力和身体状态,能够更好的投入工作和生活。</p>
<ol>
<li>公开课</li>
</ol>
<p>2016年,我业余时间完成了一门Coursera公开课的自修,并拿到证书。相比之前有所减少,主要是我的时间越来越少,很少有大块的时间用来完成课程作业了。两年前,我得益于住在公司附近,用大量早上早起的时间选修了人工智能和机器学习方面的课程,它们后来在我的工作中起到了很关键的作用并改变了我对好多事情的看法。</p>
<p>最后,感谢我的家人和我的妻子对我的支持和理解。在新的一年,希望家人和朋友们身体健康,特别祝愿我九十岁的姥姥身体健康。工作顺利,每天都有新的进步!</p>
<p>2016年12月31日<br>于石家庄</p>
<h2 id="参考">参考</h2><ul>
<li><code>[1]</code> 《把时间当做朋友》</li>
<li><code>[2]</code> 《一万小时天才理论》《精进》《刻意练习》</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>新年临近,转眼间已经是2016年的最后一天了。抓紧这最后的几个小时,跟风写个总结。</p>
<p>如果说要我2016年的关键词,那我想就想题目那样,和时间赛跑。从2011年来到北京,到现在已经有五年时间了。想起来北京上学之前,和长辈们、朋友们聊天,总会说到,北京的生活节奏很快blabla,我一直没有什么深刻的体会。如今我挤在早班地铁里,或者9点多下班打车回家的路上,有时想起那些话,心里琢磨难道这就是所谓的”生活节奏快吗”?只是我不知不觉地习惯了这样的”节奏”,很少有时间静下心来回顾和反思。李笑来在其公众号文章中曾说”慢即是快,快即是无”。我认为不光是读书是这样,一味的”快”,缺乏精益和反思,和懒惰一样可怕,整个人很容易被这样的节奏淹没。<br>2016年,我的最大敌人就是时间。繁忙的工作让我更加珍惜时间,深感时间流逝太快。人的年龄越大,越感到时间变快,可能是因为对于一个3岁小孩,他的一天占到他人生的一千分之一,一年占到他人生的三分之一,而对于一个70岁的老人,他的一天占到人生的两万分之一<code>[1]</code>。和时间赛跑,就是在和这样的节奏抗争,让我能继续保持进取心、继续保持好奇心、继续我的业余爱好,从看似重复的工作和生活中获取新鲜的东西。<br>]]>
</summary>
<category term="2016" scheme="http://zuoqy.com/tags/2016/"/>
<category term="summary" scheme="http://zuoqy.com/tags/summary/"/>
<category term="summary" scheme="http://zuoqy.com/categories/summary/"/>
</entry>
<entry>
<title><![CDATA[Median of Two Sorted Arrays]]></title>
<link href="http://zuoqy.com/2016/12/03/Median-of-Two-Sorted-Arrays/"/>
<id>http://zuoqy.com/2016/12/03/Median-of-Two-Sorted-Arrays/</id>
<published>2016-12-03T10:48:58.000Z</published>
<updated>2016-12-03T13:05:35.000Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:两个有序数组的中位数<br><strong>Abstract</strong>: Median of Two Sorted Arrays<br>最近遇到一个有意思的题,求两个有序数组的中位数,亲自做了一下发现坑很多,除了二分查找的思想运用的很巧妙,还有各种边界条件把我搞得很崩溃。于是记录下来。<br><a id="more"></a><br>首先,题目是这样的:<br>有两个有序数组,长度分别是m和n,找到两个数组的中位数,要求时间复杂度是<code>O(log (m+n))</code></p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Example</span> 1:</span><br><span class="line"><span class="keyword">nums1</span> =<span class="sqbracket"> [1, 3]</span></span><br><span class="line"><span class="keyword">nums2</span> =<span class="sqbracket"> [2]</span></span><br><span class="line"><span class="keyword">The</span> median is 2.0</span><br><span class="line"></span><br><span class="line"><span class="keyword">Example</span> 2:</span><br><span class="line"><span class="keyword">nums1</span> =<span class="sqbracket"> [1, 2]</span></span><br><span class="line"><span class="keyword">nums2</span> =<span class="sqbracket"> [3, 4]</span></span><br><span class="line"><span class="keyword">The</span> median is (2 + 3)/2 = 2.5</span><br></pre></td></tr></table></figure>
<p><code>O(m+n)</code>的解法就是一趟归并排序然后再取中位数,比较简单。<code>O(log(m+n))</code>的解法需要用到二分的思想。<br>假设<code>i</code>,<code>j</code>将数组<code>A</code>,<code>B</code>分为左右两部分:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">left_part | right_part A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]</span><br></pre></td></tr></table></figure>
<p>如果保持左右两部分数量相同,则<code>i</code>和<code>j</code>:的关系如下:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i + j = (m - i) + (n - j) ==> i + j = (m + n) / 2 ==> j = (m + n) / 2 - i</span><br></pre></td></tr></table></figure>
<p>即:我们只需要找到<code>i</code>,<code>j</code>满足:<code>i + j = (m - i) + (n - j)</code>且<code>max(left_part) <= min(right_part)</code>即可通过:<code>A[i-1]、A[i]、B[j-1]、B[j]</code>计算出中位数:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">median = (max(left_part) + min(right_part))/2</span><br></pre></td></tr></table></figure>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在A数组中二分查找,当前元素index = i: 1)当A[i - 1] > B[j],说明i过大,要找的i在A[0...i-1]中; 2)当B[j - 1] > A[i],说明i过小,要找的i在A[i+1...m]中; 循环1)2)直到找到符合条件的i和j</span><br></pre></td></tr></table></figure>
<p>以上就是这个算法的主要思想,当然有这些还不够,还需要注意<code>i = 0</code>、<code>i = m</code>、<code>j = 0</code>、<code>j = n</code>和空数组<code>[]</code>等边界情况。</p>
<p>具体代码如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">findMedianSortedArrays</span><span class="params">(<span class="keyword">int</span>[] A, <span class="keyword">int</span>[] B)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> m = A.length;</span><br><span class="line"> <span class="keyword">int</span> n = B.length;</span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> right = m;</span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span> (m > n) {</span><br><span class="line"> <span class="keyword">return</span> findMedianSortedArrays(B, A);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (m == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> (B[n/<span class="number">2</span>] + B[(n - <span class="number">1</span>) / <span class="number">2</span>])/<span class="number">2.0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (left <= right) {</span><br><span class="line"> i = (left + right) / <span class="number">2</span>;</span><br><span class="line"> j = (m + n) / <span class="number">2</span> - i;</span><br><span class="line"> <span class="keyword">if</span> (i > <span class="number">0</span> && j < n && A[i - <span class="number">1</span>] > B[j]) { <span class="comment">// i > 0 ==> j < n</span></span><br><span class="line"> right = i - <span class="number">1</span>;</span><br><span class="line"> } <span class="function"><span class="keyword">else</span> <span class="title">if</span> <span class="params">(j > <span class="number">0</span> && i < m && B[j - <span class="number">1</span>] > A[i])</span> </span>{ <span class="comment">// j > 0 ==> i < m</span></span><br><span class="line"> left = i + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> leftMax, rightMin;</span><br><span class="line"> <span class="keyword">if</span> (i == <span class="number">0</span>) {</span><br><span class="line"> leftMax = B[j-<span class="number">1</span>];</span><br><span class="line"> } <span class="function"><span class="keyword">else</span> <span class="title">if</span> <span class="params">(j == <span class="number">0</span>)</span> </span>{</span><br><span class="line"> leftMax = A[i - <span class="number">1</span>];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> leftMax = Math.max(A[i-<span class="number">1</span>], B[j-<span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (i == m) {</span><br><span class="line"> rightMin = B[j];</span><br><span class="line"> } <span class="function"><span class="keyword">else</span> <span class="title">if</span> <span class="params">(j == n)</span> </span>{</span><br><span class="line"> rightMin = A[i];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> rightMin = Math.min(A[i], B[j]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ((m + n) % <span class="number">2</span> == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> rightMin;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (leftMax + rightMin) / <span class="number">2.0</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:两个有序数组的中位数<br><strong>Abstract</strong>: Median of Two Sorted Arrays<br>最近遇到一个有意思的题,求两个有序数组的中位数,亲自做了一下发现坑很多,除了二分查找的思想运用的很巧妙,还有各种边界条件把我搞得很崩溃。于是记录下来。<br>]]>
</summary>
<category term="Algorithm" scheme="http://zuoqy.com/tags/Algorithm/"/>
<category term="Algorithm" scheme="http://zuoqy.com/categories/Algorithm/"/>
</entry>
<entry>
<title><![CDATA[Gartner Hyper Cycle]]></title>
<link href="http://zuoqy.com/2016/06/21/Gartner-Hyper-Cycle/"/>
<id>http://zuoqy.com/2016/06/21/Gartner-Hyper-Cycle/</id>
<published>2016-06-21T03:29:19.000Z</published>
<updated>2018-09-26T15:22:33.368Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:高德纳技术成熟度曲线。<br><strong>Abstract</strong>: Theory of Gartner Hyper Cycle.<br><a id="more"></a></p>
<h2 id="概要">概要</h2><p>高德纳技术成熟度曲线如图所示,其描述了一个技术成熟度和该技术实际被应用和接受程度的关系.<br><img src="http://zuoqy.com/images/2016-06-21/1.png" alt="Gartner Hyper Cycle"></p>
<h2 id="如何使用">如何使用</h2><p>人们使用技术成熟度曲线获得新兴技术的承诺和业界对风险的控制偏好.<br>你是否要抢先尝试使用新兴技术? 如果你结合承担的风险并理解风险投资不会经常得到应有的回报,你有所收获.<br>使用温和的方式是否合适? 新技术采纳者应该明白,新兴的技术往往没有得到足够的证明.<br>需要等待其更成熟之后再使用吗?如果你还存在很多关于商业可行性方面的额问题不能回答,你最好等待其他人给出更靠谱的答案之后再尝试使用.</p>
<h2 id="技术成熟度曲线">技术成熟度曲线</h2><p>1995年开始,高德纳咨询公司依其专业分析预测与推论各种新科技的成熟演变速度及要达到成熟所需的时间,分成5个阶段:</p>
<table>
<thead>
<tr>
<th style="text-align:center">阶段</th>
<th style="text-align:center">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">科技诞生的促动期 (Technology Trigger)</td>
<td style="text-align:center">在此阶段,随着媒体大肆的报导过度,非理性的渲染,产品的知名度无所不在,然而随着这个科技的缺点、问题、限制出现,失败的案例大于成功的案例,例如:.com公司 1998~2000年之间的非理性疯狂飙升期。</td>
</tr>
<tr>
<td style="text-align:center">过高期望的峰值(Peak of Inflated Expectations)</td>
<td style="text-align:center">早期公众的过分关注演绎出了一系列成功的故事——当然同时也有众多失败的例子。对于失败,有些公司采取了补救措施,而大部分却无动于衷。</td>
</tr>
<tr>
<td style="text-align:center">泡沫化的底谷期 (Trough of Disillusionment)</td>
<td style="text-align:center">在历经前面阶段所存活的科技经过多方扎实有重点的试验,而对此科技的适用范围及限制是以客观的并实际的了解,成功并能存活的经营模式逐渐成长。</td>
</tr>
<tr>
<td style="text-align:center">稳步爬升的光明期 (Slope of Enlightenment)</td>
<td style="text-align:center">在此阶段,有一新科技的诞生,在市面上受到主要媒体与业界高度的注意,例如:1996年的Internet ,Web。</td>
</tr>
<tr>
<td style="text-align:center">实质生产的高原期 (Plateau of Productivity)</td>
<td style="text-align:center">在此阶段,新科技产生的利益与潜力被市场实际接受,实质支援此经营模式的工具、方法论经过数代的演进,进入了非常成熟的阶段。</td>
</tr>
</tbody>
</table>
<h2 id="参考">参考</h2><ul>
<li><a href="https://en.wikipedia.org/wiki/Hype_cycle" target="_blank" rel="external">https://en.wikipedia.org/wiki/Hype_cycle</a></li>
<li><a href="http://www.gartner.com/technology/research/methodologies/hype-cycle.jsp" target="_blank" rel="external">http://www.gartner.com/technology/research/methodologies/hype-cycle.jsp</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:高德纳技术成熟度曲线。<br><strong>Abstract</strong>: Theory of Gartner Hyper Cycle.<br>]]>
</summary>
<category term="Theory" scheme="http://zuoqy.com/tags/Theory/"/>
<category term="Theory" scheme="http://zuoqy.com/categories/Theory/"/>
</entry>
<entry>
<title><![CDATA[HTTP重定向状态码的区别]]></title>
<link href="http://zuoqy.com/2016/05/23/HTTP-redirect-codes/"/>
<id>http://zuoqy.com/2016/05/23/HTTP-redirect-codes/</id>
<published>2016-05-23T10:13:53.000Z</published>
<updated>2016-05-23T16:05:49.000Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:HTTP重定向状态码区分。<br><strong>Abstract</strong>: Difference between HTTP redirect code 301, 302, 303, 307<br><a id="more"></a></p>
<table>
<thead>
<tr>
<th style="text-align:left">状态码</th>
<th style="text-align:left">原因短语</th>
<th style="text-align:left">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">301</td>
<td style="text-align:left">Moved Permanently</td>
<td style="text-align:left">资源被永久移除。客户端后续应该请求到新的URI上(相应报文首部给出),客户端需要向新的URI重新发起请求。后续的请求也都应该请求新的URI。</td>
</tr>
<tr>
<td style="text-align:left">302</td>
<td style="text-align:left">Found</td>
<td style="text-align:left">临时重定向。客户端后续请求仍然使用原有的URI。</td>
</tr>
<tr>
<td style="text-align:left">303</td>
<td style="text-align:left">See Other(since HTTP/1.1)</td>
<td style="text-align:left">告诉客户端,用Get方法请求给定的新URI中的资源。对于POST/PUT/DELETE请求,客户端应该假定服务器已经收到并处理了该请求,应该向新的URI再发一次GET请求来获取结果。后续请求还需要请求到老URI上。</td>
</tr>
<tr>
<td style="text-align:left">307</td>
<td style="text-align:left">Temporary Redirect (since HTTP/1.1)</td>
<td style="text-align:left">临时重定向。对于所有POST/PUT/DELETE请求,客户端<strong>应当重新发起本次请求</strong>。后续的请求还应该使用老的URI。</td>
</tr>
</tbody>
</table>
<p><code>302 Found</code>标准与实现有偏差,The HTTP/1.0(RFC 1945)规定客户端需要做临时重定向,原始的描述是<code>"Moved Temporarily"</code>,本应实现成307所描述的功能,但是主流的浏览器实现成了类似303 See Other的功能。因此在HTTP/1.1中,增加了303和307两个状态码来区分两种不同的行为。</p>
<h2 id="参考">参考</h2><ul>
<li>1 <a href="http://stackoverflow.com/questions/4764297/difference-between-http-redirect-codes" target="_blank" rel="external">Difference between HTTP redirect codes</a></li>
<li>2 Gourley, David, and Brian Totty. HTTP: the definitive guide. “ O’Reilly Media, Inc.”, 2002.</li>
<li>3 <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection" target="_blank" rel="external">List of HTTP status codes</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:HTTP重定向状态码区分。<br><strong>Abstract</strong>: Difference between HTTP redirect code 301, 302, 303, 307<br>]]>
</summary>
<category term="HTTP" scheme="http://zuoqy.com/tags/HTTP/"/>
<category term="redirect" scheme="http://zuoqy.com/tags/redirect/"/>
<category term="重定向" scheme="http://zuoqy.com/tags/%E9%87%8D%E5%AE%9A%E5%90%91/"/>
<category term="HTTP" scheme="http://zuoqy.com/categories/HTTP/"/>
</entry>
<entry>
<title><![CDATA[Java对象的共享]]></title>
<link href="http://zuoqy.com/2016/05/22/sharing-objects/"/>
<id>http://zuoqy.com/2016/05/22/sharing-objects/</id>
<published>2016-05-22T07:48:56.000Z</published>
<updated>2016-05-22T09:40:12.000Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:Java内存可见性、对象的发布和逸出以及不变性<br><strong>Abstract</strong>:Visibility、publish and escape、immutable。<br><a id="more"></a></p>
<h2 id="可见性">可见性</h2><p>可见性是指某个线程对变量写入的值,其他线程是否总能够正确的读取。通常,为了确保多线程之间对内存写入操作的可见性,必须使用同步机制。</p>
<h3 id="重排序">重排序</h3><p>在多线程情况下,存在指令重排序的情况,因此在没有同步的情况下,不能假定多线程程序的指令执行顺序。</p>
<h3 id="失效数据">失效数据</h3><p>在没有同步保证的多线程程序中,一个线程可能读取到某个变量的新值,也可能读取到其已经失效的值。</p>
<h3 id="非原子的64位操作">非原子的64位操作</h3><p>在多线程程序中使用共享且可变的long和double等类型的变量是不安全的,除非用volatile声明或者用锁保护起来。因为JVM允许将64位的操作分解为两个32位的操作。</p>
<h3 id="加锁与可见性">加锁与可见性</h3><p>加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读写操作的线程都必须在同一个锁上进行同步。</p>
<h3 id="volatile变量">volatile变量</h3><p>volatile变量不会被缓存在寄存器中或者对其他处理器不可见的地方,因此在读取volatile类型的变量时,总会返回最新写入的值。<br>仅当volatile变量能简化代码实现以及对同步策略的验证时,才应该使用它们。使用方式包括:确保变量自身状态的可见性、确保所引用对象状态的可见性、标识一些重要的程序生命周期事件的发生(如初始化或关闭)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 判断某个状态,决定是否退出循环</span></span><br><span class="line"><span class="keyword">volatile</span> <span class="keyword">boolean</span> asleep;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">while</span>(!asleep) {</span><br><span class="line"> countSomeSheep();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>注:volatile语义不足以保证递增操作(count++)的原子性,因为递增是读-修改-写操作。<strong>加锁机制既可以确保可见性,又可以确保原子性,而volatile变量只能确保可见性</strong>。</p>
<p><strong>当且仅当满足以下所有条件时,才应该使用volatile变量</strong>:</p>
<ul>
<li>对变量的写入操作比依赖变量当前的值,或者确保只有一个线程更新变量的值。</li>
<li>该变量不会与其他状态变量一起纳入不变性条件。</li>
<li>访问该变量时不需要加锁。</li>
</ul>
<h2 id="发布与逸出">发布与逸出</h2><p><strong>发布(publish)</strong>对象,是指是对象能够在当前作用域之外的代码中使用。例如将对象的引用保存到其他类的代码中,或者将引用传递到其他类的方法中。<br><strong>逸出(escape)</strong>:当不应该发布的对象被发布,则成为逸出。</p>
<h3 id="this逸出">this逸出</h3><p>在一个类的构造函数中发布对象时,只是发布了一个尚未构造完成的对象,即使发布对象的语句位于构造函数的最后一行。如果this引用在构造函数中逸出,这种对象的创建就是不正确的构造。<strong>不要在函数构造方法中,使this逸出</strong></p>
<h4 id="错误1:在构造方法内发布内部类对象">错误1:在构造方法内发布内部类对象</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThisEscape</span> </span>{</span><br><span class="line"> <span class="comment">// 不正确的构造</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ThisEscape</span><span class="params">(EventSource source)</span> </span>{</span><br><span class="line"> source.registerListener() {</span><br><span class="line"> <span class="keyword">new</span> EventListener() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onEvent</span><span class="params">(Event e)</span> </span>{</span><br><span class="line"> doSomeThing(e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>上例中,假设EventListener是ThisEscape类的内部类,在发布内部类的对象时,this隐含的也被发布了。</p>
<h4 id="错误2:在构造函数中启动线程">错误2:在构造函数中启动线程</h4><p>如果想在构造器中启动线程或者设置事件监听,应该使用一个私有构造方法和一个公共的静态工厂方法,从而避免不正确的构造过程。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SafeListener</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> EventListener listener;</span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="title">SafeListener</span><span class="params">()</span> </span>{</span><br><span class="line"> listener = <span class="keyword">new</span> EventListener() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onEvent</span><span class="params">(Event e)</span> </span>{</span><br><span class="line"> doSomething(e);</span><br><span class="line"> } </span><br><span class="line"> };</span><br><span class="line">}</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> SafeListener <span class="title">newInstance</span><span class="params">(EventSource source)</span> </span>{</span><br><span class="line"> SafeListener safe = <span class="keyword">new</span> SafeListener();</span><br><span class="line"> source.registerListener(safe.listener);</span><br><span class="line"> <span class="keyword">return</span> safe;</span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="线程封闭(Thread_Confinement)">线程封闭(Thread Confinement)</h2><p>当某个对象封闭在一个线程中时,将自动实现线程安全性,即使被封闭的对象本身不是线程安全的。</p>
<h3 id="Ad-hoc线程封闭">Ad-hoc线程封闭</h3><p>Ad-hoc线程封闭是指维护线程封闭性的职责完全有程序实现来承担。例如在volatile变量上的读-修改-写操作确保只有一个线程来完成。<br>这种线程封闭较脆弱,应该尽量避免使用。</p>
<h3 id="栈封闭">栈封闭</h3><p>栈封闭:是指只能通过局部变量才能访问对象。</p>
<h3 id="ThreadLocal类">ThreadLocal类</h3><p>ThreadLocal是维持线程封闭性的一种更规范的方法,ThreadLocal对象通常用于防止可变的单例或者全局变量进行共享。静态的ThreadLocal对象可以将包含在其中的全局变量为每个使用它的线程都保存一份,当线程终结时,会被垃圾回收掉。运用此机制,可以很好的实现线程上下文的保存。</p>
<h2 id="不变性">不变性</h2><p>不可变对象一定是线程安全的,当满足下面的条件时,对象才是不可变的:</p>
<ul>
<li>对象创建以后其状态不可修改</li>
<li>对象所有域都是final类型</li>
<li>对象是正确被创建的(在构造方法中没有this逸出)</li>
</ul>
<h3 id="final域">final域</h3><p>final域能确保初始化过程的安全性,从而可以不受限制的访问不可变对象。</p>
<h3 id="安全发布的常用模式">安全发布的常用模式</h3><ul>
<li>在静态初始化函数中初始化一个对象引用</li>
<li>将对象的引用保存到volatile类型的域或者AtomicReference对象中。</li>
<li>将对象的引用保存到某个正确构造对象的final类型的域中</li>
<li>将兑现给的引用保存到一个由锁保护的域中。</li>
</ul>
<h2 id="参考">参考</h2><ul>
<li>1 Goetz, Brian, and Tim Peierls. Java concurrency in practice. Pearson Education, 2006.</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:Java内存可见性、对象的发布和逸出以及不变性<br><strong>Abstract</strong>:Visibility、publish and escape、immutable。<br>]]>
</summary>
<category term="Java" scheme="http://zuoqy.com/tags/Java/"/>
<category term="concurrency" scheme="http://zuoqy.com/tags/concurrency/"/>
</entry>
<entry>
<title><![CDATA[二阶段提交协议]]></title>
<link href="http://zuoqy.com/2016/03/27/2-phase-commit/"/>
<id>http://zuoqy.com/2016/03/27/2-phase-commit/</id>
<published>2016-03-27T03:55:25.000Z</published>
<updated>2016-03-27T08:04:51.000Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:简要介绍二阶段提交协议的思想<br><strong>Abstract</strong>:Main ideas of 2 phase commit.<br><a id="more"></a></p>
<h2 id="两阶段提交协议(Two_Phase_Commit_Protocol)">两阶段提交协议(Two Phase Commit Protocol)</h2><p>两阶段提交协议是分布式事务处理使用的一种一致性协议。它用来协调分布式事务中的每个参与者,应对系统分布式系统执行事务过程中的短暂系统异常(如:节点失败、网络通信异常等)。</p>
<h2 id="前提">前提</h2><ul>
<li>系统有存在一个协调者(coordinator)节点,其他节点为参与者(cohorts)</li>
<li>系统中有稳定的存储,用来记录事务日志(预写日志WAL,Write-ahead logging),预写日志不会因为节点宕机而丢失。</li>
<li>系统中,任意两个节点之间都可以相互通信。(相比前两条,可放宽一些)</li>
</ul>
<h2 id="算法描述">算法描述</h2><h3 id="第一阶段:请求提交阶段Commit_Request_Phase(投票阶段_voting_phase)">第一阶段:请求提交阶段Commit Request Phase(投票阶段 voting phase)</h3><ul>
<li>协调者向所有参与值发起事务提交请求,等待所有参与者的投票响应。</li>
<li>所有参与者执行协调者发起的事务,记录Undo和Redo log。</li>
<li>所有参与者投票给协调者(事务可以执行成功,回复<em>Yes</em>,否则回复<em>No</em>)。任何一个参数者没有回复yes都会导致事务回滚。</li>
</ul>
<h3 id="第二阶段:事务提交阶段(Commit_Phase)">第二阶段:事务提交阶段(Commit Phase)</h3><h4 id="执行成功:">执行成功:</h4><ul>
<li>协调者向所有参与者发送commit命令</li>
<li>所有参与者完成事务提交,并释放事务处理期间所占用的锁和资源</li>
<li>参与者发送ACK给协调者</li>
<li>当协调者收到所有参与者的ACK之后,事务执行成功</li>
</ul>
<h4 id="执行失败:">执行失败:</h4><ul>
<li>任何一个参与者投票No或者超时,都导致事务回滚</li>
<li>每个参与者根据WAL日志回滚</li>
<li>每个参与者发送ACK给协调者</li>
<li>协调者收到所有ACK后,事务回滚完成</li>
</ul>
<p>时序图:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Coordinator Cohort QUERY TO COMMIT --------------------------------> VOTE YES/NO prepare*/abort* <------------------------------- commit*/abort* COMMIT/ROLLBACK --------------------------------> ACKNOWLEDGMENT commit*/abort* <-------------------------------- end</span><br></pre></td></tr></table></figure>
<p><code>*</code>表示改操作需要依赖稳定的存储</p>
<h2 id="算法缺陷">算法缺陷</h2><ul>
<li>同步阻塞:两阶段提交最大的缺点,等待其他参与者响应的过程中是阻塞的,无法进行其他操作。如果在阶段二协调者失败,则参与者永远处于阻塞状态,直到收到commit命令或者abort命令。</li>
<li>单点问题:协调者存在单点问题</li>
<li>太过保守:任何参与者错误或者超时,都会导致整个分布式事务失败。</li>
</ul>
<h2 id="参考">参考</h2><ul>
<li><a href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol" target="_blank" rel="external">https://en.wikipedia.org/wiki/Two-phase_commit_protocol</a></li>
<li><a href="http://www.amazon.cn/%E5%9B%BE%E4%B9%A6/dp/B00RECRKPK" target="_blank" rel="external">从paxos到zookeeper——分布式一致性原理与实践</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p><strong>摘要</strong>:简要介绍二阶段提交协议的思想<br><strong>Abstract</strong>:Main ideas of 2 phase commit.<br>]]>
</summary>
<category term="2 Phase Commit" scheme="http://zuoqy.com/tags/2-Phase-Commit/"/>
<category term="2PC" scheme="http://zuoqy.com/tags/2PC/"/>
<category term="一致性协议" scheme="http://zuoqy.com/tags/%E4%B8%80%E8%87%B4%E6%80%A7%E5%8D%8F%E8%AE%AE/"/>
<category term="二阶段提交" scheme="http://zuoqy.com/tags/%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/"/>
</entry>
<entry>
<title><![CDATA[Java ClassLoader]]></title>
<link href="http://zuoqy.com/2016/02/05/classloader/"/>
<id>http://zuoqy.com/2016/02/05/classloader/</id>
<published>2016-02-05T13:53:45.000Z</published>
<updated>2016-03-27T03:50:44.000Z</updated>
<content type="html"><![CDATA[<p><strong>摘要</strong>:Java类加载器<br><strong>Abstract</strong>: An overview of Java ClassLoaders<br><a id="more"></a></p>