-
Notifications
You must be signed in to change notification settings - Fork 7
/
atom.xml
1163 lines (961 loc) · 208 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>不如</title>
<subtitle>码农,程序猿,未来的昏析狮</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://ibruce.info/"/>
<updated>2018-09-26T15:02:48.026Z</updated>
<id>http://ibruce.info/</id>
<author>
<name>bruce</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>不蒜子</title>
<link href="http://ibruce.info/2015/04/04/busuanzi/"/>
<id>http://ibruce.info/2015/04/04/busuanzi/</id>
<published>2015-04-04T01:12:45.000Z</published>
<updated>2018-09-26T15:02:48.026Z</updated>
<content type="html"><![CDATA[<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">!!!!2018年9月 - 重要提示 !!!!</span><br><span class="line">大家好,因七牛强制过期原有的『dn-lbstatics.qbox.me』域名(预计2018年10月初),与客服沟通数次无果,即使我提出为此付费也不行,只能更换域名到『busuanzi.ibruce.info』!因我是最早的一批七牛用户,为七牛至少带来了数百个邀请用户,很痛心,很无奈!</span><br><span class="line">各位继续使用不蒜子提供的服务,只需把原有的:</span><br><span class="line"><script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script></span><br><span class="line">域名改一下即可:</span><br><span class="line"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></span><br><span class="line">只需要修改该js域名,其他均未改变。若有疑问,可以加入不蒜子交流QQ群:`419260983`,对您带来的不便,非常抱歉!!!还是那句话,不蒜子不会中断服务!!!!</span><br></pre></td></tr></table></figure>
<p>静态网站建站现在有很多快速的技术和平台,但静态是优点也有缺点,由于是静态的,一些动态的内容如评论、计数等等模块就需要借助外来平台,评论有<a href="http://duoshuo.com" target="_blank" rel="noopener">“多说”</a>,计数有<a href="http://busuanzi.ibruce.info" target="_blank" rel="noopener">“不蒜”</a>!<strong>(多说即将关闭,不蒜子还活着涅,这是程序员对程序员的承诺。)</strong></p>
<blockquote>
<p><strong><a href="http://busuanzi.ibruce.info" target="_blank" rel="noopener">“不蒜子”</a>与百度统计谷歌分析等有区别:<a href="http://busuanzi.ibruce.info" target="_blank" rel="noopener">“不蒜子”</a>可直接将访问次数显示在您在网页上(也可不显示);对于已经上线一段时间的网站,<a href="http://busuanzi.ibruce.info" target="_blank" rel="noopener">“不蒜子”</a>允许您初始化首次数据。。</strong></p>
</blockquote>
<p>普通用户只需两步走:<strong>一行脚本+一行标签</strong>,搞定一切。追求极致的用户可以进行任意DIY。</p>
<a id="more"></a>
<h1 id="一、安装脚本(必选)">一、安装脚本(必选)</h1><hr>
<p>要使用不蒜子必须在页面中引入busuanzi.js,目前最新版如下。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></span><br><span class="line"></script></span><br></pre></td></tr></table></figure>
<blockquote>
<p>不蒜子可以给任何类型的个人站点使用,如果你是用的hexo,打开<strong>themes/你的主题/layout/_partial/footer.ejs</strong>添加上述脚本即可,当然你也可以添加到 header 中。</p>
</blockquote>
<h1 id="二、安装标签(可选)">二、安装标签(可选)</h1><hr>
<p>只需要复制相应的html标签到你的网站要显示访问量的位置即可。您可以随意更改不蒜子标签为自己喜欢的显示效果,内容参考第三部分<strong>扩展开发</strong>。根据你要显示内容的不同,这分几种情况。</p>
<h2 id="1、显示站点总访问量">1、显示站点总访问量</h2><p>要显示站点总访问量,复制以下代码添加到你需要显示的位置。有两种算法可选:</p>
<p>算法a:pv的方式,单个用户连续点击n篇文章,记录n次访问量。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><span id="busuanzi_container_site_pv"></span><br><span class="line"> 本站总访问量<span id="busuanzi_value_site_pv"></span>次</span><br><span class="line"></span></span><br></pre></td></tr></table></figure></p>
<p>算法b:uv的方式,单个用户连续点击n篇文章,只记录1次访客数。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><span id="busuanzi_container_site_uv"></span><br><span class="line"> 本站访客数<span id="busuanzi_value_site_uv"></span>人次</span><br><span class="line"></span></span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>如果你是用的hexo,打开<strong>themes/你的主题/layout/_partial/footer.ejs</strong>添加即可。</p>
</blockquote>
<p><strong>实例效果参考:</strong></p>
<ul>
<li><a href="http://liam0205.me" target="_blank" rel="noopener">http://liam0205.me</a></li>
<li><a href="http://gameknife.github.io" target="_blank" rel="noopener">http://gameknife.github.io</a></li>
<li><a href="http://read.mobi" target="_blank" rel="noopener">http://read.mobi</a></li>
<li><a href="http://pgqlife.info" target="_blank" rel="noopener">http://pgqlife.info</a></li>
<li><a href="http://sdxy0506.github.io" target="_blank" rel="noopener">http://sdxy0506.github.io</a></li>
<li><a href="http://www.gcrimson.com" target="_blank" rel="noopener">http://www.gcrimson.com</a></li>
<li><a href="http://libk.net" target="_blank" rel="noopener">http://libk.net</a></li>
<li><a href="http://ztyoung.me" target="_blank" rel="noopener">http://ztyoung.me</a></li>
<li><a href="http://blog.itmyhome.com" target="_blank" rel="noopener">http://blog.itmyhome.com</a></li>
</ul>
<h2 id="2、显示单页面访问量">2、显示单页面访问量</h2><p>要显示每篇文章的访问量,复制以下代码添加到你需要显示的位置。</p>
<p>算法:pv的方式,单个用户点击1篇文章,本篇文章记录1次阅读量。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><span id="busuanzi_container_page_pv"></span><br><span class="line"> 本文总阅读量<span id="busuanzi_value_page_pv"></span>次</span><br><span class="line"></span></span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>代码中文字是可以修改的,只要保留id正确即可。</p>
</blockquote>
<p><strong>实例效果参考:</strong></p>
<ul>
<li><a href="http://dbarobin.com/2015/04/14/operation-and-maintenance-engineer-tips" target="_blank" rel="noopener">http://dbarobin.com/2015/04/14/operation-and-maintenance-engineer-tips</a></li>
<li><a href="http://blog.jamespan.me/2015/05/06/mvn-incremental-compilation" target="_blank" rel="noopener">http://blog.jamespan.me/2015/05/06/mvn-incremental-compilation</a></li>
<li><a href="http://cubernet.cn/blog/optimization-3" target="_blank" rel="noopener">http://cubernet.cn/blog/optimization-3</a></li>
</ul>
<blockquote>
<p>注意:不蒜子为保持极简,暂不支持在站点文章摘要列表中(如首页)逐个显示每篇文章的阅读次数,如果您非常需要这一功能,可以留言。根据需要程度再考虑开发相应的功能。</p>
</blockquote>
<h2 id="3、显示站点总访问量和单页面访问量">3、显示站点总访问量和单页面访问量</h2><p>你懂的吧,上面两种标签代码都安装。</p>
<p><strong>实例效果参考:</strong></p>
<ul>
<li><a href="http://cubernet.cn/blog/swift-1" target="_blank" rel="noopener">http://cubernet.cn/blog/swift-1</a></li>
<li><a href="http://lvzejun.cn/2015/03/31/ubuntu-software" target="_blank" rel="noopener">http://lvzejun.cn/2015/03/31/ubuntu-software</a></li>
<li><a href="http://www.lvzejun.cn/2015/04/13/libvirt1md" target="_blank" rel="noopener">http://www.lvzejun.cn/2015/04/13/libvirt1md</a></li>
</ul>
<h2 id="4、只计数不显示">4、只计数不显示</h2><p>只安装脚本代码,不安装标签代码。</p>
<blockquote>
<p><strong>至此,不蒜子已经可以正常运行,如果你还要自定义一些内容或有疑问,请继续阅读。</strong></p>
</blockquote>
<h1 id="附录:扩展开发(自定义)">附录:扩展开发(自定义)</h1><hr>
<p>不蒜子之所以称为极客的算子,正是因为不蒜子自身只提供<strong>标签+数字</strong>,至于显示的style和css动画效果,任你发挥。</p>
<ul>
<li><strong>busuanzi_value_site_pv</strong> 的作用是异步回填访问数,这个id一定要正确。</li>
<li><strong>busuanzi_container_site_pv</strong>的作用是为防止计数服务访问出错或超时(3秒)的情况下,使整个标签自动隐藏显示,带来更好的体验。这个id可以省略。</li>
</ul>
<p>因此,你也可以使用极简模式:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span><br><span class="line">本站访客数<span id="busuanzi_value_site_uv"></span>人次</span><br><span class="line">本文总阅读量<span id="busuanzi_value_page_pv"></span>次</span><br></pre></td></tr></table></figure>
<p>或者个性化一下:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Total <span id="busuanzi_value_site_pv"></span> views.</span><br><span class="line">您是xxx的第<span id="busuanzi_value_site_uv"></span>个小伙伴</span><br><span class="line"><span id="busuanzi_value_page_pv"></span> Hits</span><br></pre></td></tr></table></figure>
<p>1、我只要统计不显示?<br>只引入busuanzi.js,不引入显示标签即可。</p>
<p>2、你的标签太丑了,我想美化一下可以么?<br>可以的,您可以用自己站点的css进行控制,只要内层span的id正确以便回填访问次数即可,甚至标签都可以不是span。</p>
<p>3、中文字体太丑了,我的主题不适合?<br>您可以将<strong>本站总访问量xxx次</strong>改成<strong>view xxx times</strong>等英文以获得更和谐的显示效果。</p>
<p>4、在访问量数据未取回来之前,我不想让页面显示为诸如“本站总访问量 次”,显得太low,怎么办?<br>只需要如下css,不蒜子执行完毕会自动将标签显示出来,其他以此类推:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><span id="busuanzi_container_site_pv" style='display:none'></span><br><span class="line"> 本站总访问量<span id="busuanzi_value_site_pv"></span>次</span><br><span class="line"></span></span><br></pre></td></tr></table></figure></p>
<p>上面的做法还是很low?!欣赏一下这位小伙伴的做法,请戳看效果:<a href="http://blog.jamespan.me/2015/05/13/the-jump-guide" target="_blank" rel="noopener">http://blog.jamespan.me/2015/05/13/the-jump-guide</a><br>右键看下源码,没加载出来前就显示个菊花转转转:<br>首先,你要引入<strong>font-awesome</strong>字体:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"></span><br><span class="line">或</span><br><span class="line"><link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css"></span><br></pre></td></tr></table></figure></p>
<p>其次,修改不蒜子标签:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><span id="busuanzi_value_page_pv"><i class="fa fa-spinner"></i></span> Hits</span><br><span class="line">或(旋转效果)</span><br><span class="line"><span id="busuanzi_value_page_pv"><i class="fa fa-spinner fa-spin"></i></span> Hits</span><br></pre></td></tr></table></figure></p>
<p>和谐多了!</p>
<p>5、我的网站已经运行一段时间了,想初始化访问次数怎么办?<br>请先注册登录,自行修改阅读次数。</p>
<p>有任何其他问题或疑问可以留言。</p>
<p>更新日志:</p>
<blockquote>
<p>1). 2015-04-04:不蒜子1.0 正式发布,极简的网站计数器服务。<br>2). 2015-04-24:不蒜子2.0 正式发布,区分pv/uv的统计方式,统计更精准,满足更多需求。<br>3). 2015-05-18:不蒜子2.3 正式发布,去掉对jQuery的依赖,异步化执行,速度更快。<br>4). 2016-01-01:不蒜子2.4 正式发布,同时支持http和https访问,启用 <a href="http://busuanzi.ibruce.info" target="_blank" rel="noopener">“新主页”</a> 。</p>
</blockquote>
<p><img src="http://dn-bruce.qbox.me/pay.jpg" alt="微信扫码打赏:不蒜子的服务器是收费的,感谢您的捐助。"></p>
<blockquote>
<p>如果您愿意捐助其他金额请戳 <a href="http://ibruce.info/about">http://ibruce.info/about</a>,扫描支付宝/微信二维码。</p>
</blockquote>
<p><strong>声明</strong>:经用户建议,新增不蒜子交流QQ群:<code>419260983</code>,欢迎大家加入。—— 不如,2017.02</p>
]]></content>
<summary type="html">
Hexo博客计数,Jekyll博客计数器,Octopress访问统计,GitHub Pages博客访问量统计,静态网站计数,静态博客计数,网站计数器,网站计数插件,博客计数器,WordPress计数插件, DedeCMS计数插件, Z-Blog计数器插件, Joomla计数器, emlog计数器, MediaWiki计数器
</summary>
<category term="default" scheme="http://ibruce.info/categories/default/"/>
<category term="hexo" scheme="http://ibruce.info/tags/hexo/"/>
</entry>
<entry>
<title>mongoDB 3.0 安全权限访问控制</title>
<link href="http://ibruce.info/2015/03/03/mongodb3-auth/"/>
<id>http://ibruce.info/2015/03/03/mongodb3-auth/</id>
<published>2015-03-03T01:25:11.000Z</published>
<updated>2015-12-03T14:02:24.000Z</updated>
<content type="html"><![CDATA[<p>MongoDB3.0权限,啥都不说了,谷歌百度出来的全是错的。先安装好盲沟,简单的没法说。</p>
<p>首先,不使用 <em>–auth</em> 参数,启动 mongoDB:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mongodb-linux-i686-3.0.0/bin/mongod -f mongodb-linux-i686-3.0.0/mongodb.conf</span><br></pre></td></tr></table></figure>
<blockquote>
<p>此时你 <em>show dbs</em> 会看到只有一个local数据库,那个所谓的admin是不存在的。</p>
</blockquote>
<p>mongoDB 没有炒鸡无敌用户root,只有能管理用户的用户 <a href="http://docs.mongodb.org/manual/reference/built-in-roles/#userAdminAnyDatabase" target="_blank" rel="noopener">userAdminAnyDatabase</a>。</p>
<p>打开 <strong>mongo shell</strong>:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mongodb-linux-i686-3.0.0/bin/mongo</span><br></pre></td></tr></table></figure>
<p>添加管理用户:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">use admin</span><br><span class="line">db.createUser(</span><br><span class="line"> {</span><br><span class="line"> user: "buru",</span><br><span class="line"> pwd: "12345678",</span><br><span class="line"> roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]</span><br><span class="line"> }</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<blockquote>
<p>roles 中的 db 参数是必须的,不然会报错:<em>Error: couldn’t add user: Missing expected field “db”</em>。另外,有很多文章记录的是使用 <a href="http://docs.mongodb.org/v2.2/reference/method/db.addUser" target="_blank" rel="noopener">db.addUser(…)</a> 方法,这个方法是旧版的,3.0中已经不存在,详见:<a href="http://docs.mongodb.org/manual/reference/method/js-user-management" target="_blank" rel="noopener">http://docs.mongodb.org/manual/reference/method/js-user-management</a>。</p>
</blockquote>
<p>切换到admin下,查看刚才创建的用户:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">show users</span><br><span class="line">或</span><br><span class="line">db.system.users.find()</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">{ "_id" : "admin.buru", "user" : "buru", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "gwVwuA/dXvxgSHavEnlyvA==", "storedKey" : "l2QEVTEujpkCuqDEKqfIWbSv4ms=", "serverKey" : "M1ofNKXg2sNCsFrBJbX4pXbSgvg=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }</span><br></pre></td></tr></table></figure>
<blockquote>
<p>怎么关闭 mongoDB?千万不要 kill -9 pid,可以 kill -2 pid 或 db.shutdownServer()</p>
</blockquote>
<p>下面使用 <em>–auth</em> 参 数,重新启动 mongoDB:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mongodb-linux-i686-3.0.0/bin/mongod --auth -f mongodb-linux-i686-3.0.0/mongodb.conf</span><br></pre></td></tr></table></figure>
<p>再次打开 <strong>mongo shell</strong>:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mongodb-linux-i686-3.0.0/bin/mongo</span><br><span class="line">use admin</span><br><span class="line">db.auth("buru","12345678") #认证,返回1表示成功</span><br><span class="line">或</span><br><span class="line">mongodb-linux-i686-3.0.0/bin/mongo -u buru -p 12345678 --authenticationDatabase admin</span><br></pre></td></tr></table></figure>
<p>此时</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">show collections</span><br></pre></td></tr></table></figure>
<p>报错<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">2015-03-17T10:15:56.011+0800 E QUERY Error: listCollections failed: {</span><br><span class="line"> "ok" : 0,</span><br><span class="line"> "errmsg" : "not authorized on admin to execute command { listCollections: 1.0 }",</span><br><span class="line"> "code" : 13</span><br><span class="line">}</span><br><span class="line"> at Error (<anonymous>)</span><br><span class="line"> at DB._getCollectionInfosCommand (src/mongo/shell/db.js:643:15)</span><br><span class="line"> at DB.getCollectionInfos (src/mongo/shell/db.js:655:20)</span><br><span class="line"> at DB.getCollectionNames (src/mongo/shell/db.js:666:17)</span><br><span class="line"> at shellHelper.show (src/mongo/shell/utils.js:625:12)</span><br><span class="line"> at shellHelper (src/mongo/shell/utils.js:524:36)</span><br><span class="line"> at (shellhelp2):1:1 at src/mongo/shell/db.js:643</span><br></pre></td></tr></table></figure></p>
<p>因为,用户buru只有用户管理的权限。</p>
<p>下面创建用户,用户都跟着库走,创建的用户都是</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">use tianhe</span><br><span class="line">db.createUser(</span><br><span class="line"> {</span><br><span class="line"> user: "bao",</span><br><span class="line"> pwd: "12345678",</span><br><span class="line"> roles: [</span><br><span class="line"> { role: "readWrite", db: "tianhe" },</span><br><span class="line"> { role: "read", db: "tianhe2" }</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>查看刚刚创建的用户。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">show users</span><br><span class="line"></span><br><span class="line">{</span><br><span class="line"> "_id" : "tianhe.bao",</span><br><span class="line"> "user" : "bao",</span><br><span class="line"> "db" : "tianhe",</span><br><span class="line"> "roles" : [</span><br><span class="line"> {</span><br><span class="line"> "role" : "readWrite",</span><br><span class="line"> "db" : "tianhe"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "role" : "read",</span><br><span class="line"> "db" : "tianhe2"</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>查看整个mongoDB全部的用户:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">use admin</span><br><span class="line">db.system.users.find()</span><br><span class="line"></span><br><span class="line">{ "_id" : "admin.buru", "user" : "buru", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "gwVwuA/dXvxgSHavEnlyvA==", "storedKey" : "l2QEVTEujpkCuqDEKqfIWbSv4ms=", "serverKey" : "M1ofNKXg2sNCsFrBJbX4pXbSgvg=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }</span><br><span class="line">{ "_id" : "tianhe.bao", "user" : "bao", "db" : "tianhe", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "//xy1V1fbqEHC1gzQqZHGQ==", "storedKey" : "ZS/o54zzl/FdcXLQJ98KdAVTfF0=", "serverKey" : "iIpNYz2Gk8KhyK3zgz6muBt0PI4=" } }, "roles" : [ { "role" : "readWrite", "db" : "tianhe" }, { "role" : "read", "db" : "tianhe2" } ] }</span><br></pre></td></tr></table></figure>
<p>创建完毕,验证一下:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">use buru</span><br><span class="line">show collections</span><br><span class="line"></span><br><span class="line">2015-03-17T10:30:06.461+0800 E QUERY Error: listCollections failed: {</span><br><span class="line"> "ok" : 0,</span><br><span class="line"> "errmsg" : "not authorized on buru to execute command { listCollections: 1.0 }",</span><br><span class="line"> "code" : 13</span><br><span class="line">}</span><br><span class="line"> at Error (<anonymous>)</span><br><span class="line"> at DB._getCollectionInfosCommand (src/mongo/shell/db.js:643:15)</span><br><span class="line"> at DB.getCollectionInfos (src/mongo/shell/db.js:655:20)</span><br><span class="line"> at DB.getCollectionNames (src/mongo/shell/db.js:666:17)</span><br><span class="line"> at shellHelper.show (src/mongo/shell/utils.js:625:12)</span><br><span class="line"> at shellHelper (src/mongo/shell/utils.js:524:36)</span><br><span class="line"> at (shellhelp2):1:1 at src/mongo/shell/db.js:643</span><br><span class="line">`</span><br></pre></td></tr></table></figure></p>
<p>显然没权限,先auth:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">db.auth("bao","12345678")</span><br><span class="line">1</span><br><span class="line">show collections</span><br><span class="line">news</span><br><span class="line">system.indexes</span><br><span class="line">wahaha</span><br></pre></td></tr></table></figure>
<p>完毕!</p>
<p><strong>参考:</strong><br>Mongo Shell:<a href="http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell" target="_blank" rel="noopener">http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell</a><br>Enable Access Control:<a href="http://docs.mongodb.org/manual/tutorial/enable-authentication" target="_blank" rel="noopener">http://docs.mongodb.org/manual/tutorial/enable-authentication</a><br>Add a User to a Database:<a href="http://docs.mongodb.org/manual/tutorial/add-user-to-database" target="_blank" rel="noopener">http://docs.mongodb.org/manual/tutorial/add-user-to-database</a><br>User Methods:<a href="http://docs.mongodb.org/manual/reference/method/js-user-management" target="_blank" rel="noopener">http://docs.mongodb.org/manual/reference/method/js-user-management</a><br>Role Methods:<a href="http://docs.mongodb.org/manual/reference/method/js-role-management" target="_blank" rel="noopener">http://docs.mongodb.org/manual/reference/method/js-role-management</a></p>
]]></content>
<summary type="html">
mongoDB 3.0 权限控制 用户权限 创建用户 adduser auth 访问控制 安全控制 security
</summary>
<category term="mongodb" scheme="http://ibruce.info/categories/mongodb/"/>
<category term="mongodb" scheme="http://ibruce.info/tags/mongodb/"/>
</entry>
<entry>
<title>微服务框架 Colossus</title>
<link href="http://ibruce.info/2014/12/02/hello-colossus/"/>
<id>http://ibruce.info/2014/12/02/hello-colossus/</id>
<published>2014-12-02T02:54:28.000Z</published>
<updated>2016-01-03T09:30:57.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>一直认为 Akka 是 JVM 上实现微服务框架的最理想基础设施,现在 Colossus 终于来了!</p>
</blockquote>
<p>Tumblr 开源了其用 Scala 实现的微服务框架 Colossus。Colossus 是轻量级的 Scala 微服务 I/O 框架,基于 Akka Actor 实现,项目地址请戳 <a href="http://github.com/tumblr/colossus" target="_blank" rel="noopener">http://github.com/tumblr/colossus</a>。</p>
<a id="more"></a>
<p>Tumblr 面临的最大问题之一是如何架设及扩展不断持续增长的服务平台。这是病得治,微服务架构是药。它将小而专的应用封装成一个特性或组件,抛弃之前那种一个独立的大应用包含整站逻辑的做法。</p>
<p>微服务框架提供清晰的职责分离,有助于促进建造良好的基础设施,更容易排查缺陷和性能瓶颈。微服务有如此多的优点,但同样也面临挑战,如微服务要易于构建、维护、部署和监控,最重要的是他们需要极高的性能和容错性。单个服务每秒必须响应数万或数十万请求,并且有严格的延迟时间和正常运行时间要求。</p>
<p>Colossus 是 Tumblr 用来解决如上问题的新框架。他提供一个轻量级、简单的模型,来创建高性能的微服务。他用 scala 实现,基于 NIO 和 Akka Actor。该框架对 Tumblr 内部的服务搭建产生了极大的影响。</p>
<p>微服务在 Tumblr 不是新鲜事物,过去旧的框架很难写出服务高性能、高稳定性、高可用的服务。构建一个服务,只有少数几个具有领域知识的工程师才能高效完成的。Colossus 的出现改变了这一局面,使得更容易开发快速的容错服务,大大降低了准入门槛。</p>
<p>Colossus 主要有两个目标: </p>
<h3 id="性能">性能</h3><p>目前,最重要的目标就是 Colossus 的程序应该至少与直接使用NIO(不使用任何框架)写的程序一样快。Colossus 是设计用来封装那些直接使用 NIO 服务的 I/O 层,目前的已有框架都有性能问题。因此,我们要重新打造一个无损性能的框架。</p>
<p>微服务架构用来并发处理来自各种客户端大量无状态小请求。Reactor 模型是一种实现方案,他使用单线程的事件循环去处理多元的客户端 TCP 连接。Colossus 提供一个干净的此种模型的实现,保证尽可能少的开销。在很多情况下,并发的代码不容易写,实际上使用Futures 和 Akka actors 是简单高效的,他是真正的并行。</p>
<p>这种混合 Actor/Reactor 模型保证了所需的性能。我们基准测试达到了数百万QPS,一些生产系统已经处理数千亿的请求,其中 99.99% 延迟都在5毫秒以内。</p>
<h3 id="简单">简单</h3><p>Colossus 的另一个目标是小而聚焦,低的准入门槛。简单包含两个方面:框架自身简单和框架用起来简单。</p>
<p>框架自身简单指的是 Colossus 只聚焦于微服务。为此 Colossus 的核心是一个广义的 NIO 包装,我们的大部分工作都在微服务用例,来保证代码的小而简单直接。</p>
<p>框架用起来简单指的是API层面,这主要用到 Scala 语言的优势。Scala 的最大优点是极具表现力的代码和设计简单的DSL。此外,因为scala重视类型安全性和函数式编程,我们确保 Colossus 尽量集成这些优点。这导致程序猿写应用更简单,更聚焦于业务逻辑而不是代码自身。</p>
<p>这些原则使得 Colossus 变成 Tumblr 基础设施的基本组成部分,并使 Tumblr 走向一个更加面向服务的体系结构。 Colossus 已经大大改善了 Tumblr 内部构造服务的方式,在生产系统系统中取得巨大的成功。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">libraryDependencies += "com.tumblr" % "colossus_2.11" % "0.5.1-M1"</span><br></pre></td></tr></table></figure>
<p>a little http server</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> colossus._</span><br><span class="line"><span class="keyword">import</span> service._</span><br><span class="line"><span class="keyword">import</span> protocols.http._</span><br><span class="line"><span class="keyword">import</span> <span class="type">UrlParsing</span>._</span><br><span class="line"><span class="keyword">import</span> <span class="type">HttpMethod</span>._</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">Main</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">implicit</span> <span class="keyword">val</span> io_system = <span class="type">IOSystem</span>()</span><br><span class="line"></span><br><span class="line"> <span class="type">Service</span>.become[<span class="type">Http</span>](<span class="string">"http-echo"</span>, <span class="number">9000</span>){</span><br><span class="line"> <span class="keyword">case</span> request @ <span class="type">Get</span> on <span class="type">Root</span> => request.ok(<span class="string">"Hello world!"</span>)</span><br><span class="line"> <span class="keyword">case</span> request @ <span class="type">Get</span> on <span class="type">Root</span> / <span class="string">"echo"</span> / str => request.ok(str)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>KeyValExample</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">KeyValDB</span> <span class="keyword">extends</span> <span class="title">Actor</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">import</span> <span class="type">KeyValDB</span>._</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> db = collection.mutable.<span class="type">Map</span>[<span class="type">ByteString</span>, <span class="type">ByteString</span>]()</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Get</span>(key, promise) => promise.success(db.get(key))</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Set</span>(key, value, promise) => {</span><br><span class="line"> db(key) = value</span><br><span class="line"> promise.success(())</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="class"><span class="keyword">object</span> <span class="title">KeyValDB</span> </span>{</span><br><span class="line"> <span class="keyword">case</span> <span class="class"><span class="keyword">class</span> <span class="title">Get</span>(<span class="params">key: <span class="type">ByteString</span>, promise: <span class="type">Promise</span>[<span class="type">Option</span>[<span class="type">ByteString</span>]] = <span class="type">Promise</span>(</span>))</span></span><br><span class="line"><span class="class"> <span class="title">case</span> <span class="title">class</span> <span class="title">Set</span>(<span class="params">key: <span class="type">ByteString</span>, value: <span class="type">ByteString</span>, promise: <span class="type">Promise</span>[<span class="type">Unit</span>] = <span class="type">Promise</span>(</span>))</span></span><br><span class="line"><span class="class">}</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="title">object</span> <span class="title">KeyValExample</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">start</span></span>(port: <span class="type">Int</span>)(<span class="keyword">implicit</span> io: <span class="type">IOSystem</span>): <span class="type">ServerRef</span> = {</span><br><span class="line"> <span class="keyword">import</span> io.actorSystem.dispatcher</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> db = io.actorSystem.actorOf(<span class="type">Props</span>[<span class="type">KeyValDB</span>])</span><br><span class="line"></span><br><span class="line"> <span class="type">Service</span>.become[<span class="type">Redis</span>](<span class="string">"key-value-example"</span>, port){</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Command</span>(<span class="string">"GET"</span>, args) => {</span><br><span class="line"> <span class="keyword">val</span> dbCmd = <span class="type">KeyValDB</span>.<span class="type">Get</span>(args(<span class="number">0</span>))</span><br><span class="line"> db ! dbCmd</span><br><span class="line"> dbCmd.promise.future.map{</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Some</span>(value) => <span class="type">BulkReply</span>(value)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">None</span> => <span class="type">NilReply</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Command</span>(<span class="string">"SET"</span>, args) => {</span><br><span class="line"> <span class="keyword">val</span> dbCmd = <span class="type">KeyValDB</span>.<span class="type">Set</span>(args(<span class="number">0</span>), args(<span class="number">1</span>))</span><br><span class="line"> db ! dbCmd</span><br><span class="line"> dbCmd.promise.future.map{_ =></span><br><span class="line"> <span class="type">StatusReply</span>(<span class="string">"OK"</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><br></pre></td></tr></table></figure>
<p>HttpExample</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">HttpExample</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Here we're demonstrating a common way of breaking out the business logic</span></span><br><span class="line"><span class="comment"> * from the server setup, which makes functional testing easy</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">HttpRoutes</span>(<span class="params">redis: <span class="type">LocalClient</span>[<span class="type">Command</span>, <span class="type">Reply</span>]</span>) </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">invalidReply</span></span>(reply: <span class="type">Reply</span>) = <span class="string">s"Invalid reply from redis <span class="subst">$reply</span>"</span> </span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> handler: <span class="type">PartialFunction</span>[<span class="type">HttpRequest</span>, <span class="type">Response</span>[<span class="type">HttpResponse</span>]] = {</span><br><span class="line"> <span class="keyword">case</span> req @ <span class="type">Get</span> on <span class="type">Root</span> => req.ok(<span class="string">"Hello World!"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> req @ <span class="type">Get</span> on <span class="type">Root</span> / <span class="string">"get"</span> / key => redis.send(<span class="type">Commands</span>.<span class="type">Get</span>(<span class="type">ByteString</span>(key))).map{</span><br><span class="line"> <span class="keyword">case</span> <span class="type">BulkReply</span>(data) => req.ok(data.utf8String)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">NilReply</span> => req.notFound(<span class="string">"(nil)"</span>)</span><br><span class="line"> <span class="keyword">case</span> other => req.error(invalidReply(other))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> req @ <span class="type">Get</span> on <span class="type">Root</span> / <span class="string">"set"</span> / key / value => redis.send(<span class="type">Commands</span>.<span class="type">Set</span>(<span class="type">ByteString</span>(key), <span class="type">ByteString</span>(value))).map{</span><br><span class="line"> <span class="keyword">case</span> <span class="type">StatusReply</span>(msg) => req.ok(msg)</span><br><span class="line"> <span class="keyword">case</span> other => req.error(invalidReply(other))</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><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">start</span></span>(port: <span class="type">Int</span>, redisAddress: <span class="type">InetSocketAddress</span>)(<span class="keyword">implicit</span> system: <span class="type">IOSystem</span>): <span class="type">ServerRef</span> = {</span><br><span class="line"> <span class="type">Service</span>.serve[<span class="type">Http</span>](<span class="string">"http-example"</span>, port){context =></span><br><span class="line"> <span class="keyword">val</span> redis = context.clientFor[<span class="type">Redis</span>](redisAddress.getHostName, redisAddress.getPort)</span><br><span class="line"> <span class="comment">//because our routes object has no internal state, we can share it among</span></span><br><span class="line"> <span class="comment">//connections. If the class did have some per-connection internal state,</span></span><br><span class="line"> <span class="comment">//we'd just create one per connection</span></span><br><span class="line"> <span class="keyword">val</span> routes = <span class="keyword">new</span> <span class="type">HttpRoutes</span>(redis)</span><br><span class="line"></span><br><span class="line"> context.handle{connection => </span><br><span class="line"> connection.become(routes.handler)</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><br></pre></td></tr></table></figure>
<p><img src="http://static.tumblr.com/dbcxhwx/lQ7m5bev3/engineering.png" alt="tumblr colossus"></p>
<h1 id="参考文献:">参考文献:</h1><ol>
<li><a href="http://engineering.tumblr.com/post/102906359034/colossus-a-new-service-framework-from-tumblr" target="_blank" rel="noopener">Colossus: A New Service Framework from Tumblr</a></li>
<li><a href="http://www.jdon.com/46881" target="_blank" rel="noopener">Tumblr推出开源微服务框架Colossus</a></li>
<li><a href="http://github.com/tumblr/colossus" target="_blank" rel="noopener">Github: tumblr colossus</a></li>
<li><a href="http://tumblr.github.io/colossus/" target="_blank" rel="noopener">Colossus Page</a></li>
<li><a href="http://tumblr.github.io/colossus/docs/" target="_blank" rel="noopener">Colossus Documentation</a> </li>
</ol>
]]></content>
<summary type="html">
Colossus 基于 Scala Akka 的 microservice framework,Java 微服务框架DropWizard,Akka Actor ,微服务,高性能,高可扩展,简单易用
</summary>
<category term="scala" scheme="http://ibruce.info/categories/scala/"/>
<category term="scala" scheme="http://ibruce.info/tags/scala/"/>
<category term="microservice" scheme="http://ibruce.info/tags/microservice/"/>
<category term="akka" scheme="http://ibruce.info/tags/akka/"/>
<category term="actor" scheme="http://ibruce.info/tags/actor/"/>
</entry>
<entry>
<title>ZooKeeper 简介</title>
<link href="http://ibruce.info/2014/10/23/zookeeper/"/>
<id>http://ibruce.info/2014/10/23/zookeeper/</id>
<published>2014-10-23T01:32:17.000Z</published>
<updated>2016-01-03T09:42:20.000Z</updated>
<content type="html"><![CDATA[<p>系统越来越大后切分出的模块越来越多,一些模块还部署了双机。配置文件混乱,更新部署极易出错,准备上 <a href="http://zookeeper.apache.org" target="_blank" rel="noopener">zookeeper</a>。</p>
<p>下载当前稳定版的 <a href="http://zookeeper.apache.org/releases.html" target="_blank" rel="noopener">zookeeper</a>,解压并拷贝 conf/zoo_sample.cfg 为 zoo.cfg,配置所需要的项目。最少配是只指定其中的 dataDir 项,其他项则使用默认值。</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">tickTime=2000 # 心跳基本时间单位,毫秒级,zk基本上所有时间都是这个时间的整数倍</span><br><span class="line">clientPort=2181 # 监听客户端连接的端口</span><br><span class="line">dataDir=/zookeeper/server0/data # 内存数据库快照存放地址</span><br><span class="line">dataLogDir=/zookeeper/server0/dataLog # 事务日志存放地址,未指定则同dataDir项,建议分开</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="主要配置项说明">主要配置项说明</h2><h3 id="最小配置">最小配置</h3><p>最小配是保证zk正常运行必不可少的配置项。</p>
<ul>
<li><strong>clientPort</strong>:监听客户端连接的服务端口</li>
<li><strong>dataDir</strong>:内存数据库快照地址,事务日志地址(除非由 dataLogDir 另行指定)</li>
<li><strong>tickTime</strong>:毫秒级的基本时间单位,其他时间如心跳/超时等都为该单位时间的整数倍</li>
</ul>
<blockquote>
<p> 建议另行指定 dataLogDir,以便将事务日志存储在单独的路径下,这很重要,因事物日志存储的设备效率直接影响zk的性能。</p>
</blockquote>
<h3 id="高级配置">高级配置</h3><p>高级参数是可选的,可以通过这些参数调优zk性能。有些参数还可以通过 java system properties 动态指定,格式为 zookeeper.keyword 。</p>
<ul>
<li><strong>dataLogDir</strong>:事务日志目录,可以使用专用的设备,以避免事务日志与快照之间的竞争。(No Java system property)</li>
<li><strong>globalOutstandingLimit</strong>:zk接收的请求队列大小,默认1000,设置太大对导致内存溢出。(Java system property: zookeeper.globalOutstandingLimit)</li>
<li><strong>preAllocSize</strong>:预先分配事务日志空间块,单位kb,默认64M。如果快照写入很频繁,减少这个值,参考 snapCount。 (Java system property: zookeeper.preAllocSize)</li>
<li><strong>snapCount</strong>:每当 snapCount 个事物日志写入时,快照被创建,同时创建新的事务日志文件,默认值100,000。(Java system property: zookeeper.snapCount)</li>
</ul>
<p>垃圾清理参数</p>
<ul>
<li><strong>autopurge.purgeInterval</strong>:清理频率,单位小时,默认0,表示不开启清理功能。</li>
<li><strong>autopurge.snapRetainCount</strong>:需要保留的文件数目,默认是保留3个。</li>
</ul>
<h3 id="集群配置">集群配置</h3><ul>
<li><strong>electionAlg</strong>:领导选举算法,默认3。(No Java system property)</li>
<li><strong>initLimit</strong>:tickTime的倍数,表示leader选举结束后,followers与leader同步需要的时间,leader的数据非常多时或followers比较多,则该值应该适当大一些。(No Java system property)</li>
<li><strong>syncLimit</strong>:tickTime的倍数,表示follower和observer与leader交互时的最大等待时间,只不过是在与leader同步完毕之后,正常请求转发或ping等消息交互时的超时时间。(No Java system property)</li>
<li><strong>server.x</strong>=[hostname]:nnnnn[:nnnnn], etc<br>集群配置中,在dataDir目录下必须有一个myid文件,其中的值就是数字x,范围是1-255。第一个nnnnn是与leader通讯使用,第二个nnnnn是选举leader使用,electionAlg等于0时不需要此参数。(No Java system property)</li>
</ul>
<p>集群配置的例子<br>conf下的zoo.cfg文件:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">server.1=127.0.0.1:12888:13888</span><br><span class="line">server.2=127.0.0.1:22888:23888</span><br><span class="line">server.3=127.0.0.1:32888:33888</span><br></pre></td></tr></table></figure></p>
<p>dataDir下的myid文件:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">1</span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>更多参数参考 <a href="http://zookeeper.apache.org/doc/trunk/zookeeperAdmin.html" target="_blank" rel="noopener">ZK Administrator’s Guide</a>、<a href="http://blog.csdn.net/lovingprince/article/details/6853753" target="_blank" rel="noopener">ZK 配置项详解</a>、<a href="http://www.udpwork.com/item/2002.html" target="_blank" rel="noopener">ZK 配置文件</a> 或 <a href="http://nileader.blog.51cto.com/1381108/1032157" target="_blank" rel="noopener">ZK 管理员指南</a>。</p>
</blockquote>
<h2 id="Java_API">Java API</h2><p>zk 官方的Java API是非常难用的,建议使用第三方基于官方API封装的工具包。</p>
<ul>
<li>Netflix curator:<a href="http://github.com/Netflix/curator" target="_blank" rel="noopener">http://github.com/Netflix/curator</a>(强大)</li>
<li>sgroschupf zkclient:<a href="http://github.com/sgroschupf/zkclient" target="_blank" rel="noopener">http://github.com/sgroschupf/zkclient</a>(简单)</li>
<li>adyliu zkclient:<a href="http://github.com/adyliu/zkclient" target="_blank" rel="noopener">http://github.com/adyliu/zkclient</a>(极简)</li>
</ul>
<p>看一下官方API的主要接口:<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ZooKeeper</span><span class="params">(String connectString, <span class="keyword">int</span> sessionTimeout, Watcher watcher)</span> </span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> IOException</span></span><br></pre></td></tr></table></figure></p>
<ul>
<li>connectString:逗号分隔,如localhost:2181,127.0.0.1:2181,zk会选出一个建立连接</li>
<li>sessionTimeout:session超时时间</li>
<li>watcher:回调函数</li>
</ul>
<blockquote>
<p>该方法是非阻塞的,如果需要阻塞,可以在Watcher中自己处理。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">create</span><span class="params">(String path, <span class="keyword">byte</span> data[], List<ACL> acl, CreateMode createMode)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> KeeperException, InterruptedException</span></span><br><span class="line"><span class="function">``` </span></span><br><span class="line"><span class="function">- path:znode路径</span></span><br><span class="line"><span class="function">- data:znode上的数据</span></span><br><span class="line"><span class="function">- acl:权限信息, 如果不想指定权限, 可传入org.*.ZooDefs.Ids.OPEN_ACL_UNSAFE。</span></span><br><span class="line"><span class="function">- createMode:枚举类CreateMode,PERSISTENT / PERSISTENT_SEQUENTIAL / EPHEMERAL / EPHEMERAL_SEQUENTIAL</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">```java</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> List<String> <span class="title">getChildren</span><span class="params">(<span class="keyword">final</span> String path, Watcher watcher)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> KeeperException, InterruptedException</span></span><br></pre></td></tr></table></figure>
<ul>
<li>path:znode路径</li>
<li>watcher:回调函数</li>
</ul>
<blockquote>
<p>监听 path node 自身的删除事件,以及 children nodes 的创建/删除事件。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Stat <span class="title">exists</span><span class="params">(<span class="keyword">final</span> String path, Watcher watcher)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> KeeperException, InterruptedException</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>监听 path node 自身的创建/删除/数据更新事件,path 不存在返回 null。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">byte</span>[] getData(<span class="keyword">final</span> String path, Watcher watcher, Stat stat)</span><br><span class="line"> <span class="keyword">throws</span> KeeperException, InterruptedException</span><br></pre></td></tr></table></figure>
<blockquote>
<p>监听 path node 的删除/数据更新事件, 注意, 不监听 path node 的创建事件。stat 是传出参数,返回当前节点状态。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Stat <span class="title">setData</span><span class="params">(<span class="keyword">final</span> String path, <span class="keyword">byte</span> data[], <span class="keyword">int</span> version)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> KeeperException, InterruptedException</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>version 参数指定要更新数据的当前版本, 就是stats中的 version 值,如果和现有版本不匹配, 更新操作将失败。指定 version 为-1则忽略版本检查。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">(<span class="keyword">final</span> String path, <span class="keyword">int</span> version)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException, KeeperException</span></span><br></pre></td></tr></table></figure>
<p>总结:</p>
<blockquote>
<p>getChildren, getData, exists 方法可指定是否监听相应的事件;而create,delete,setData 方法则会触发相应的事件。</p>
</blockquote>
<h2 id="权限设置">权限设置</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// new一个acl</span></span><br><span class="line">List<ACL> acls = <span class="keyword">new</span> ArrayList<ACL>();</span><br><span class="line"><span class="comment">// 添加第一个id,采用用户名密码形式</span></span><br><span class="line">Id id1 = <span class="keyword">new</span> Id(<span class="string">"digest"</span>, DigestAuthenticationProvider.generateDigest(<span class="string">"admin:admin"</span>));</span><br><span class="line">ACL acl1 = <span class="keyword">new</span> ACL(ZooDefs.Perms.ALL, id1);</span><br><span class="line">acls.add(acl1);</span><br><span class="line"><span class="comment">// 添加第二个id,所有用户可读权限</span></span><br><span class="line">Id id2 = <span class="keyword">new</span> Id(<span class="string">"world"</span>, <span class="string">"anyone"</span>);</span><br><span class="line">ACL acl2 = <span class="keyword">new</span> ACL(ZooDefs.Perms.READ, id2);</span><br><span class="line">acls.add(acl2);</span><br><span class="line"></span><br><span class="line"><span class="comment">// zk用admin认证,创建/buru ZNode。</span></span><br><span class="line"><span class="keyword">final</span> ZooKeeper zk = <span class="keyword">new</span> ZooKeeper(<span class="string">"localhost:2181,127.0.0.1:2181"</span>, <span class="number">2000</span>, <span class="keyword">null</span>);</span><br><span class="line">zk.addAuthInfo(<span class="string">"digest"</span>, <span class="string">"admin:admin"</span>.getBytes());</span><br><span class="line"></span><br><span class="line">Stat exists = zk.exists(<span class="string">"/buru"</span>, <span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">if</span> (exists != <span class="keyword">null</span>)</span><br><span class="line"> zk.setData(<span class="string">"/buru"</span>, <span class="string">"2323"</span>.getBytes(), -<span class="number">1</span>);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> zk.create(<span class="string">"/buru"</span>, <span class="string">"data112"</span>.getBytes(), acls, CreateMode.PERSISTENT);</span><br></pre></td></tr></table></figure>
<p>更多内容参考文章, <a href="http://nettm.iteye.com/blog/1721778" target="_blank" rel="noopener">ZK 权限配置</a>、<a href="http://www.cnblogs.com/wangxiaowei/p/3315137.html" target="_blank" rel="noopener">ZK ACL使用</a>、<a href="http://nileader.blog.51cto.com/1381108/795525" target="_blank" rel="noopener">ZK 权限控制</a> 或 <a href="http://www.wuzesheng.com/?p=2438" target="_blank" rel="noopener">说说Zookeeper中的ACL</a>。</p>
<h2 id="管理工具">管理工具</h2><p>很可惜,没有顺手的,试试下面几个吧!</p>
<ul>
<li><a href="http://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip" target="_blank" rel="noopener">ZooInspector</a> </li>
<li><a href="http://github.com/nettm/ZooInspector" target="_blank" rel="noopener">ZooInspector 改进版</a></li>
<li><a href="http://github.com/killme2008/node-zk-browser" target="_blank" rel="noopener">node-zk-browser</a></li>
<li><a href="http://git.oschina.net/gznofeng/Node_Zookeeper_Admin" target="_blank" rel="noopener">Node_Zookeeper_Admin</a></li>
<li><a href="http://jm-blog.aliapp.com/?p=1450" target="_blank" rel="noopener">taokeeper</a></li>
<li><a href="http://github.com/ryuubaishi/zookeepercontroller" target="_blank" rel="noopener">zookeepercontroller</a></li>
</ul>
]]></content>
<summary type="html">
zookeeper 简介,zookeeper helloworld
</summary>
<category term="distribution" scheme="http://ibruce.info/categories/distribution/"/>
<category term="java" scheme="http://ibruce.info/tags/java/"/>
<category term="zookeeper" scheme="http://ibruce.info/tags/zookeeper/"/>
</entry>
<entry>
<title>Akka 快速入门</title>
<link href="http://ibruce.info/2014/05/20/hello-akka/"/>
<id>http://ibruce.info/2014/05/20/hello-akka/</id>
<published>2014-05-20T02:22:18.000Z</published>
<updated>2016-01-03T09:29:53.000Z</updated>
<content type="html"><![CDATA[<p>Akka的优点太多,高性能、高可靠、高并发、分布式、可容错、可扩展、事件驱动,不一一叙述。不同版本的API差异很大,本文代码运行在 <a href="http://www.scala-lang.org" target="_blank" rel="noopener">Scala 2.10.3</a> 和 <a href="http://akka.io" target="_blank" rel="noopener">Akka 2.3.2</a> 之上。<br><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.typesafe.akka<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>akka-actor_2.10<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.3.2<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.scala-lang<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>scala-library<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.10.3<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></p>
<h1 id="定义">定义</h1><p>定义Actor很简单,继承 akka.actor.Actor ,实现receive方法即可。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Hello</span> <span class="keyword">extends</span> <span class="title">Actor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> msg: <span class="type">String</span> => println(<span class="string">"hello "</span> + msg)</span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"unexpected message."</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h1 id="启动">启动</h1><p>创建Actor实例需要通过 ActorSystem 。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> system = <span class="type">ActorSystem</span>(<span class="string">"HelloSystem"</span>)</span><br><span class="line"><span class="keyword">val</span> hello = system.actorOf(<span class="type">Props</span>[<span class="type">Hello</span>], name = <span class="string">"hello"</span>)</span><br><span class="line"><span class="keyword">val</span> hello1 = system.actorOf(<span class="type">Props</span>[<span class="type">Hello</span>])</span><br><span class="line"><span class="keyword">val</span> hello2 = system.actorOf(<span class="type">Props</span>(<span class="keyword">new</span> <span class="type">Hello</span>()))</span><br></pre></td></tr></table></figure>
<p>如果要在 Actor 中继续创建子 Actor,需要使用内置的 ActorContext 对象。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line">context.actorOf(<span class="type">Props</span>[children], name = <span class="string">"children"</span>)</span><br></pre></td></tr></table></figure>
<p>如果要创建远程 Actor,需要通过 actorSelection 方法,原 actorFor 方法不再使用。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line">context.actorSelection(<span class="string">"akka.tcp://[email protected]:5150/user/RemoteActor"</span>)</span><br></pre></td></tr></table></figure>
<h1 id="发消息">发消息</h1><p>巨简单,就是一个!,可以发送任意类型的消息,此消息是异步的。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line">hello ! <span class="string">"bruce"</span></span><br><span class="line">hello ! <span class="number">10086</span></span><br></pre></td></tr></table></figure>
<p>同步消息的发送需要使用 Future 对象。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">implicit val timeout = Timeout(5 seconds)</span><br><span class="line">val future = hello ? "sha"</span><br><span class="line">val result = Await.result(future, timeout.duration).asInstanceOf[String]</span><br></pre></td></tr></table></figure>
<h1 id="停止">停止</h1><p>有两种方式停止一个Actor。</p>
<p>一种是通过内部 ActorContext.stop() 方法,该方法会将 children actor 逐层杀掉后,再自刎。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"stop"</span> => context.stop(self)</span><br><span class="line"> ...</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>另一种是外部喂毒药,通过 ActorRef.tell() 方法实现。后一个参数是向谁reply,这里显然不需要,传空。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line">hello.tell(<span class="type">PoisonPill</span>.getInstance, <span class="type">ActorRef</span>.noSender);</span><br></pre></td></tr></table></figure>
<h1 id="哼哈示例">哼哈示例</h1><blockquote>
<p>哼哈二将本是两位佛寺的门神俗称,是执金刚神的一种。明代小说《封神演义》作者陈仲琳据此附会两员神将,形象威武凶猛。一名郑伦,能鼻哼白气制敌;一名陈奇,能口哈黄气擒将。</p>
</blockquote>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">HengHa</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"> <span class="keyword">val</span> system = <span class="type">ActorSystem</span>(<span class="string">"HengHaSystem"</span>)</span><br><span class="line"> <span class="keyword">val</span> ha = system.actorOf(<span class="type">Props</span>[<span class="type">Ha</span>], name = <span class="string">"ha"</span>)</span><br><span class="line"> <span class="keyword">val</span> heng = system.actorOf(<span class="type">Props</span>(<span class="keyword">new</span> <span class="type">Heng</span>(ha)), name = <span class="string">"heng"</span>)</span><br><span class="line"></span><br><span class="line"> heng ! <span class="string">"start"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Heng</span>(<span class="params">ha: <span class="type">ActorRef</span></span>) <span class="keyword">extends</span> <span class="title">Actor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"start"</span> => ha ! <span class="string">"heng"</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">"ha"</span> => </span><br><span class="line"> println(<span class="string">"哈"</span>)</span><br><span class="line"> ha ! <span class="string">"heng"</span></span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"heng what?"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Ha</span> <span class="keyword">extends</span> <span class="title">Actor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"heng"</span> => </span><br><span class="line"> println(<span class="string">"哼"</span>)</span><br><span class="line"> sender ! <span class="string">"ha"</span></span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"ha what?"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Run 起来,结果:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">哼</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><br></pre></td></tr></table></figure>
<h1 id="远程示例">远程示例</h1><h3 id="local工程">local工程</h3><p>application.conf<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">akka {</span><br><span class="line"> loglevel = "DEBUG"</span><br><span class="line"> actor {</span><br><span class="line"> provider = "akka.remote.RemoteActorRefProvider"</span><br><span class="line"> }</span><br><span class="line"> remote {</span><br><span class="line"> enabled-transports = ["akka.remote.netty.tcp"]</span><br><span class="line"> netty.tcp {</span><br><span class="line"> hostname = "127.0.0.1"</span><br><span class="line"> port = 5155</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">object Local extends App {</span><br><span class="line"> val system = ActorSystem("LocalSystem")</span><br><span class="line"> val localActor = system.actorOf(Props[LocalActor], name = "LocalActor") // the local actor</span><br><span class="line"> localActor ! "START" // start the action</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LocalActor</span> <span class="keyword">extends</span> <span class="title">Actor</span> </span>{</span><br><span class="line"> <span class="comment">// create the remote actor</span></span><br><span class="line"> <span class="keyword">val</span> remote = context.actorSelection(<span class="string">"akka.tcp://[email protected]:5150/user/RemoteActor"</span>)</span><br><span class="line"> <span class="keyword">var</span> counter = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"START"</span> =></span><br><span class="line"> remote ! <span class="string">"Hello from the LocalActor"</span></span><br><span class="line"> <span class="keyword">case</span> msg: <span class="type">String</span> =></span><br><span class="line"> println(<span class="string">s"LocalActor received message: '<span class="subst">$msg</span>'"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (counter < <span class="number">5</span>) {</span><br><span class="line"> sender ! <span class="string">"Hello back to you"</span></span><br><span class="line"> counter += <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="remote工程">remote工程</h3><p>application.conf<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">akka {</span><br><span class="line"> loglevel = "DEBUG"</span><br><span class="line"> actor {</span><br><span class="line"> provider = "akka.remote.RemoteActorRefProvider"</span><br><span class="line"> }</span><br><span class="line"> remote {</span><br><span class="line"> enabled-transports = ["akka.remote.netty.tcp"]</span><br><span class="line"> netty.tcp {</span><br><span class="line"> hostname = "127.0.0.1"</span><br><span class="line"> port = 5150</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">HelloRemote</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"> <span class="keyword">val</span> system = <span class="type">ActorSystem</span>(<span class="string">"HelloRemoteSystem"</span>)</span><br><span class="line"> <span class="keyword">val</span> remoteActor = system.actorOf(<span class="type">Props</span>[<span class="type">RemoteActor</span>], name = <span class="string">"RemoteActor"</span>)</span><br><span class="line"></span><br><span class="line"> remoteActor ! <span class="string">"The RemoteActor is alive"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RemoteActor</span> <span class="keyword">extends</span> <span class="title">Actor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">receive</span> </span>= {</span><br><span class="line"> <span class="keyword">case</span> msg: <span class="type">String</span> =></span><br><span class="line"> println(<span class="string">s"RemoteActor received message '<span class="subst">$msg</span>'"</span>)</span><br><span class="line"> sender ! <span class="string">"Hello from the RemoteActor"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<!--
swind.code-life.info/categories/akka.html
-->]]></content>
<summary type="html">
akka示例,akka例子,akka入门,hello akka,akka快速入门,远程actor,remote actor,akka example,akka sample,akka demo
</summary>
<category term="scala" scheme="http://ibruce.info/categories/scala/"/>
<category term="scala" scheme="http://ibruce.info/tags/scala/"/>
<category term="akka" scheme="http://ibruce.info/tags/akka/"/>
</entry>
<entry>
<title>对接口设计的一个小看法</title>
<link href="http://ibruce.info/2014/04/16/a-small-point-of-interface-design/"/>
<id>http://ibruce.info/2014/04/16/a-small-point-of-interface-design/</id>
<published>2014-04-16T08:33:56.000Z</published>
<updated>2015-12-03T14:02:20.000Z</updated>
<content type="html"><![CDATA[<p>与另一产品线的同事们讨论对接接口,对一些领导和架构师的某观点十分不认可。内容如下:</p>
<blockquote>
<ul>
<li>接口中的数据类型全部是String,不要出现Int等其他类型,理由是统一好处理。</li>
<li>接口中Array类型请用String替代,值用逗号分割,理由是接口中不应该出现数据结构。</li>
</ul>
</blockquote>
<p>我强烈反对上述观点。我粗浅的认为设计的基本原则是<strong>世界是什么样子,你的设计就是什么样子</strong>,这是好设计的前提。设计原本很简单,就是对真实世界的合理抽象,不是人为扭曲。</p>
<a id="more"></a>
<p>接口设计的第一原则是,让使用者易懂易用,如果必须看文档看注释才能使用,那这个设计是失败的,是对码农代码的侮辱,还做个什么架构师?</p>
<p>对于第一条:</p>
<ol>
<li>调用者和接收者都将进行类型转换,不可避免的必须对转换异常进行处理,代码臃肿丑陋。</li>
<li>Null值如何处理,对于本该是Int的值,不传可以默认0,这造成接口中所有参数必须都传值。</li>
<li>给后来读代码的人造成极大的困惑,有些明显是Boolean类型的值。</li>
</ol>
<p>对于第二条:</p>
<ol>
<li>分隔符怎么通知使用方,必须写文档或注释吧?</li>
<li>不但没有避免双方人为沟通,反而增加沟通成本。如果接口中是Array,双方仅通过接口就已经明白对方的意思;如果是个拼接的字符串,实际上这个沟通成本后退到双方各自更深层次的代码中,换句话说,在更深层次中双方耦合了,而不是只在接口上耦合。</li>
<li>全部同上。</li>
</ol>
<p>举个例子,其中有一个接口是这样的:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> List<Map<String, Object>> jsonPersonGetAll(String eid, String begin, String count) <span class="keyword">throws</span> Exception;</span><br></pre></td></tr></table></figure>
<p>这个接口毛病很多,我吐槽一下。</p>
<ol>
<li>方法名不能传达准确的意思,json 在哪里?输入 json 还是输出 json ?</li>
<li>返回值是集合套集合,不应该是对象吗?想取一下 Map 中的 photoUrl 值,key 大小写不能错,取回后还得强转成 String!</li>
<li>begin/count 这两个变量可以看出,该方法是分页查询某 eid 下全部 person ,那么 begin/count 用 Int 更合理吧?</li>
<li>抛出Exception,咋不抛的更大呢,为什么不抛出具体的异常,这让上层如何处理?</li>
</ol>
<blockquote>
<p>上文中所谓的 ‘接口设计原则’ 、 ‘基本原则’ ,全是我个人的观点,水平有限,欢迎拍砖。</p>
</blockquote>
]]></content>
<summary type="html">
<p>与另一产品线的同事们讨论对接接口,对一些领导和架构师的某观点十分不认可。内容如下:</p>
<blockquote>
<ul>
<li>接口中的数据类型全部是String,不要出现Int等其他类型,理由是统一好处理。</li>
<li>接口中Array类型请用String替代,值用逗号分割,理由是接口中不应该出现数据结构。</li>
</ul>
</blockquote>
<p>我强烈反对上述观点。我粗浅的认为设计的基本原则是<strong>世界是什么样子,你的设计就是什么样子</strong>,这是好设计的前提。设计原本很简单,就是对真实世界的合理抽象,不是人为扭曲。</p>
</summary>
<category term="java" scheme="http://ibruce.info/categories/java/"/>
<category term="java" scheme="http://ibruce.info/tags/java/"/>
<category term="design" scheme="http://ibruce.info/tags/design/"/>
</entry>
<entry>
<title>通过 Spray 创建简单的 Restful Http Server</title>
<link href="http://ibruce.info/2014/04/06/hello-spray/"/>
<id>http://ibruce.info/2014/04/06/hello-spray/</id>
<published>2014-04-06T09:35:19.000Z</published>
<updated>2015-12-03T14:02:22.000Z</updated>
<content type="html"><![CDATA[<p>好久没写文章,实在太忙。最近我在项目中,搭建了很多小的零散的 Restful 服务,各个服务之间通过rest接口调用,相互协作。</p>
<p>Java出身的码农,动辄就是Tomcat、Spring,RestEasy、Hibernate、JPA、Jackson、HttpClient… 这些巨无霸非常臃肿,其实我的项目就是一个简简单单的Rest服务,请求Json参数 → 调用资源接口 → 返回Json数据,还好有Spray!</p>
<p>使用Scala系的工具,无论是体重上(包的大小)还是身高上(代码的长度),都能极大的减负,同时提升逼格和优雅度。下面介绍一个简单的Restful Http Server的开发示例。</p>
<p>首先安装 <a href="http://www.scala-sbt.org" target="_blank" rel="noopener">SBT</a>(简单理解为scala下的maven),安装方法参考 <a href="http://www.scala-sbt.org/release/docs/Getting-Started/Setup.html" target="_blank" rel="noopener">sbt documentation - setup</a>。</p>
<p>win7环境下,直接下载 <a href="http://repo.scala-sbt.org/scalasbt/sbt-native-packages/org/scala-sbt/sbt/0.13.1/sbt.zip" target="_blank" rel="noopener">0.13.1-zip包</a> 解压到c:\sbt,配置SBT_HOME到path,cmd中敲入sbt检查有无成功,%SBT_HOME%\conf\sbtconfig.txt中可以配置启动参数。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># Set the java args to high</span><br><span class="line">-Xmx1024M</span><br><span class="line">-XX:MaxPermSize=512m</span><br><span class="line">-XX:ReservedCodeCacheSize=256m</span><br><span class="line"></span><br><span class="line"># Set the extra SBT options</span><br><span class="line">-Dsbt.log.format=true</span><br><span class="line">-Dfile.encoding=ISO-8859-1</span><br><span class="line">-Dsbt.boot.directory=H:/sbt/boot/ </span><br><span class="line">-Dsbt.ivy.home=H:/sbt/</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<blockquote>
<p>1、-Dfile.encoding 配置成 ISO-8859-1 而不是 UTF-8,是因为在 cmd 下按键盘的上下键,无法切换历史输入的sbt命令,显示全是乱码,这让我很不爽,只好忍痛ISO啦。<br>2、-Dsbt.boot.directory 和 -Dsbt.ivy.home 是为将sbt自动下载的jar放到非系统盘,以免统一管理,这个看个人爱好。</p>
</blockquote>
<p>下面跑一个官方的例子,从Spray的官方文档 <a href="http://spray.io/introduction/getting-started" target="_blank" rel="noopener">Getting Started</a> 上,找到 <a href="https://github.com/spray/spray-template" target="_blank" rel="noopener">spray-template</a>,在branch中下载1.3版的 <a href="http://github.com/spray/spray-template/tree/on_spray-can_1.3" target="_blank" rel="noopener">spray-can sample</a>。<br>解压,执行sbt命令。在浏览器运行 <a href="http://127.0.0.1:8080" target="_blank" rel="noopener">http://127.0.0.1:8080</a> 看结果。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">sbt</span><br><span class="line">compile</span><br><span class="line">run</span><br></pre></td></tr></table></figure>
<blockquote>
<p>build.sbt 已配置 Revolver.settings,可使用命令:re-start和re-stop,详细参考 <a href="http://github.com/spray/sbt-revolver" target="_blank" rel="noopener">sbt-revolver</a>。</p>
</blockquote>
<p>瞄一下 <a href="https://github.com/spray/spray-template/blob/on_spray-can_1.3/src/main/scala/com/example/MyService.scala" target="_blank" rel="noopener">src/main/scala/com/example/MyService.scala</a>,可以看到只有简单的。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">trait MyService extends HttpService {</span><br><span class="line"> val myRoute =</span><br><span class="line"> path("") {</span><br><span class="line"> get {</span><br><span class="line"> respondWithMediaType(`text/html`) {</span><br><span class="line"> complete {</span><br><span class="line"> <html></span><br><span class="line"> <body></span><br><span class="line"> <h1>Say hello to <i>spray-routing</i> on <i>spray-can</i>!</h1></span><br><span class="line"> </body></span><br><span class="line"> </html></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><br></pre></td></tr></table></figure></p>
<p>Boot方法启动一个Actor并绑定到8080端口。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">object Boot extends App {</span><br><span class="line"> // we need an ActorSystem to host our application in</span><br><span class="line"> implicit val system = ActorSystem("on-spray-can")</span><br><span class="line"> // create and start our service actor</span><br><span class="line"> val service = system.actorOf(Props[MyServiceActor], "demo-service")</span><br><span class="line"> implicit val timeout = Timeout(5.seconds)</span><br><span class="line"> // start a new HTTP server on port 8080 with our service actor as the handler</span><br><span class="line"> IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>在 <a href="http://github.com/spray/spray-template/tree/on_jetty_1.3" target="_blank" rel="noopener">spray-servlet on jetty</a> 分支下,有部署到servlet容器的工程模板代码。另外在 <a href="http://github.com/spray/spray" target="_blank" rel="noopener">Spray</a> 项目的源代码下,也有一些 <a href="http://github.com/spray/spray/tree/master/examples" target="_blank" rel="noopener">example</a>,找到 <a href="http://github.com/spray/spray/tree/master/examples/spray-can/simple-http-server" target="_blank" rel="noopener">spray-can/simple-http-server</a>,也可以试着跑一下,其中的 <a href="https://github.com/spray/spray/blob/master/examples/spray-can/simple-http-server/src/main/scala/spray/examples/DemoService.scala" target="_blank" rel="noopener">DemoService.scala</a> 例子内容比较多。</p>
<blockquote>
<p>更多玩法参考 <a href="http://github.com/spray/spray/wiki/Example-Projects" target="_blank" rel="noopener">wiki example-projects</a> 或 <a href="http://spray.io/documentation/1.2.1/spray-can/" target="_blank" rel="noopener">document spray-can</a>。</p>
</blockquote>
<p>又要忙项目,推荐两个文章吧,自己写 spray 工程可以参考一下:<a href="http://sysgears.com/articles/building-rest-service-with-scala" target="_blank" rel="noopener">Building REST Service with Scala</a>、<a href="http://www.cakesolutions.net/teamblogs/2012/07/29/api-first-rest-in-akka-and-spray" target="_blank" rel="noopener">API first REST in Akka and Spray</a>、<a href="https://github.com/mhamrah/stockman" target="_blank" rel="noopener">stockman</a>。</p>
<!--
参考文档
* [用 Spray 建立一個簡單的 RESTful API Server](http://swind.code-life.info/posts/build-restful-api-server-by-spray.html)
-->]]></content>
<summary type="html">
Spray Restful Http Server 例子,spray can,spray servlet,sbt
</summary>
<category term="scala" scheme="http://ibruce.info/categories/scala/"/>
<category term="scala" scheme="http://ibruce.info/tags/scala/"/>
<category term="akka" scheme="http://ibruce.info/tags/akka/"/>
<category term="spray" scheme="http://ibruce.info/tags/spray/"/>
<category term="sbt" scheme="http://ibruce.info/tags/sbt/"/>
</entry>
<entry>
<title>转折点</title>
<link href="http://ibruce.info/2014/02/28/turning-point-2014/"/>
<id>http://ibruce.info/2014/02/28/turning-point-2014/</id>
<published>2014-02-28T08:37:49.000Z</published>
<updated>2015-12-03T14:02:26.000Z</updated>
<content type="html"><![CDATA[<p>二月的最后一天,新的三月,重要转折点之一。离开落后愚昧,大踏步,走向互联网的节奏。<br><a id="more"></a></p>
]]></content>
<summary type="html">
<p>二月的最后一天,新的三月,重要转折点之一。离开落后愚昧,大踏步,走向互联网的节奏。<br>
</summary>
</entry>
<entry>
<title>微菜谱</title>
<link href="http://ibruce.info/2014/02/14/caipus/"/>
<id>http://ibruce.info/2014/02/14/caipus/</id>
<published>2014-02-14T06:51:16.000Z</published>
<updated>2016-01-03T09:49:27.000Z</updated>
<content type="html"><![CDATA[<p><img src="http://bruce.u.qiniudn.com/2014/02/14/caipu-qrcode-1280.jpg" alt="QRCode"></p>
<a id="more"></a>
<p>花一天时间,做了这个微信应用 <strong><a href="http://bruce.u.qiniudn.com/2014/02/14/caipu-qrcode-258.jpg" target="_blank" rel="noopener">微菜谱</a></strong>,目前测试阶段,欢迎提出意见和建议,感谢推荐!</p>
<ul>
<li>名称:微菜谱</li>
<li>微信号:caipus</li>
<li>功能介绍:微菜谱,菜谱大全,菜谱查询,家常菜做法。只需输入菜名,小菜君即可告诉您具体的主料,配料及制作步骤。</li>
<li>我们的口号:没有难做的菜!</li>
</ul>
<blockquote>
<p>本司承接各种微信公众号申请、开发工作,物美价廉,童叟无欺,请联系<strong>[email protected]</strong>。</p>
</blockquote>
]]></content>
<summary type="html">
微菜谱,微信菜谱,菜谱大全,菜谱查询,家常菜做法。
</summary>
<category term="default" scheme="http://ibruce.info/categories/default/"/>
<category term="weixin" scheme="http://ibruce.info/tags/weixin/"/>
</entry>
<entry>
<title>简单美观的文字标签云组件</title>
<link href="http://ibruce.info/2014/02/10/the-most-beautiful-word-cloud/"/>
<id>http://ibruce.info/2014/02/10/the-most-beautiful-word-cloud/</id>
<published>2014-02-10T09:35:20.000Z</published>
<updated>2015-12-03T14:02:26.000Z</updated>
<content type="html"><![CDATA[<p>经常在微博或微信的文章中看到漂亮的分析图。我认为在大数据的时代,目前最关键的就是如何让非专业人员轻松的进行数据分析,比如可以象使用office一样制作信息图(infographic),而不是用专业的制图工具。这一步跨过去,看到的将是欣欣向荣的真正大数据时代。</p>
<p>而这之前,首先缺少的就是,可以让普通开发人员使用的大数据时代的可视化图表组件,比如标签云图,所幸,业界已经有<a href="http://github.com/ecomfe/echarts" target="_blank" rel="noopener">ECharts</a>和<a href="http://github.com/timdream/wordcloud" target="_blank" rel="noopener">WordCloud</a>这两大利器,本文只介绍后者。</p>
<p><img src="http://bruce.u.qiniudn.com/2014/02/10/wordcloud-cn.jpg" alt="中文"></p>
<a id="more"></a>
<p>首先页面必须是html5编写。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><!DOCTYPE html></span><br><span class="line"><html></span><br><span class="line"> <head></span><br><span class="line"> <meta charset="UTF-8"></span><br><span class="line"> <title></title></span><br><span class="line"> </head></span><br><span class="line"> <body> </span><br><span class="line"> </body></span><br><span class="line"></html></span><br></pre></td></tr></table></figure></p>
<p>引入<a href="http://jquery.com/" target="_blank" rel="noopener">jQuery</a>和<a href="http://github.com/timdream/wordcloud2.js" target="_blank" rel="noopener">WordCloud2.js</a>。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><script src="src/wordcloud2.js"></script></span><br><span class="line"><script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script></span><br></pre></td></tr></table></figure>
<p>定义canvas容器。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><div id="canvas-container" align="center"></span><br><span class="line"> <canvas id="canvas" width="800px" height="600px"></canvas></span><br><span class="line"></div></span><br></pre></td></tr></table></figure>
<p>绘图。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><script> </span><br><span class="line">var options = eval({</span><br><span class="line"> "list": [['傻猎豹', 10], ['不如', 9], ['麻花疼', 7], ['麻云', 6],['李眼红', 4], ['雷布斯', 5],['周红衣', 4],['刘墙洞', 3],['李国情', 3]],</span><br><span class="line"> "gridSize": 8,</span><br><span class="line"> "weightFactor": 16,</span><br><span class="line"> "fontFamily": 'Hiragino Mincho Pro, serif',</span><br><span class="line"> "color": 'random-dark',</span><br><span class="line"> "backgroundColor": '#f0f0f0',</span><br><span class="line"> "rotateRatio": 0</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">var canvas = document.getElementById('canvas');</span><br><span class="line"></span><br><span class="line">WordCloud(canvas, options);</span><br><span class="line"></script></span><br></pre></td></tr></table></figure>
<blockquote>
<p>至此,全部完毕。执行页面,美丽的云图便展现在你面前,具体的API可以参考<a href="http://github.com/timdream/wordcloud2.js/blob/master/API.md" target="_blank" rel="noopener">这里</a>。</p>
</blockquote>
<p>下面举个英文的例子,为了美观稍微改变一下参数:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">"list": [['bruce-sha', 10], ['buru', 9], ['tencent', 7], ['alibaba', 6], ['baidu', 4], ['xiaomi', 5],['360', 4],['jingdong', 3],['dangdang', 3],['ibruce.info', 1]],</span><br><span class="line">"gridSize": 16,</span><br><span class="line">"weightFactor": 16,</span><br><span class="line">"fontFamily": 'Times, serif',</span><br><span class="line">"color": 'random-light',</span><br><span class="line">"backgroundColor": '#333',</span><br><span class="line">"rotateRatio": 0</span><br></pre></td></tr></table></figure>
<p><img src="http://bruce.u.qiniudn.com/2014/02/10/wordcloud-en.jpg" alt="英文"></p>
]]></content>
<summary type="html">
标签云 关键字云 词汇云 文字云 词云 超级简单的绘图组件 javascript html5 canvas api
</summary>
<category term="big data" scheme="http://ibruce.info/categories/big-data/"/>
<category term="javascript" scheme="http://ibruce.info/tags/javascript/"/>
<category term="big data" scheme="http://ibruce.info/tags/big-data/"/>
<category term="word cloud" scheme="http://ibruce.info/tags/word-cloud/"/>
</entry>
<entry>
<title>爬爬网</title>
<link href="http://ibruce.info/2014/01/27/pa-pa-wang/"/>
<id>http://ibruce.info/2014/01/27/pa-pa-wang/</id>
<published>2014-01-27T09:39:53.000Z</published>
<updated>2015-12-03T14:02:24.000Z</updated>
<content type="html"><![CDATA[<p>放鞭放炮,赶在201314交替之际,爬爬网的alpha版,终于千呼万唤终于始出来啦。</p>
<p><img src="http://bruce.u.qiniudn.com/2014/01/papawang.jpg" alt="爬爬网"></p>
<p>本人是爬爬科技的ceo,我们爬爬科技的口号是:爬你,爬我,爬他全家。。。</p>
<a id="more"></a>]]></content>
<summary type="html">
爬爬网 爬爬科技 PaPaWang papawang.com papaTech
</summary>
<category term="default" scheme="http://ibruce.info/categories/default/"/>
<category term="papa" scheme="http://ibruce.info/tags/papa/"/>
</entry>
<entry>
<title>搭建eclipse+maven+scala-ide的scala web开发环境</title>
<link href="http://ibruce.info/2014/01/15/scala-web-environment-with-eclipse-maven-scala-ide/"/>
<id>http://ibruce.info/2014/01/15/scala-web-environment-with-eclipse-maven-scala-ide/</id>
<published>2014-01-15T02:35:48.000Z</published>
<updated>2015-12-03T14:02:24.000Z</updated>
<content type="html"><![CDATA[<p>江湖传闻,scala开发的最佳利器乃 <a href="http://www.jetbrains.com" target="_blank" rel="noopener">JetBrains</a> 的神作 <a href="http://www.jetbrains.com/idea" target="_blank" rel="noopener">IntelliJ IDEA</a>,外加构建工具 <a href="http://www.scala-sbt.org" target="_blank" rel="noopener">sbt</a> 是也。</p>
<p>但因历史原因,项目组成员对 <a href="http://www.eclipse.org/downloads" target="_blank" rel="noopener">Eclipse</a>+<a href="http://maven.apache.org" target="_blank" rel="noopener">Maven</a>组合更为熟悉,为了快速实现项目原型,不增加不确定因素带来的风险,搭建一套 <a href="http://www.eclipse.org/downloads" target="_blank" rel="noopener">Eclipse</a>+<a href="http://maven.apache.org" target="_blank" rel="noopener">Maven</a>+<a href="http://scala-ide.org" target="_blank" rel="noopener">Scala-IDE</a> 的开发环境。</p>
<p>基本原则是,必须完全满足但不限于以下几点内容:</p>
<ul>
<li>方便重构,方便调试,支持热部署。</li>
<li>可直接使用已有maven的本地和私服仓库。</li>
<li>可以无束缚的只用自己熟悉的语言编写代码。</li>
<li>可以快速混合编译scala+java代码,包括交叉引用的文件。</li>
</ul>
<p>如果你有洁癖,可以自己下载<a href="http://www.eclipse.org/downloads" target="_blank" rel="noopener">Eclipse</a>,然后安装各种插件。但是可能会遇到插件依赖包版本冲突之类的问题,为了速度,我直接下载官方打包好的 <a href="http://scala-ide.org/download/sdk.html" target="_blank" rel="noopener">Scala-IDE</a>,有各种平台可供选择。</p>
<p>使用 <a href="http://git-scm.com" target="_blank" rel="noopener">Git</a> 管理项目源代码,需要安装 <a href="http://www.eclipse.org/egit" target="_blank" rel="noopener">EGit</a> 插件,Eclipse插件更新地址 <a href="http://download.eclipse.org/egit/updates" target="_blank" rel="noopener">EGit Updates</a>。</p>
<p>假设项目名称为 <strong>feeling</strong>,使用 JDK 1.7,Servlet 3.0,最终目录结构如下。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">.</span><br><span class="line">├── .settings #eclipse工程目录</span><br><span class="line">├── .classpath #eclipse classpath文件</span><br><span class="line">├── .project #eclipse project文件</span><br><span class="line">├── src #源代码</span><br><span class="line">| ├── main #源代码主目录</span><br><span class="line">| | ├── java #java代码</span><br><span class="line">| | ├── scala #scala代码</span><br><span class="line">| | ├── resources #资源文件</span><br><span class="line">| | └── webapp #web主目录</span><br><span class="line">| | ├── WEB-INF #WEB-INF目录</span><br><span class="line">| | | └── web.xml #web.xml文件</span><br><span class="line">| | └── index.jsp #主页面</span><br><span class="line">| └── test #测试代码</span><br><span class="line">| ├── java #java测试代码</span><br><span class="line">| ├── scala #scala测试代码</span><br><span class="line">| └── resources #测试资源文件</span><br><span class="line">├── .gitignore #git忽略配置</span><br><span class="line">├── target #编译输出目录</span><br><span class="line">├── README.md #markdown格式的说明文件</span><br><span class="line">└── pom.xml #maven的pom文件</span><br></pre></td></tr></table></figure></p>
<a id="more"></a>
<p>pom.xml文件<br><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">modelVersion</span>></span>4.0.0<span class="tag"></<span class="name">modelVersion</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>feeling<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>feeling<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>0.0.1<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">packaging</span>></span>war<span class="tag"></<span class="name">packaging</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- <name>${project.artifactId}</name> --></span></span><br><span class="line"> <span class="tag"><<span class="name">name</span>></span>feeling<span class="tag"></<span class="name">name</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">description</span>></span>our wonderfully feeling application<span class="tag"></<span class="name">description</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span>http://feeling.com<span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">inceptionYear</span>></span>2014<span class="tag"></<span class="name">inceptionYear</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">organization</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">name</span>></span>feeling<span class="tag"></<span class="name">name</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span>http://feeling.com<span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">organization</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">licenses</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">license</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">name</span>></span>The Apache Software License, Version 2.0<span class="tag"></<span class="name">name</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span>http://www.apache.org/licenses/LICENSE-2.0.txt<span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">distribution</span>></span>repo<span class="tag"></<span class="name">distribution</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">license</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">licenses</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">developers</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">developer</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">id</span>></span>bruce<span class="tag"></<span class="name">id</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">name</span>></span>bruce sha<span class="tag"></<span class="name">name</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span>http://bruce-sha.github.io<span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">email</span>></span>[email protected]<span class="tag"></<span class="name">email</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">developer</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">developers</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">scm</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">connection</span>></span>http://17.20.13.23/scm/git/feeling<span class="tag"></<span class="name">connection</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">developerConnection</span>></span>http://17.20.13.23/scm/git/feeling<span class="tag"></<span class="name">developerConnection</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span>http://17.20.13.23<span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">scm</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">properties</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scala.version</span>></span>2.10.3<span class="tag"></<span class="name">scala.version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">maven.compiler.source</span>></span>1.7<span class="tag"></<span class="name">maven.compiler.source</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">maven.compiler.target</span>></span>1.7<span class="tag"></<span class="name">maven.compiler.target</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">encoding</span>></span>UTF-8<span class="tag"></<span class="name">encoding</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">properties</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 个性化开发 --></span></span><br><span class="line"> <span class="tag"><<span class="name">profiles</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">profile</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">id</span>></span>dev<span class="tag"></<span class="name">id</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">activation</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">activeByDefault</span>></span>true<span class="tag"></<span class="name">activeByDefault</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">activation</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">properties</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">build.param</span>></span>this is dev<span class="tag"></<span class="name">build.param</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">properties</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">profile</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">profile</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">id</span>></span>release<span class="tag"></<span class="name">id</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">activation</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">activeByDefault</span>></span>false<span class="tag"></<span class="name">activeByDefault</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">activation</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">properties</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">build.param</span>></span>this is relase<span class="tag"></<span class="name">build.param</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">properties</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">profile</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">profiles</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependencies</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- google --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.google.guava<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>guava<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>15.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.google.inject<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>guice<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>3.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- servlet --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>javax.servlet<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>javax.servlet-api<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="comment"><!-- <version>2.5</version> --></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>3.0.1<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>provided<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- <dependency> --></span></span><br><span class="line"> <span class="comment"><!-- <groupId>javax.servlet</groupId> --></span></span><br><span class="line"> <span class="comment"><!-- <artifactId>jsp-api</artifactId> --></span></span><br><span class="line"> <span class="comment"><!-- <version>2.0</version> --></span></span><br><span class="line"> <span class="comment"><!-- <scope>provided</scope> --></span></span><br><span class="line"> <span class="comment"><!-- </dependency> --></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- scala --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.scala-lang<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>scala-library<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>${scala.version}<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- test --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>junit<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>junit<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>4.11<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 其他包不再一一描述 --></span> </span><br><span class="line"> <span class="comment"><!-- log --></span></span><br><span class="line"> <span class="comment"><!-- json --></span></span><br><span class="line"> <span class="comment"><!-- mongodb --></span></span><br><span class="line"> <span class="comment"><!-- quartz --></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">dependencies</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">build</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">finalName</span>></span>feeling<span class="tag"></<span class="name">finalName</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 必须要,资源文件中占位符被profile替换的关键配置 --></span></span><br><span class="line"> <span class="tag"><<span class="name">resources</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">resource</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">directory</span>></span>src/main/resources<span class="tag"></<span class="name">directory</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">includes</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">include</span>></span>*.*<span class="tag"></<span class="name">include</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">includes</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">filtering</span>></span>true<span class="tag"></<span class="name">filtering</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">resource</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">resources</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 必须干掉,否则不编译src/main/java下的代码 --></span></span><br><span class="line"> <span class="comment"><!-- <sourceDirectory>src/main/scala</sourceDirectory> --></span></span><br><span class="line"> <span class="comment"><!-- <testSourceDirectory>src/test/scala</testSourceDirectory> --></span></span><br><span class="line"> <span class="tag"><<span class="name">plugins</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">plugin</span>></span></span><br><span class="line"> <span class="comment"><!-- see http://davidb.github.com/scala-maven-plugin --></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>net.alchim31.maven<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>scala-maven-plugin<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>3.1.6<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="comment"><!-- 必须要,否则不能混合编译交叉引用文件 --></span></span><br><span class="line"> <span class="tag"><<span class="name">executions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">execution</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">id</span>></span>scala-compile-first<span class="tag"></<span class="name">id</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">phase</span>></span>process-resources<span class="tag"></<span class="name">phase</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goals</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goal</span>></span>add-source<span class="tag"></<span class="name">goal</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goal</span>></span>compile<span class="tag"></<span class="name">goal</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">goals</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">execution</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">execution</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">id</span>></span>scala-test-compile<span class="tag"></<span class="name">id</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">phase</span>></span>process-test-resources<span class="tag"></<span class="name">phase</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goals</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goal</span>></span>testCompile<span class="tag"></<span class="name">goal</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">goals</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">execution</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">executions</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">plugin</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">plugin</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.maven.plugins<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>maven-surefire-plugin<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.13<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">useFile</span>></span>false<span class="tag"></<span class="name">useFile</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">disableXmlReport</span>></span>true<span class="tag"></<span class="name">disableXmlReport</span>></span></span><br><span class="line"> <span class="comment"><!-- If you have classpath issue like NoDefClassError,... --></span></span><br><span class="line"> <span class="comment"><!-- useManifestOnlyJar>false</useManifestOnlyJar --></span></span><br><span class="line"> <span class="tag"><<span class="name">includes</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">include</span>></span>**/*Test.*<span class="tag"></<span class="name">include</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">include</span>></span>**/*Suite.*<span class="tag"></<span class="name">include</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">includes</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">plugin</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">plugin</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.maven.plugins<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>maven-war-plugin<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.4<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="comment"><!-- 移除web.xml的依赖,Servlet 3.0可以不要web.xml文件 --></span></span><br><span class="line"> <span class="tag"><<span class="name">failOnMissingWebXml</span>></span>false<span class="tag"></<span class="name">failOnMissingWebXml</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">plugin</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- jetty6,不支持servlet3 --></span></span><br><span class="line"> <span class="comment"><!-- <plugin> --></span></span><br><span class="line"> <span class="comment"><!-- <groupId>org.mortbay.jetty</groupId> --></span></span><br><span class="line"> <span class="comment"><!-- <artifactId>maven-jetty-plugin</artifactId> --></span></span><br><span class="line"> <span class="comment"><!-- <version>6.1.26</version> --></span></span><br><span class="line"> <span class="comment"><!-- <configuration> --></span></span><br><span class="line"> <span class="comment"><!-- <scanIntervalSeconds>10</scanIntervalSeconds> --></span></span><br><span class="line"> <span class="comment"><!-- <stopKey>foo</stopKey> --></span></span><br><span class="line"> <span class="comment"><!-- <stopPort>9999</stopPort> --></span></span><br><span class="line"> <span class="comment"><!-- </configuration> --></span></span><br><span class="line"> <span class="comment"><!-- <executions> --></span></span><br><span class="line"> <span class="comment"><!-- <execution> --></span></span><br><span class="line"> <span class="comment"><!-- <id>start-jetty</id> --></span></span><br><span class="line"> <span class="comment"><!-- <phase>pre-integration-test</phase> --></span></span><br><span class="line"> <span class="comment"><!-- <goals> --></span></span><br><span class="line"> <span class="comment"><!-- <goal>run</goal> --></span></span><br><span class="line"> <span class="comment"><!-- </goals> --></span></span><br><span class="line"> <span class="comment"><!-- <configuration> --></span></span><br><span class="line"> <span class="comment"><!-- <scanIntervalSeconds>0</scanIntervalSeconds> --></span></span><br><span class="line"> <span class="comment"><!-- <daemon>true</daemon> --></span></span><br><span class="line"> <span class="comment"><!-- </configuration> --></span></span><br><span class="line"> <span class="comment"><!-- </execution> --></span></span><br><span class="line"> <span class="comment"><!-- <execution> --></span></span><br><span class="line"> <span class="comment"><!-- <id>stop-jetty</id> --></span></span><br><span class="line"> <span class="comment"><!-- <phase>post-integration-test</phase> --></span></span><br><span class="line"> <span class="comment"><!-- <goals> --></span></span><br><span class="line"> <span class="comment"><!-- <goal>stop</goal> --></span></span><br><span class="line"> <span class="comment"><!-- </goals> --></span></span><br><span class="line"> <span class="comment"><!-- </execution> --></span></span><br><span class="line"> <span class="comment"><!-- </executions> --></span></span><br><span class="line"> <span class="comment"><!-- </plugin> --></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- tomcat7:run 注意tomcat:run跑的是6,不支持servlet3 --></span></span><br><span class="line"> <span class="tag"><<span class="name">plugin</span>></span></span><br><span class="line"> <span class="comment"><!-- http://tomcat.apache.org/maven-plugin-2.0/tomcat7-maven-plugin --></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.tomcat.maven<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>tomcat7-maven-plugin<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.2<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">path</span>></span>/<span class="tag"></<span class="name">path</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">port</span>></span>80<span class="tag"></<span class="name">port</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">plugin</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- jetty:run --></span></span><br><span class="line"> <span class="tag"><<span class="name">plugin</span>></span></span><br><span class="line"> <span class="comment"><!-- http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin --></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.mortbay.jetty<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="comment"><!-- <artifactId>maven-jetty-plugin</artifactId> 这是jetty6 不支持servlet3 --></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jetty-maven-plugin<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>8.1.13.v20130916<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">stopPort</span>></span>9966<span class="tag"></<span class="name">stopPort</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">stopKey</span>></span>foo<span class="tag"></<span class="name">stopKey</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scanIntervalSeconds</span>></span>0<span class="tag"></<span class="name">scanIntervalSeconds</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">connectors</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">connector</span> <span class="attr">implementation</span>=<span class="string">"org.eclipse.jetty.server.nio.SelectChannelConnector"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">port</span>></span>80<span class="tag"></<span class="name">port</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">maxIdleTime</span>></span>60000<span class="tag"></<span class="name">maxIdleTime</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">connector</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">connectors</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">webAppConfig</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">contextPath</span>></span>/<span class="tag"></<span class="name">contextPath</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">webAppConfig</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">plugin</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">plugins</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">build</span>></span></span><br><span class="line"><span class="tag"></<span class="name">project</span>></span></span><br></pre></td></tr></table></figure></p>
<p>web.xml</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><?xml version="1.0" encoding="UTF-8"?></span><br><span class="line"><!-- <web-app --></span><br><span class="line"><!-- xmlns="http://java.sun.com/xml/ns/javaee" --></span><br><span class="line"><!-- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" --></span><br><span class="line"><!-- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" --></span><br><span class="line"><!-- version="2.5" --></span><br><span class="line"><!-- > --></span><br><span class="line"><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</span><br><span class="line"> xmlns="http://java.sun.com/xml/ns/javaee"</span><br><span class="line"> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"</span><br><span class="line"> id="WebApp_ID" version="3.0"></span><br><span class="line"></span><br><span class="line"> <display-name>feeling</display-name></span><br><span class="line"></span><br><span class="line"> <!-- <servlet> --></span><br><span class="line"> <!-- <servlet-name>feeling</servlet-name> --></span><br><span class="line"> <!-- <servlet-class>feelings.service.FeelingService</servlet-class> --></span><br><span class="line"> <!-- </servlet> --></span><br><span class="line"></span><br><span class="line"> <!-- <servlet-mapping> --></span><br><span class="line"> <!-- <servlet-name>feeling</servlet-name> --></span><br><span class="line"> <!-- <url-pattern>/feeling</url-pattern> --></span><br><span class="line"> <!-- </servlet-mapping> --></span><br><span class="line"> </span><br><span class="line"> <welcome-file-list></span><br><span class="line"> <welcome-file>index.html</welcome-file></span><br><span class="line"> <welcome-file>index.htm</welcome-file></span><br><span class="line"> <welcome-file>index.jsp</welcome-file></span><br><span class="line"> <welcome-file>default.html</welcome-file></span><br><span class="line"> <welcome-file>default.htm</welcome-file></span><br><span class="line"> <welcome-file>default.jsp</welcome-file></span><br><span class="line"> </welcome-file-list></span><br><span class="line"> </span><br><span class="line"></web-app></span><br></pre></td></tr></table></figure>
<p>.project文件:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><?xml version="1.0" encoding="UTF-8"?></span><br><span class="line"><projectDescription></span><br><span class="line"> <name>feeling</name></span><br><span class="line"> <comment></comment></span><br><span class="line"> <projects></span><br><span class="line"> </projects></span><br><span class="line"> <buildSpec></span><br><span class="line"> <buildCommand></span><br><span class="line"> <name>org.scala-ide.sdt.core.scalabuilder</name></span><br><span class="line"> <arguments></span><br><span class="line"> </arguments></span><br><span class="line"> </buildCommand></span><br><span class="line"> <buildCommand></span><br><span class="line"> <name>org.eclipse.m2e.core.maven2Builder</name></span><br><span class="line"> <arguments></span><br><span class="line"> </arguments></span><br><span class="line"> </buildCommand></span><br><span class="line"> </buildSpec></span><br><span class="line"> <natures></span><br><span class="line"> <nature>org.scala-ide.sdt.core.scalanature</nature></span><br><span class="line"> <nature>org.eclipse.jdt.core.javanature</nature></span><br><span class="line"> <nature>org.eclipse.m2e.core.maven2Nature</nature></span><br><span class="line"> </natures></span><br><span class="line"></projectDescription></span><br></pre></td></tr></table></figure></p>
<p>.classpath文件:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><?xml version="1.0" encoding="UTF-8"?></span><br><span class="line"><classpath></span><br><span class="line"> <classpathentry kind="src" output="target/classes" path="src/main/java"></span><br><span class="line"> <attributes></span><br><span class="line"> <attribute name="optional" value="true"/></span><br><span class="line"> <attribute name="maven.pomderived" value="true"/></span><br><span class="line"> </attributes></span><br><span class="line"> </classpathentry></span><br><span class="line"> <classpathentry kind="src" output="target/classes" path="src/main/scala"></span><br><span class="line"> <attributes></span><br><span class="line"> <attribute name="optional" value="true"/></span><br><span class="line"> <attribute name="maven.pomderived" value="true"/></span><br><span class="line"> </attributes></span><br><span class="line"> </classpathentry></span><br><span class="line"> <classpathentry including="**/*.java" kind="src" path="src/main/resources"/></span><br><span class="line"> <classpathentry kind="src" output="target/test-classes" path="src/test/java"></span><br><span class="line"> <attributes></span><br><span class="line"> <attribute name="optional" value="true"/></span><br><span class="line"> <attribute name="maven.pomderived" value="true"/></span><br><span class="line"> </attributes></span><br><span class="line"> </classpathentry></span><br><span class="line"> <classpathentry kind="src" output="target/test-classes" path="src/test/scala"></span><br><span class="line"> <attributes></span><br><span class="line"> <attribute name="optional" value="true"/></span><br><span class="line"> <attribute name="maven.pomderived" value="true"/></span><br><span class="line"> </attributes></span><br><span class="line"> </classpathentry></span><br><span class="line"> <classpathentry including="**/*.java" kind="src" path="src/test/resources"/></span><br><span class="line"> <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/></span><br><span class="line"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"></span><br><span class="line"> <attributes></span><br><span class="line"> <attribute name="maven.pomderived" value="true"/></span><br><span class="line"> </attributes></span><br><span class="line"> </classpathentry></span><br><span class="line"> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"></span><br><span class="line"> <attributes></span><br><span class="line"> <attribute name="maven.pomderived" value="true"/></span><br><span class="line"> </attributes></span><br><span class="line"> </classpathentry></span><br><span class="line"> <classpathentry kind="output" path="target/classes"/></span><br><span class="line"></classpath></span><br></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
基于eclipse+maven+scala-ide的scala web开发环境
</summary>
<category term="scala" scheme="http://ibruce.info/categories/scala/"/>
<category term="scala" scheme="http://ibruce.info/tags/scala/"/>
<category term="eclipse" scheme="http://ibruce.info/tags/eclipse/"/>
<category term="maven" scheme="http://ibruce.info/tags/maven/"/>
<category term="scala-ide" scheme="http://ibruce.info/tags/scala-ide/"/>
</entry>
<entry>
<title>如何在Amazon EC2中挂载EBS作为永久存储</title>
<link href="http://ibruce.info/2014/01/06/attach-ebs-volume-to-amazon-ec2/"/>
<id>http://ibruce.info/2014/01/06/attach-ebs-volume-to-amazon-ec2/</id>
<published>2014-01-06T02:02:56.000Z</published>
<updated>2016-01-03T09:26:40.000Z</updated>
<content type="html"><![CDATA[<p>如何在Amazon AWS上申请EC2不再多说,很多前辈给出了教程,本文只说如何挂载那免费的30G EBS。我申请的一年的免费的32位Red Hat Enterprise Linux,详细参数:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Red Hat Enterprise Linux 6.4 - ami-80bbf3d2 (64-bit) / ami-9cbbf3ce (32-bit)</span><br><span class="line">Red Hat Enterprise Linux version 6.4, EBS-boot.</span><br><span class="line">Root device type: ebs Virtualization type: paravirtual</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>首先登陆<a href="http://console.aws.amazon.com/console/home" target="_blank" rel="noopener">『AWS管理控制台』</a>,进入<a href="http://console.aws.amazon.com/ec2/v2/home" target="_blank" rel="noopener">『EC2』</a>。</p>
<p>左侧树依次点击『ELASTIC BLOCK STORE』-『Volumes』,点击『Create Volume』创建一个新的volume。</p>
<blockquote>
<ul>
<li>按照<a href="http://aws.amazon.com/cn/free" target="_blank" rel="noopener">AWS 免费套餐条款</a>,每月有30 GB 的 EBS 可用。但也没必要最大额,够用即可。</li>
<li>Availability Zone要与你的主机实例在同一个区域,查看『INSTANCES』-『instances』列表中的Availability Zone选项。</li>
</ul>
</blockquote>
<p>创建完毕后,在左侧列表中选中右键Attach Volume,在Instances列表中选中你的Instance。在Device中输入设备名,如:<strong>/dev/sdf</strong>。</p>
<p>接下来用putty登陆进你的云主机,不会不知道怎么做吧。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">login as: ec2-user</span><br><span class="line">Authenticating with public key "imported-openssh-key"</span><br><span class="line">Last login: Thu Jan 2 21:10:51 2014 from 219.133.173.33</span><br><span class="line"></span><br><span class="line">[ec2-user@ip-172-32-11-222 ~]$ df -h</span><br><span class="line">Filesystem Size Used Avail Use% Mounted on</span><br><span class="line">/dev/xvde1 6.0G 1.7G 4.2G 29% /</span><br><span class="line">none 298M 0 298M 0% /dev/shm</span><br></pre></td></tr></table></figure>
<p>使用<em>df -h</em>看到确实没有新建的volume,这是正常的,因尚未挂载。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[ec2-user@ip-172-32-11-222 /]$ df -T</span><br><span class="line">Filesystem Type 1K-blocks Used Available Use% Mounted on</span><br><span class="line">/dev/xvde1 ext4 6193088 1892740 4237736 31% /</span><br><span class="line">none tmpfs 305112 0 305112 0% /dev/shm</span><br></pre></td></tr></table></figure>
<p>用<em>df -T</em>查看当前系统的文件格式,为ext4。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[ec2-user@ip-172-32-11-222 /]$ mkfs.ext4 /dev/sdf</span><br><span class="line">mke2fs 1.41.12 (17-May-2010)</span><br><span class="line">Could not stat /dev/sdf --- No such file or directory</span><br><span class="line"></span><br><span class="line">The device apparently does not exist; did you specify it correctly?</span><br></pre></td></tr></table></figure>
<p>尝试根据当前文件系统来格式化新加的volume,报错没有文件,找不到设备?<br>可是我新创建的volume明明就是/dev/sdf啊,这是为什么?</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[ec2-user@ip-172-32-11-222 /]$ sudo fdisk -l</span><br><span class="line"></span><br><span class="line">Disk /dev/xvde1: 6442 MB, 6442450944 bytes</span><br><span class="line">255 heads, 63 sectors/track, 783 cylinders</span><br><span class="line">Units = cylinders of 16065 * 512 = 8225280 bytes</span><br><span class="line">Sector size (logical/physical): 512 bytes / 512 bytes</span><br><span class="line">I/O size (minimum/optimal): 512 bytes / 512 bytes</span><br><span class="line">Disk identifier: 0x00000000</span><br><span class="line"></span><br><span class="line">Disk /dev/xvdj: 10.7 GB, 10737418240 bytes</span><br><span class="line">255 heads, 63 sectors/track, 1305 cylinders</span><br><span class="line">Units = cylinders of 16065 * 512 = 8225280 bytes</span><br><span class="line">Sector size (logical/physical): 512 bytes / 512 bytes</span><br><span class="line">I/O size (minimum/optimal): 512 bytes / 512 bytes</span><br><span class="line">Disk identifier: 0x00000000</span><br></pre></td></tr></table></figure>
<p>用<em>fdisk -l</em>命令看下设备名,居然名字是<strong>/dev/xvdj</strong>。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[ec2-user@ip-172-32-11-222 /]$ sudo mkfs.ext4 /dev/xvdj</span><br><span class="line">mke2fs 1.41.12 (17-May-2010)</span><br><span class="line">Filesystem label=</span><br><span class="line">OS type: Linux</span><br><span class="line">Block size=4096 (log=2)</span><br><span class="line">Fragment size=4096 (log=2)</span><br><span class="line">Stride=0 blocks, Stripe width=0 blocks</span><br><span class="line">655360 inodes, 2621440 blocks</span><br><span class="line">131072 blocks (5.00%) reserved for the super user</span><br><span class="line">First data block=0</span><br><span class="line">Maximum filesystem blocks=2684354560</span><br><span class="line">80 block groups</span><br><span class="line">32768 blocks per group, 32768 fragments per group</span><br><span class="line">8192 inodes per group</span><br><span class="line">Superblock backups stored on blocks:</span><br><span class="line"> 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632</span><br><span class="line"></span><br><span class="line">Writing inode tables: done</span><br><span class="line">Creating journal (32768 blocks): done</span><br><span class="line">Writing superblocks and filesystem accounting information: done</span><br><span class="line"></span><br><span class="line">This filesystem will be automatically checked every 21 mounts or</span><br><span class="line">180 days, whichever comes first. Use tune2fs -c or -i to override.</span><br></pre></td></tr></table></figure>
<p>格式化。下面创建挂载点,将新增的volumn挂在上。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[ec2-user@ip-172-32-11-222 /]$ sudo mkdir /mnt/ebs</span><br><span class="line">[ec2-user@ip-172-32-11-222 /]$ sudo mount /dev/xvdj /mnt/ebs</span><br><span class="line">[ec2-user@ip-172-32-11-222 /]$ df -h</span><br><span class="line">Filesystem Size Used Avail Use% Mounted on</span><br><span class="line">/dev/xvde1 6.0G 1.9G 4.1G 31% /</span><br><span class="line">none 298M 0 298M 0% /dev/shm</span><br><span class="line">/dev/xvdj 9.9G 151M 9.2G 2% /mnt/ebs</span><br></pre></td></tr></table></figure>
<h1 id="参考文献">参考文献</h1><p><a href="http://stackoverflow.com/questions/13788619/boto-and-attaching-a-ebs-to-ec2-now-what" target="_blank" rel="noopener">amazon ec2 - boto and attaching a ebs to ec2</a><br><a href="http://blog.sina.com.cn/s/blog_3d4a28be0101jc0h.html" target="_blank" rel="noopener">How to convert Amazon instance store EC2 to EBS based EC2</a><br><a href="http://blog.sina.com.cn/s/blog_704836f40101anhf.html" target="_blank" rel="noopener">调整amazon EC2云主机的EBS启动磁盘大小 </a><br><a href="http://www.storyday.com/html/y2011/2959_in-ec2-using-ebs-as-a-permanent-storage.html" target="_blank" rel="noopener">在EC2中使用EBS作为永久存储</a><br><a href="http://www.vmzj.com/212.html" target="_blank" rel="noopener">在EC2中使用EBS作为永久存储</a><br><a href="http://www.cnblogs.com/hopeworld/archive/2009/05/25/1488617.html" target="_blank" rel="noopener">linux查看硬盘使用情况命令</a></p>
]]></content>
<summary type="html">
在Amazon EC2中挂载EBS作为永久存储 报错Could not stat /dev/sdf - No such file or directory
</summary>
<category term="cloud" scheme="http://ibruce.info/categories/cloud/"/>
<category term="ec2" scheme="http://ibruce.info/tags/ec2/"/>
<category term="ebs" scheme="http://ibruce.info/tags/ebs/"/>
</entry>
<entry>
<title>对传统BI的思考和吐槽</title>
<link href="http://ibruce.info/2013/12/31/thinking-of-bi/"/>
<id>http://ibruce.info/2013/12/31/thinking-of-bi/</id>
<published>2013-12-31T05:12:52.000Z</published>
<updated>2015-12-03T14:02:26.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>声明:本文是用 iPad 写于上班路上,所有观点解释权归本人 Bruce-Sha 所有。在大数据和移动互联网风起云涌的13年末,本文是对自己四年工作的总结,也是对未来的从业思考。</p>
</blockquote>
<p>在做了四年BI之后,我对中国式的BI有一些自己的看法。<strong>传统BI厂商早已黔驴技穷,注定穷途末路。</strong></p>
<p>我在一个国内著名的企业软件厂商工作。其BI团队虽小,但是该做的事情一样都没落下,从静态报表工具到OLAP多维分析,从建模工具语义层到互动式仪表板,从嵌入式组件到独立门户,从桌面软件到移动应用。别人有的产品他基本都做过,虽然每个产品最后难逃不再继续研发不再继续销售的命运。而每到年底他们思考的都是明年再新做个什么?</p>
<a id="more"></a>
<p>不客气的说,领导水平不足,产品定位不清,导致浪费无数的资源,但从没有人对失败的产品负责,不知道这是谁的悲哀。一个真心想做好产品的人,即使再愚蠢,在行业近十年之久,不可能看不到一丁点趋势和前景。</p>
<p>第一、BI都是低端的报表产品。</p>
<p>目前的<strong>商业智能</strong>其实是<strong>商业弱智</strong>。BI厂商拿得出手的都是低端的静态报表工具,甚至就连静态报表也做不好。</p>
<p>差在哪里?产品易用性和用户体验。难用到连研发人员自己都不会用,不想用。思考问题的唯一方式是:看看别家都在做什么呢?模仿别人,实在没得模仿就模仿Excel,我真心不明白,难道报表只能用Excel的方式做吗?BI业界典型的产品之一是IBM Cognos,强大的代表,也是难用的代表。去看看互联网产品的用户界面是多么的简单吧。</p>
<p>其次对于性能,也就是报表打开的响应速度,根本没人关心。理由是数据量就有这么大,或者Java就应该慢。导致打开很慢,浏览器崩溃。有的产品过于灵活,这是不应该的,导致本来可以通过SQL转移给数据库的计算量,直接转到报表引擎内部,怎能不慢?</p>
<p>还有一个现象很奇怪,所有人都把能做复杂报表作为卖点之一。我只能说这帮人你们已经被淘汰了,你们会随着复杂报表也就是所谓中国式报表一起淘汰。有人说客户需求是这样的,用户就是需要这个,我想说有能解决的办法就行,你的产品理念不应如此。报表是简单的才是美的。只要做到了体验好,不支持就不支持,微博支持排版吗?但是你可以放个链接,转移出去。</p>
<p>第二、整个行业毫无创新。</p>
<p>无论是国内的众多伪BI厂商,还是国际巨头BO、Cognos、BIEE,新秀QV,大家都毫无想像力。比如,移动BI这一方向。</p>
<p>我把苹果App Store中的接近四十个BI产品安装研究了一天。其思路都异常狭窄,都是在把桌面BI,也就是各种报表和仪表板,用html5的方式从桌面直接搬到移动端。先不说性能,屏幕狭小,只有触摸点击动作,其用户体验是极差的。移动BI怎么能这么做。唯一的理由是别家都有了,我么也得做。这是传统软件企业落后的根本原因,不思考,不创新,一帮行业的老人在颤颤兢兢中等待互联网来革自己的命。</p>
<p>移动端的特性是随身携带,特点是屏幕小。请不要说平板的屏幕不小,屏幕就像钱永远没有不缺的时候。曾经有个产品总监说,现在电脑屏幕都足够大了,你多加一行工具条怕什么呢,我为某司有这种产监深深的感到羞愧。</p>
<p>我不敢妄言移动上无BI,或BI与移动无缘,至少可以说,到目前为止我还没有看到移动BI。移动上有BI,但不是目前市面上的产品。</p>
<p>我的观点。</p>
<p>BI是一个消化数据的行业,它从不产生数据。简单说,BI就是玩数据,不是被数据玩。BI可以玩财务数据,也可以玩行政数据,BI可以玩结构化数据,也可以玩非结构化数据,BI可以玩数值类型的数据,也可以玩文字类型数据,将来不排除玩图片玩视频。总之,BI是花花公子,什么数据都应该去玩,但只能玩别人绝不能被人玩。</p>
<p>第一,当前市面上的报表产品已经足够解决一切复杂问题了,而恰恰需要一种超简单的报表,我称之为mini报表,或轻报表。迷你到只有一图一表的程度,图和表本身也是极简的。表可能只有一个或两三个数字,图只有一个条形图。</p>
<p>第二个问题,传统的报表都是死的,我需要报表有生命。万事万物都是有生命的。何谓有生命?可以跟你聊天说话。可以记得自己之前的状态。报表有通知功能,自己知道自己变了,知道哪个部分不舒服,知道哪个部分好兴奋,完了通知你。他们可以作为传统死报表的守护神,引导你去打开。</p>
<p>第三,传统报表表现形式过于单一。去看看互联网分析报告,生动的图文并茂,那才是将来的报表。至于严肃的财务报告,我还是那句话,有人有产品已经做得很好了,不需要你重新开发一套。</p>
<p>第四,传统的东西从来都不是无用的,新兴的要去用好他们。我只是认为传统已经做的足够好了,没有必要再去思考如何重做。</p>
<p>第五,BI的真正领域是预测。在这个领域里,传统的BI人一定会被淘汰,或者说只具备传统思维的BI人,他们是互联网思维人的天下。这里有各种预测算法,有大数据,有文本分析,这是只会做报表的人想都不用想的,他们也想不到。这里才是真正的BI。</p>
<p>第六,我总是想,只有个人BI市场打开才是BI的春天,其实人人都有数据,人人都是分析师。</p>
<p>最后,我想吐槽,一个团队的领导的眼光至关重要,好的方向+合适的人才可以解决一切。有的人你把自己的观点,要做的产品想法告诉他,他仅仅去执行都执行不好,做出来的东西还是跟现有的一样,这就像某人批评刘德华演什么都是刘德华。</p>
<blockquote>
<p>对于具体应该做什么产品,我有几个自己的想法,当然在这篇博文中略去。</p>
</blockquote>
]]></content>
<summary type="html">
<blockquote>
<p>声明:本文是用 iPad 写于上班路上,所有观点解释权归本人 Bruce-Sha 所有。在大数据和移动互联网风起云涌的13年末,本文是对自己四年工作的总结,也是对未来的从业思考。</p>
</blockquote>
<p>在做了四年BI之后,我对中国式的BI有一些自己的看法。<strong>传统BI厂商早已黔驴技穷,注定穷途末路。</strong></p>
<p>我在一个国内著名的企业软件厂商工作。其BI团队虽小,但是该做的事情一样都没落下,从静态报表工具到OLAP多维分析,从建模工具语义层到互动式仪表板,从嵌入式组件到独立门户,从桌面软件到移动应用。别人有的产品他基本都做过,虽然每个产品最后难逃不再继续研发不再继续销售的命运。而每到年底他们思考的都是明年再新做个什么?</p>
</summary>
</entry>
<entry>
<title>使用Dropbox建立Git私有仓库</title>
<link href="http://ibruce.info/2013/12/30/git-with-dropbox/"/>
<id>http://ibruce.info/2013/12/30/git-with-dropbox/</id>
<published>2013-12-30T05:47:21.000Z</published>
<updated>2016-01-03T09:28:55.000Z</updated>
<content type="html"><![CDATA[<p>无论是小型的团队或是个人都有协同开发的需要,<a href="http://github.com" target="_blank" rel="noopener">GitHub</a>上提供了仓库但是必须是public的,对于暂不公开的代码,或自己的小实验室,怎么玩呢?<br>用 <a href="http://www.dropbox.com" target="_blank" rel="noopener">Dropbox</a> 是个很好的选择,我之前是直接同步workspace,但是换台机器直接打开经常会报错。还是用 <a href="http://git-scm.com" target="_blank" rel="noopener">Git</a> 管理吧,满足个人多台机器工作,同时也满足多人协同办公。</p>
<blockquote>
<p>本文主要介绍 <a href="http://www.dropbox.com" target="_blank" rel="noopener">Dropbox</a> 作为 <a href="http://git-scm.com/book/zh" target="_blank" rel="noopener">Git</a> 私有仓库。你也可以使用其它云存储工具,如 <a href="http://skydrive.live.com" target="_blank" rel="noopener">SkyDrive</a>, <a href="http://drive.google.com" target="_blank" rel="noopener">Google Drive</a>等,或国内的<a href="http://www.kuaipan.cn/" target="_blank" rel="noopener">金山快盘</a>,<a href="http://pan.baidu.com" target="_blank" rel="noopener">百度云盘</a>,<a href="http://yunpan.360.cn" target="_blank" rel="noopener">360云盘</a>。对于源代码这些重要资料,我强烈推荐大家使用国外的云产品,百度云曾经丢过我的文件,实在信不过,只作为电影备份盘。</p>
</blockquote>
<a id="more"></a>
<h3 id="环境准备">环境准备</h3><ul>
<li>安装 <a href="http://git-scm.com/book/zh/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git" target="_blank" rel="noopener">Git客户端</a> </li>
<li><a href="https://db.tt/ALifTz8G" target="_blank" rel="noopener">注册</a>并安装 <a href="http://www.dropbox.com/downloading?src=index" target="_blank" rel="noopener">Dropbox客户端</a></li>
</ul>
<h3 id="建立_Git_Server">建立 Git Server</h3><p>到你Dropbox中私有仓库的目标目录repository下,执行git init命令,注意加上bare参数,bare参数不会生成.git目录,而是把.git中的内容开放出来,你不会直接看到项目的源代码。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">cd ~/Dropbox #切换到Dropbox目录</span><br><span class="line">nkdir repository #建立仓库根目录</span><br><span class="line">cd repository #切换到仓库目录</span><br><span class="line">mkdir ${PROJECT}.git #建立项目仓库,${PROJECT}替换为你的项目名称</span><br><span class="line">cd ${PROJECT}.git #切换到项目目录</span><br><span class="line">git init --bare #初始化为git repository,即git server端的资料</span><br></pre></td></tr></table></figure></p>
<p>至此,仓库建立完毕。你可以使用eclipse连接git仓库,share你的project,进行代码开发。下面介绍通过命令行如何使用:</p>
<h3 id="建立本地仓库">建立本地仓库</h3><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">cd ~/workspace #切换到工作空间</span><br><span class="line">mkdir {PROJECT} #建立项目目录</span><br><span class="line">cd ${PROJECT} #切换到项目目录</span><br><span class="line">git init #初始化</span><br></pre></td></tr></table></figure>
<h3 id="链接到Git_Server">链接到Git Server</h3><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">git add</span><br><span class="line">touch READM.md</span><br><span class="line">git commit --all -m "Initial commit"</span><br><span class="line">git remote add origin ~/Dropbox/repository/${PROJECT}.git/</span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<p>OK。提交本地代码,执行:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">git push origin master</span><br></pre></td></tr></table></figure></p>
<p>需要获取原创更新,执行:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">git pull origin master</span><br></pre></td></tr></table></figure></p>
<p>另外,如果在push时中遇到如下错误,是因为Git默认http post的缓存为1M,具体可以<a href="http://blog.chengyunfeng.com/?p=488" target="_blank" rel="noopener">参考</a>。</p>
<blockquote>
<p>Error writing request body to server</p>
</blockquote>
<h1 id="参考文献">参考文献</h1><p><a href="http://weizhifeng.net/git-with-dropbox.html" target="_blank" rel="noopener">把Dropbox改造为Git私有仓库</a><br><a href="http://kenlai.logdown.com/posts/52372--git-dropbox-sourcetree-source-code-management" target="_blank" rel="noopener">使用 Git + Dropbox + SourceTree 做 Source Code Management</a><br><a href="http://blog.jimu.in/?p=11" target="_blank" rel="noopener">使用 Dropbox 作为 Git 私有仓库</a><br><a href="http://www.mrmu.com.tw/2011/05/06/git-using-dropbox-as-server" target="_blank" rel="noopener">Git教學:Git的遠端操作及利用Dropbox建立Server進行協同開發(Windows)</a><br><a href="http://blog.csdn.net/feizxiang3/article/details/8065506" target="_blank" rel="noopener">远端仓库初始化成裸仓库 git init –bare</a><br><a href="http://hi.baidu.com/aatfjctoytaefkr/item/00c693450a5b36af60d7b93f" target="_blank" rel="noopener">GIT初始化–bare参数:git init & git init –bare</a><br><a href="http://www.cnblogs.com/bamanzi/archive/2012/08/15/git-hg-bare-repo.html" target="_blank" rel="noopener">什么叫做bare repo?</a></p>
]]></content>
<summary type="html">
<p>无论是小型的团队或是个人都有协同开发的需要,<a href="http://github.com" target="_blank" rel="noopener">GitHub</a>上提供了仓库但是必须是public的,对于暂不公开的代码,或自己的小实验室,怎么玩呢?<br>用 <a href="http://www.dropbox.com" target="_blank" rel="noopener">Dropbox</a> 是个很好的选择,我之前是直接同步workspace,但是换台机器直接打开经常会报错。还是用 <a href="http://git-scm.com" target="_blank" rel="noopener">Git</a> 管理吧,满足个人多台机器工作,同时也满足多人协同办公。</p>
<blockquote>
<p>本文主要介绍 <a href="http://www.dropbox.com" target="_blank" rel="noopener">Dropbox</a> 作为 <a href="http://git-scm.com/book/zh" target="_blank" rel="noopener">Git</a> 私有仓库。你也可以使用其它云存储工具,如 <a href="http://skydrive.live.com" target="_blank" rel="noopener">SkyDrive</a>, <a href="http://drive.google.com" target="_blank" rel="noopener">Google Drive</a>等,或国内的<a href="http://www.kuaipan.cn/" target="_blank" rel="noopener">金山快盘</a>,<a href="http://pan.baidu.com" target="_blank" rel="noopener">百度云盘</a>,<a href="http://yunpan.360.cn" target="_blank" rel="noopener">360云盘</a>。对于源代码这些重要资料,我强烈推荐大家使用国外的云产品,百度云曾经丢过我的文件,实在信不过,只作为电影备份盘。</p>
</blockquote>
</summary>
</entry>
<entry>
<title>为hexo博客添加访问次数统计功能</title>
<link href="http://ibruce.info/2013/12/22/count-views-of-hexo/"/>
<id>http://ibruce.info/2013/12/22/count-views-of-hexo/</id>
<published>2013-12-22T06:53:13.000Z</published>
<updated>2016-01-03T09:28:08.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>最新的统计服务已经开放,两行代码轻松搞定,你可以直接使用:<a href="http://ibruce.info/2015/04/04/busuanzi">不蒜子</a> 微服务。</p>
</blockquote>
<h1 id="方法一:自搭服务">方法一:自搭服务</h1><p>hexo是静态博客,虽有速度快的优点,但无法存储动态数据是不可否认的劣势之一。没有出路就要思考出路,没人阻止你借助第三方系统实现动态数据处理,比如评论功能借助<a href="http://disqus.com" target="_blank" rel="noopener">Disqus</a>或<a href="http://duoshuo.com" target="_blank" rel="noopener">多说</a>。统计功能也可以这样处理,我们借助<a href="http://developer.baidu.com/bae" target="_blank" rel="noopener">BAE</a>或<a href="http://sae.sina.com.cn" target="_blank" rel="noopener">SAE</a>平台搭建自己的统计服务。</p>
<blockquote>
<p><a href="http://sae.sina.com.cn" target="_blank" rel="noopener">SAE</a>已经提供<a href="http://sae.sina.com.cn/?m=devcenter&catId=194" target="_blank" rel="noopener">Counter</a>服务,但是貌似只支持php语言,这里是<a href="http://static.sae.sina.com.cn/flash/video/counter" target="_blank" rel="noopener">SAE计数器服务示例</a>。在<a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/rt" target="_blank" rel="noopener">BAE2.0</a>中也有专门的<a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/rt/counter" target="_blank" rel="noopener">Counter(计数器)</a>服务,但是BAE3.0中尚未提供,应该是还未迁移过来。</p>
</blockquote>
<a id="more"></a>
<p>这里以新版<a href="http://developer.baidu.com/cloud/rt" target="_blank" rel="noopener">BAE</a>,即<a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/bae" target="_blank" rel="noopener">BAE3.0</a>为例介绍整个流程。难度不大,只简单说下。</p>
<h3 id="创建bae应用">创建bae应用</h3><p>到<a href="http://developer.baidu.com/console" target="_blank" rel="noopener">管理控制台</a>,点击『创建应用』,创建你的应用。请参考<a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/bae/start" target="_blank" rel="noopener">新手入门</a>。</p>
<h3 id="添加Redis服务">添加Redis服务</h3><p>点击刚刚创建的应用,进入应用『基本信息』页面,点击『应用引擎』-『扩展服务』-『扩展新服务』-『Redis』,填写基本信息即可。请参考<a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/bae/redis" target="_blank" rel="noopener">Redis(数据库)</a>。</p>
<blockquote>
<p>也可以使用MySQL或MongoDB服务,我选择Redis的原因之一是其有原子自增操作incr。</p>
</blockquote>
<h3 id="jsonp跨域访问">jsonp跨域访问</h3><p>以本文为例,hexo博客地址为<a href="http://ibruce.info">http://ibruce.info</a>,而bae应用地址为<a href="http://lbservice.duapp.com" target="_blank" rel="noopener">http://lbservice.duapp.com</a>。两个不同的域之间,是不能直接访问数据的,此时就要借助<a href="http://baike.baidu.com/view/2131174.htm" target="_blank" rel="noopener">jsonp</a>,请参考<a href="http://www.clanfei.com/2012/08/1637.html" target="_blank" rel="noopener">JQuery中利用JSONP解决AJAX跨域问题</a>和<a href="http://www.iteye.com/topic/1130452" target="_blank" rel="noopener">JQuery+AJAX+JSONP跨域访问</a>。</p>
<h3 id="修改hexo页面">修改hexo页面</h3><p>打开footer.ejs编辑,增加代码。<br><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">font</span> <span class="attr">id</span>=<span class="string">"counter"</span>></span></span><br><span class="line"> 本站共到访 <span class="tag"><<span class="name">font</span> <span class="attr">id</span>=<span class="string">"counterValue"</span> <span class="attr">style</span>=<span class="string">"color:white"</span>></span>?<span class="tag"></<span class="name">font</span>></span> 次</span><br><span class="line"><span class="tag"></<span class="name">font</span>></span></span><br></pre></td></tr></table></figure></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><script type=<span class="string">"text/javascript"</span>></span><br><span class="line">$(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> $.ajax({</span><br><span class="line"> type : <span class="string">"GET"</span>,</span><br><span class="line"> url : <span class="string">'http://lbservice.duapp.com/xxx'</span>,</span><br><span class="line"> dataType : <span class="string">"jsonp"</span>,</span><br><span class="line"> jsonp : <span class="string">"jsonpCallback"</span>,</span><br><span class="line"> success : <span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>{</span><br><span class="line"> $(<span class="string">"#counterValue"</span>).text(data);</span><br><span class="line"> },</span><br><span class="line"> error : <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> $(<span class="string">"#counter"</span>).html(<span class="string">""</span>); </span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">});</span><br><span class="line"><<span class="regexp">/script></span></span><br></pre></td></tr></table></figure>
<h3 id="修改应用代码">修改应用代码</h3><p>进入应用『基本信息』页面,点击『应用引擎』-『点击复制』SVN/GIT地址,获取源码到你喜欢的工具。<br>添加必要的Redis代码,关键内容如下。<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 配置相关信息</span></span><br><span class="line"><span class="keyword">final</span> String databaseName = <span class="string">"yBeHeL"</span>;</span><br><span class="line"><span class="keyword">final</span> String host = <span class="string">"redis.duapp.com"</span>;</span><br><span class="line"><span class="keyword">final</span> <span class="keyword">int</span> port = <span class="number">80</span>;</span><br><span class="line"><span class="keyword">final</span> String username = <span class="string">"8TAbwkckuyxq"</span>;<span class="comment">// 用户名(api key);</span></span><br><span class="line"><span class="keyword">final</span> String password = <span class="string">"HszPIhnaG9d"</span>;<span class="comment">// 密码(secret key)</span></span><br><span class="line"><span class="comment">// 创建连接</span></span><br><span class="line">Jedis jedis = <span class="keyword">new</span> Jedis(host, port);</span><br><span class="line">jedis.connect();</span><br><span class="line">jedis.auth(username + <span class="string">"-"</span> + password + <span class="string">"-"</span> + databaseName);</span><br><span class="line"><span class="comment">// 操作数据库</span></span><br><span class="line">jedis.set(<span class="string">"ibruce.info"</span>, <span class="string">"0"</span>);</span><br><span class="line"><span class="keyword">long</span> counter = jedis.incr(<span class="string">"ibruce.info"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 准备输出</span></span><br><span class="line">String jsonpCallback = request.getParameter(<span class="string">"jsonpCallback"</span>);</span><br><span class="line">PrintWriter out = response.getWriter();</span><br><span class="line">out.println(String.format(<span class="string">"try{%s(%s);}catch(e){}"</span>, jsonpCallback, counter));</span><br><span class="line">out.flush();</span><br><span class="line">out.close();</span><br></pre></td></tr></table></figure></p>
<h3 id="部署bae和hexo">部署bae和hexo</h3><p>通过SVN/GIT提交代码部署bae应用,然后在同一页面点击『快捷发布』完成发布。通过hexo d -g命令部署hexo代码。</p>
<h3 id="小结">小结</h3><p>本文目的并不仅是添加统计功能,而是想抛砖引玉,介绍一种添加动态数据的通用方法,其他类似的需求都可以如此处理。如果你想为自己的hexo博客添加访问次数统计功能,又觉得太麻烦,我可以在我的bae应用下帮你统计,有需要的请留言。</p>
<blockquote>
<p>上文所述的这种方法已经废弃,现在有更高大上的做法了,参考 <a href="http://ibruce.info/2013/11/22/hexo-your-blog">hexo你的博客</a> 补充部分。</p>
</blockquote>
<h1 id="方法二:使用firebase">方法二:使用firebase</h1><p>在你的站点需要显示的位置添加标签:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><font id="counter"></font></span><br></pre></td></tr></table></figure></p>
<p>引入firebase:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><script src="//buru.u.qiniudn.com/firebase-2.0.5.js"></script></span><br></pre></td></tr></table></figure>
<blockquote>
<p>你也可以添加firebase官网的链接,但我测试速度较慢。</p>
</blockquote>
<p>引入计数:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><script src="//ibruce.info/js/counter.js"></script></span><br></pre></td></tr></table></figure></p>
<p>具体参考本人博客。</p>
<h1 id="方法三:使用不蒜子">方法三:使用不蒜子</h1><p>最新的统计服务,仅两行代码就搞定,你可以直接使用:<a href="http://ibruce.info/2015/04/04/busuanzi">不蒜子</a>微服务,墙裂推荐。</p>
]]></content>
<summary type="html">
hexo 站点访问次数 文章阅读次数 统计hexo博客
</summary>
<category term="default" scheme="http://ibruce.info/categories/default/"/>
<category term="hexo" scheme="http://ibruce.info/tags/hexo/"/>
<category term="javascript" scheme="http://ibruce.info/tags/javascript/"/>
</entry>
<entry>
<title>如何停止一个正在运行的java线程</title>
<link href="http://ibruce.info/2013/12/19/how-to-stop-a-java-thread/"/>
<id>http://ibruce.info/2013/12/19/how-to-stop-a-java-thread/</id>
<published>2013-12-19T07:37:27.000Z</published>
<updated>2016-01-03T09:36:53.000Z</updated>
<content type="html"><![CDATA[<p><img src="http://bruce.u.qiniudn.com/2013/12/19/stop.jpg" alt="stop"></p>
<p>与此问题相关的内容主要涉及三部分:已废弃的Thread.stop()、迷惑的thread.interrupt系列、最佳实践Shared Variable。</p>
<a id="more"></a>
<h2 id="已废弃的Thread-stop()">已废弃的Thread.stop()</h2><hr>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Deprecated</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">stop</span><span class="params">()</span> </span>{</span><br><span class="line"> stop(<span class="keyword">new</span> ThreadDeath());</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>如上是Hotspot JDK 7中的java.lang.Thread.stop()的代码,学习一下它的doc:</p>
<blockquote>
<p>该方法天生是不安全的。使用<em>thread.stop()</em>停止一个线程,导致释放(解锁)所有该线程已经锁定的监视器(因沿堆栈向上传播的未检查异常<em>ThreadDeath</em>而解锁)。如果之前受这些监视器保护的任何对象处于不一致状态,则不一致状态的对象(受损对象)将对其他线程可见,这可能导致任意的行为。</p>
</blockquote>
<p>是不是差点被这段话绕晕,俗点说:目标线程可能持有一个监视器,假设这个监视器控制着某两个值之间的逻辑关系,如var1必须小于var2,某一时刻var1等于var2,本来应该受保护的逻辑关系,不幸的是此时恰好收到一个stop命令,产生一个ThreadDeath错误,监视器被解锁。这就导致逻辑错误,当然这种情况也可能不会发生,是不可预料的。注意:ThreadDeath是何方神圣?是个java.lang.Error,不是java.lang.Exception。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadDeath</span> <span class="keyword">extends</span> <span class="title">Error</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">4417128565033088268L</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p><em>thread.stop()</em>方法的许多应用应该由“只修改某些变量以指示目标线程应该停止”的代码取代。目标线程应周期性的检查该变量,当发现该变量指示其要停止运行,则退出<em>run</em>方法。如果目标线程等待很长时间,则应该使用<em>interrupt</em>方法中断该等待。</p>
</blockquote>
<p>其实这里已经暗示停止一个线程的最佳方法:<strong>条件变量</strong> 或 <strong>条件变量+中断</strong>。</p>
<blockquote>
<p>更多请查看:<br><a href="http://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html" target="_blank" rel="noopener">Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a></p>
</blockquote>
<p>上文请参考我的翻译xxx。</p>
<p>其它关于stop方法的doc:</p>
<blockquote>
<ol>
<li>该方法强迫停止一个线程,并抛出一个新创建的ThreadDeath对象作为异常。</li>
<li>停止一个尚未启动的线程是允许的,如果稍后启动该线程,它会立即终止。</li>
<li>通常不应试图捕获ThreadDeath,除非它必须执行某些异常的清除操作。如果catch子句捕获了一个ThreadDeath对象,则必须重新抛出该对象,这样该线程才会真正终止。</li>
</ol>
</blockquote>
<p><strong>小结:</strong><br>Thread.stop()不安全,已不再建议使用。</p>
<h2 id="令人迷惑的thread-interrupt()">令人迷惑的thread.interrupt()</h2><hr>
<p>Thread类中有三个方法会令新手迷惑,他们是:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> Thread.interrupt() <span class="comment">// 无返回值</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">boolean</span> Thread.isInterrupted() <span class="comment">// 有返回值</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> Thread.interrupted() <span class="comment">// 静态,有返回值</span></span><br></pre></td></tr></table></figure>
<p>如果按照近几年流行的<a href="http://book.douban.com/subject/4262627" target="_blank" rel="noopener">重构</a>,<a href="http://book.douban.com/subject/4199741" target="_blank" rel="noopener">代码整洁之道</a>,<a href="http://book.douban.com/subject/1152111" target="_blank" rel="noopener">程序员修炼之道</a>等书的观点,这几个方法的命名相对于其实现的功能来说,不够直观明确,极易令人混淆,是低级程序猿的代码。逐个分析:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">interrupt</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span> != Thread.currentThread())</span><br><span class="line"> checkAccess();</span><br><span class="line"> <span class="keyword">synchronized</span> (blockerLock) {</span><br><span class="line"> Interruptible b = blocker;</span><br><span class="line"> <span class="keyword">if</span> (b != <span class="keyword">null</span>) {</span><br><span class="line"> interrupt0(); <span class="comment">// Just to set the interrupt flag</span></span><br><span class="line"> b.interrupt(<span class="keyword">this</span>);</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"> interrupt0();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>中断本线程。无返回值。具体作用分以下几种情况:</p>
<ul>
<li>如果该线程正阻塞于<em>Object</em>类的<em>wait()</em>、<em>wait(long)</em>、<em>wait(long, int)</em>方法,或者<em>Thread</em>类的<em>join()</em>、<em>join(long)</em>、<em>join(long, int)</em>、<em>sleep(long)</em>、<em>sleep(long, int)</em>方法,则该线程的<strong>中断状态将被清除,并收到一个<em>java.lang.InterruptedException</em></strong>。</li>
<li>如果该线程正阻塞于<em>interruptible channel</em>上的I/O操作,则该通道将被关闭,同时该线程的<strong>中断状态被设置,并收到一个<em>java.nio.channels.ClosedByInterruptException</em></strong>。</li>
<li>如果该线程正阻塞于一个<em>java.nio.channels.Selector</em>操作,则该线程的<strong>中断状态被设置</strong>,它将立即从选择操作返回,并可能带有一个非零值,就好像调用<em>java.nio.channels.Selector.wakeup()</em>方法一样。</li>
<li>如果上述条件都不成立,则该线程的<strong>中断状态将被设置</strong>。</li>
</ul>
<blockquote>
<p><strong>小结</strong>:第一种情况最为特殊,阻塞于wait/join/sleep的线程,中断状态会被清除掉,同时收到著名的InterruptedException;而其他情况中断状态都被设置,并不一定收到异常。</p>
</blockquote>
<p>中断一个不处于活动状态的线程不会有任何作用。如果是其他线程在中断该线程,则java.lang.Thread.checkAccess()方法就会被调用,这可能抛出java.lang.SecurityException。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">interrupted</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> currentThread().isInterrupted(<span class="keyword">true</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>检测当前线程是否已经中断,是则返回true,否则false,<strong>并清除中断状态</strong>。换言之,如果该方法被连续调用两次,第二次必将返回false,除非在第一次与第二次的瞬间线程再次被中断。如果中断调用时线程已经不处于活动状态,则返回false。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isInterrupted</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> isInterrupted(<span class="keyword">false</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>检测当前线程是否已经中断,是则返回true,否则false。中断状态不受该方法的影响。如果中断调用时线程已经不处于活动状态,则返回false。</p>
<blockquote>
<p>interrupted()与isInterrupted()的唯一区别是,<strong>前者会读取并清除中断状态,后者仅读取状态。</strong></p>
</blockquote>
<p>在hotspot源码中,两者均通过调用的native方法isInterrupted(boolean)来实现,区别是参数值ClearInterrupted不同。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">isInterrupted</span><span class="params">(<span class="keyword">boolean</span> ClearInterrupted)</span></span>;</span><br></pre></td></tr></table></figure>
<p>经过上面的分析,三者之间的区别已经很明确,来看一个具体案例,是我在工作中看到某位架构师的代码,只给出最简单的概要结构:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">while</span>(!Thread.currentThread().isInterrupted()) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">10000L</span>);</span><br><span class="line"> ... <span class="comment">//为篇幅,省略其它io操作</span></span><br><span class="line"> ... <span class="comment">//为简单,省略其它interrupt操作</span></span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) { <span class="keyword">break</span>; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我最初被这段代码直接绕晕,用thread.isInterrupted()方法作为循环中止条件可以吗?</p>
<p>根据上文的分析,当该方法阻塞于wait/join/sleep时,中断状态会被清除掉,同时收到InterruptedException,也就是接收到的值为false。上述代码中,当sleep之后的调用otherDomain.xxx(),otherDomain中的代码包含wait/join/sleep并且InterruptedException被catch掉的时候,线程无法正确的中断。</p>
<p>因此,在编写多线程代码的时候,<strong>任何时候捕获到InterruptedException,要么继续上抛,要么重置中断状态,这是最安全的做法</strong>,参考<a href="http://book.douban.com/subject/1888733" target="_blank" rel="noopener">『Java Concurrency in Practice』</a>。凡事没有绝对,如果你可以确保一定没有这种情况发生,这个代码也是可以的。</p>
<blockquote>
<p>下段内容引自:<strong><a href="http://book.douban.com/subject/10484692" target="_blank" rel="noopener">『Java并发编程实战』</a> 第5章 基础构建模块 5.4 阻塞方法与中断方法 p77</strong></p>
</blockquote>
<p>当某个方法抛出InterruptedException时,表示该方法是一个阻塞方法。当在代码中调用一个将抛出InterruptedException异常的方法时,你自己的方法也就变成了一个阻塞方法,并且必须要处理对中断的相应。对于库代码来说,有两种选择:</p>
<ul>
<li>传递InterruptedException。这是最明智的策略,将异常传递给方法的调用者。</li>
<li>恢复中断。在不能上抛的情况下,如Runnable方法,必须捕获InterruptedException,并通过当前线程的interrupt()方法恢复中断状态,这样在调用栈中更高层的代码将看到引发了一个中断。如下代码是模板:</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// ① 调用阻塞方法</span></span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> Thread.currentThread().interrupt(); <span class="comment">// ② 恢复被中断的状态</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>最后再强调一遍,②处的 Thread.currentThread().interrupt() 非常非常重要。</p>
<h2 id="最佳实践:Shared_Variable">最佳实践:Shared Variable</h2><hr>
<p>不记得哪本书上曾曰过,最佳实践是个烂词。在这里这个词最能表达意思,停止一个线程最好的做法就是利用共享的条件变量。</p>
<p>对于本问题,我认为准确的说法是:<strong>停止一个线程的最佳方法是让它执行完毕,没有办法立即停止一个线程,但你可以控制何时或什么条件下让他执行完毕。</strong></p>
<p>通过条件变量控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。为保证线程间的即时通信,需要使用使用volatile关键字或锁,确保读线程与写线程间变量状态一致。下面给一个最佳模板:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> bruce_sha (bruce-sha.github.io)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 2013-12-23</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BestPractice</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> finished = <span class="keyword">false</span>; <span class="comment">// ① volatile条件变量</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stopMe</span><span class="params">()</span> </span>{</span><br><span class="line"> finished = <span class="keyword">true</span>; <span class="comment">// ② 发出停止信号</span></span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">while</span> (!finished) { <span class="comment">// ③ 检测条件变量</span></span><br><span class="line"> <span class="comment">// do dirty work // ④业务代码</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<hr>
<p>本文尚未完成,请耐心等待。</p>
<hr>
<p>当④处的代码阻塞于wait()或sleep()时,线程不能立刻检测到条件变量。因此②处的代码最好同时调用interrupt()方法。</p>
<p>小结:<br><a href="http://forward.com.au/javaProgramming/HowToStopAThread.html" target="_blank" rel="noopener">How to Stop a Thread or a Task ?</a> 详细讨论了如何停止一个线程, 总结起来有三点:</p>
<ol>
<li>使用violate boolean变量来标识线程是否停止。</li>
<li>停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性。</li>
<li>对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO。</li>
</ol>
<h2 id="总结:">总结:</h2><hr>
<blockquote>
<p>要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了中断(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的的工作。—— <a href="http://book.douban.com/subject/10484692" target="_blank" rel="noopener">『Java并发编程实战』</a> 第7章 取消与关闭 p111</p>
<p>中断是一种协作机制。一个线程不能强制其它线程停止正在执行的操作而去执行其它的操作。当线程A中断B时,A仅仅是要求B在执行到某个可以暂停的地方停止正在执行的操作——前提是如果线程B愿意停下来。—— <a href="http://book.douban.com/subject/10484692" target="_blank" rel="noopener">『Java并发编程实战』</a> 第5章 基础构建模块 p77</p>
</blockquote>
<p>总之,中断只是一种协作机制,需要被中断的线程自己处理中断。停止一个线程最佳实践是 <strong>中断 + 条件变量</strong>。</p>
<h2 id="参考文献">参考文献</h2><hr>
<ol>