-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.html
1057 lines (763 loc) · 63.5 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>QINGUAN</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="QINGUAN">
<meta property="og:url" content="http://qinguan.github.io/index.html">
<meta property="og:site_name" content="QINGUAN">
<meta property="og:locale" content="zh-CN">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="QINGUAN">
<link rel="alternate" href="/atom.xml" title="QINGUAN" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">QINGUAN</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
<a class="main-nav-link" href="/about">About</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="搜索"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://qinguan.github.io"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-maven-version-separation" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/09/27/maven-version-separation/" class="article-date">
<time datetime="2018-09-27T15:20:37.000Z" itemprop="datePublished">2018-09-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/09/27/maven-version-separation/">使用maven version:set分离服务的发布版本</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>开发过程中需要分离测试环境依赖和线上环境依赖。</p>
<p>举个简单的例子,有个依赖包hexo-api,在正式环境中是version=hexo-api-1.0,测试环境中是version=hexo-api-1.0-SNAPSHOT。</p>
<p>但实际项目中pom.xml是这样配的,如:<br><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></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="tag"><<span class="name">groupId</span>></span>com.qinguan<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"><span class="tag"><<span class="name">artifactId</span>></span>hexo<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"><span class="tag"><<span class="name">name</span>></span>hexo-api<span class="tag"></<span class="name">name</span>></span></span><br><span class="line"><span class="tag"><<span class="name">packaging</span>></span>jar<span class="tag"></<span class="name">packaging</span>></span></span><br><span class="line"><span class="tag"><<span class="name">version</span>></span>1.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line">...</span><br></pre></td></tr></table></figure></p>
<p>我不想因为发布一个测试版本1.0-SNAPSHOT的jar而去提交一个如下的变更。<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">version</span>></span>1.0-SNAPSHOT<span class="tag"></<span class="name">version</span>></span></span><br></pre></td></tr></table></figure></p>
<p>那怎么办呢?可以试试<a href="https://www.mojohaus.org/versions-maven-plugin/index.html" target="_blank" rel="noopener">maven-version-plugin</a>。</p>
<p>具体用法:<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//假定之前打包命令</span></span><br><span class="line">clean compile -Papi deploy </span><br><span class="line"></span><br><span class="line"><span class="comment">//测试包打包命令</span></span><br><span class="line">clean versions:set -DnewVersion=<span class="number">1.0</span>-SNAPSHOT compile -Papi deploy</span><br></pre></td></tr></table></figure></p>
<p>通过<a href="https://www.mojohaus.org/versions-maven-plugin/set-mojo.html" target="_blank" rel="noopener">versions:set</a>更改pom.xml中的version值,newVersion参数为目标版本号。</p>
<p>此时打开pom.xml,可以看到文件中的version值已由1.0更新为1.0-SNAPSHOT了,jar包也变成了hexo-api-1.0-SNAPSHOT.jar。</p>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/09/27/maven-version-separation/" data-id="cjmkqpb9h0008yr4gujvzl6a0" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Maven/">Maven</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-enum-compatibility-issue" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/09/27/enum-compatibility-issue/" class="article-date">
<time datetime="2018-09-27T14:37:29.000Z" itemprop="datePublished">2018-09-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/09/27/enum-compatibility-issue/">Thrift枚举对象兼容性问题</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>最近在一个基于Thrift框架的RPC服务中碰到一个枚举值的兼容性问题。<br>简单描述如下:<br>有一个位置定义,起初只有两个枚举值,LEFT和RIGHT。然后发布schema定义版本1.0,客户端服务引用version = 1.0。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// commom.thrift</span></span><br><span class="line"><span class="keyword">enum</span> Position {</span><br><span class="line"> LEFT =<span class="number">0</span>,</span><br><span class="line"> RIGHT =<span class="number">1</span>,</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>经过一段时间,Position需要扩展,增加UP和DOWN,发布schema定义版本2.0。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// commom.thrift</span></span><br><span class="line"><span class="keyword">enum</span> Position {</span><br><span class="line"> LEFT =<span class="number">0</span>,</span><br><span class="line"> RIGHT =<span class="number">1</span>,</span><br><span class="line"> UP =<span class="number">2</span>,</span><br><span class="line"> DOWN =<span class="number">3</span>,</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>此时,客户端还在使用schema=1.0版本。若服务端返回了Position.UP或者Position.DOWN数据到客户端,客户端是不识别这两个枚举值的。问题就这样产生了,客户端有可能因为没有对未知数据进行兼容处理而直接报错异常了。</p>
<p>实际上,有几种设计是违反<strong>向后兼容</strong>原则的,上述情况就是典型的一种,即<strong>向枚举类型中新增枚举值</strong>。这种场景下,原则上要求所有客户端均先于服务端进行升级到最新版本。鉴于分布式服务应用场景,要求所有客户端都统一升级,是几乎不可能实现的。</p>
<p>针对<strong>向已有枚举类型中新增枚举值</strong>的情况,Thrift和Protobuf各有一些处理:</p>
<ol>
<li>Thrift:针对不认识的枚举值,默认返回null(通过findByValue实现)</li>
<li>Protobuf:针对不认识的枚举值,默认返回枚举类型的第一个值(或者返回default value,如有)<ul>
<li>注: 所以Protobuf定义的schema enum一般都会设第一个值为UNKNOWN以兼容低版本客户端</li>
</ul>
</li>
</ol>
<p>在阿里巴巴的Java编码规范中也有相关规则:<strong>强制“接口返回值不允许使用枚举类型或者包含枚举类型的POJO对象”</strong>。</p>
<p>Thrift/Protobuf协议本身对新增枚举值的处理没有问题(至少在反序列化方法中没有简单粗暴地直接抛出异常),但对业务层的处理及使用提出了一定的要求,即业务层有可能读到null(Thrift)或者UNKONWN类型枚举(Protobuf),需要做一定的防御性判断。</p>
<p>进一步阅读资料:</p>
<ol>
<li><a href="https://github.com/bkayser/thrift-versioning-doc" target="_blank" rel="noopener">thrift-versioning-doc</a></li>
<li><a href="https://developers.google.com/protocol-buffers/docs/proto#updating" target="_blank" rel="noopener">Updating A Message Type</a></li>
</ol>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/09/27/enum-compatibility-issue/" data-id="cjmkqpb9c0003yr4gws31sh8h" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/RPC/">RPC</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-a-high-coupling-case" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/08/22/a-high-coupling-case/" class="article-date">
<time datetime="2018-08-22T05:49:03.000Z" itemprop="datePublished">2018-08-22</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/08/22/a-high-coupling-case/">一个内部API高度耦合的Java实现案例</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>某项目使用了“一套API,多种底层存储”的设计方式,可以灵活切换多种数据源,但同时能够保持暴露的API接口定义一致。这种设计方式,优点是显而易见的。</p>
<p>随着业务的增长,数据量越来越大。单种类存储已经捉襟见肘,需要依赖多种存储共同保障服务的状况下,早期高度耦合的内部API实现使得存储的改造工作量增大了不少。</p>
<p>以某项目为例,原本业务数据量没那么大,选择了Redis作为线上业务的主要存储,以Couchbase为辅,同时利用HBase实现了一套在线冷备服务,其中RedisAPI和HBaseAPI互斥。但随着数据量的突飞猛进,Redis的容量已不堪重负,计划全部迁移到容量更大的分布性缓存Couchbase。同时,在Couchbase存储未命中时,回源到HBase服务进行兜底读取。</p>
<p>由于上层业务逻辑基于同一套存储API实现的,为同时使用多种存储,要求对这套存储API进行改造。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><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><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//common jar</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BaseTestRepo</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">default</span> <span class="keyword">public</span> <T> <span class="function">T <span class="title">get</span><span class="params">(String key)</span> </span>{</span><br><span class="line"> <span class="comment">//TODO</span></span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException(<span class="string">"not implement!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">default</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(String key, <span class="keyword">byte</span>[] value)</span> </span>{</span><br><span class="line"> <span class="comment">//TODO</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//couchbase.repo jar</span></span><br><span class="line"><span class="meta">@Repository</span>(<span class="string">"cbDataRepo"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CocuhbaseRepo</span> <span class="keyword">implements</span> <span class="title">BaseTestRepo</span> </span>{</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//hbase.repo jar</span></span><br><span class="line"><span class="meta">@Repository</span>(<span class="string">"dataRepo"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HBaseRepo</span> <span class="keyword">implements</span> <span class="title">BaseTestRepo</span> </span>{</span><br><span class="line"> <span class="comment">//TODO</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//redis.repo jar</span></span><br><span class="line"><span class="meta">@Component</span>(<span class="string">"dataRepo"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisRepo</span> <span class="keyword">implements</span> <span class="title">BaseTestRepo</span> </span>{</span><br><span class="line"> <span class="comment">//TODO</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//serviceA</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestAService</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> BaseTestRepo dataRepo;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> BaseTestRepo cbDataRepo;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <T> <span class="function">T <span class="title">getEntity</span><span class="params">(String key)</span> </span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException(<span class="string">"not implement!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setEntity</span><span class="params">(String key, Object value)</span> </span>{</span><br><span class="line"> <span class="comment">//TODO</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//serviceB</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestBService</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> BaseTestRepo dataRepo;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> BaseTestRepo cbDataRepo;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//通用API</span></span><br><span class="line"> <span class="keyword">public</span> <T> <span class="function">T <span class="title">getEntity</span><span class="params">(String key)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> dataRepo.get(key);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//通用API</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setEntity</span><span class="params">(String key, Object value)</span> </span>{</span><br><span class="line"> dataRepo.set(key, toByte(value))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//TODO</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">上层业务模块A需要依赖redis的高效部署业务,则可以引入service.jar、redis.repo jar、common.jar、couchbase.repo jar来实现;</span><br><span class="line">上层业务模块B需要依赖hbase来实现离线任务,则可引入service.jar、hbase.repo jar、common.jar、couchbase.repo jar来实现。</span><br></pre></td></tr></table></figure>
<p>如上所示,上层业务模块只需要按需导入hbase.repo jar或者redis.repo jar,然后利用Spring自动注入机制,获取dataRepo实例,基于同一套get/set API进行存储的读写操作。对上层业务而言,它是无需感知底层业务代码使用了哪一套存储。</p>
<p>此时,若将RedisRepo全部迁移到CouchbaseRepo,则会涉及到dataRepoAPI的使用变更,redis部分变化,但必须保持HBase不变。</p>
<p>解决方案:<br>1、新增Proxy,将底层RepoAPI再加一层代理,按需路由到不同的repo实现。<br>2、基于Spring Condition条件化构建Repo Bean。</p>
<p>话说回来,这类还是场景考虑不全,实现的不够理想。</p>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/08/22/a-high-coupling-case/" data-id="cjmkqpb980001yr4g9vaiuo4l" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Spring/">Spring</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-netty-version-conflicting-exception" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/08/15/netty-version-conflicting-exception/" class="article-date">
<time datetime="2018-08-14T16:02:02.000Z" itemprop="datePublished">2018-08-15</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/08/15/netty-version-conflicting-exception/">Netty版本冲突引起Cassandra访问异常</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>Java RPC服务单元测试访问Cassandra时,发现始终报如下错误:<br><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></pre></td><td class="code"><pre><span class="line">java.lang.AbstractMethodError: </span><br><span class="line">io.netty.util.concurrent.MultithreadEventExecutorGroup.newChild(Ljava/util/</span><br><span class="line"> concurrent/Executor;[Ljava/lang/Object;)Lio/netty/util/concurrent/EventExecutor</span><br></pre></td></tr></table></figure></p>
<p>排查了一圈,最终发现是Netty引起的。<br>Java RPC服务依赖的是Netty3.X版本,Cassandra驱动测试包依赖的是Netty4.X版本,将Netty版本调整成一致解决。</p>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/08/15/netty-version-conflicting-exception/" data-id="cjmkqpb9p000hyr4gpng29ztm" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Netty/">Netty</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-rmi-deserialization-vulnerability" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/08/13/rmi-deserialization-vulnerability/" class="article-date">
<time datetime="2018-08-13T08:04:23.000Z" itemprop="datePublished">2018-08-13</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/08/13/rmi-deserialization-vulnerability/">RMI反序列化漏洞</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>接到公司安全组提的漏洞单,说部分Java机器开了jmxremote端口,存在安全隐患。</p>
<p>查了下原来有不少Java服务默认开启了非安全模式的jmxremote端口。</p>
<p>修复(降低风险)方案有如下几种方式:</p>
<ol>
<li>使用<a href="https://github.com/ikkisoft/SerialKiller" target="_blank" rel="noopener">SerialKiller</a>,详细的在RMI反序列化漏洞文章中有介绍,略复杂</li>
<li>对于jmxremote没有强依赖的服务,可以直接禁用jmxremote端口,删除相关配置项</li>
<li>授权访问:配置认证密码或者ssl,或者配置ip白名单 -Dcom.sun.management.jmxremote.host=serverXXX</li>
</ol>
<p>关于RMI反序列化漏洞的介绍:<br><a href="http://blog.nsfocus.net/java-deserialization-vulnerability-overlooked-mass-destruction/" target="_blank" rel="noopener">http://blog.nsfocus.net/java-deserialization-vulnerability-overlooked-mass-destruction/</a><br>关于JMX配置的一些介绍:<br><a href="https://docs.oracle.com/javase/7/docs/technotes/guides/management/agent.html" target="_blank" rel="noopener">https://docs.oracle.com/javase/7/docs/technotes/guides/management/agent.html</a><br><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html" target="_blank" rel="noopener">https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html</a></p>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/08/13/rmi-deserialization-vulnerability/" data-id="cjmkqpbaa0015yr4g2tdkz4uj" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Serialization/">Serialization</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-https-package-capture-by-charles" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/06/05/https-package-capture-by-charles/" class="article-date">
<time datetime="2018-06-05T12:58:59.000Z" itemprop="datePublished">2018-06-05</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/06/05/https-package-capture-by-charles/">使用charles抓取iOS数据包</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>由于分析竞手的项目涉及到iOS端的请求抓包,翻阅并验证了一下,以下是整理的大致抓包流程。</p>
<p>工具软件: charles 3.11.5版本</p>
<ol>
<li>安装mac端证书<ul>
<li>Help->SSL Proxy->Install Charles Root Certificate.</li>
</ul>
</li>
<li>开启代理设置<ul>
<li>Proxy->Proxy Setting…->Proxies </li>
<li>HTTP Proxy 可以默认一个端口如8888,并勾选Enable transparent HTTP proxying</li>
<li>Socks Proxy可以Enable,并且Enable HTTP proxying over SOCKS,设置端口如80,443等</li>
</ul>
</li>
<li>开启SSL代理(支持HTTPS抓包)<ul>
<li>Proxy->SSL Proxying Setting,选中Enable SSL Proxying</li>
<li>在Locations里面添加*:443,表示匹配所有的HTTPS网站</li>
</ul>
</li>
<li>给iPhone安装证书<ul>
<li>Help->SSL Proxy->Install Charles Root Certificate On a Mobile Device or remote Browers…</li>
<li>根据弹出的提示框进行操作,也就是用iPhone的Safari打开地址<a href="http://charlesproxy.com/getssl" target="_blank" rel="noopener">http://charlesproxy.com/getssl</a> 下载安装。</li>
<li>iOS11以上系统需要在设置 > 关于本机 > 证书信任设置,信任该证书。</li>
</ul>
</li>
<li>手机连接wifi(同电脑一个局域网或者直接连电脑的热点),配置HTTP代理,选择’手动’,输入电脑的ip地址和端口号(默认8888)</li>
</ol>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/06/05/https-package-capture-by-charles/" data-id="cjmkqpb9f0005yr4gux2jk4hh" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Charles/">Charles</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-java-unused-import-statment" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/06/05/java-unused-import-statment/" class="article-date">
<time datetime="2018-06-05T12:41:44.000Z" itemprop="datePublished">2018-06-05</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/06/05/java-unused-import-statment/">Java import 失效</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>下午碰到一个小问题,IDEA 中的springboot项目里所有的import都无效了,显示unused import statment。</p>
<p>解决方案:File->Invalidate Caches/Restart,选择Invalidate and Restart。</p>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/06/05/java-unused-import-statment/" data-id="cjmkqpb9g0007yr4gz8gsgq50" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-zero-width-space" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/05/10/zero-width-space/" class="article-date">
<time datetime="2018-05-10T09:51:38.000Z" itemprop="datePublished">2018-05-10</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/05/10/zero-width-space/">看不见的字符-<200b></a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>今天配置一个数据库链接地址时,报URL地址有问题,但在sublime Text里明明是正常的。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://shgq.1.abc:1234/pools;http://shgq.2.abc:1234/pools#xxf</span><br></pre></td></tr></table></figure>
<p>打开debug日志发现是地址解析错误,无法识别<code><200b></code>,但字符串中没有这个<code><200b></code>,是哪里冒出来的呢?</p>
<p>于是,我重新把配置贴到用vim打开的文本中,才发现了隐藏字符。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://shgq.1.abc:1234/pools;http://shgq.2.abc:1234<200b>/pools#xxf</span><br></pre></td></tr></table></figure></p>
<p>搜了一把这个<code><200></code>,学名<a href="https://en.wikipedia.org/wiki/Zero-width_space" target="_blank" rel="noopener">zero-width space(ZWSP)</a>,是一种不可见的打印字符。</p>
<p>wikipedia给出一种实际的例子,可以很明显地看出区别,如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LoremIpsumDolorSitAmetConsecteturAdipiscingElitSedDoEiusmodTemporIncididuntUtLaboreEtDoloreMagnaAliquaUtEnimAdMinimVeniamQuisNostrudExercitationUllamcoLaborisNisiUtAliquipExEaCommodoConsequatDuisAuteIrureDolorInReprehenderitInVoluptateVelitEsseCillumDoloreEuFugiatNullaPariaturExcepteurSintOccaecatCupidatatNonProidentSuntInCulpaQuiOfficiaDeseruntMollitAnimIdEstLaborum</span><br></pre></td></tr></table></figure>
<p>通过vim编辑时变成如下文本,中间多了很多<code><200b></code>。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Lorem<200b>Ipsum<200b>Dolor<200b>Sit<200b>Amet<200b>Consectetur<200b>Adipiscing<200b>Elit<2b>Sed<200b>Do<200b>Eiusmod<200b>Tempor<200b>Incididunt<200b>Ut<200b>Labore<200b>Et<200b>Dole<200b>Magna<200b>Aliqua<200b>Ut<200b>Enim<200b>Ad<200b>Minim<200b>Veniam<200b>Quis<200b>Norud<200b>Exercitation<200b>Ullamco<200b>Laboris<200b>Nisi<200b>Ut<200b>Aliquip<200b>Ex<200ba<200b>Commodo<200b>Consequat<200b>Duis<200b>Aute<200b>Irure<200b>Dolor<200b>In<200b>Reprehderit<200b>In<200b>Voluptate<200b>Velit<200b>Esse<200b>Cillum<200b>Dolore<200b>Eu<200b>Fugi<200b>Nulla<200b>Pariatur<200b>Excepteur<200b>Sint<200b>Occaecat<200b>Cupidatat<200b>Non<20>Proident<200b>Sunt<200b>In<200b>Culpa<200b>Qui<200b>Officia<200b>Deserunt<200b>Mollit<200bnim<200b>Id<200b>Est<200b>Laborum</span><br></pre></td></tr></table></figure></p>
<p>如果使用chrome浏览器,打开<code>检查</code>功能查看wikipedia的这个例子,在<code>Elements</code> Tab页下面,找到上述字符串对应的html页面代码,可以看到上面<200b>均表示成了<code>&#8203;</code>。</200b></p>
<p>好了,再测试一下,​<code>这</code>前面隐藏了一个分隔符!不信的话,试试复制一下,用vim编辑看看。:-D</p>
<p>零宽字符有什么用呢,可以作为内容的水印,这是另外一个话题了,可以发掘利用一下。</p>
<p>以后粘贴复制文本时需要多长一个心眼了。</p>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/05/10/zero-width-space/" data-id="cjmkqpbaj001gyr4gqbru0o51" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Unicode/">Unicode</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-nginx-resolver-vaild-and-timeout" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/05/08/nginx-resolver-vaild-and-timeout/" class="article-date">
<time datetime="2018-05-08T08:03:18.000Z" itemprop="datePublished">2018-05-08</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/05/08/nginx-resolver-vaild-and-timeout/">nginx resolver数据缓存及解析超时时间参数</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>在配置nginx的resolver时,有两个与时间相关的参数。</p>
<p>一、valid<br><figure class="highlight plain"><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">Syntax: resolver address ... [valid=time] [ipv6=on|off];</span><br><span class="line">Default: —</span><br><span class="line">Context: http, server, location</span><br><span class="line"></span><br><span class="line">如:</span><br><span class="line">resolver 127.0.0.1 [::1]:5353 valid=30s;</span><br></pre></td></tr></table></figure></p>
<p><code>valid</code> flag means how long nginx will consider answer from resolver as valid and will not ask resolver for that period.<br>即DNS返回结果的缓存时间,在缓存有效时间内,nginx不会再次向DNS请求解析结果。</p>
<p>二、resolver_timeout<br><figure class="highlight plain"><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">Syntax: resolver_timeout time;</span><br><span class="line">Default: </span><br><span class="line">resolver_timeout 30s;</span><br><span class="line">Context: http, server, location</span><br></pre></td></tr></table></figure></p>
<p><code>resolve_timeout</code> sets how long nginx will wait for anwser from resolver (DNS).<br>即nginx发起的DNS查询请求超时时间。</p>
<p>更多参考:</p>
<ul>
<li><a href="http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver" target="_blank" rel="noopener">http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver</a></li>
<li><a href="https://stackoverflow.com/questions/28606696/what-is-the-difference-in-resolver-valid-time-and-resolver-timeout-in-nginx" target="_blank" rel="noopener">https://stackoverflow.com/questions/28606696/what-is-the-difference-in-resolver-valid-time-and-resolver-timeout-in-nginx</a> </li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/05/08/nginx-resolver-vaild-and-timeout/" data-id="cjmkqpba80013yr4go6hfog94" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/NGINX/">NGINX</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<article id="post-slf4j-stackoverflowerror" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/05/04/slf4j-stackoverflowerror/" class="article-date">
<time datetime="2018-05-04T06:47:34.000Z" itemprop="datePublished">2018-05-04</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/05/04/slf4j-stackoverflowerror/">SLF4J及日志栈溢出问题</a>
</h1>
<br/>
<div>
<span id="busuanzi_container_page_pv" style='display:none'>
<!-- 本文总阅读量<span id="busuanzi_value_page_pv"></span>次 -->
</span>
</div>
</header>
<div class="article-entry" itemprop="articleBody">
<p>新接手一项目,在本地测试运行时,发现报如下错误:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the <span class="class"><span class="keyword">class</span> <span class="title">path</span>, <span class="title">preempting</span> <span class="title">StackOverflowError</span>.</span></span><br></pre></td></tr></table></figure></p>
<p>很明显,是项目的classpath中同时存在log4j-over-slf4j.jar和slf4j-log4j12.jar,引起死循环。但为什么项目在线上能好好运行呢?<br>原来线上运行的程序在打包时做了手脚,在最终的依赖包中,删除了log4j和slf4j-log4j12,如下:<br><figure class="highlight"><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"><dependencySets></span><br><span class="line"> <dependencySet></span><br><span class="line"> <outputDirectory>lib</outputDirectory></span><br><span class="line"> <excludes></span><br><span class="line"> <exclude>log4j:log4j</exclude></span><br><span class="line"> <exclude>org.slf4j:slf4j-log4j12</exclude></span><br><span class="line"> </excludes></span><br><span class="line"> <useProjectArtifact>false</useProjectArtifact></span><br><span class="line"> </dependencySet></span><br><span class="line"></dependencySets></span><br></pre></td></tr></table></figure></p>
<p>让我们来重头捋一下Java日志框架及该问题的成因。</p>
<p>我们用的最多的几个日志框架有Log4j、Jakarta Commons Logging以及Logback-classic。尤其值得一提的是Logback-classic,它是Log4j作者写的又一个开源日志组件,由于实现了SLF4J’s的Logger接口,native,性能优越,可以直接作为SLF4J的实现。</p>
<p><a href="https://www.slf4j.org/index.html" target="_blank" rel="noopener">SLF4J</a>,全称Simple Logging Facade for Java,是一个抽象的日志框架,支持程序动态绑定日志框架实现,如绑定到<code>java.util.logging</code>、<code>logback</code>以及 <code>log4j</code>等,使用时需要在应用中添加依赖slf4j-api。</p>
<p><strong>SLF4J对应用日志做了封装抽象,因此,业务可以针对SLF4J进行编程,再按需绑定具体的日志框架。无论是灵活性还是可扩展性,都有很多的提升。</strong></p>
<p>为了支持动态绑定不同的日志框架,SLF4J提供了几个绑定工具,如下:</p>
<table>
<thead>
<tr>
<th>绑定</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>slf4j-log4j12</strong></td>
<td>Log4j绑定,Log4j是一个使用非常广泛的日志组件</td>
</tr>
<tr>
<td><strong>slf4j-jcl</strong></td>
<td>Jakarta Commons Logging绑定</td>
</tr>
<tr>
<td><strong>slf4j-jdk14</strong></td>
<td>java.util.logging, JDK绑定</td>
</tr>
<tr>
<td><strong>slf4j-simple</strong></td>
<td>输出会定向到System.err,只有INFO级别以上的才会输出</td>
</tr>
<tr>
<td><strong>slf4j-nop</strong></td>
<td>丢弃日志绑定</td>
</tr>
</tbody>
</table>
<p>为了方便jcl、log4j、jul用户迁移到SLF4J,SLF4J提供了几个桥接模块。</p>
<table>
<thead>
<tr>
<th style="text-align:left">模块</th>
<th style="text-align:left">目标对象</th>
<th style="text-align:left">使用说明</th>
<th style="text-align:left">实现原理</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>log4j-over-slf4j</strong></td>
<td style="text-align:left">log4j用户</td>
<td style="text-align:left">将log4j.jar替换为log4j-over-slf4j.jar</td>
<td style="text-align:left">直接替换了log4j中的大量同名类,如Logger、Category、Level等</td>
</tr>
<tr>
<td style="text-align:left"><strong>jcl-over-slf4j</strong></td>
<td style="text-align:left">jcl用户</td>
<td style="text-align:left">将commons-logging.jar替换为jcl-over-slf4j.jar</td>
<td style="text-align:left">保留了jcl的接口,但底层实现采用了slf4j</td>
</tr>
<tr>
<td style="text-align:left"><strong>jul-to-slf4j</strong></td>
<td style="text-align:left">jul用户</td>
<td style="text-align:left">直接依赖</td>
<td style="text-align:left">由于jul在java.*namespace下,无法替换。该模块采用了翻译手段,将LogRecord转换为slf4j类似对象。<strong> 该方案存在一定的性能损失,不推荐使用</strong></td>
</tr>
</tbody>
</table>
<p>按上面的介绍,使用slf4j时就需要注意一下几个问题:</p>
<ol>
<li><p><strong><code>jcl-over-slf4j.jar</code>和<code>slf4j-jcl.jar</code>不能同时依赖!</strong></p>
<ul>
<li>原因:jcl会将实现委托到slf4j,而slf4j又会绑定到jcl。</li>
</ul>
</li>
<li><p><strong><code>log4j-over-slf4j.jar</code>和<code>slf4j-log4j12.jar</code>不能同时依赖!</strong></p>
<ul>
<li>原因: log4j会将实现委托到slf4j,而slf4j又会绑定到log4j实现。</li>
</ul>
</li>
<li><p><strong><code>jul-to-slf4j.jar</code>和<code>slf4j-jdk14.jar</code>不能同时依赖!</strong></p>
<ul>
<li>原因:jul会将实现委托到slf4j,而slf4j又会绑定到jdk实现。</li>
</ul>
</li>
</ol>
<p><strong>上述三个情况下,若同时依赖均会产生循环依赖问题,导致StackOverflowError!</strong></p>
<p>回到开头的问题,即上述第2个注意问题,怎么解决也已经很明显了。</p>
<p><strong>参考资料</strong></p>
<ol>
<li><a href="https://www.slf4j.org/manual.html" target="_blank" rel="noopener">SLF4J user manual</a></li>
<li><a href="https://www.slf4j.org/legacy.html" target="_blank" rel="noopener">Bridging legacy APIs</a></li>
</ol>
</div>
<footer class="article-footer">
<a data-url="http://qinguan.github.io/2018/05/04/slf4j-stackoverflowerror/" data-id="cjmkqpbac0018yr4geqfbzo7o" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Java/">Java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/SLF4J/">SLF4J</a></li></ul>
</footer>
</div>
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://qinguan-github-io.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</article>
<nav id="page-nav">
<span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/">下一页 »</a>
</nav>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">标签</h3>
<div class="widget">
<ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Charles/">Charles</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Git/">Git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Java/">Java</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Lua/">Lua</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Maven/">Maven</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/NGINX/">NGINX</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Netty/">Netty</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/NoSQL/">NoSQL</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/OpenResty/">OpenResty</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/RPC/">RPC</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SLF4J/">SLF4J</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SQL/">SQL</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Serialization/">Serialization</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Spring/">Spring</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Springboot/">Springboot</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Unicode/">Unicode</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/other/">other</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">标签云</h3>
<div class="widget tagcloud">
<a href="/tags/Charles/" style="font-size: 10px;">Charles</a> <a href="/tags/Git/" style="font-size: 10px;">Git</a> <a href="/tags/Java/" style="font-size: 20px;">Java</a> <a href="/tags/Lua/" style="font-size: 15px;">Lua</a> <a href="/tags/Maven/" style="font-size: 15px;">Maven</a> <a href="/tags/NGINX/" style="font-size: 10px;">NGINX</a> <a href="/tags/Netty/" style="font-size: 10px;">Netty</a> <a href="/tags/NoSQL/" style="font-size: 10px;">NoSQL</a> <a href="/tags/OpenResty/" style="font-size: 10px;">OpenResty</a> <a href="/tags/RPC/" style="font-size: 10px;">RPC</a> <a href="/tags/SLF4J/" style="font-size: 10px;">SLF4J</a> <a href="/tags/SQL/" style="font-size: 10px;">SQL</a> <a href="/tags/Serialization/" style="font-size: 10px;">Serialization</a> <a href="/tags/Spring/" style="font-size: 10px;">Spring</a> <a href="/tags/Springboot/" style="font-size: 10px;">Springboot</a> <a href="/tags/Unicode/" style="font-size: 10px;">Unicode</a> <a href="/tags/other/" style="font-size: 10px;">other</a>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">归档</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/09/">九月 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/08/">八月 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/06/">六月 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/05/">五月 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/04/">四月 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/03/">三月 2018</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">最新文章</h3>
<div class="widget">
<ul>
<li>
<a href="/2018/09/27/maven-version-separation/">使用maven version:set分离服务的发布版本</a>
</li>
<li>
<a href="/2018/09/27/enum-compatibility-issue/">Thrift枚举对象兼容性问题</a>
</li>
<li>
<a href="/2018/08/22/a-high-coupling-case/">一个内部API高度耦合的Java实现案例</a>
</li>
<li>
<a href="/2018/08/15/netty-version-conflicting-exception/">Netty版本冲突引起Cassandra访问异常</a>
</li>