-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathatom.xml
4649 lines (4255 loc) · 660 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Arccode's blog]]></title>
<link href="/atom.xml" rel="self"/>
<link href="http://arccode.net/"/>
<updated>2016-05-05T10:08:23.000Z</updated>
<id>http://arccode.net/</id>
<author>
<name><![CDATA[arccode]]></name>
<email><![CDATA[[email protected]]]></email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title><![CDATA[微信支付SDK-两行代码解决支付]]></title>
<link href="http://arccode.net/2016/05/02/%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98SDK-%E4%B8%A4%E8%A1%8C%E4%BB%A3%E7%A0%81%E8%A7%A3%E5%86%B3%E6%94%AF%E4%BB%98/"/>
<id>http://arccode.net/2016/05/02/微信支付SDK-两行代码解决支付/</id>
<published>2016-05-02T02:39:51.000Z</published>
<updated>2016-05-05T10:08:23.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>让使用微信支付的朋友最快速度接入微信支付.</p>
<h2 id="核心">核心</h2><p>两行代码解决微信支付提供的各种服务, 开箱即用, 可扩展性超强(只需根据服务的上下行协议定义协议类后, 放入工厂即可获取调用结果).</p>
<h2 id="架构图">架构图</h2><p><img src="/images/wx-pay-sdk.jpg" alt=""></p>
<a id="more"></a>
<h2 id="项目源代码">项目源代码</h2><ul>
<li>源码地址 <a href="http://wocoding.com/item.htm?hashId=wZlZgM81" target="_blank" rel="external">http://wocoding.com/item.htm?hashId=wZlZgM81</a></li>
</ul>
<h2 id="目前支持的服务及调用示例">目前支持的服务及调用示例</h2><p>所有服务在单元测试类(WXPayClientTest.java)中均已测试通过, 下行参数<code>response.isSuccess == true</code>表示服务调用成功.</p>
<h3 id="扫码支付">扫码支付</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1</a></p>
<figure class="highlight autoit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> nonceStr = SDKUtils.genRandomStringByLength(<span class="number">32</span>)<span class="comment">;</span></span><br><span class="line">UnifiedOrderRequest request = new UnifiedOrderRequest(<span class="string">"wuspace-899"</span>,SDKUtils.genOutTradeNo(),<span class="number">1</span>, <span class="string">"192.168.1.1"</span>, asyncNotifyUrl, <span class="string">"NATIVE"</span>, nonceStr)<span class="comment">;</span></span><br><span class="line">UnifiedOrderResponse response = wxPayClient.<span class="built_in">execute</span>(request)<span class="comment">;</span></span><br><span class="line"><span class="built_in">Assert</span>.assertNotNull(response)<span class="comment">;</span></span><br><span class="line"><span class="built_in">LOG</span>.info(JSON.toJSONString(response))<span class="comment">;</span></span><br><span class="line">// TODO 开发人员根据 response中的属性值处理业务逻辑, 此处可完美嵌入业务层(小型系统)或服务层(大型系统)</span><br></pre></td></tr></table></figure>
<h3 id="公众号支付">公众号支付</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1</a></p>
<figure class="highlight autoit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> nonceStr = SDKUtils.genRandomStringByLength(<span class="number">32</span>)<span class="comment">;</span></span><br><span class="line">UnifiedOrderRequest request = new UnifiedOrderRequest(<span class="string">"wuspace-899"</span>,SDKUtils.genOutTradeNo(),</span><br><span class="line"> <span class="number">1</span>, <span class="string">"192.168.1.1"</span>, asyncNotifyUrl, <span class="string">"JSAPI"</span>, nonceStr)<span class="comment">;</span></span><br><span class="line">request.setOpenId(<span class="string">"oKVmeuHht8J0Ni58CSNe474AHA3E"</span>)<span class="comment">;</span></span><br><span class="line">UnifiedOrderResponse response = wxPayClient.<span class="built_in">execute</span>(request)<span class="comment">;</span></span><br><span class="line"><span class="built_in">Assert</span>.assertNotNull(response)<span class="comment">;</span></span><br><span class="line"><span class="built_in">LOG</span>.info(JSON.toJSONString(response))<span class="comment">;</span></span><br><span class="line">// TODO 开发人员根据 response中的属性值处理业务逻辑, 此处可完美嵌入业务层(小型系统)或服务层(大型系统)</span><br></pre></td></tr></table></figure>
<h3 id="APP支付">APP支付</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1</a></p>
<figure class="highlight autoit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> nonceStr = SDKUtils.genRandomStringByLength(<span class="number">32</span>)<span class="comment">;</span></span><br><span class="line">UnifiedOrderRequest request = new UnifiedOrderRequest(<span class="string">"wuspace-899"</span>,SDKUtils.genOutTradeNo(),</span><br><span class="line"> <span class="number">1</span>, <span class="string">"192.168.1.1"</span>, asyncNotifyUrl, <span class="string">"APP"</span>, nonceStr)<span class="comment">;</span></span><br><span class="line">UnifiedOrderResponse response = wxPayClient.<span class="built_in">execute</span>(request)<span class="comment">;</span></span><br><span class="line"><span class="built_in">Assert</span>.assertNotNull(response)<span class="comment">;</span></span><br><span class="line"><span class="built_in">LOG</span>.info(JSON.toJSONString(response))<span class="comment">;</span></span><br><span class="line">// TODO 开发人员根据 response中的属性值处理业务逻辑, 此处可完美嵌入业务层(小型系统)或服务层(大型系统)</span><br></pre></td></tr></table></figure>
<h3 id="商家支付">商家支付</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2</a></p>
<figure class="highlight autoit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> nonceStr = SDKUtils.genRandomStringByLength(<span class="number">32</span>)<span class="comment">;</span></span><br><span class="line"><span class="built_in">String</span> customerOpenId = <span class="string">"oKVmeuHht8J0Ni58CSNe474AHA3E"</span><span class="comment">;</span></span><br><span class="line">MchPayRequest mchPayRequest = new MchPayRequest(SDKUtils.genOutTradeNo(),</span><br><span class="line"> customerOpenId, <span class="string">"NO_CHECK"</span>, <span class="number">100</span>, <span class="string">"xxxx年xx月结算"</span>, <span class="string">"192.168.1.1"</span>, nonceStr)<span class="comment">;</span></span><br><span class="line">MchPayResponse response = wxPayVIPClient.<span class="built_in">execute</span>(mchPayRequest)<span class="comment">;</span></span><br><span class="line"><span class="built_in">Assert</span>.assertNotNull(response)<span class="comment">;</span></span><br><span class="line"><span class="built_in">LOG</span>.info(JSON.toJSONString(response))<span class="comment">;</span></span><br><span class="line">// TODO 开发人员根据 response中的属性值处理业务逻辑, 此处可完美嵌入业务层(小型系统)或服务层(大型系统)</span><br></pre></td></tr></table></figure>
<h3 id="退款">退款</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_4&index=6" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_4&index=6</a></p>
<figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">String nonceStr = SDKUtils.genRandomStringByLength(32)<span class="comment">;</span></span><br><span class="line">RefundRequest request = new RefundRequest("T<span class="number">1512141601489</span><span class="number">1124211768</span>",</span><br><span class="line"> SDKUtils.genOutRefundNo(), 1, 1, "<span class="number">112102020</span>", nonceStr)<span class="comment">;</span></span><br><span class="line">RefundResponse response = wxPayVIPClient.execute(request)<span class="comment">;</span></span><br><span class="line">Assert.assertNotNull(response)<span class="comment">;</span></span><br><span class="line">LOG.info(JSON.toJSONString(response))<span class="comment">;</span></span><br><span class="line">// TODO 开发人员根据 response中的属性值处理业务逻辑, 此处可完美嵌入业务层(小型系统)或服务层(大型系统)</span><br></pre></td></tr></table></figure>
<h3 id="支付异步通知解析">支付异步通知解析</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7</a></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">String notifyTxt = "<span class="tag"><<span class="title">xml</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">appid</span>></span><span class="cdata"><![CDATA[wx2421b1c4370eccdcd]]></span><span class="tag"></<span class="title">appid</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">attach</span>></span><span class="cdata"><![CDATA[支付测试]]></span><span class="tag"></<span class="title">attach</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">bank_type</span>></span><span class="cdata"><![CDATA[CFT]]></span><span class="tag"></<span class="title">bank_type</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">fee_type</span>></span><span class="cdata"><![CDATA[CNY]]></span><span class="tag"></<span class="title">fee_type</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">is_subscribe</span>></span><span class="cdata"><![CDATA[Y]]></span><span class="tag"></<span class="title">is_subscribe</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">mch_id</span>></span><span class="cdata"><![CDATA[10000100]]></span><span class="tag"></<span class="title">mch_id</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">nonce_str</span>></span><span class="cdata"><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></span><span class="tag"></<span class="title">nonce_str</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">openid</span>></span><span class="cdata"><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></span><span class="tag"></<span class="title">openid</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">out_trade_no</span>></span><span class="cdata"><![CDATA[1409811653]]></span><span class="tag"></<span class="title">out_trade_no</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">result_code</span>></span><span class="cdata"><![CDATA[SUCCESS]]></span><span class="tag"></<span class="title">result_code</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">return_code</span>></span><span class="cdata"><![CDATA[SUCCESS]]></span><span class="tag"></<span class="title">return_code</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">sign</span>></span><span class="cdata"><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></span><span class="tag"></<span class="title">sign</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">sub_mch_id</span>></span><span class="cdata"><![CDATA[10000100]]></span><span class="tag"></<span class="title">sub_mch_id</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">time_end</span>></span><span class="cdata"><![CDATA[20140903131540]]></span><span class="tag"></<span class="title">time_end</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">total_fee</span>></span>1<span class="tag"></<span class="title">total_fee</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">trade_type</span>></span><span class="cdata"><![CDATA[JSAPI]]></span><span class="tag"></<span class="title">trade_type</span>></span>\n" +</span><br><span class="line"> " <span class="tag"><<span class="title">transaction_id</span>></span><span class="cdata"><![CDATA[1004400740201409030005092168]]></span><span class="tag"></<span class="title">transaction_id</span>></span>\n" +</span><br><span class="line"> "<span class="tag"></<span class="title">xml</span>></span>";</span><br><span class="line">PayNotifyResponse response = wxPayClient.parseNotify(notifyTxt, PayNotifyResponse.class);</span><br><span class="line">Assert.assertNotNull(response);</span><br><span class="line">LOG.info(JSON.toJSONString(response));</span><br><span class="line">// TODO 开发人员根据 response中的属性值处理业务逻辑, 此处可完美嵌入业务层(小型系统)或服务层(大型系统)</span><br></pre></td></tr></table></figure>
<h3 id="刷卡支付">刷卡支付</h3><p>文档详见: <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1" target="_blank" rel="external">https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1</a></p>
<p>目前未使用, 待续……</p>
<h2 id="扩展">扩展</h2><p>该SDK设计了一个服务工厂, 该工厂中包含HTTP执行器/返回数据解析方式(json/xml)/入参数据格式(json/xml)构造等, 开发人员需要增加服务仅需要根据服务协议文档编写上下行协议, 并在协议中指明API接口和返回数据类型, 再将上行协议放入工厂中执行即可; 可参考已完成的服务协议进行扩展编写.</p>
<h2 id="本系列文章">本系列文章</h2><ul>
<li><a href="">微信支付SDK-两行代码解决支付</a></li>
<li>微信服务号SDK-两行代码解决API调用</li>
<li>支付宝支付SDK-两行代码解决支付</li>
<li>平安银行银企直连SDK-两行代码解决API调用</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>让使用微信支付的朋友最快速度接入微信支付.</p>
<h2 id="核心">核心</h2><p>两行代码解决微信支付提供的各种服务, 开箱即用, 可扩展性超强(只需根据服务的上下行协议定义协议类后, 放入工厂即可获取调用结果).</p>
<h2 id="架构图">架构图</h2><p><img src="/images/wx-pay-sdk.jpg" alt=""></p>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="SDK" scheme="http://arccode.net/tags/SDK/"/>
<category term="微信" scheme="http://arccode.net/tags/%E5%BE%AE%E4%BF%A1/"/>
<category term="SDK" scheme="http://arccode.net/categories/SDK/"/>
</entry>
<entry>
<title><![CDATA[免费跳过视频网站的广告--通杀主流平台]]></title>
<link href="http://arccode.net/2016/03/06/%E5%85%8D%E8%B4%B9%E8%B7%B3%E8%BF%87%E8%A7%86%E9%A2%91%E7%BD%91%E7%AB%99%E7%9A%84%E5%B9%BF%E5%91%8A-%E9%80%9A%E6%9D%80%E4%B8%BB%E6%B5%81%E5%B9%B3%E5%8F%B0/"/>
<id>http://arccode.net/2016/03/06/免费跳过视频网站的广告-通杀主流平台/</id>
<published>2016-03-06T07:24:52.000Z</published>
<updated>2016-03-06T08:21:50.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>看了腾讯动漫各位导演的演讲, 好有感触, 特意找了一部提及的动漫(妖怪名单)进行观看, 总共18集; 看了2集觉得不错, 想直接看完, 但每次都有60s广告时间, 烦人. 速度浏览了下该站点前端代码, 哈哈, 很好解决这问题, 接下来详细讲解各种手法.</p>
<p>以下手法经测试, 可以<code>秒杀</code>目前流行的各大视频平台.</p>
<h2 id="工具">工具</h2><p>chrome浏览器</p>
<h2 id="手法分类">手法分类</h2><p>针对不同的用户群体, 给出相适应的<code>避广告</code>手法. </p>
<a id="more"></a>
<h3 id="多窗口等待法">多窗口等待法</h3><p>右键–>在新标签页中打开链接</p>
<ul>
<li>优点: 简单</li>
<li>缺点: 对机器及网络的性能要求较高</li>
</ul>
<h3 id="欺骗站点法">欺骗站点法</h3><p>右键–>检查–>toggle device mode–>Ctrl + F5(强制刷新页面)</p>
<ul>
<li>优点: 对机器及网络的性能要求较低</li>
<li>缺点: 需要懂一点点IT技术</li>
</ul>
<p>注: 有些视频网站提示报错, 可以切换Device, 通常使用Apple iPhone和Google Nexus即可解决.</p>
<p><img src="/images/del_ad_01.jpg" alt=""></p>
<h3 id="杀鸡取卵法">杀鸡取卵法</h3><p>右键–>检查–>Network–>Type(用于排序, 方便查找)–>复制类型未media的地址</p>
<ul>
<li>优点: 对机器及网络的性能要求较低, 视频大小可以控制</li>
<li>缺点: 需要懂一些前端知识</li>
</ul>
<p>直接获取地址: </p>
<p><img src="/images/del_ad_02.jpg" alt=""></p>
<p>间接获取地址, 该方法适用于那些地址隐藏较深的站点: </p>
<p><img src="/images/del_ad_03.jpg" alt=""></p>
<p>通过该手法, 结合<code>爬虫</code>技术, 那想象空间真的很大……….(此处省略3万字)</p>
<h2 id="声明">声明</h2><p>该博文仅作为技术交流, 切勿用于商业目的.</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>看了腾讯动漫各位导演的演讲, 好有感触, 特意找了一部提及的动漫(妖怪名单)进行观看, 总共18集; 看了2集觉得不错, 想直接看完, 但每次都有60s广告时间, 烦人. 速度浏览了下该站点前端代码, 哈哈, 很好解决这问题, 接下来详细讲解各种手法.</p>
<p>以下手法经测试, 可以<code>秒杀</code>目前流行的各大视频平台.</p>
<h2 id="工具">工具</h2><p>chrome浏览器</p>
<h2 id="手法分类">手法分类</h2><p>针对不同的用户群体, 给出相适应的<code>避广告</code>手法. </p>]]>
</summary>
<category term="社会工程学" scheme="http://arccode.net/tags/%E7%A4%BE%E4%BC%9A%E5%B7%A5%E7%A8%8B%E5%AD%A6/"/>
<category term="随笔" scheme="http://arccode.net/tags/%E9%9A%8F%E7%AC%94/"/>
<category term="社会工程学" scheme="http://arccode.net/categories/%E7%A4%BE%E4%BC%9A%E5%B7%A5%E7%A8%8B%E5%AD%A6/"/>
</entry>
<entry>
<title><![CDATA[微信扫码登录场景实现及延伸]]></title>
<link href="http://arccode.net/2015/10/27/%E5%BE%AE%E4%BF%A1%E6%89%AB%E7%A0%81%E7%99%BB%E5%BD%95%E5%9C%BA%E6%99%AF%E5%AE%9E%E7%8E%B0%E5%8F%8A%E5%BB%B6%E4%BC%B8/"/>
<id>http://arccode.net/2015/10/27/微信扫码登录场景实现及延伸/</id>
<published>2015-10-27T03:02:07.000Z</published>
<updated>2015-10-27T03:42:41.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>用过微信网页版的人应该都清楚网页登陆的流程,大致描述一下这个过程: </p>
<ol>
<li>打开网页版登陆<a href="https://wx.qq.com/" target="_blank" rel="external">链接</a></li>
<li>页面会显示一个二维码</li>
<li>用微信客户端扫描二维码,让用户确认登陆网页版</li>
<li>如果确认登陆,网页版会自动进入聊天界面</li>
</ol>
<p>这个过程的交互方式和一般的WEB应用不太一样,<code>步骤4</code>网页自动跳转,明显是由服务端主动推送了内容给网页端,网页端收到跳转确认后才触发的,这里就引出了今天要讨论的问题:<code>服务端推送技术</code>.服务端推送又称为Comet,服务端异步处理等.很早以前就出现了,但一直没有一个统一的标准,存在着不少Comet技术框架,各个Web容器也各自实现了自己的Comet支持.最近公司的产品也出现了和微信网页版登陆类似的场景,需要用到Comet技术,我简单的研究了下,写下来记录一下.</p>
<a id="more"></a>
<h2 id="解决方案">解决方案</h2><h3 id="Tomcat内置">Tomcat内置</h3><p>omcat 内置支持,需要实现CometProcessor接口.但是应用就依赖Tomcat容器了, 由于依赖性过大, 暂不考虑.</p>
<h3 id="Servlet3新特性">Servlet3新特性</h3><p>Servlet3提供一套完整的异步处理API,包括AsyncContext,AsyncLiseter,AsyncEvent. 要求Tomcat7.0++.</p>
<h3 id="SpringMVC3-2">SpringMVC3.2</h3><p>SpringMVC3.2 在Servlet3的基础上做了进一步的封装,编码更为简单,提供Callable,WebAsyncTask,DeferredResult三种方式进行异步编程支持,非常方便.</p>
<h2 id="应用场景">应用场景</h2><p>扫描动态二维码关注微信公众账号.</p>
<h3 id="流程">流程</h3><ol>
<li>客户端调用服务端接口获取动态二维码以及二维码内容中内置的ID.(这个时候在客户端能看到一个二维码了,等待用户扫描)</li>
<li>客户端马上调用服务端的一个长连接接口,与服务端建立长连接,等待服务端通知.(这个过程是在后台发生的,用户无法感知)</li>
<li>用户拿出微信扫描二维码,就会有一个扫描事件通知到服务端的扫描接口.(这个时候服务端接收到扫描动作,完成自己的业务操作以后,通知长连接接口,用户已经扫描了,可以返回了).</li>
</ol>
<h3 id="关键点">关键点</h3><ol>
<li>步骤2里面要求客户端–服务端建立长连接,不会立即返回,客户端一直在等待状态.(Servlet3 的API可以支持,需要把Timeout时间设置长一点,一般是60S够了)</li>
<li>步骤3中 扫描接口要通知长连接接口,如何做到? 必须存在一个公共的容器,容器里面存着上下文信息,扫描接口把执行完毕的上下文告知长连接接口就可以了.</li>
</ol>
<h2 id="实现">实现</h2><p>配置web.xml的命名空间</p>
<figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><web-<span class="keyword">app</span> xmlns:xsi=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span><br><span class="line"> xmlns=<span class="string">"http://java.sun.com/xml/ns/javaee"</span></span><br><span class="line"> xmlns:web=<span class="string">"http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"</span></span><br><span class="line"> xsi:schemaLocation=<span class="string">"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"</span></span><br><span class="line"> id=<span class="string">"WebApp_ID"</span> <span class="keyword">version</span>=<span class="string">"3.0"</span>></span><br><span class="line"></web-<span class="keyword">app</span>></span><br></pre></td></tr></table></figure>
<p>配置tomcat</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="title">Connector</span> <span class="attribute">port</span>=<span class="value">"8080"</span> <span class="attribute">protocol</span>=<span class="value">"org.apache.coyote.http11.Http11NioProtocol"</span></span><br><span class="line"> <span class="attribute">connectionTimeout</span>=<span class="value">"20000"</span> <span class="attribute">asyncTimeout</span>=<span class="value">"150000"</span> <span class="attribute">URIEncoding</span>=<span class="value">"utf-8"</span> <span class="attribute">redirectPort</span>=<span class="value">"8443"</span> /></span></span><br></pre></td></tr></table></figure>
<h3 id="Servlet新特性">Servlet新特性</h3><h4 id="实现长连接">实现长连接</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@WebServlet</span>(value = <span class="string">"/scan/*"</span>,asyncSupported = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ScanServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> Logger logger = Logger.getLogger(getClass());</span><br><span class="line"> </span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> ServletException </span>{</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> </span>{</span><br><span class="line"> ScanRetain.MAP.clear();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doGet</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException </span>{</span><br><span class="line"> doPost(req, resp);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doPost</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException </span>{</span><br><span class="line"> logger.debug(<span class="string">">>>>>>>>>>>>>>>>>开始访问长连接Servlet....."</span>);</span><br><span class="line"> String pathInfo = req.getPathInfo();</span><br><span class="line"> String key = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (pathInfo != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">int</span> i = pathInfo.lastIndexOf(<span class="string">'/'</span>);</span><br><span class="line"> <span class="keyword">if</span> (i >= <span class="number">0</span>) {</span><br><span class="line"> key = pathInfo.substring(i + <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (key == <span class="keyword">null</span>) {</span><br><span class="line"> PrintWriter writer = resp.getWriter();</span><br><span class="line"> writer.write(<span class="string">"error:not found scan key"</span>);</span><br><span class="line"> writer.flush();</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> req.startAsync(req, resp);</span><br><span class="line"> <span class="keyword">if</span> (req.isAsyncStarted()) {</span><br><span class="line"> <span class="keyword">final</span> AsyncContext asyncContext = req.getAsyncContext();</span><br><span class="line"> <span class="keyword">final</span> String theKey = key;</span><br><span class="line"> asyncContext.setTimeout(<span class="number">60</span> * <span class="number">1000L</span>);</span><br><span class="line"></span><br><span class="line"> asyncContext.addListener(<span class="keyword">new</span> AsyncListener() {</span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onComplete</span><span class="params">(AsyncEvent asyncEvent)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> ScanRetain.MAP.remove(theKey);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onTimeout</span><span class="params">(AsyncEvent asyncEvent)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> ScanRetain.MAP.remove(theKey);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onError</span><span class="params">(AsyncEvent asyncEvent)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> ScanRetain.MAP.remove(theKey);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStartAsync</span><span class="params">(AsyncEvent asyncEvent)</span> <span class="keyword">throws</span> IOException </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"> logger.debug(<span class="string">">>>>>>>>>>>>>>>>>将长连接上下文对象加入队列等待处理........."</span>);</span><br><span class="line"> ScanRetain.MAP.put(theKey, asyncContext);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="公共Context容器存放类以及提供给扫描后对长连接响应处理的逻辑">公共Context容器存放类以及提供给扫描后对长连接响应处理的逻辑</h4><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> class ScanRetain {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 公共上下文容器</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> ConcurrentHashMap<<span class="keyword">String</span>, AsyncContext> MAP = <span class="keyword">new</span> ConcurrentHashMap<<span class="keyword">String</span>, AsyncContext>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Logger logger = Logger.getLogger(getClass());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> doReturn(<span class="keyword">String</span> <span class="variable">key</span>){</span><br><span class="line"> logger.debug(<span class="string">">>>>>>>>>>>>>>>>>长连接正在响应....."</span>);</span><br><span class="line"> AsyncContext asyncContext = MAP.<span class="built_in">get</span>(<span class="variable">key</span>);</span><br><span class="line"> <span class="keyword">if</span> (asyncContext == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();</span><br><span class="line"> DBObject data = <span class="keyword">new</span> BasicDBObject(<span class="string">"result"</span>,<span class="number">1</span>)</span><br><span class="line"> .<span class="built_in">append</span>(<span class="string">"info"</span>,<span class="string">"ok"</span>)</span><br><span class="line"> .<span class="built_in">append</span>(<span class="string">"now"</span>,System.currentTimeMillis());</span><br><span class="line"> <span class="keyword">String</span> <span class="built_in">str</span> = JSON.serialize(data);</span><br><span class="line"> OutputStream os = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> os = res.getOutputStream();</span><br><span class="line"> os.write(<span class="built_in">str</span>.getBytes(<span class="string">"utf-8"</span>));</span><br><span class="line"> logger.debug(<span class="string">">>>>>>>>>>>>>>>>>长连接响应完毕....."</span>);</span><br><span class="line"> os.flush();</span><br><span class="line"> asyncContext.setTimeout(<span class="number">100</span>L);<span class="comment">// 一定要加这一句才会及时返回</span></span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="扫描事件触发长连接响应的逻辑">扫描事件触发长连接响应的逻辑</h4><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Long</span> senceId = <span class="number">0</span>L;</span><br><span class="line"><span class="keyword">if</span> (qrSenceId != <span class="keyword">null</span>) {</span><br><span class="line"> senceId = <span class="keyword">Long</span>.parseLong(qrSenceId);</span><br><span class="line">}</span><br><span class="line">scanRetain.doReturn(senceId + <span class="string">""</span>);</span><br></pre></td></tr></table></figure>
<h3 id="SpringMVC3-2-1">SpringMVC3.2</h3><p>Spring的代码实现简单很多,把Servlet方案中的第二步和第三步合成了一步,但是也不那么直观,不利于理解.</p>
<h4 id="实现长连接-1">实现长连接</h4><figure class="highlight nimrod"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"> // 上下文容器</span><br><span class="line"> public <span class="keyword">static</span> final <span class="type">ConcurrentHashMap</span><<span class="type">String</span>, <span class="type">DeferredResult</span><<span class="type">String</span>>> <span class="type">MAP</span> = new <span class="type">ConcurrentHashMap</span><<span class="type">String</span>, <span class="type">DeferredResult</span><<span class="type">String</span>>>();</span><br><span class="line"></span><br><span class="line">@<span class="type">RequestMapping</span>(<span class="string">"doScan/{key}"</span>)</span><br><span class="line"> @<span class="type">ResponseBody</span></span><br><span class="line"> public <span class="type">DeferredResult</span><<span class="type">String</span>> doScan(@<span class="type">PathVariable</span>(<span class="string">"key"</span>) <span class="type">String</span> key) {</span><br><span class="line"> <span class="type">DeferredResult</span><<span class="type">String</span>> <span class="literal">result</span> = new <span class="type">DeferredResult</span><<span class="type">String</span>>();</span><br><span class="line"> <span class="type">MAP</span>.put(key, <span class="literal">result</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">result</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h4 id="扫描事件触发长连接响应的逻辑-1">扫描事件触发长连接响应的逻辑</h4><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">@RequestMapping(value=<span class="string">"/newScan/{key}"</span>,produces = <span class="string">"text/plain;charset=utf-8;"</span>)</span><br><span class="line"> @ResponseBody</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">String</span> newScan(@PathVariable(<span class="string">"key"</span>) <span class="built_in">String</span> key,</span><br><span class="line"> HttpServletRequest req, HttpServletResponse res) {</span><br><span class="line"> DeferredResult<<span class="built_in">String</span>> <span class="built_in">data</span> = Scans<span class="built_in">.</span><span class="built_in">MAP</span><span class="built_in">.</span>get(key);</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">data</span>!=<span class="built_in">null</span>){</span><br><span class="line"> <span class="built_in">data</span><span class="built_in">.</span>setResult(<span class="string">"this is result:"</span>+System<span class="built_in">.</span>currentTimeMillis());</span><br><span class="line"> Scans<span class="built_in">.</span><span class="built_in">MAP</span><span class="built_in">.</span>remove(key);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"new scan test finished :"</span>+key+<span class="string">"now is :"</span>+System<span class="built_in">.</span>currentTimeMillis();</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h4 id="异步处理方式,_不适用于该场景">异步处理方式, 不适用于该场景</h4><p>异步操作比较适用于一些比较耗时的操作(如大数据计算,文件处理),它们的响应一般不存在其他的触发点,就是取决于Callable内部代码块的执行结束.</p>
<p>Callable: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@ResponseBody</span></span><br><span class="line"> <span class="annotation">@RequestMapping</span>(<span class="string">"call"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> Callable<String> <span class="title">call</span><span class="params">(HttpServletRequest req, HttpServletResponse res)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Callable<String>() {</span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> TimeUnit.SECONDS.sleep(<span class="number">5</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"hello,callable"</span>;</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>WebAsyncTask: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@ResponseBody</span></span><br><span class="line"> <span class="annotation">@RequestMapping</span>(<span class="string">"async"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> WebAsyncTask<String> <span class="title">async</span><span class="params">(HttpServletRequest req, HttpServletResponse res)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> Callable<String> callable = <span class="keyword">new</span> Callable<String>() {</span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> TimeUnit.SECONDS.sleep(<span class="number">5</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"hello,WebAsyncTask"</span>;</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> WebAsyncTask<String>(<span class="number">1000</span>*<span class="number">60L</span>,callable);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h2 id="注意">注意</h2><p>如果在web.xml中配置了其它的servlet或filter, 必须在他们的根标签中增加支持异步操作标签<code><async-supported>true</async-supported></code>.</p>
<h2 id="总结">总结</h2><p>综上,我们大致可以总结出异步处理的两种应用场景:</p>
<ol>
<li><p>多点操作,单点的响应往往依赖于其他点的触发,最典型的就是微信扫描登录了.这个基本的编码思路应该是这样的:</p>
<ul>
<li>定义一个上下文存储容器,容器要支持并发,最好选用Concurrent类型.</li>
<li>开发长连接接口,客户端请求连接后,将上下文加入存储容器.</li>
<li>开发响应的触发逻辑代码段.</li>
<li>触发业务完成以后,调用响应触发逻辑.</li>
</ul>
</li>
<li><p>单点操作,但是操作往往非常耗时,不能及时响应.这种场景一般会把耗时操作全部抽离到Callable代码段,响应的触发点就是Callable代码的结束处.</p>
</li>
</ol>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>用过微信网页版的人应该都清楚网页登陆的流程,大致描述一下这个过程: </p>
<ol>
<li>打开网页版登陆<a href="https://wx.qq.com/">链接</a></li>
<li>页面会显示一个二维码</li>
<li>用微信客户端扫描二维码,让用户确认登陆网页版</li>
<li>如果确认登陆,网页版会自动进入聊天界面</li>
</ol>
<p>这个过程的交互方式和一般的WEB应用不太一样,<code>步骤4</code>网页自动跳转,明显是由服务端主动推送了内容给网页端,网页端收到跳转确认后才触发的,这里就引出了今天要讨论的问题:<code>服务端推送技术</code>.服务端推送又称为Comet,服务端异步处理等.很早以前就出现了,但一直没有一个统一的标准,存在着不少Comet技术框架,各个Web容器也各自实现了自己的Comet支持.最近公司的产品也出现了和微信网页版登陆类似的场景,需要用到Comet技术,我简单的研究了下,写下来记录一下.</p>]]>
</summary>
<category term="异步处理" scheme="http://arccode.net/tags/%E5%BC%82%E6%AD%A5%E5%A4%84%E7%90%86/"/>
<category term="推送" scheme="http://arccode.net/tags/%E6%8E%A8%E9%80%81/"/>
<category term="长轮询" scheme="http://arccode.net/tags/%E9%95%BF%E8%BD%AE%E8%AF%A2/"/>
<category term="Java" scheme="http://arccode.net/categories/Java/"/>
</entry>
<entry>
<title><![CDATA[使用jconsole监控JVM运行状况]]></title>
<link href="http://arccode.net/2015/10/27/%E4%BD%BF%E7%94%A8jconsole%E7%9B%91%E6%8E%A7JVM%E8%BF%90%E8%A1%8C%E7%8A%B6%E5%86%B5/"/>
<id>http://arccode.net/2015/10/27/使用jconsole监控JVM运行状况/</id>
<published>2015-10-27T02:22:12.000Z</published>
<updated>2015-10-27T02:56:16.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>最近有个服务运行几天就会莫名其妙的无法调用, 但使用<code>jps -m|grep 服务名称</code>指令查看后发现服务进程仍然存在; 因此怀疑可能是有内存泄漏导致进程编程”僵尸”进程.</p>
<p>为此决定使用jconsole来对指定进程的JVM进行监控, 下面将详细介绍如何配置Linux JRE来监控内存.</p>
<a id="more"></a>
<h2 id="修改启动脚本">修改启动脚本</h2><p>在启动脚本中增加启动参数, 启动脚本可以是自定义的shell脚本或tomcat自带的脚本等等.</p>
<figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">JAV<span class="built_in">A_OPTS</span>=<span class="string">"$JAVA_OPTS -Djava.rmi.server.hostname=192.168.10.89 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=39700 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true"</span></span><br></pre></td></tr></table></figure>
<h2 id="修改JRE">修改JRE</h2><figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 切换到jdk的子目录</span></span><br><span class="line">/usr/local/jdk1.<span class="number">8.0_60</span>/jre/<span class="class"><span class="keyword">lib</span>/<span class="title">management</span></span></span><br><span class="line"><span class="comment"># 修改权限并修改文件名</span></span><br><span class="line">chmod <span class="number">600</span> jmxremote.password.template</span><br><span class="line">mv jmxremote.password.template jmxremote.password</span><br><span class="line"><span class="comment"># 修改文件内容</span></span><br><span class="line">vim jmxremote.password</span><br><span class="line"><span class="comment"># 在文件末尾增加用户名和密码</span></span><br><span class="line">monitorRole <span class="number">123456</span></span><br><span class="line">controlRole <span class="number">123456</span></span><br></pre></td></tr></table></figure>
<h2 id="启动jconsole">启动jconsole</h2><p>启动程序脚本;</p>
<p>启动jconsole, 如下图;</p>
<p><img src="/images/java/jconsole_monitor_1.png" alt=""></p>
<p><img src="/images/java/jconsole_monitor_2.png" alt=""></p>
<p><img src="/images/java/jconsole_monitor_3.png" alt=""></p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>最近有个服务运行几天就会莫名其妙的无法调用, 但使用<code>jps -m|grep 服务名称</code>指令查看后发现服务进程仍然存在; 因此怀疑可能是有内存泄漏导致进程编程”僵尸”进程.</p>
<p>为此决定使用jconsole来对指定进程的JVM进行监控, 下面将详细介绍如何配置Linux JRE来监控内存.</p>]]>
</summary>
<category term="JVM" scheme="http://arccode.net/tags/JVM/"/>
<category term="监控" scheme="http://arccode.net/tags/%E7%9B%91%E6%8E%A7/"/>
<category term="Java" scheme="http://arccode.net/categories/Java/"/>
</entry>
<entry>
<title><![CDATA[Linux下MySQL_5.5的修改字符集编码为UTF8]]></title>
<link href="http://arccode.net/2015/07/07/Linux%E4%B8%8BMySQL-5-5%E7%9A%84%E4%BF%AE%E6%94%B9%E5%AD%97%E7%AC%A6%E9%9B%86%E7%BC%96%E7%A0%81%E4%B8%BAUTF8/"/>
<id>http://arccode.net/2015/07/07/Linux下MySQL-5-5的修改字符集编码为UTF8/</id>
<published>2015-07-07T08:15:53.000Z</published>
<updated>2015-07-07T08:53:11.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>mysql安装后, 插入的数据位乱码, 经检查为默认字符集是<code>latin1</code>, 而程序使用<code>UTF-8</code>.</p>
<h2 id="检查字符集">检查字符集</h2><p>1.使用ssh终端连接mysql</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -h <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> -u root -p</span><br></pre></td></tr></table></figure>
<p>2.查看字符集</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'character%'</span>;</span></span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>显示如下</p>
<figure class="highlight gherkin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+--------------------------+----------------------------+</span><br><span class="line">|<span class="string"> Variable_name </span>|<span class="string"> Value </span>|</span><br><span class="line">+--------------------------+----------------------------+</span><br><span class="line">|<span class="string"> character_set_client </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_connection </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_database </span>|<span class="string"> latin1 </span>|</span><br><span class="line">|<span class="string"> character_set_filesystem </span>|<span class="string"> binary </span>|</span><br><span class="line">|<span class="string"> character_set_results </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_server </span>|<span class="string"> latin1 </span>|</span><br><span class="line">|<span class="string"> character_set_system </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_sets_dir </span>|<span class="string"> /usr/share/mysql/charsets/ </span>|</span><br><span class="line">+--------------------------+----------------------------+</span><br></pre></td></tr></table></figure>
<p>character_set_database和character_set_server的默认字符集还是latin1.</p>
<h2 id="解决方案">解决方案</h2><p>修改mysql的<code>my.cnf</code>文件中的字符集键值, <code>my.cnf</code>一般位于<code>/etc</code>目录下, 没找到的话可以使用<code>find / -name my.cnf</code>查找.</p>
<p>1.在[client]字段里加入default-character-set=utf8</p>
<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">[client]</span></span><br><span class="line"><span class="setting">port = <span class="value"><span class="number">3306</span></span></span></span><br><span class="line"><span class="setting">socket = <span class="value">/var/lib/mysql/mysql.sock</span></span></span><br><span class="line"><span class="setting">default-character-set=<span class="value">utf8</span></span></span><br></pre></td></tr></table></figure>
<p>2.在[mysqld]字段里加入character-set-server=utf8</p>
<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">[mysqld]</span></span><br><span class="line"><span class="setting">port = <span class="value"><span class="number">3306</span></span></span></span><br><span class="line"><span class="setting">socket = <span class="value">/var/lib/mysql/mysql.sock</span></span></span><br><span class="line"><span class="setting">character-set-server=<span class="value">utf8</span></span></span><br></pre></td></tr></table></figure>
<p>3.在[mysql]字段里加入default-character-set=utf8</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[mysql]</span><br><span class="line">no-<span class="keyword">auto</span>-rehash</span><br><span class="line"><span class="keyword">default</span>-character-<span class="built_in">set</span>=utf8</span><br></pre></td></tr></table></figure>
<p>修改完成后,service mysql restart重启mysql服务就生效.</p>
<p><strong><em>注意:</em></strong> [mysqld]字段与[mysql]字段是有区别的.</p>
<p>4.再次查看字符集</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'character%'</span>;</span></span><br></pre></td></tr></table></figure>
<p>如下所示: </p>
<figure class="highlight gherkin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+--------------------------+----------------------------+</span><br><span class="line">|<span class="string"> Variable_name </span>|<span class="string"> Value </span>|</span><br><span class="line">+--------------------------+----------------------------+</span><br><span class="line">|<span class="string"> character_set_client </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_connection </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_database </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_filesystem </span>|<span class="string"> binary </span>|</span><br><span class="line">|<span class="string"> character_set_results </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_server </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_set_system </span>|<span class="string"> utf8 </span>|</span><br><span class="line">|<span class="string"> character_sets_dir </span>|<span class="string"> /usr/share/mysql/charsets/ </span>|</span><br><span class="line">+--------------------------+----------------------------+</span><br></pre></td></tr></table></figure>
<p>5.如果上面的都修改了还乱码,那剩下问题就一定在connection连接层上.</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">SET</span> <span class="keyword">NAMES</span> <span class="string">'utf8'</span>;</span></span><br></pre></td></tr></table></figure>
<p>该语句相当于</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">SET</span> character_set_client = utf8;</span></span><br><span class="line"><span class="operator"><span class="keyword">SET</span> character_set_results = utf8;</span></span><br><span class="line"><span class="operator"><span class="keyword">SET</span> character_set_connection = utf8;</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>mysql安装后, 插入的数据位乱码, 经检查为默认字符集是<code>latin1</code>, 而程序使用<code>UTF-8</code>.</p>
<h2 id="检查字符集">检查字符集</h2><p>1.使用ssh终端连接mysql</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -h <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> -u root -p</span><br></pre></td></tr></table></figure>
<p>2.查看字符集</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'character%'</span>;</span></span><br></pre></td></tr></table></figure>]]>
</summary>
<category term="MySQL" scheme="http://arccode.net/tags/MySQL/"/>
<category term="乱码" scheme="http://arccode.net/tags/%E4%B9%B1%E7%A0%81/"/>
<category term="DB" scheme="http://arccode.net/categories/DB/"/>
</entry>
<entry>
<title><![CDATA[ngrok暴露本地Web应用映射到外网]]></title>
<link href="http://arccode.net/2015/07/05/ngrok%E6%9A%B4%E9%9C%B2%E6%9C%AC%E5%9C%B0Web%E5%BA%94%E7%94%A8%E6%98%A0%E5%B0%84%E5%88%B0%E5%A4%96%E7%BD%91/"/>
<id>http://arccode.net/2015/07/05/ngrok暴露本地Web应用映射到外网/</id>
<published>2015-07-05T04:55:41.000Z</published>
<updated>2015-08-10T15:16:21.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>开发第三方支付, 需要接收第三方回调(即在外网上暴露api), 发现<a href="https://ngrok.com/" target="_blank" rel="external">ngrok</a>可以完成该业务, 下面具体介绍其使用.</p>
<a id="more"></a>
<h2 id="下载并运行">下载并运行</h2><h3 id="下载">下载</h3><ul>
<li>下载地址: <a href="https://ngrok.com/download" target="_blank" rel="external">https://ngrok.com/download</a></li>
</ul>
<h3 id="解压">解压</h3><ul>
<li>unzip ngrok_2.0.19_darwin_amd64.zip -d /path/to/ngrok</li>
</ul>
<h3 id="运行">运行</h3><ul>
<li>cd /path/to/ngrok</li>
<li>ngrok -h</li>
</ul>
<h2 id="映射本地端口">映射本地端口</h2><ul>
<li>ngrok http 8080(映射本地8080端口到外网)</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">ngrok by @inconshreveable (Ctrl+C to quit)</span><br><span class="line"> </span><br><span class="line">Tunnel Status online </span><br><span class="line">Version <span class="number">2.0</span><span class="number">.19</span>/<span class="number">2.0</span><span class="number">.19</span> </span><br><span class="line">Web Interface http:<span class="comment">//127.0.0.1:4040 </span></span><br><span class="line">Forwarding http:<span class="comment">//430ca81c.ngrok.io -> localhost:8080 </span></span><br><span class="line">Forwarding https:<span class="comment">//430ca81c.ngrok.io -> localhost:8080 </span></span><br><span class="line"> </span><br><span class="line">Connections ttl opn rt1 rt5 p50 p90 </span><br><span class="line"> <span class="number">0</span> <span class="number">0</span> <span class="number">0.00</span> <span class="number">0.00</span> <span class="number">0.00</span> <span class="number">0.00</span></span><br></pre></td></tr></table></figure>
<p>可以通过<code>http://127.0.0.1:4040</code>登录控制台查看配置等参数.</p>
<h2 id="付费功能">付费功能</h2><p>ngrok自动映射的<code>subDomain</code>是随机的, 关闭重启将改变, 可升级为付费用户自定义<code>subDomain</code>, 详情见<a href="https://ngrok.com/product" target="_blank" rel="external">高级特性</a>.</p>
<h2 id="服务被墙了怎么办">服务被墙了怎么办</h2><h3 id="自建Tunnel">自建Tunnel</h3><p>详见<a href="http://tonybai.com/2015/03/14/selfhost-ngrok-service/" target="_blank" rel="external">搭建自己的ngrok服务</a>.</p>
<h3 id="国内有位工程师搭了个服务,_开箱即用">国内有位工程师搭了个服务, 开箱即用</h3><p> 详见<a href="http://www.tunnel.mobi/" target="_blank" rel="external">http://www.tunnel.mobi/</a></p>
<p> 使用步骤:</p>
<ul>
<li><p><a href="https://ngrok.com/download" target="_blank" rel="external">下载官方1.x客户端,请注意,官方目前并不准备开放2.0客户端的第三方服务器支持</a></p>
</li>
<li><p>1.7版客户端分流:<a href="https://ngrokd.b0.upaiyun.com/clients/ngrok_for_linux.zip" target="_blank" rel="external">Linux版本</a>,<a href="https://ngrokd.b0.upaiyun.com/clients/ngrok_for_macosx.zip" target="_blank" rel="external">Mac OSX版本</a>,<a href="https://ngrokd.b0.upaiyun.com/clients/ngrok_for_win.zip" target="_blank" rel="external">32Bit Win版本</a>,<a href="https://ngrokd.b0.upaiyun.com/clients/ngrok_for_win_64bit.zip" target="_blank" rel="external">64Bit Win版本</a></p>
</li>
<li><p><a href="https://ngrokd.b0.upaiyun.com/ngrok.cfg" target="_blank" rel="external">下载配置文件ngrok.cfg</a></p>
</li>
<li><p>运行客户端时,请添加-config以载入配置文件。</p>
</li>
</ul>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">例如 ngrok -config ngrok<span class="class">.cfg</span> -subdomain example <span class="number">8080</span></span><br><span class="line">意为将本地的<span class="number">8080</span>端口链接到example<span class="class">.tunnel</span><span class="class">.mobi</span>上</span><br></pre></td></tr></table></figure>
<ul>
<li>更多使用技巧,可见<a href="https://ngrok.com/docs/1" target="_blank" rel="external">文档</a>。</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>开发第三方支付, 需要接收第三方回调(即在外网上暴露api), 发现<a href="https://ngrok.com/">ngrok</a>可以完成该业务, 下面具体介绍其使用.</p>]]>
</summary>
<category term="Ngrok" scheme="http://arccode.net/tags/Ngrok/"/>
<category term="代理" scheme="http://arccode.net/tags/%E4%BB%A3%E7%90%86/"/>
<category term="代理" scheme="http://arccode.net/categories/%E4%BB%A3%E7%90%86/"/>
</entry>
<entry>
<title><![CDATA[SOA服务总线设计]]></title>
<link href="http://arccode.net/2015/07/03/SOA%E6%9C%8D%E5%8A%A1%E6%80%BB%E7%BA%BF%E8%AE%BE%E8%AE%A1/"/>
<id>http://arccode.net/2015/07/03/SOA服务总线设计/</id>
<published>2015-07-03T15:51:46.000Z</published>
<updated>2015-07-03T16:06:22.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>基于总线的设计,借鉴了计算机内部硬件组成的设计思想(通过总线传输数据).在分布式系统中,不同子系统之间需要实现相互通信和远程调用,比较直接的方式就是“点对点”的通信方式,但是这样会暴露出一些很明显的问题:系统之间紧密耦合、配置和引用混乱、服务调用关系错综复杂、难以统一管理、异构系统之间存在不兼容等.而基于总线的设计,正是为了解决上述问题.总线则作为中枢系统,提供统一的服务入口,并实现了服务统一管理、服务路由、协议转换、数据格式转换等功能.这样能够将不同系统有效地连接起来,并大大降低了连接数(每个子系统只需要和总线建立连接)和系统间连接拓扑的复杂度.<br><a id="more"></a><br>如图所示:</p>
<p><img src="/images/arc/SOA_ARC.jpg" alt=""></p>
<p>基于服务总线的设计,往往需要ESB(Enterprise Service Bus,企业服务总线)产品来充当基础设施.ESB采用了“总线”这样一种模式来管理和简化应用之间的集成拓扑结构,以广为接受的开放标准为基础来支持应用之间在消息、事件和服务的级别上动态的互连互通. ESB是一种在松散耦合的服务和应用之间标准的集成方式.</p>
<p>在其内部设计和实现中,通常会应用到一些经典的架构模式,例如:Broker模式、消息总线模式、管道过滤器模式、发布订阅模式等.这里,我们将重点介绍这几种核心的架构模式.</p>
<h3 id="Broker模式">Broker模式</h3><p>Broker可以看作是服务总线中的一部分,通常应用于同步调用的场景(调用服务后阻塞,等待远程服务响应完成后再返回结果).Broker可以看作是代理、分发,意味着对服务的调用,可以直接通过Broker来完成.在软件架构设计中,向已经存在(引用或者调用)关系的两者中间加入“第三者”是一种常见的解耦方式,显然,Broker也不例外.</p>
<p>如下图所示:</p>
<p><img src="/images/arc/SOA_Broker.jpg" alt=""></p>
<p>为了进一步加深读者对Broker模式的理解,这里通过举例来说明:</p>
<p>在现实生活中,我们需要租房子(可以看作是一种服务调用),可以通过几种途径来完成.第一,我们可以先在网上了解,然后跑到目标小区去询问和看房,最后再找房东签合同等;第二,也可以直接找附近的地产中介,说出我们的要求和预算,请中介直接帮我们搞定一切.很明显,第一种方式,需要自己对目标有足够的了解而且还要亲自去找房东签合同(依赖具体,可以看作是紧密耦合),而第二种方式,仅仅需要告知中介需要什么即可完成租房,甚至都不需要知道哪个小区有房子、房东到底是谁等这些信息(依赖抽象,并通过第三者来实现解耦).当然,找中介虽然省事,也会产生额外的经济开销.同理,通过Broker来调用服务也可能会产生一定的开销.</p>
<h3 id="消息总线模式">消息总线模式</h3><p>SOA系统有三种基础组件:消息总线、信息转换/处理引擎和存储库.一般来说,这些组件会集成到ESB中,而在这些基础组件中,消息总线是最重要的.消息总线主要应用于异步通信场景(投递消息后立刻返回结果,而不用等待远程系统返回执行成功),可以大大提升响应速度和系统吞吐量.当然,消息总线也同时支持同步通信模式.基于消息总线模式的设计中,消息中间件屏蔽了系统间底层通信的细节,是比较典型的(异步)松耦合的架构.</p>
<p>在企业中,随着业务的逐渐发展,各大系统之间的调用交互变得非常频繁,关系错综复杂.</p>
<p>想象如果有几十或者上百个系统(在保险、金融领域很常见),将变得难以维护.通过引入消息中间件,便能有效的解决这些问题.不同系统之间,只需要连接到消息总线,能保证成功投递/接收消息即可.对于消息投递者(生产者)而言,根本不用关心消息的接收者(消费者)到底有哪些,也不用关心消费者接收到消息之后该如何处理.对于接收者(消费者)而言,只需要关注与自己有关的消息,接收到消息后并做出相应的处理即可.</p>
<p>如下图所示:</p>
<p><img src="/images/arc/SOA_Message_Bus.jpg" alt=""></p>
<p>比较典型的应用场景:张三在CRM系统中录入了一条客户签约订单的信息并审核通过后,CRM系统会向消息总线投递一条消息(消息中包含必要信息,例如员工ID,订单编号,产品编号和交易金额等,而CRM系统不用关心有哪些系统会接受到该消息).员工绩效奖金管理系统订阅了该消息主题,收到消息通知后开始处理(给张三执行加奖金操作),同时,进销存系统收到消息通知后也会即时地更新商品库存的数量.这样,便实现异构系统之间的松耦合、异步通信.当然,真实场景可能比这里更复杂,但是实现思路上都大同小异.</p>
<p>在理解了上述实例之后,有必要了解下Java EE中被广泛应用和实现的JMS(Java消息服务).</p>
<p>JMS是一种关于消息的规范,业界流行的ActiveMQ则是实现了JMS规范的开源消息总线.</p>
<p>JMS支持两种模式:发布/订阅模式和队列模式(点对点模式).其中,发布/订阅模式借鉴了现实生活中的出版社(发布图书)和读者(订阅图书),消息的消费者(读者)只需要订阅自己感兴趣的消息(图书)即可,消息生产者(出版社)生产(出版)了消费者(读者)感兴趣的新消息(新书)后,会通知消费者(读者)接受处理.在JMS发布/订阅模式中,通常以Topic(主题)来标识消息,是一对多的模式,意味着同一个主题可以同时被多个消费者来订阅和消费.而在JMS 队列模型中,通常以Queue name来标识消息,是一对一的模型,意味着同一条消息只能被一个消费者接收并消费(且只能被消费一次).在生产者和消费者都是集群的环境中,通常需要将这两种模式结合起来使用,情况会复杂很多,而且需要考虑容错性、负载均衡、消息一致性、消息优先级等复杂的问题.</p>
<p>业界主流的消息总线(消息中间件)产品,普遍支持消息过滤、自动重试、分布式事务、持久化、消息优先级、消息回溯、(生产者/消费者/中间件自身)集群、故障转移等高级特性.</p>
<h2 id="总结">总结</h2><p>基于总线架构的主要优势在于:</p>
<blockquote>
<p>可扩展性</p>
</blockquote>
<p>使用消息架构,可以在不影响现有应用的情况下增加或移除应用.</p>
<blockquote>
<p>低复杂度</p>
</blockquote>
<p>每个应用只需要和总线通信,只有1个连接点,降低了应用程序集成的复杂度.</p>
<blockquote>
<p>灵活性</p>
</blockquote>
<p>对于构成复杂处理的应用程序通信组来说,只需要改变配置和控制路由参数就能满足业务逻辑或者需求变更,而不需要更改服务本身.</p>
<blockquote>
<p>松耦合</p>
</blockquote>
<p>应用程序直接和消息总线通信,不依赖其它应用程序,因此可以替换和修改.</p>
<blockquote>
<p>可扩展性</p>
</blockquote>
<p>可以把多个应用程序附加到总线上,进行并发处理,达到负载均衡的目的.</p>
<p>基于总线的模型,可以面向同构和异构系统(跨平台).当前主要应用于传统企业应用的整合与系统集成中(例如:电信、保险、金融等行业).由于基于总线的模型是一种集中式的架构,总线自身容易形成性能瓶颈,但可以通过横向扩展, 然后在其上层增加LVS进行负载均衡, 实现高并发,高可用.</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>基于总线的设计,借鉴了计算机内部硬件组成的设计思想(通过总线传输数据).在分布式系统中,不同子系统之间需要实现相互通信和远程调用,比较直接的方式就是“点对点”的通信方式,但是这样会暴露出一些很明显的问题:系统之间紧密耦合、配置和引用混乱、服务调用关系错综复杂、难以统一管理、异构系统之间存在不兼容等.而基于总线的设计,正是为了解决上述问题.总线则作为中枢系统,提供统一的服务入口,并实现了服务统一管理、服务路由、协议转换、数据格式转换等功能.这样能够将不同系统有效地连接起来,并大大降低了连接数(每个子系统只需要和总线建立连接)和系统间连接拓扑的复杂度.<br>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="SOA" scheme="http://arccode.net/tags/SOA/"/>
<category term="分布式" scheme="http://arccode.net/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
<category term="架构" scheme="http://arccode.net/categories/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title><![CDATA[CAP原理和最终一致性]]></title>
<link href="http://arccode.net/2015/07/03/CAP%E5%8E%9F%E7%90%86%E5%92%8C%E6%9C%80%E7%BB%88%E4%B8%80%E8%87%B4%E6%80%A7/"/>
<id>http://arccode.net/2015/07/03/CAP原理和最终一致性/</id>
<published>2015-07-03T14:33:44.000Z</published>
<updated>2015-07-03T14:46:28.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>在足球比赛里,一个球员在一场比赛中进三个球,称之为帽子戏法(Hat-trick).在分布式数据系统中,也有一个帽子原理(CAP Theorem),不过此帽子非彼帽子.CAP原理中,有三个要素:</p>
<ul>
<li>一致性(Consistency)</li>
<li>可用性(Availability)</li>
<li>分区容忍性(Partition tolerance)</li>
</ul>
<p>CAP原理指的是,这三个要素最多只能同时实现两点,不可能三者兼顾.因此在进行分布式架构设计时,必须做出取舍.而对于分布式数据系统,分区容忍性是基本要求,否则就失去了价值.因此设计分布式数据系统,就是在一致性和可用性之间取一个平衡.对于大多数web应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是目前多数分布式数据库产品的方向.</p>
<p>当然,牺牲一致性,并不是完全不管数据的一致性,否则数据是混乱的,那么系统可用性再高分布式再好也没有了价值.牺牲一致性,只是不再要求关系型数据库中的强一致性,而是只要系统能达到最终一致性即可,考虑到客户体验,这个最终一致的时间窗口,要尽可能的对用户透明,也就是需要保障“用户感知到的一致性”.通常是通过数据的多份异步复制来实现系统的高可用和数据的最终一致性的,“用户感知到的一致性”的时间窗口则取决于数据复制到一致状态的时间.</p>
<a id="more"></a>
<h2 id="最终一致性(eventually_consistent)">最终一致性(eventually consistent)</h2><p>对于一致性,可以分为从客户端和服务端两个不同的视角.从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题.从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致.一致性是因为有并发读写才有的问题,因此在理解一致性的问题时,一定要注意结合考虑并发读写的场景.</p>
<p>从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性.对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性.如果能容忍后续的部分或者全部访问不到,则是弱一致性.如果经过一段时间后要求能访问到更新后的数据,则是最终一致性.</p>
<p>最终一致性根据更新数据后各进程访问到数据的时间和方式的不同,又可以区分为:</p>
<blockquote>
<p><strong>因果一致性</strong>.如果进程A通知进程B它已更新了一个数据项,那么进程B的后续访问将返回更新后的值,且一次写入将保证取代前一次写入.与进程A无因果关系的进程C的访问遵守一般的最终一致性规则.</p>
<p><strong>“读己之所写(read-your-writes)”一致性</strong>.当进程A自己更新一个数据项之后,它总是访问到更新过的值,绝不会看到旧值.这是因果一致性模型的一个特例.</p>
<p><strong>会话(Session)一致性</strong>.这是上一个模型的实用版本,它把访问存储系统的进程放到会话的上下文中.只要会话还存在,系统就保证“读己之所写”一致性.如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新的会话.</p>
<p><strong>单调(Monotonic)读一致性</strong>.如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回在那个值之前的值.</p>
<p><strong>单调写一致性</strong>.系统保证来自同一个进程的写操作顺序执行.要是系统不能保证这种程度的一致性,就非常难以编程了.</p>
</blockquote>
<p>上述最终一致性的不同方式可以进行组合,例如单调读一致性和读己之所写一致性就可以组合实现.并且从实践的角度来看,这两者的组合,读取自己更新的数据,和一旦读取到最新的版本不会再读取旧版本,对于此架构上的程序开发来说,会少很多额外的烦恼.</p>
<p>从服务端角度,如何尽快将更新后的数据分布到整个系统,降低达到最终一致性的时间窗口,是提高系统的可用度和用户体验非常重要的方面.对于分布式数据系统:</p>
<ul>
<li>N — 数据复制的份</li>
<li>W — 更新数据时需要保证写完成的节点数</li>
<li>R — 读取数据的时候需要读取的节点数</li>
</ul>
<p>如果W+R>N,写的节点和读的节点重叠,则是强一致性.例如对于典型的一主一备同步复制的关系型数据库,N=2,W=2,R=1,则不管读的是主库还是备库的数据,都是一致的.</p>
<p>如果W+R<=N,则是弱一致性.例如对于一主一备异步复制的关系型数据库,N=2,W=1,R=1,则如果读的是备库,就可能无法读取主库已经更新过的数据,所以是弱一致性.</p>
<p>对于分布式系统,为了保证高可用性,一般设置N>=3.不同的N,W,R组合,是在可用性和一致性之间取一个平衡,以适应不同的应用场景.</p>
<p>如果N=W,R=1,任何一个写节点失效,都会导致写失败,因此可用性会降低,但是由于数据分布的N个节点是同步写入的,因此可以保证强一致性.* 如果N=R,W=1,只需要一个节点写入成功即可,写性能和可用性都比较高.但是读取其他节点的进程可能不能获取更新后的数据,因此是弱一致性.这种情况下,如果W<(N+1)/2,并且写入的节点不重叠的话,则会存在写冲突 </p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>在足球比赛里,一个球员在一场比赛中进三个球,称之为帽子戏法(Hat-trick).在分布式数据系统中,也有一个帽子原理(CAP Theorem),不过此帽子非彼帽子.CAP原理中,有三个要素:</p>
<ul>
<li>一致性(Consistency)</li>
<li>可用性(Availability)</li>
<li>分区容忍性(Partition tolerance)</li>
</ul>
<p>CAP原理指的是,这三个要素最多只能同时实现两点,不可能三者兼顾.因此在进行分布式架构设计时,必须做出取舍.而对于分布式数据系统,分区容忍性是基本要求,否则就失去了价值.因此设计分布式数据系统,就是在一致性和可用性之间取一个平衡.对于大多数web应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是目前多数分布式数据库产品的方向.</p>
<p>当然,牺牲一致性,并不是完全不管数据的一致性,否则数据是混乱的,那么系统可用性再高分布式再好也没有了价值.牺牲一致性,只是不再要求关系型数据库中的强一致性,而是只要系统能达到最终一致性即可,考虑到客户体验,这个最终一致的时间窗口,要尽可能的对用户透明,也就是需要保障“用户感知到的一致性”.通常是通过数据的多份异步复制来实现系统的高可用和数据的最终一致性的,“用户感知到的一致性”的时间窗口则取决于数据复制到一致状态的时间.</p>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="SOA" scheme="http://arccode.net/tags/SOA/"/>
<category term="分布式" scheme="http://arccode.net/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
<category term="架构" scheme="http://arccode.net/categories/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title><![CDATA[SOA初探]]></title>
<link href="http://arccode.net/2015/07/03/SOA%E5%88%9D%E6%8E%A2/"/>
<id>http://arccode.net/2015/07/03/SOA初探/</id>
<published>2015-07-03T14:23:05.000Z</published>
<updated>2015-07-03T15:53:02.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>曾今SOA的概念犹如今日“云计算、大数据”一样,被炒得火热,不少企业便纷纷响应,并宣称会拥抱和实施SOA.而事实上,业界出现了两种极端:一种是由于各类文章和书籍关于SOA的描述往往太过抽象,再加上各大厂商的呼吁,使得SOA往往显得“高大上”,令不少企业和架构师们望而却步.第二种恰好相反,有部分人却认为SOA无非是“新瓶装旧酒”.</p>
<p>个人理解,SOA在宏观上确实太复杂,因为它涉及到的不仅仅是技术和架构本身.而从技术的视角来看,并非难以落地.</p>
<p>SOA全称“面向服务架构”,它提供的是一种架构风格和理念,而并非是一种技术或者产品.并不是说项目中用了WebService、WCF、Hessian、RMI之类的就是SOA了.</p>
<p>通俗点来讲,SOA提倡将不同应用程序的业务功能封装成“服务”并宿主起来,通常以接口和契约的形式暴露并提供给外界应用访问(通过交换消息),达到不同系统可重用的目的.</p>
<p>流行的WebService等可以看作是实现SOA基础设施的技术方法.当然,实践SOA不仅需要解决服务调用的问题,还包括服务编排、服务治理、服务路由、服务监控等一系列的问题.在大型分布式系统中,SOA被广泛实践,但是在不同的应用场景中,设计方法也大不相同.</p>
<p>SOA是一个组件模型,它能将不同的服务通过定义良好的接口和契约联系起来.服务是SOA的基石,在开始服务设计和SOA实践之前,有必要先了解服务的概念以及服务的常见特性.</p>
<a id="more"></a>
<h2 id="何为服务">何为服务</h2><p>服务的概念非常宽泛,在宏观上,服务的理解是“为他人做事,满足他人需要,而且通常是不以实物形式提供劳动的…”.在SOA系统中,服务指的是应用程序的功能单元,它通常体现了业务功能.服务是一种抽象,它向服务使用者隐藏了服务内部的实现细节.根据服务设计的基本原则,服务可能会具有以下特性:</p>
<blockquote>
<p>自治(理)性 </p>
</blockquote>
<p>服务应该是独立部署和运行存在的,且边界清晰,应尽量减少对外部的引用和依赖.</p>
<blockquote>
<p>粗粒度</p>
</blockquote>
<p>服务调用是需要开销的,这也是实现松耦合的分布式系统必须付出的代价.因此,应尽量通过一次服务调用传输所有需要的数据,而不是分多次去调用服务和组装数据.</p>
<blockquote>
<p>可见性</p>
</blockquote>
<p>服务是对外提供的,必须在某公共的地方可搜寻和发现,且服务要有必要的描述.</p>
<blockquote>
<p>无状态</p>
</blockquote>
<p>服务不应该依赖于其他服务的上下文、会话等,尽量减少不必要的状态管理流程所带来的资源消耗.但是,对于业务流程服务而言,状态数据是不可避免的.</p>
<blockquote>
<p>幂等性</p>
</blockquote>
<p>当消费者调用服务后,服务调用可能会有“成功、失败、超时”这三种状态,当服务并没有最终响应完成时,消费者可以尝试反复地调用服务,这样仍不会影响到最终结果.</p>
<blockquote>
<p>可重用性</p>
</blockquote>
<p>服务应该是可以被重用的,相同功能应可以调用相同的服务,这也是软件设计的原则.</p>
<blockquote>
<p>可组合</p>
</blockquote>
<p>服务是可以被当作成一个步骤的,服务也可以调用其它的服务.这样能够灵活的组合.</p>
<p>有关服务的“粗粒度、无状态、幂等性”等特性,一直是饱受争议的话题,可谓见仁见智.这里有必要说明下,这些特性并不是服务不可或缺的,应当在实践中根据需求来取舍.</p>
<h2 id="SOA所面临的问题及解决方案">SOA所面临的问题及解决方案</h2><p>SOA架构将公共的业务拆分出来,形成可共用的服务,最大程度的保障了代码和逻辑的复用,避免了系统的重复建设,并且让应用程序的部署找到了一种持续可扩展的方案,给应用抗负载能力带来了质的飞跃.</p>
<h3 id="一致性问题">一致性问题</h3><p>SOA架构所面临的一大问题就是如何解决集成服务应用普遍存在的一致性问题,举例来说,同时调用多个服务,当其中一个服务调用失败时,其他服务已经处理执行的结果该如何进行回滚,这在单机本地调用的情况下使用事务比较好处理,而分布式环境下的事务将问题复杂化,并且性能开销难以承受,因此,只有在极端情况下才会考虑强一致性,一般情况下更多的关注最终一致性.</p>
<h3 id="安全问题">安全问题</h3><p>面向企业的平台级的SOA架构,需要对参数传递、响应内容以及各种用户私有信息的交互,有着更严格的且特殊的安全需求,如何构建一个安全的SOA架构体系,也给技术人员带来了很大的挑战.</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>曾今SOA的概念犹如今日“云计算、大数据”一样,被炒得火热,不少企业便纷纷响应,并宣称会拥抱和实施SOA.而事实上,业界出现了两种极端:一种是由于各类文章和书籍关于SOA的描述往往太过抽象,再加上各大厂商的呼吁,使得SOA往往显得“高大上”,令不少企业和架构师们望而却步.第二种恰好相反,有部分人却认为SOA无非是“新瓶装旧酒”.</p>
<p>个人理解,SOA在宏观上确实太复杂,因为它涉及到的不仅仅是技术和架构本身.而从技术的视角来看,并非难以落地.</p>
<p>SOA全称“面向服务架构”,它提供的是一种架构风格和理念,而并非是一种技术或者产品.并不是说项目中用了WebService、WCF、Hessian、RMI之类的就是SOA了.</p>
<p>通俗点来讲,SOA提倡将不同应用程序的业务功能封装成“服务”并宿主起来,通常以接口和契约的形式暴露并提供给外界应用访问(通过交换消息),达到不同系统可重用的目的.</p>
<p>流行的WebService等可以看作是实现SOA基础设施的技术方法.当然,实践SOA不仅需要解决服务调用的问题,还包括服务编排、服务治理、服务路由、服务监控等一系列的问题.在大型分布式系统中,SOA被广泛实践,但是在不同的应用场景中,设计方法也大不相同.</p>
<p>SOA是一个组件模型,它能将不同的服务通过定义良好的接口和契约联系起来.服务是SOA的基石,在开始服务设计和SOA实践之前,有必要先了解服务的概念以及服务的常见特性.</p>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="SOA" scheme="http://arccode.net/tags/SOA/"/>
<category term="分布式" scheme="http://arccode.net/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
<category term="架构" scheme="http://arccode.net/categories/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title><![CDATA[理解RMI中的Stub]]></title>
<link href="http://arccode.net/2015/06/02/%E7%90%86%E8%A7%A3RMI%E4%B8%AD%E7%9A%84Stub/"/>
<id>http://arccode.net/2015/06/02/理解RMI中的Stub/</id>
<published>2015-06-02T03:53:25.000Z</published>
<updated>2015-07-03T14:19:05.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>以前编程时也碰到过<code>Stub</code>这个名词, 但没关注, 今天研究<code>Paypal Rest API</code>又碰到到这个词, 决定探查个究竟.</p>
<h2 id="通俗解释">通俗解释</h2><p>Stub 跟 Proxy 是一对,俗称<code>代理-桩</code>,一般用在远程方法调用.</p>
<p>Proxy 相当于是拿在手里的遥控器,而 Stub 相当于长在电视机里的遥控接收器,它们有着一一对应的接口方法.</p>
<p>Proxy 的接口供客户端程序调用,然后它内部会把信息包装好,以某种方式(比如 RMI)传递给 Stub,而后者通过对应的接口作用于服务端系统,从而完成了<code>远程调用</code>.</p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>以前编程时也碰到过<code>Stub</code>这个名词, 但没关注, 今天研究<code>Paypal Rest API</code>又碰到到这个词, 决定探查个究竟.</p>
<h2 id="通俗解释">通俗解释</h2><p>]]>
</summary>
<category term="名词解释" scheme="http://arccode.net/tags/%E5%90%8D%E8%AF%8D%E8%A7%A3%E9%87%8A/"/>
<category term="设计模式" scheme="http://arccode.net/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title><![CDATA[阿里巴巴分布式服务框架-Dubbo问与答]]></title>
<link href="http://arccode.net/2015/05/23/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4%E5%88%86%E5%B8%83%E5%BC%8F%E6%9C%8D%E5%8A%A1%E6%A1%86%E6%9E%B6-Dubbo%E9%97%AE%E4%B8%8E%E7%AD%94/"/>
<id>http://arccode.net/2015/05/23/阿里巴巴分布式服务框架-Dubbo问与答/</id>
<published>2015-05-23T14:07:16.000Z</published>
<updated>2015-05-24T02:36:43.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,每天为2000+ 个服务提供3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。Dubbo自2011年开源后,已被许多非阿里系公司使用。</p>
<p>项目主页:<a href="http://dubbo.io/Home-zh.htm" target="_blank" rel="external">http://dubbo.io/Home-zh.htm</a></p>
<h2 id="先来个自我介绍吧!">先来个自我介绍吧!</h2><p>我叫梁飞,花名虚极,之前负责Dubbo服务框架,现已调到天猫。</p>
<p>我的博客:<a href="http://javatar.iteye.com" target="_blank" rel="external">http://javatar.iteye.com</a></p>
<h2 id="Dubbo是什么?能做什么?">Dubbo是什么?能做什么?</h2><p>Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Home-zh.htm" target="_blank" rel="external">http://alibaba.github.io/dubbo-doc-static/Home-zh.htm</a></p>
<h2 id="Dubbo适用于哪些场景?">Dubbo适用于哪些场景?</h2><p>当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。</p>
<a id="more"></a>
<p>当服务越来越多时,服务的URL地址信息就会爆炸式增长,配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。</p>
<p>当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。</p>
<p>接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?等等……</p>
<p>在遇到这些问题时,都可以用Dubbo来解决。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-%E5%85%A5%E9%97%A8" target="_blank" rel="external">Dubbo的背景及需求</a></p>
<h2 id="Dubbo的设计思路是什么?">Dubbo的设计思路是什么?</h2><p>该框架具有极高的扩展性,采用微核+插件体系,并且文档齐全,很方便二次开发,适应性极强。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Developer+Guide-zh.htm#DeveloperGuide-zh-%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1" target="_blank" rel="external">开发者指南 - 框架设计</a></p>
<h2 id="Dubbo的需求和依赖情况?">Dubbo的需求和依赖情况?</h2><p>Dubbo运行JDK1.5之上,缺省依赖javassist、netty、spring等包,但不是必须依赖,通过配置Dubbo可不依赖任何三方库运行。</p>
<p>可参见:<a href="http://code.alibabatech.com/wiki/display/dubbo/User+Guide#UserGuide-Dependency" target="_blank" rel="external">用户指南 - 依赖</a></p>
<h2 id="Dubbo的性能如何?">Dubbo的性能如何?</h2><p>Dubbo通过长连接减少握手,通过NIO及线程池在单连接上并发拼包处理消息,通过二进制流压缩数据,比常规HTTP等短连接协议更快。在阿里巴巴内部,每天支撑2000多个服务,30多亿访问量,最大单机支撑每天近1亿访问量。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A" target="_blank" rel="external">Dubbo性能测试报告</a></p>
<h2 id="和淘宝HSF相比,Dubbo的特点是什么?">和淘宝HSF相比,Dubbo的特点是什么?</h2><p>1. <strong>Dubbo比HSF的部署方式更轻量</strong>,HSF要求使用指定的JBoss等容器,还需要在JBoss等容器中加入sar包扩展,对用户运行环境的侵入性大,如果你要运行在Weblogic或Websphere等其它容器上,需要自行扩展容器以兼容HSF的ClassLoader加载,而Dubbo没有任何要求,可运行在任何Java环境中。</p>
<p>2. <strong>Dubbo比HSF的扩展性更好,很方便二次开发</strong>,一个框架不可能覆盖所有需求,Dubbo始终保持平等对待第三方理念,即所有功能,都可以在不修改Dubbo原生代码的情况下,在外围扩展,包括Dubbo自己内置的功能,也和第三方一样,是通过扩展的方式实现的,而HSF如果你要加功能或替换某部分实现是很困难的,比如支付宝和淘宝用的就是不同的HSF分支,因为加功能时改了核心代码,不得不拷一个分支单独发展,HSF现阶段就算开源出来,也很难复用,除非对架构重写。</p>
<p>3. <strong>HSF依赖比较多内部系统</strong>,比如配置中心,通知中心,监控中心,单点登录等等,如果要开源还需要做很多剥离工作,而Dubbo为每个系统的集成都留出了扩展点,并已梳理干清所有依赖,同时为开源社区提供了替代方案,用户可以直接使用。</p>
<p>4. <strong>Dubbo比HSF的功能更多</strong>,除了ClassLoader隔离,Dubbo基本上是HSF的超集,Dubbo也支持更多协议,更多注册中心的集成,以适应更多的网站架构。</p>
<h2 id="Dubbo在安全机制方面是如何解决的?">Dubbo在安全机制方面是如何解决的?</h2><p>Dubbo主要针对内部服务,对外的服务,阿里有开放平台来处理安全和流控,所以Dubbo在安全方面实现的功能较少,基本上只防君子不防小人,只防止误调用。</p>
<p>Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-%E4%BB%A4%E7%89%8C%E9%AA%8C%E8%AF%81" target="_blank" rel="external">Dubbo的令牌验证</a></p>
<h2 id="Dubbo在阿里巴巴内部以及外部的应用情况?">Dubbo在阿里巴巴内部以及外部的应用情况?</h2><p>在阿里内部,除淘系以外的其它阿里子公司,都在使用Dubbo,包括:中文主站,国际主站,AliExpress,阿里云,阿里金融,阿里学院,良无限,来往等等。</p>
<p>开源后,已被:去哪儿,京东,吉利汽车,方正证劵,海尔,焦点科技,中润四方,华新水泥,海康威视,等公司广泛使用,并不停的有新公司加入,社区讨论及贡献活跃,得到用户很高的评价。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Community-zh.htm#Community-zh-%E5%B7%B2%E7%9F%A5%E7%94%A8%E6%88%B7" target="_blank" rel="external">Dubbo的已知用户</a></p>
<h2 id="在分布式事务、多语言支持方面,Dubbo的计划是什么?">在分布式事务、多语言支持方面,Dubbo的计划是什么?</h2><p>分布式事务可能暂不会支持,因为如果只是支持简单的XA/JTA两阶段提交事务,实用性并不强。用户可以自行实现业务补偿的事件,或更复杂的分布式事务,Dubbo有很多扩展点可以集成。</p>
<p>在多语言方面,Dubbo实现了C++版本,但在内部使用面极窄,没有得到很强的验证,并且C++开发资源紧张,没有精力准备C++开源事项。</p>
<h2 id="Dubbo采用的开源协议?商业应用应该注意哪些事项?">Dubbo采用的开源协议?商业应用应该注意哪些事项?</h2><p>Dubbo采用Apache License 2.0开源协议,它是一个商业友好的协议,你可以免费用于非开源的商业软件中。</p>
<p>你可以对它进行改造和二次发布,只要求保留阿里的著作权,并在再发布时保留原始许可声明。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Download-zh.htm#Download-zh-%E5%BC%80%E6%BA%90%E8%AE%B8%E5%8F%AF" target="_blank" rel="external">Dubbo的开源许可证</a></p>
<h2 id="Dubbo开发团队情况?">Dubbo开发团队情况?</h2><p>Dubbo共有六个开发人员参与开发和测试,每一个开发人员都是很有经验,团队合作很默契,开发过程也很有节奏,有完善质量保障流程。团队组成:</p>
<ul>
<li><p>梁飞 (开发人员/产品管理)</p>
</li>
<li><p>刘昊旻 (开发人员/过程管理)</p>
</li>
<li><p>刘超 (开发人员/用户支持)</p>
</li>
<li><p>李鼎 (开发人员/用户支持)</p>
</li>
<li><p>陈雷 (开发人员/质量保障)</p>
</li>
<li><p>闾刚 (开发人员/开源运维)</p>
</li>
</ul>
<p><img src="http://dl.iteye.com/upload/attachment/0076/4588/a376775f-1550-3746-aad6-808c5989d96e.jpg" alt=""></p>
<p>从左至右:刘超,梁飞,闾刚,陈雷,刘昊旻,李鼎</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Community-zh.htm#Community-zh-%E5%9B%A2%E9%98%9F" target="_blank" rel="external">Dubbo的团队成员</a></p>
<h2 id="其他开发者如何参与?可以做哪些工作?">其他开发者如何参与?可以做哪些工作?</h2><p>开发者可以在Github上fork分支,然后将修改push过来,我们审核并测试后,会合并到主干中。</p>
<p>Github地址:<a href="https://github.com/alibaba/dubbo" target="_blank" rel="external">https://github.com/alibaba/dubbo</a></p>
<p>开发者可以在JIRA上认领小的BUG修复,也可以在开发者指南页面领取大的功能模块。</p>
<p>JIRA:<a href="http://code.alibabatech.com/jira/browse/DUBBO" target="_blank" rel="external">http://code.alibabatech.com/jira/browse/DUBBO</a>(暂不可用)</p>
<p>开发者指南:<a href="http://alibaba.github.io/dubbo-doc-static/Developer+Guide-zh.htm" target="_blank" rel="external">http://alibaba.github.io/dubbo-doc-static/Developer+Guide-zh.htm</a></p>
<h2 id="Dubbo未来的发展计划?">Dubbo未来的发展计划?</h2><p>Dubbo的RPC框架已基本稳定,未来的重心会放在服务治理上,包括架构分析、监控统计、降级控制、流程协作等等。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Roadmap-zh.htm" target="_blank" rel="external">http://alibaba.github.io/dubbo-doc-static/Roadmap-zh.htm</a></p>
<h2 id="声明">声明</h2><p>本文属于转载:<a href="http://www.iteye.com/magazines/103" target="_blank" rel="external">原文(阿里巴巴分布式服务框架 Dubbo 团队成员梁飞专访)</a></p>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,每天为2000+ 个服务提供3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。Dubbo自2011年开源后,已被许多非阿里系公司使用。</p>
<p>项目主页:<a href="http://dubbo.io/Home-zh.htm">http://dubbo.io/Home-zh.htm</a></p>
<h2 id="先来个自我介绍吧!">先来个自我介绍吧!</h2><p>我叫梁飞,花名虚极,之前负责Dubbo服务框架,现已调到天猫。</p>
<p>我的博客:<a href="http://javatar.iteye.com">http://javatar.iteye.com</a></p>
<h2 id="Dubbo是什么?能做什么?">Dubbo是什么?能做什么?</h2><p>Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。</p>
<p>可参见:<a href="http://alibaba.github.io/dubbo-doc-static/Home-zh.htm">http://alibaba.github.io/dubbo-doc-static/Home-zh.htm</a></p>
<h2 id="Dubbo适用于哪些场景?">Dubbo适用于哪些场景?</h2><p>当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。</p>]]>
</summary>
<category term="Dubbo" scheme="http://arccode.net/tags/Dubbo/"/>
<category term="NIO" scheme="http://arccode.net/tags/NIO/"/>
<category term="WebService" scheme="http://arccode.net/tags/WebService/"/>
<category term="多线程" scheme="http://arccode.net/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
<category term="中间件" scheme="http://arccode.net/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
</entry>
<entry>
<title><![CDATA[前后端完全分离之API设计]]></title>
<link href="http://arccode.net/2015/04/18/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E4%B9%8BAPI%E8%AE%BE%E8%AE%A1/"/>
<id>http://arccode.net/2015/04/18/前后端完全分离之API设计/</id>
<published>2015-04-18T09:46:48.000Z</published>
<updated>2016-01-23T11:11:44.000Z</updated>
<content type="html"><![CDATA[<h2 id="背景">背景</h2><p>API就是开发者使用的界面。我的目标不仅是能用,而且好用, 跨平台(PC, Android, IOS, etc…)使用; 本文将详细介绍API的设计及异常处理, 并将异常信息进行封装友好地反馈给前端.</p>
<p>上篇文章<a href="http://arccode.net/2015/04/08/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E5%88%9D%E6%8E%A2/">前后端完全分离初探</a>只是讲了些宽泛的概念, 接下来的文章将直接上干货, 干货的源码会挂在<code>github</code>上.</p>
<p>前后端完全分离后, 前端和后端如何交互? </p>
<p>答: 通过双方协商好的API.</p>
<p>接下来我分享我自己设计的API接口, 欢迎各位朋友指教.</p>
<h2 id="API设计理念">API设计理念</h2><ol>
<li>将涉及的实体抽象成资源, 即按<code>id</code>访问资源, 在<code>url</code>上做文章, 以后再也不用为<code>url</code>起名字而苦恼了.</li>
<li>使用<code>HTTP</code>动词对资源进行<code>CRUD</code>(增删改查); get->查, post->增, put->改, delete->删.</li>
<li>URL命名规则, 对于资源无法使用一个单数名词表示的情况, 我使用中横线(<code>-</code>)连接.<ul>
<li>资源采用名词命名, e.g: 产品 -> product</li>
<li>新增资源, e.g: 新增产品, url -> /product , verb -> POST</li>
<li>修改资源, e.g: 修改产品, url -> /products/{id} , verb -> PUT</li>
<li>资源详情, e.g: 指定产品详情, url -> /products/{id} , verb -> GET</li>
<li>删除资源, e.g: 删除产品, url -> /products/{id} , verb -> DELETE</li>
<li>资源列表, e.g: 产品列表, url -> /products , verb -> GET</li>
<li>资源关联关系, e.g: 收藏产品, url -> /products/{id}/star , verb -> PUT</li>
<li>资源关联关系, e.g: 删除收藏产品, url -> /products/{id}/star , verb -> DELETE</li>
</ul>
</li>
</ol>
<p>目前我API的设计只涉及这两点, 至于第三点<code>HATEOAS</code>(Hypermedia As The Engine Of Application State)那就由读者自己去选择了.</p>
<a id="more"></a>
<h2 id="项目地址">项目地址</h2><p>本文中只涉及了设计的理念, 具体的实现请下载源码<a href="https://github.com/arccode/rest-api" target="_blank" rel="external">rest-api</a>, 项目内写了比较详细的注释.</p>
<h2 id="项目实战">项目实战</h2><p>实战将从业务场景出发, 详细介绍如何使用HTTP verb对资源进行操作(<code>状态转移</code>), 使用JSON返回结果(<code>资源表述</code>), 并定义JSON的基础结构.</p>
<h3 id="JSON结构">JSON结构</h3><p>requestParams: </p>
<figure class="highlight clojure"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="collection">{</span><br><span class="line">}</span></span><br></pre></td></tr></table></figure>
<p>responseBody: </p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> }</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">{</span><br><span class="line"> }</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<p><code>meta</code>中封装操作成功或失败的消息, <code>data</code>中封装返回的具体数据.</p>
<p>当新建商品或更新产品时, 相关属性封装在JSON中, 通过POST或PUT发送,</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<p>当用户对商品进行操作后, 将得到响应结果,</p>
<p>GET, POST, PUT操作成功, 返回如下结果</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">201</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"创建成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<p>DELETE操作成功, 返回如下结果</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">204</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"删除成功"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h3 id="业务场景一">业务场景一</h3><p>电商网站的管理员对商品进行新增,编辑,删除,浏览的操作; 暂时不考虑认证授权, 只关注对商品的操作.</p>
<p>为了以后便于做分布式, 所有资源id(表主键)均采用uuid.</p>
<h4 id="新增商品">新增商品</h4><p>1, url: <code>/api/product</code></p>
<p>2, method: <code>POST</code></p>
<p>3, requestParams: </p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<p>4, responseBody</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">201</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"创建成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h4 id="编辑商品">编辑商品</h4><p>1, url: <code>/api/products/{id}</code></p>
<p>2, method: <code>PUT</code></p>
<p>3, requestParams: </p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"iPhone 6"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"此次苹果发布会发布了iPhone 6与iPhone 6 Plus,搭载iOS 8,尺寸分别是4.7和5.5英寸。外观设计不再棱角分明,表层玻璃边有一个弧度向下延伸,与阳极氧化铝金属机身边框衔接。机身背部采用三段式设计。机身更薄,续航能力更强。"</span></span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<p>4, responseBody</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">200</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"修改成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"iPhone 6"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"此次苹果发布会发布了iPhone 6与iPhone 6 Plus,搭载iOS 8,尺寸分别是4.7和5.5英寸。外观设计不再棱角分明,表层玻璃边有一个弧度向下延伸,与阳极氧化铝金属机身边框衔接。机身背部采用三段式设计。机身更薄,续航能力更强。"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h4 id="删除商品">删除商品</h4><p>1, url: <code>/api/products/{id}</code></p>
<p>2, method: <code>DELETE</code></p>
<p>3, responseBody</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">204</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"删除成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">{}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h4 id="获取商品详情">获取商品详情</h4><p>1, url: <code>/api/products/{id}</code></p>
<p>2, method: <code>GET</code></p>
<p>3, responseBody</p>
<p>删除前</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">200</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"查询成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<p>删除后</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">404</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"指定产品不存在"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h4 id="获取商品列表(未分页)">获取商品列表(未分页)</h4><p>1, url: <code>/api/products</code></p>
<p>2, method: <code>GET</code></p>
<p>3, responseBody</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">200</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"获取全部商品成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">[</span><br><span class="line"> {</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"> </span>},</span><br><span class="line"> {</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"9db1992a-c342-4ff0-a2a4-aeb3dbfd93f6"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"> </span>},</span><br><span class="line"> {</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"4481619b-45c5-4729-9539-f93bb01f10d8"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"Apple Watch SPORT"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。"</span></span><br><span class="line"> </span>}</span><br><span class="line"> ]</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h3 id="业务场景二">业务场景二</h3><p><code>业务场景一</code>中只涉及了单个资源的操作, 但实际场景中还有些关联操作; 如用户去电商网站浏览商品, 并收藏了一些商品, 之后又取消收藏了部分商品.</p>
<p>暂时不考虑用户认证授权, 以后加了<code>token</code>后, 用户信息可以从中获取.</p>
<h4 id="收藏商品">收藏商品</h4><p>1, url: <code>/api/products/{id}/star</code></p>
<p>2, method: <code>PUT</code></p>
<p>3, responseBody</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">200</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"收藏商品[5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9]成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">[</span><br><span class="line"> {</span><br><span class="line"> "<span class="attribute">id</span>": <span class="value"><span class="string">"5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9"</span></span>,</span><br><span class="line"> "<span class="attribute">name</span>": <span class="value"><span class="string">"iPhone 6"</span></span>,</span><br><span class="line"> "<span class="attribute">description</span>": <span class="value"><span class="string">"此次苹果发布会发布了iPhone 6与iPhone 6 Plus,搭载iOS 8,尺寸分别是4.7和5.5英寸。外观设计不再棱角分明,表层玻璃边有一个弧度向下延伸,与阳极氧化铝金属机身边框衔接。机身背部采用三段式设计。机身更薄,续航能力更强。"</span></span><br><span class="line"> </span>}</span><br><span class="line"> ]</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h4 id="取消收藏商品">取消收藏商品</h4><p>1, url: <code>/api/products/{id}/star</code></p>
<p>2, method: <code>DELETE</code></p>
<p>3, responseBody</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">200</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"删除收藏商品[5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9]成功"</span></span><br><span class="line"> </span>}</span>,</span><br><span class="line"> "<span class="attribute">data</span>": <span class="value">[]</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h2 id="自定义异常和异常处理">自定义异常和异常处理</h2><p>所有自定义异常继承RuntimeException, 在业务层抛出, 统一在Controller层进行处理.</p>
<p>异常分为全局异常和局部异常, 例如http method unsupported(405), unauthorized(401), accessDenied(403), not found(404)等属于全局异常; 针对对独立业务的一些异常属于局部异常, 例如产品编辑出错; </p>
<p>异常在Controller中进行处理, 并封装成json返回给前端, 封装后的数据如下, 相关实现见<a href="https://github.com/arccode/rest-api" target="_blank" rel="external">源码</a>;</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">404</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"指定产品不存在"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "<span class="attribute">meta</span>": <span class="value">{</span><br><span class="line"> "<span class="attribute">code</span>": <span class="value"><span class="number">405</span></span>,</span><br><span class="line"> "<span class="attribute">message</span>": <span class="value"><span class="string">"Request method 'POST' not supported"</span></span><br><span class="line"> </span>}</span><br><span class="line"></span>}</span><br></pre></td></tr></table></figure>
<h2 id="项目运行截图部分">项目运行截图部分</h2><p><img src="/images/arc/arc_add.png" alt=""></p>
<p><img src="/images/arc/arc_list.png" alt=""></p>
<p><img src="/images/arc/arc_exception.png" alt=""></p>
<h2 id="本系列文章">本系列文章</h2><ul>
<li><a href="http://arccode.net/2015/04/08/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E5%88%9D%E6%8E%A2/">前后端完全分离初探</a></li>
<li><a href="http://arccode.net/2015/04/18/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E4%B9%8BAPI%E8%AE%BE%E8%AE%A1/">前后端完全分离之API设计</a></li>
<li>前后端完全分离之安全认证与授权-上</li>
<li>前后端完全分离之安全认证与授权-下</li>
<li>前后端完全分离之前端模块化开发</li>
<li>前后端完全分离之前端路由系统</li>
<li>前后端完全分离之后端面向服务的模块化开发</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="背景">背景</h2><p>API就是开发者使用的界面。我的目标不仅是能用,而且好用, 跨平台(PC, Android, IOS, etc…)使用; 本文将详细介绍API的设计及异常处理, 并将异常信息进行封装友好地反馈给前端.</p>
<p>上篇文章<a href="http://arccode.net/2015/04/08/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E5%88%9D%E6%8E%A2/">前后端完全分离初探</a>只是讲了些宽泛的概念, 接下来的文章将直接上干货, 干货的源码会挂在<code>github</code>上.</p>
<p>前后端完全分离后, 前端和后端如何交互? </p>
<p>答: 通过双方协商好的API.</p>
<p>接下来我分享我自己设计的API接口, 欢迎各位朋友指教.</p>
<h2 id="API设计理念">API设计理念</h2><ol>
<li>将涉及的实体抽象成资源, 即按<code>id</code>访问资源, 在<code>url</code>上做文章, 以后再也不用为<code>url</code>起名字而苦恼了.</li>
<li>使用<code>HTTP</code>动词对资源进行<code>CRUD</code>(增删改查); get->查, post->增, put->改, delete->删.</li>
<li>URL命名规则, 对于资源无法使用一个单数名词表示的情况, 我使用中横线(<code>-</code>)连接.<ul>
<li>资源采用名词命名, e.g: 产品 -> product</li>
<li>新增资源, e.g: 新增产品, url -> /product , verb -> POST</li>
<li>修改资源, e.g: 修改产品, url -> /products/{id} , verb -> PUT</li>
<li>资源详情, e.g: 指定产品详情, url -> /products/{id} , verb -> GET</li>
<li>删除资源, e.g: 删除产品, url -> /products/{id} , verb -> DELETE</li>
<li>资源列表, e.g: 产品列表, url -> /products , verb -> GET</li>
<li>资源关联关系, e.g: 收藏产品, url -> /products/{id}/star , verb -> PUT</li>
<li>资源关联关系, e.g: 删除收藏产品, url -> /products/{id}/star , verb -> DELETE</li>
</ul>
</li>
</ol>
<p>目前我API的设计只涉及这两点, 至于第三点<code>HATEOAS</code>(Hypermedia As The Engine Of Application State)那就由读者自己去选择了.</p>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="Javascript" scheme="http://arccode.net/tags/Javascript/"/>
<category term="Rest" scheme="http://arccode.net/tags/Rest/"/>
<category term="架构" scheme="http://arccode.net/categories/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title><![CDATA[SpringSecurity-AntPathMatcher-Rest-Match]]></title>
<link href="http://arccode.net/2015/04/14/SpringSecurity-AntPathMatcher-Rest-Match/"/>
<id>http://arccode.net/2015/04/14/SpringSecurity-AntPathMatcher-Rest-Match/</id>
<published>2015-04-14T15:05:45.000Z</published>
<updated>2015-04-18T09:17:10.000Z</updated>
<content type="html"><![CDATA[<h2 id="简介">简介</h2><p>使用Spring的AntPathMatch对RESTful的URL进行匹配.</p>
<h2 id="匹配规则">匹配规则</h2><p>ANT方式的通配符有三种:</p>
<ul>
<li><code>? 匹配任何单字符</code></li>
<li><code>* 匹配0或者任意数量的字符</code></li>
<li><code>** 匹配0或者更多的目录</code></li>
</ul>
<p>注: 所有的通配符均不包含路径分隔符<code>/</code>.</p>
<a id="more"></a>
<h2 id="单元测试">单元测试</h2><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor"><span class="keyword">import</span> org.junit.Test;</span></span><br><span class="line"><span class="preprocessor"><span class="keyword">import</span> org.slf4j.Logger;</span></span><br><span class="line"><span class="preprocessor"><span class="keyword">import</span> org.slf4j.LoggerFactory;</span></span><br><span class="line"><span class="preprocessor"><span class="keyword">import</span> org.springframework.util.AntPathMatcher;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span><br><span class="line"> * AntPathMatchTest :</span><br><span class="line"> *</span><br><span class="line"> * @author http://arccode.net</span><br><span class="line"> * @since 2015-04-14 23:12</span><br><span class="line"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AntPathMatchTest</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(AntPathMatchTest.<span class="keyword">class</span>);</span><br><span class="line"></span><br><span class="line"> @Test</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> test() {</span><br><span class="line"></span><br><span class="line"> AntPathMatcher matcher = <span class="keyword">new</span> AntPathMatcher();</span><br><span class="line"> String[] currUrls = {<span class="string">"/bop/wallpapers"</span>, <span class="string">"/bop/wallpapers1"</span>, <span class="string">"/bop/wallpapers11"</span>, <span class="string">"/bop/wallpapers/1"</span>, <span class="string">"/bop/wallpapers/1/labels/2"</span>};</span><br><span class="line"> String[] userAuths = {<span class="string">"/bop/wallpapers"</span>, <span class="string">"/bop/wallpapers?"</span>,<span class="string">"/bop/wallpapers/*"</span>, <span class="string">"/bop/wallpapers/**"</span>};</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (String currUrl: currUrls) {</span><br><span class="line"> <span class="keyword">for</span> (String userAuth: userAuths) {</span><br><span class="line"> logger.info(currUrl + <span class="string">"-----"</span> + userAuth + <span class="string">"-----"</span> + matcher.match(userAuth, currUrl) + <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><br></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<![CDATA[<h2 id="简介">简介</h2><p>使用Spring的AntPathMatch对RESTful的URL进行匹配.</p>
<h2 id="匹配规则">匹配规则</h2><p>ANT方式的通配符有三种:</p>
<ul>
<li><code>? 匹配任何单字符</code></li>
<li><code>* 匹配0或者任意数量的字符</code></li>
<li><code>** 匹配0或者更多的目录</code></li>
</ul>
<p>注: 所有的通配符均不包含路径分隔符<code>/</code>.</p>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="Spring" scheme="http://arccode.net/tags/Spring/"/>
<category term="路由" scheme="http://arccode.net/tags/%E8%B7%AF%E7%94%B1/"/>
<category term="架构" scheme="http://arccode.net/categories/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title><![CDATA[前后端完全分离初探]]></title>
<link href="http://arccode.net/2015/04/08/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E5%88%9D%E6%8E%A2/"/>
<id>http://arccode.net/2015/04/08/前后端完全分离初探/</id>
<published>2015-04-08T15:13:37.000Z</published>
<updated>2015-04-20T01:50:51.000Z</updated>
<content type="html"><![CDATA[<h2 id="核心思路">核心思路</h2><p>后端负责业务逻辑处理, 前端负责展示逻辑的处理.</p>
<h2 id="背景">背景</h2><ul>
<li><p>2013年3月,首次接触了<a href="http://underscorejs.org/" target="_blank" rel="external">underscore.js</a>这个区区45k大小的js库, 在使用其提供的简单模板完成表格异步分页后,从此我开始慢慢践行前后端完全分离的架构.</p>
</li>
<li><p>2013年7月,我接触到了REST这种重用HTTP应用协议的架构, 更坚定了我践行前后端分离的决心.</p>
</li>
<li><p>2014年3月,国内刮起了nodeJS的风暴, 此时我采用国外的一个开源项目<a href="http://mean.io/" target="_blank" rel="external">MEAN</a>开发了一套完整的CMS系统, 此次开发学习到了API接口如何标准化, API具体的设计参考了<a href="https://developer.github.com/v3/" target="_blank" rel="external">github API</a>, <a href="https://instagram.com/developer/endpoints/" target="_blank" rel="external">Instagram API</a>.</p>
</li>
<li><p>2015年1月, 公司开发新产品, 需要新开发一套支持平台及数据下发平台, 在该产品中我主要参与API的设计, 并主导开发支撑平台, 下面我就介绍下这套支撑平台.</p>
</li>
</ul>
<a id="more"></a>
<h2 id="工具">工具</h2><p>工欲善其事, 必先利其器, 先介绍下自己使用的工具</p>
<p><a href="https://www.jetbrains.com/idea/" target="_blank" rel="external">IntelliJ IDEA</a></p>
<p>强大的Java集成开发工具, 没有之一.</p>
<p><a href="https://www.jetbrains.com/webstorm/" target="_blank" rel="external">Webstorm</a></p>
<p>强大的HTML,CSS,Javascript集成开发工具, 没有之一.</p>
<p><a href="http://www.sublimetext.com/" target="_blank" rel="external">Sublime</a></p>
<p>强大的文本编辑工具, 目前我主要用于临时文件查看, 临时json数据格式化查看, 集成了plugin后, 它的功能会吓到人.</p>
<p><a href="http://www.navicat.com/" target="_blank" rel="external">Navicat</a></p>
<p>小巧强大的关系数据库建模工具, 相当易用.</p>
<p><a href="https://www.zennaware.com/cornerstone/index.php" target="_blank" rel="external">Cornerstone</a></p>
<p>强大的版本控制客户端.</p>
<p><a href="http://25.io/mou/" target="_blank" rel="external">Mou</a></p>
<p>一款简洁强大的MarkDown可视化编辑工具.</p>
<p><a href="http://www.emtec.com/zoc/" target="_blank" rel="external">Zoc6</a></p>
<p>一款强大的Linux SSH客户端工具.</p>
<p><a href="https://panic.com/transmit/" target="_blank" rel="external">Transmit</a></p>
<p>一款强大的Linux SFTP客户端可视化工具.</p>
<h2 id="代理">代理</h2><p><a href="https://www.cloudtizi.com/" target="_blank" rel="external">云梯</a></p>
<p>强大的VPN, 连接稳定, 速度快, 价格实惠, 可做全局代理, 包括terminal.</p>
<h2 id="项目">项目</h2><h3 id="目的">目的</h3><ul>
<li>提高使用者的工作效率</li>
<li>提高开发人员的开发效率</li>
</ul>
<h3 id="前端类库">前端类库</h3><p><a href="http://vuejs.org/" target="_blank" rel="external">vue.js</a></p>
<p>一个用于创建web交互界面的库, 主要用于数据双向绑定.</p>
<p><a href="http://visionmedia.github.io/superagent/" target="_blank" rel="external">superagent.js</a></p>
<p>一个非常方便的客户端请求代理模块,可方便的使用get,post,put,delete,head动词, 异步回调中封装了http状态码和业务数据等等.</p>
<p><a href="http://www.dropzonejs.com/" target="_blank" rel="external">dropzone.js</a></p>
<p>一个强大的文件上传库, 可获取文件mime, 文件大小等; 针对图片可生成缩略图, 获取图片宽度,高度.</p>
<p><a href="http://backgridjs.com/" target="_blank" rel="external">backgrid.js</a></p>
<p>一个强大的表格组件.</p>
<p><a href="https://github.com/flatiron/director" target="_blank" rel="external">director.js</a></p>
<p>一个强大的前端路由库, 通过<code>#</code>符号进行路径组织, 结合<code>vue</code>的<code>component</code>可进行单页的局部模块刷新.</p>
<h3 id="后端类库">后端类库</h3><p><a href="http://spring.io/guides/tutorials/bookmarks/" target="_blank" rel="external">spring hateoas</a></p>
<p>spring 超文本驱动库, 可根据需求返回不同的httpStatus及links.</p>
<p><a href="http://projects.spring.io/spring-framework/" target="_blank" rel="external">spring framework</a></p>
<p>对架构进行分层, 目前分三层</p>
<ul>
<li>web接入层: 提供API接口,对前端传过来的数据进行校验, 校验未通过的话使用spring hateoas包装返回错误消息, 校验通过的话调用业务逻辑层</li>
<li>业务逻辑层: 该层主要处理上层传进来的数据, 调用数据访问层进行数据持久化, 该层具有强大的扩展性;<ul>
<li>对于高并发写<code>db</code>的操作, 可集成消息队列库(如<a href="http://activemq.apache.org/" target="_blank" rel="external">ActiveMQ</a>)将消息发送至队列, 采用队列依次消费减轻<code>db</code>写负担.</li>
<li>对于高并发频繁读<code>db</code>的操作, 可集成缓存库(如<a href="http://redis.io/" target="_blank" rel="external">redis</a>), 把读<code>db</code>的操作改为先读缓存, 未命中才去读<code>db</code>, 这样可大大减轻数据库读负担.</li>
<li>在该层上还可以使用<code>spring AOP</code>对业务进行事务控制及日志记录.</li>
</ul>
</li>
<li>数据访问层: 该层只做一件事, 对数据库中的表记录进行增删改查, 不进行业务逻辑判断.</li>
</ul>
<p><a href="http://mybatis.github.io/mybatis-3/zh/" target="_blank" rel="external">mybatis</a></p>
<p>一个半自动ORM(对象关系映射库)库, 简单灵活, 使用原生sql, 可完成较复杂的查询.</p>
<p><a href="http://mybatis.github.io/generator/running/runningWithMaven.html" target="_blank" rel="external">mybatis-generator-maven-plugin</a></p>
<p>mybatis插件, 可根据数据库中的表生成JavaBean,Dao接口,Dao的xml文件, 本人修改了部分源码, 可在JavaBean中自动添加注释, 详见另一篇博文<a href="http://arccode.net/2015/02/07/MyBatis-Generator%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/">MyBatis-Generator最佳实践</a>.</p>
<p><a href="http://projects.spring.io/spring-security/" target="_blank" rel="external">spring security</a></p>
<p>完成用户的认证和授权.</p>
<p><a href="https://code.google.com/p/thumbnailator/" target="_blank" rel="external">thumbnailator</a>(注: 需翻墙)</p>
<p>一个Java端的图片处理库, 可完成图片压缩, 裁剪, 水印等功能.</p>
<h3 id="实战">实战</h3><p>如何提高使用者的工作效率?</p>
<ul>
<li>使用者看见就知道怎么使用</li>
<li>为使用者自动完成部分可推导的表单填写</li>
<li>拥有尽可能全的提示</li>
</ul>
<p>如何提高开发人员的开发效率?</p>
<ul>
<li>技术选型时权衡考虑学习曲线与其提供的功能</li>
<li>设计一套通用的可扩展的架构</li>
<li>对常用操作及模块进行封装.</li>
</ul>
<p>目前, 后端中将数据访问层和业务逻辑层中常用的方法封装成了泛型接口, 并使用抽象类来实现最基础的逻辑, 开发人员如果觉得指定方法无法满足需求, 可重写指定方法或使用新方法. 具体封装如下:</p>
<figure class="highlight gherkin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">/<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> GenericDao : 封装通用dao操作, 服务于GenericService</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@author http://arccode.net</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@since 2014-12-03</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line">public interface GenericDao<span class="variable"><Model, PK></span> {</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 插入</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param model</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> int insertSelective(Model model);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 删除</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param id</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> int deleteByPrimaryKey(PK id);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 更新</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param model</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> int updateByPrimaryKeySelective(Model model);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 查询单条记录</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param id</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> Model selectByPrimaryKey(PK id);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 分页查询</span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param page</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param model</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@return</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> List selectAndPage(Page<span class="variable"><Model></span> page, Model model);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight gherkin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">/<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> GenericService : 所有自定义Service的顶级接口,封装常用的增删查改操作</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> Model : 代表数据库中的表映射的Java对象类型</span><br><span class="line"> <span class="keyword">*</span> PK :代表对象的主键类型</span><br><span class="line"> <span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@author http://arccode.net</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@since 2014-12-03</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line">public interface GenericService<span class="variable"><Model, PK></span> {</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 分页获取集合, 带过滤功能</span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param page 分页对象</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param model 分页条件, eg: model.name = "zhangsan", 对应sql语句为where name like %zhangsan%</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@return</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> List getModels(Page<span class="variable"><Model></span> page, Model model);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 新增</span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param model</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@return</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> Integer addModel(Model model);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 删除</span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param id</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@return</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> Integer removeModelById(PK id);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 修改</span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param model</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@return</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> Integer modifyModelById(Model model);</span><br><span class="line"></span><br><span class="line"> /<span class="keyword">*</span><span class="keyword">*</span></span><br><span class="line"> <span class="keyword">*</span> 根据主键id获取单条记录详情</span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@param id</span></span><br><span class="line"> <span class="keyword">*</span> <span class="comment">@return</span></span><br><span class="line"> <span class="keyword">*</span>/</span><br><span class="line"> Model getModelById(PK id);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * GenericServiceSupport : 通用接口的实现类</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@author</span> http://arccode.net</span><br><span class="line"> * <span class="doctag">@since</span> 2014-12-03</span><br><span class="line"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">GenericServiceSupport</span><<span class="title">Model</span>, <span class="title">PK</span>> <span class="keyword">implements</span> <span class="title">GenericService</span><<span class="title">Model</span>, <span class="title">PK</span>></span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * 定义成抽象方法,由子类实现,完成dao的注入</span><br><span class="line"> * <span class="doctag">@return</span></span><br><span class="line"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> GenericDao<Model, PK> getDao();</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> List <span class="title">getModels</span><span class="params">(Page<Model> page, Model model)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> getDao().selectAndPage(page, model);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Integer <span class="title">addModel</span><span class="params">(Model model)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> getDao().insertSelective(model);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Integer <span class="title">removeModelById</span><span class="params">(PK id)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> getDao().deleteByPrimaryKey(id);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Integer <span class="title">modifyModelById</span><span class="params">(Model model)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> getDao().updateByPrimaryKeySelective(model);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="annotation">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Model <span class="title">getModelById</span><span class="params">(PK id)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> getDao().selectByPrimaryKey(id);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>熟练使用该模式, 开发一套业务逻辑的增删改查, 那速度是相当快.</p>
<p>针对前段也做了一些组件式的封装, 表格采用<code>backgrid.js</code>完成展示及分页操作, 只需要在html文件指定位置写入两个带id的html元素(一个用于展示表格, 一个用于分页), 之后copy写好的js模板, 在js中修改属性便可快速完成表格异步分页.</p>
<p>接下来将陆续发布实战干货.</p>
<h2 id="本系列文章">本系列文章</h2><ul>
<li><a href="http://arccode.net/2015/04/08/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E5%88%9D%E6%8E%A2/">前后端完全分离初探</a></li>
<li><a href="http://arccode.net/2015/04/18/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E5%85%A8%E5%88%86%E7%A6%BB%E4%B9%8BAPI%E8%AE%BE%E8%AE%A1/">前后端完全分离之API设计</a></li>
<li>前后端完全分离之安全认证与授权-上</li>
<li>前后端完全分离之安全认证与授权-下</li>
<li>前后端完全分离之前端模块化开发</li>
<li>前后端完全分离之前端路由系统</li>
<li>前后端完全分离之后端面向服务的模块化开发</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h2 id="核心思路">核心思路</h2><p>后端负责业务逻辑处理, 前端负责展示逻辑的处理.</p>
<h2 id="背景">背景</h2><ul>
<li><p>2013年3月,首次接触了<a href="http://underscorejs.org/">underscore.js</a>这个区区45k大小的js库, 在使用其提供的简单模板完成表格异步分页后,从此我开始慢慢践行前后端完全分离的架构.</p>
</li>
<li><p>2013年7月,我接触到了REST这种重用HTTP应用协议的架构, 更坚定了我践行前后端分离的决心.</p>
</li>
<li><p>2014年3月,国内刮起了nodeJS的风暴, 此时我采用国外的一个开源项目<a href="http://mean.io/">MEAN</a>开发了一套完整的CMS系统, 此次开发学习到了API接口如何标准化, API具体的设计参考了<a href="https://developer.github.com/v3/">github API</a>, <a href="https://instagram.com/developer/endpoints/">Instagram API</a>.</p>
</li>
<li><p>2015年1月, 公司开发新产品, 需要新开发一套支持平台及数据下发平台, 在该产品中我主要参与API的设计, 并主导开发支撑平台, 下面我就介绍下这套支撑平台.</p>
</li>
</ul>]]>
</summary>
<category term="Java" scheme="http://arccode.net/tags/Java/"/>
<category term="Javascript" scheme="http://arccode.net/tags/Javascript/"/>
<category term="Rest" scheme="http://arccode.net/tags/Rest/"/>
<category term="架构" scheme="http://arccode.net/categories/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title><![CDATA[冒泡型事件和捕获型事件]]></title>
<link href="http://arccode.net/2015/03/06/%E5%86%92%E6%B3%A1%E5%9E%8B%E4%BA%8B%E4%BB%B6%E5%92%8C%E6%8D%95%E8%8E%B7%E5%9E%8B%E4%BA%8B%E4%BB%B6/"/>
<id>http://arccode.net/2015/03/06/冒泡型事件和捕获型事件/</id>
<published>2015-03-06T01:18:47.000Z</published>
<updated>2015-03-06T01:37:28.000Z</updated>
<content type="html"><![CDATA[<p>冒泡型事件的基本思想是、事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="title">html</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">head</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">title</span>></span><span class="tag"></<span class="title">title</span>></span> </span><br><span class="line"> <span class="tag"></<span class="title">head</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">body</span> <span class="attribute">onclick</span>=<span class="value">"theClick()"</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">div</span> <span class="attribute">onclick</span>=<span class="value">"theClick()"</span>></span>点击<span class="tag"></<span class="title">div</span>></span> </span><br><span class="line"> <span class="tag"></<span class="title">body</span>></span> </span><br><span class="line"><span class="tag"></<span class="title">html</span>></span></span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>IE5.5冒泡顺序如下:</p>
<p>(1)<div></p>
<p>(2)<body></p>
<p>(3)<document></p>
<p><img src="/images/javascript/javascript-capture-bubble-1.jpg" alt=""></p>
<p>为什么称作冒泡、因为事件按照DOM的层次结构像水泡一样不断上升。让我想起了一首歌:”吹泡泡、吹泡泡、泡泡飞啊飞得高、飞到天空中、问声太阳好”。</p>
<p>IE6呢、稍微修改了冒泡型事件、这样<html>元素也可以接收冒泡的事件、还是上面的代码。</p>
<p>IE6的冒泡顺序如下:</p>
<p>(1)<div></p>
<p>(2)<body></p>
<p>(3)<html></p>
<p>(4)<document></p>
<p><img src="/images/javascript/javascript-capture-bubble-2.jpg" alt=""></p>
<p>Mozilla1.0及更高版本也支持冒泡事件但到达了另一层次。类似IE6.0,它也支持<html>元素级别的事件、不过,事件”起泡”一直上升到Windows窗口对象。继续前面的代码、点击<div>元素将造成下图所示的事件冒泡:</p>
<p><img src="/images/javascript/javascript-capture-bubble-3.jpg" alt=""></p>
<p>捕获型事件:</p>
<p>IE使用冒泡型事件、相对的、Netscape使用了另一种称为捕获型事件(eventcapturing)的解决方案、事件的捕获和冒泡刚好相反的两种 过程——捕获型事件中、事件从最不精确的对象(document对象)开始触发、然后到最精确(也可以在窗口级别捕获事件,不过必寻由开发人员特别指 定)。Netscape不会将页面上的很多元素暴露给事件。继续使用前面的代码示例、事件按照下面的路径传播:</p>
<p>(1)document</p>
<p>(2)<div></p>
<p>有些人也称之为自顶向下的事件模型,因为它是从DOM层次的顶端开始向下延伸的:</p>
<p><img src="/images/javascript/javascript-capture-bubble-4.jpg" alt=""></p>
<p>DOM事件流:</p>
<p>DOM(文档对象模型)结构是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素结点与根节点之间按特定的顺序传播,路径所经过的节点都会收到该事件,这个传播过程可称为DOM事件流。事件顺序有两种类型:事件捕捉和事件冒泡。</p>
<p>DOM标准的事件模型</p>
<p>我们已经对上面两个不同的事件模型进行了解释和对比。DOM标准同时支持两种事件模型,即捕获型事件与冒泡型事件,但是,捕获型事件先发生。两种事件流都 会触发DOM中的所有对象,从document对象开始,也在document对象结束(大部分兼容标准的浏览器会继续将事件是捕捉/冒泡延续到 window对象)。继续使用前面的例子、在与DOM兼容的浏览器中点击<div>元素时、事件流的进行如下图:</p>
<p><img src="/images/javascript/javascript-capture-bubble-5.jpg" alt=""></p>
<p>注意因为事件的目标(<div>元素)是最精确的元素(于是,在DOM树中最深),实际上它会接收两次事件,一次在捕获过程中,另一次在冒泡 过程中。DOM事件模型的最独特的性质是,文本节点也触发事件(在IE不会)。所以如果点击示例中的<div>点 击</div>、实际的事件流应该是:</p>
<p><img src="/images/javascript/javascript-capture-bubble-6.jpg" alt=""></p>
<p>参考资料</p>
<ul>
<li><a href="http://hzw2312.blog.51cto.com/2590340/780230" target="_blank" rel="external">好好学一遍JavaScript 笔记(八)——冒泡型事件、捕获型事件</a></li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>冒泡型事件的基本思想是、事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="title">html</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">head</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">title</span>></span><span class="tag"></<span class="title">title</span>></span> </span><br><span class="line"> <span class="tag"></<span class="title">head</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">body</span> <span class="attribute">onclick</span>=<span class="value">"theClick()"</span>></span> </span><br><span class="line"> <span class="tag"><<span class="title">div</span> <span class="attribute">onclick</span>=<span class="value">"theClick()"</span>></span>点击<span class="tag"></<span class="title">div</span>></span> </span><br><span class="line"> <span class="tag"></<span class="title">body</span>></span> </span><br><span class="line"><span class="tag"></<span class="title">html</span>></span></span><br></pre></td></tr></table></figure>]]>
</summary>
<category term="Event" scheme="http://arccode.net/tags/Event/"/>
<category term="Javascript" scheme="http://arccode.net/tags/Javascript/"/>
<category term="Javascript" scheme="http://arccode.net/categories/Javascript/"/>
</entry>
<entry>
<title><![CDATA[致我们即将逝去的高中]]></title>
<link href="http://arccode.net/2015/03/01/%E8%87%B4%E6%88%91%E4%BB%AC%E5%8D%B3%E5%B0%86%E9%80%9D%E5%8E%BB%E7%9A%84%E9%AB%98%E4%B8%AD/"/>
<id>http://arccode.net/2015/03/01/致我们即将逝去的高中/</id>
<published>2015-03-01T04:22:50.000Z</published>
<updated>2015-03-01T04:51:45.000Z</updated>
<content type="html"><![CDATA[<p><strong>看见一位朋友画了这篇漫画, 深有体会, 特此转载到该博文, 其实除了高中, 我们的初中又何尝不是如此呢!</strong></p>