forked from zhoudl-git/zhoudl-git.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
506 lines (243 loc) · 326 KB
/
search.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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>这踉踉跄跄的生活啊</title>
<link href="/2019/04/03/zhe-lang-lang-qiang-qiang-de-sheng-huo-a/"/>
<url>/2019/04/03/zhe-lang-lang-qiang-qiang-de-sheng-huo-a/</url>
<content type="html"><![CDATA[<p>曾经灌了那么多的鸡汤,听了那么多的道理,却依然敌不过生活的一次重击。</p><p>这踉踉跄跄的一生啊!</p><p>生活的无奈在于,有的人生来就是光鲜靓丽,鲜花掌声为伴,而有的人生却是起起落落,落落落落落落落…落到谷底。</p><p>忽然想起一句不记得是哪部电影里的台词 : 有些人光是活着就已经竭尽全力了!</p><p>小时候经历挫折的时候,大人总会告诉你,过去就好了,但是越成长越是发现,生活总在给你出难题,一山迈过,还有一山在等着你。</p><p>生活的艰难有时便是如此难熬,永远挣不脱,逃不掉,毫无选择余地,任凭现实的捶打。</p><p>刚才浏览新闻发现下边这个新闻,感慨颇深。</p><p><img src="http://ww1.sinaimg.cn/large/007GTbzZgy1g1nkhuex34j30hs0ocgnh.jpg" alt=""></p><p>事情经过是这样的,事情发生在杭州文一路文菁路口附近,当时傅警官正带队整治电动车的交通违法行为,一位小伙因骑单车逆行被交警拦下。之后小伙接到一个电话,讲电话的时候还很平静,没想到挂了电话,意外的一幕却发生了……他一下子把手机摔在了地上。</p><p>交警上前问小伙怎么了,小伙却突然崩溃大叫,开始跪下求交警放他走,称让他“干嘛都可以”。</p><p><img src="https://hiphotos.baidu.com/feed/pic/item/4afbfbedab64034facc0e475a1c379310a551db7.jpg" alt="img"></p><p>接下来,情绪愈发激动的小伙冲向了不远处的桥。因为担心他有轻生的念头,交警们赶紧放下手头的事,跑到河边把他拉了下来,劝小伙冷静下来,告诉他“没什么事的”。</p><p><img src="https://hiphotos.baidu.com/feed/pic/item/b64543a98226cffcc11c99c1b7014a90f603ea8b.jpg" alt="img"></p><p>乍一看,估计这又是什么雷人男子要开始“卖惨”,但万万没想到,真相却让人感到心酸。</p><p>原来,小伙是因为压力太大憋得太久,一下子没有控制住自己的情绪。</p><p>他哭诉说,自己每天都要加班到晚上十一二点,结果这次加班的时候女友没带钥匙,让自己送钥匙回家。一上路,这边单位在催自己赶紧回去加班,那边女朋友又在催钥匙,一急之下,他为了省时间选择了逆行,没想到第一次干这种事就被交警看到了。</p><p><img src="https://hiphotos.baidu.com/feed/pic/item/8326cffc1e178a82583159d3f803738da977e8b0.jpg" alt="img"></p><p>小伙称自己“要发泄一下,我太烦了”,“我只是想哭一下”。</p><p>看小伙情绪这么激动,交警告诉他要发泄一下可以的,“我们可以在这里守着你”。交警对小伙说,“可以辞职,换一个轻松点的,都是要靠自己调节的,觉得累了就请假,你上班还不是为了生活嘛”。</p><p>在大家的开导下,小伙的情绪也慢慢平复下来,还向交警道歉,称自己“平常也特别讨厌别人逆行”,并表示愿意接受逆向行车的处罚。</p><p>网友听完小伙的话纷纷表示感同身受,“看哭了”,仿佛看到了同为“社畜”的自己。</p><p><img src="https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=1696737209,2601098893&fm=173&app=25&f=JPEG?w=640&h=429&s=C010EC330D5E4DC840D578DB0000C0B3" alt="img"></p><p><img src="https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=468998622,1597369044&fm=173&app=25&f=JPEG?w=640&h=428&s=6894ED13055A5DCC5CFCA9DB000010B3" alt="img"></p><p><img src="https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2069663692,3811562253&fm=173&app=25&f=JPEG?w=640&h=853&s=2498CC33198FCCCC5C54E4DA0000C0B1" alt="img"></p><p>还有网友不忘称赞温暖的交警。</p><p><img src="https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2988944824,3687077029&fm=173&app=25&f=JPEG?w=640&h=719&s=E094EC33591EE8C854DC20DE000050B3" alt="img"></p><p>就像交警们说的那样,工作压力太大了,要劳逸结合,可以请假,也可以换工作,受不了了就要发泄出来,要学会自己调节。</p><p>但是擦干眼泪,还是要继续前行。</p><p>生活不易,大家要微笑面对。</p><p>如果不是心有所系,谁会咬着牙独自承受,忍受煎熬?</p><p>或许你会问,为什么活着这么艰难,还是有这么多人咬牙坚持?</p><p>生活本就是不易的,当你觉得容易的时候,一定是有人在替你承担那份不容易。</p><p>正是明白生活的那份不易,正是从不易的土壤里生长起来的人,才明白为何要坚强。</p><p>因为在这个世界上,有自己爱的和在乎的人。</p><p>所以,我们才要去挤星稀时候的早班车和月亮高上的夜班车,苦苦坚持,苦苦努力,苦苦奋斗,理由不过是给家人更好的环境,给老人安逸的晚年,也是给自己一个更好的将来。</p><p>成长的过程,便是将哭声渐渐调成静音,也只有在夜半无人时候的时候,你才放肆地将眼泪抛洒,那些煎熬与苦痛一倾而出,你不止一次的问,生活为什么这样难这样苦?</p><p>但是,第二天你还是会像什么也没发生一样,一捧冷水打湿脸颊,擦去泪水的痕迹,然后早起上班,日复一日。</p><p>生活艰难,这是现实,但你要相信打不死你的终将使你强大,只要坚持下去,一切都会好的!</p><p>愿每份心酸都有人心疼,</p><p>愿每份付出都有回报,</p><p>愿每个人都能被温柔以待。</p><p>愿天下人人如龙,</p><p>愿有梦的人都能实现!</p>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 猿说 </tag>
<tag> 程序人生 </tag>
</tags>
</entry>
<entry>
<title>不拼尽全力试一把,你怎么知道自己不行?</title>
<link href="/2019/04/03/bu-pin-jin-quan-li-shi-yi-ba-ni-zen-me-zhi-dao-zi-ji-bu-xing/"/>
<url>/2019/04/03/bu-pin-jin-quan-li-shi-yi-ba-ni-zen-me-zhi-dao-zi-ji-bu-xing/</url>
<content type="html"><![CDATA[<h2 id="不拼尽全力试一把,你怎么知道自己不行?"><a href="#不拼尽全力试一把,你怎么知道自己不行?" class="headerlink" title="不拼尽全力试一把,你怎么知道自己不行?"></a>不拼尽全力试一把,你怎么知道自己不行?</h2><p>前几天看到一句话是这么说的:</p><blockquote><p>未来已经到来,只是分布不均。<br>有些人已经在利用 10 年后的技术和资源,而有些人还停留在 10 年前。<br>这种时间差造成的信息不对称是一个人重要的竞争砝码。<br>那么在现有条件下,如何缩小这种时间差,来对抗未来分布的不均?</p><p>答案是——<br>去聆听那些走在时间前头的人。</p></blockquote><p>94 年出生,今年已经满打满算 25 岁了,但是依旧一事无成,造成这种结果的原因我认为有两点。</p><h4 id="生来懒惰"><a href="#生来懒惰" class="headerlink" title="生来懒惰"></a>生来懒惰</h4><p>这点在孩童时期可能还不太明显,因为哪个时候整天生活在温室,学校有两个姐姐照顾,回到家是整个家的宠儿。</p><p>随着年龄的增长,慢慢走出了这个温室,懒惰这个特性就显露无疑了。</p><ul><li><p>生活上很懒,就显得整个人很邋遢;</p></li><li><p>社交上很懒,就造成没多少好朋友;</p></li><li><p>学习上很难,就迟迟没有任何进步。</p></li></ul><p>以上种种,只是一小部分,但是你会发现就靠着这几点,你的人生很有可能已经发生了很大的转折了。</p><h4 id="天马行空"><a href="#天马行空" class="headerlink" title="天马行空"></a>天马行空</h4><p>做任何事情都是三分钟热度,没有任何自己的坚持。天天都在方法论上,从不付诸实践,计划总是大于执行力。说句不怕你笑话的,现如今这个无纸化时代,我用纸和笔手写下来做的计划表可能比你做的笔记都多。</p><p>可是,结果呢?</p><p>显而易见,我并没有坚持下去所谓的计划。都是满腔热血的书写着自己的计划,给别人吹嘘这自己的梦想。</p><p>殊不知,在别人看来,我可能就是个沙雕,是个只会吹牛皮的沙雕。</p><h4 id="自律能力差"><a href="#自律能力差" class="headerlink" title="自律能力差"></a>自律能力差</h4><p>上述天马行空的原因其实本质上还是自律能力差,但是在这里我为什么要把自律能力拎出来单独说,是因为自律这个东西太过重要。很多人死就死在不自律上,无论你是减肥、学习亦或是交朋友,无法自律,哪便意味着失败,而且是彻彻底底的失败。</p><p>以前看过一篇文章叫自律到极致的人有多可怕?里边有这样一段话:</p><blockquote><p>我刚进写作圈时认识的一个作者,如今的一个大编辑。</p><p>我们俩刚认识的时候,她是个银行坐柜台的。因为都喜欢写作,经常互相鼓舞。</p><p>她的工作比我更惨,我做办公室,有电脑,有手机,没事就能写上几篇练练手,她不行,每天不停地接待顾客,连手机都不让带。</p><p>可就是这样,她天天比我写得多,进步神速。我最常问她的一句话就是:你都不睡觉的吗?</p><p>她却反问我:“不对自己狠一点,怎么过上想要的生活?”</p><p>我特怕她哪天倒下去了,可结果,她熬出来了。签了几本书,几份供稿合约,妥妥地把工作辞了。</p><p>她用爱好赚的钱,比她老公上班还要多。过去,她总说家人不支持她的破爱好。</p><p>可现在,每次她一坐在书桌前,老公就乖乖端来一杯茶。</p></blockquote><p>一个人深到骨子里的自律,就是对自己下手狠。自律能带来自由,也能带来地位。你连自己都管不住,生活凭什么优待你?好日子都不会太舒服,太宠着自己的人,生活也永远不会太称心如意。</p><p>之前知乎上有个很火的话题:是什么让你保持长久的自律?</p><p>我最喜欢的一个回答是:</p><blockquote><p>“对我而言,高度自律是因为内心有强烈的驱动力,并且深刻的知道不自律的后果。正是因为之前痛苦的体验,才能让我在每次要坚持不下去时,都能因为不想再有那样体验而咬牙完成。我想,痛苦某种程度来说,也是一种财富吧。”</p></blockquote><p>喜欢的理由很简单,我觉得很多人的自律都来源于恐惧,很少有人是因为内心的极度渴望。就像一位学者曾说:我做到这些最大的动力是恐慌,我不能接受一个停滞的自己。</p><p>人生苦难重重,自律是解决人生问题最主要的工具,也是消除人生痛苦最重要的方法。</p><p>哲学家康德曾经说过:所谓自由,不是随心所欲,而是自我主宰。</p><p>所以请记得:你的健康、时间、感情、未来,都无比珍贵、不能有一丝一毫的挥霍!</p><h4 id="改变:做一个自律的人"><a href="#改变:做一个自律的人" class="headerlink" title="改变:做一个自律的人"></a>改变:做一个自律的人</h4><h5 id="给自律开个头:早起"><a href="#给自律开个头:早起" class="headerlink" title="给自律开个头:早起"></a>给自律开个头:早起</h5><p>很少有人提到,其实想要保持自律,最重要而且力所能及马上就能做的,不是读书,不是冥想,不是拿小本本撰写誓言,而是很简单的两个字:<strong>早起</strong>!</p><p>你一定想不到,早起对于一个人的生活和成长有多么重要的影响和帮助。</p><p>正常人每天的睡眠大都在7到8小时,然而有趣的是,欧洲心理学家发现:在早起的人的潜意识里,每天的可支配时间是要更长一些的。而相对的是,在晚起者的潜意识里,每天则要变得短了许多。这是因为,在大部分人的潜意识里,是以中午 12 点为一天的分界线的,如果总是很晚才起床,一起床半天就没了,人的心理难免受影响,近而焦虑,同时效率也会相对变得低下,总之坏处多多。</p><p>早起这段时间我花 1 小时看专业的书籍,或者听相关专业的课程,偶尔练练我喜欢的小提琴,剩下的时间用来慢悠悠的享受早餐。</p><p>感受一下,当上班的日子有几个小时完全属于自己,你会觉得每天都有一个小假的愉悦感,工作也会积极很多。</p><h5 id="自律不能一步到位"><a href="#自律不能一步到位" class="headerlink" title="自律不能一步到位"></a>自律不能一步到位</h5><p>我们总想着制定一个计划,就能彻底改变自己的生活。但事实告诉我们,这是非常幼稚的想法。</p><p>首先,如果人要完全依靠意志力去坚持某样事情,那几乎是不可能的。因为意志力是有限的,它就像体力一样,是会被消耗的。</p><p>所以我们必须养成习惯,因为习惯可以帮助我们在消耗非常少的意志力的情况下,轻松地坚持做某件事。但一个习惯的养成是非常困难的,需要长期的刻意训练。</p><p>所以,当你试图一次性控制太多事情的时候,你很容易因为意志力的过度消耗而变得身心疲惫,乃至崩溃。而结果,往往是一段时间的彻底放纵,以及对自己失去信心。</p><p>因此,我们要循序渐进,一件一件去改变,一口是吃不成大胖子的。</p><p>值得注意的是,中途我们可能会懈怠,比如想减肥的你,某一天控制不住去吃了一顿饱饭。或者想要每天睡前阅读的自己,某一天晚上去玩游戏了,而没有去阅读。</p><p>这完全有可能发生,但这绝不意味着失败。</p><p>我们很容易觉得,某一天没有坚持下来,这就是失败了,所以我还是放弃吧。</p><p>不,你没有失败!你明天依然可以继续你的计划,<strong>只要你多坚持一天,你就多收获一分</strong>。</p><p><strong>成功的关键不是你执行的多完美,而是你坚持的有多久。</strong></p><p>所以从今天开始,扔掉那些写的太满的计划,先从一件小事做起,中途断过也没关系,这不意味着失败,做好了就奖励自己,没做好也别自责。当这件事养成习惯以后,你会发现自己可以毫不费力的坚持它了。</p><h5 id="远离诱惑"><a href="#远离诱惑" class="headerlink" title="远离诱惑"></a>远离诱惑</h5><p>记住,永远不要高估自己的意志力,面对诱惑,很少有人能够坚持不动摇。我们对抗诱惑也会消耗意志力,而时间久了,你就会彻底崩溃。</p><p>前段时间,有位朋友无意说了句,玩抖音的都会不知不觉的刷几个小时。之前也看过很多文章,说抖音、王者多么多么吸引人,一下忍不住想试试自己会不会中招。结果证明,我也就一凡夫俗子。</p><p>后来,实在受不了颓废的自己,半夜爬起来卸载了这些软件。</p><p>所以,<strong>无论任何时候,你都要主动远离诱惑,就像不要考验人性一样,也不要考验自己的意志力。</strong>它们都是禁不起考验的。</p><h5 id="自律和你的身体状态有关"><a href="#自律和你的身体状态有关" class="headerlink" title="自律和你的身体状态有关"></a>自律和你的身体状态有关</h5><p>事实上,你的身体状态,会极大影响你的意志、情绪、智力等等能力。有没有发现,当你没睡够的时候,会变得很容易发脾气,处理工作也容易走神,应该做的事情也不想去做。当我意识到这一点的时候,我就开始非常注意休息,因为只有身体得到良好的休息,才有足够的精力去处理工作。</p><p>这里的休息,包括很多方面:</p><blockquote><p>①早睡早起</p><p>②中午小憩,但不会超过30分钟</p><p>③体育锻炼</p><p>④周末彻底放下工作,去做一些不消耗太多精力的事情</p><p>⑤听音乐</p></blockquote><p>这一切都以恢复精力为目的,只有精力充沛,你才能做好事情,才能做到自律。保持自律并不是只靠意志力就能做好的,普通人很难拥有太过强大的意志力,而且也没有这个必要。你要利用一切可以帮助你自律的因素,无论是环境,还是身体条件,甚至是别人的督促,公司/学校的强制安排。</p><p>只有这样,我们才能更高效的保持自律。</p><p>(先写这么多,4 月底我会再来总结的)</p><h4 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h4><p>4 月要完成的事情</p><ul><li><p>习惯养成</p><ul><li>睡前读书一小时习惯养成</li><li>6 点早起习惯养成</li><li>跑步半小时运动习惯养成</li></ul></li><li><p>技术学习</p><ul><li>JVM </li><li>J.U.C</li><li>Dubbo</li></ul></li></ul>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 猿说 </tag>
<tag> 自律 </tag>
</tags>
</entry>
<entry>
<title>JVM 从入门到实战-02-什么样的对象要被 GC?</title>
<link href="/2019/03/13/jvm-cong-ru-men-dao-shi-zhan-02-shi-me-yang-de-dui-xiang-yao-bei-gc/"/>
<url>/2019/03/13/jvm-cong-ru-men-dao-shi-zhan-02-shi-me-yang-de-dui-xiang-yao-bei-gc/</url>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>上一篇文章 JVM 基本介绍 我们了解了一些基本的 JVM 知识,本篇开始逐步学习垃圾回收,我们都知道既然叫垃圾回收,那回收的就应该是垃圾,可是我们怎么知道哪些对象是垃圾呢? 哪些对象需要被回收? 什么时候需要回收呢?</p><h2 id="判断算法"><a href="#判断算法" class="headerlink" title="判断算法"></a>判断算法</h2><h3 id="引用计数算法"><a href="#引用计数算法" class="headerlink" title="引用计数算法"></a>引用计数算法</h3><p>给每个对象设置一个计数器,每当该对象被引用时引用计数器加 1,有引用断开时引用计数减 1。当引用计数为 0 时表示该对象可以被回收。</p><p>这个可以用数据算法中的图形表示,对象 A-对象 B-对象 C 都有引用,所以不会被回收,对象 B 由于没有被引用,没有路径可以达到对象 B,对象 B 的引用计数就就是 0,对象 B 就会被回收。</p><p><img src="https://images.cnblogs.com/cnblogs_com/aigongsi/201204/201204061525051904.jpg" alt="2"></p><h5 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h5><p>客观地说,引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软公司的 COM(Component Object Model)技术、使用 ActionScript 3 的 FlashPlayer、Python 语言和在游戏脚本领域被广泛应用的 Squirrel 中都使用了引用计数算法进行内存管理。</p><h5 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h5><p>主流的 Java 虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。因为相互引用,计数器值永远也不会成为 0 ,所以永远达不到被 GC 回收的条件。</p><p>例如下图:对象A,对象B 循环引用,没有其他的对象引用A和B,则A和B 都不会被回收。</p><p><img src="https://images.cnblogs.com/cnblogs_com/aigongsi/201204/201204061525055775.jpg" alt="3"></p><h3 id="可达性分析算法"><a href="#可达性分析算法" class="headerlink" title="可达性分析算法"></a>可达性分析算法</h3><p>该算法的原理是:以 <code>GC Roots</code> 的对象作为起始点,然后以该节点为基准开始向下搜索,搜索过程中搜索路径我们称之为<strong>引用链</strong>,当一个对象到 <code>GC Roots</code> 没有任何引用链连接的时候,说明该对象是不可用的。</p><p><strong>注意</strong>:我们在 上边的所说的引用都是指定的<strong>强引用关系</strong>。</p><p>因为我们知道 Java 中存在四种引用对象,根据引用强度(从上至下依次减弱)可依次划分为:</p><ul><li>强引用 <code>Strong Reference</code></li><li>软引用 <code>Weak Reference</code></li><li>弱引用 <code>Phantom Reference</code></li><li>虚引用 <code>Soft Reference</code></li></ul><p>详细的概念大家下去可以自行查看,此处不再赘述。</p><h5 id="可以用作-GC-Roots-的对象"><a href="#可以用作-GC-Roots-的对象" class="headerlink" title="可以用作 GC Roots 的对象"></a>可以用作 GC Roots 的对象</h5><ul><li>方法区 : 类静态变量引用的对象</li><li>方法区 : 常量引用的对象</li><li>虚拟机栈 : 本地变量表中引用的对象</li><li>本地方法栈 : JNI (带 Native 关键字)引用的对象</li></ul><p>如下图,对象 D 和根对象之间毫无引用链,则会被回收。</p><p><img src="https://images.cnblogs.com/cnblogs_com/aigongsi/201204/201204061525065426.jpg" alt="4"></p><p>由于这种算法即使存在互相引用的对象,但如果这两个对象无法访问到根对象,还是会被回收。如下图:对象 C 和对象 D 互相引用,但是由于无法访问根节点,还是会被回收。</p><p><img src="https://images.cnblogs.com/cnblogs_com/aigongsi/201204/20120406152506377.jpg" alt="5"></p><h4 id="不可达是不是就一定会被回收"><a href="#不可达是不是就一定会被回收" class="headerlink" title="不可达是不是就一定会被回收?"></a>不可达是不是就一定会被回收?</h4><p>答案是<strong>不一定</strong>。</p><p>一个对象在真正被回收之前,需要经历两次标记过程:</p><p><strong>第一次标记</strong>:</p><p>如果对象在进行可达性分析之后发现没有与 GC Roots 相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象<strong>是否有必要执行</strong> finalize() 方法,此时分两种情况:</p><ul><li>对象没有覆盖 finalize() 方法 </li><li>finalize() 方法已经被虚拟机调用过</li></ul><p>在这两种情况下虚拟机都认为此时没有必要执行垃圾回收。</p><p><strong>第二次标记</strong>:</p><p>在第一次标记判定基础之上,如果判定为有必要执行 finalize() 方法,则虚拟机会把这个对象放置到一个叫做 F-Queue 的队列之中,并在之后用 Finalizer 线程去执行回收。(此处的执行指的是 虚拟机会去触发这个方法,但是并不保证回收成功,也不承诺会等待他运行结束,因为如果有个别对象在 finalize() 方法中执行缓慢甚至发生死循环的时候,有可能会导致 F-Queue 队列中的其他对象发生永久等待,最后导致整个垃圾回收系统崩溃)</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>引用计数法:简单高效,但是对于相互循环引用的对象无法判断是否应该被回收</li><li>可达性分析:目前大多虚拟机厂商采用的垃圾回收算法,通过判断其他对象是否和根节点之间存在引用链来分析是否应该被回收</li><li>不可达的对象不一定会立即被回收</li></ul><p><img src="http://images.zhoudl.top/%E6%9C%8B%E5%8F%8B%E5%9C%88%E5%AE%A3%E4%BC%A0%E4%BA%8C%E7%BB%B4%E7%A0%81.png" alt=""></p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> Java </tag>
<tag> JVM </tag>
</tags>
</entry>
<entry>
<title>JVM从入门到实战---01 JVM基本介绍</title>
<link href="/2019/03/12/jvm-cong-ru-men-dao-shi-zhan-01-jvm-ji-ben-jie-shao/"/>
<url>/2019/03/12/jvm-cong-ru-men-dao-shi-zhan-01-jvm-ji-ben-jie-shao/</url>
<content type="html"><![CDATA[<h3 id="什么是-JVM"><a href="#什么是-JVM" class="headerlink" title="什么是 JVM"></a>什么是 JVM</h3><p>先来看下百度百科的解释:</p><blockquote><p>JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,JVM 是一种用于计算设备的规范,它是<strong>一个虚构出来的计算机</strong>,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。</p></blockquote><p>晦涩难懂有没有,简单理解就是说虚拟机是物理机的软件实现。</p><p>Java 的设计理念是 WORA(Write Once Run Anywhere,一次编写到处运行)。编译器将 Java 文件编译为 Java .class 文件,然后将 .class 文件输入到 JVM 中,JVM 执行类文件的加载和执行,最后转变成机器可以识别的机器码进行最终的操作。</p><h3 id="为什么要学习-JVM"><a href="#为什么要学习-JVM" class="headerlink" title="为什么要学习 JVM"></a>为什么要学习 JVM</h3><p>每个 Java 开发人员都知道字节码经由 JRE(Java 运行时环境)执行。但他们或许不知道 JRE 其实是由 Java 虚拟机(JVM)实现,JVM 分析字节码,解释并执行它。作为开发人员,了解 JVM的 架构是非常重要的,因为它使我们能够编写出更高效的代码。</p><p>但是 JVM 在帮我们实现 Write Once Run Anywhere 的同时,有利有弊,因为在这个过程中涉及到了内存管理,尤其是多线程情况下的内存管理问题,所以我们更应该学习 JVM 的知识来帮助自己写出更好的代码。</p><p>根据上边对 JVM 的概念介绍我们知道,JVM 的主要作用在于以下两方面,之后我们的介绍也会以此着手。</p><ul><li>软件层面的机器码翻译</li><li>内存管理</li></ul><p>最近也在学习《深入理解 Java 虚拟机》这本书,此处贴个书中的图过来:</p><p><img src="https://github.com/Bylant/LearningPunch/blob/master/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3java%E8%99%9A%E6%8B%9F%E6%9C%BA-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/assets/1552285007986.png?raw=true" alt="图来自于《深入理解 Java 虚拟机 第 2 版》"></p><p>下边就详细介绍一下这张图中的各个组件</p><h3 id="运行时数据区"><a href="#运行时数据区" class="headerlink" title="运行时数据区"></a>运行时数据区</h3><p>这个区域描述的是 Java 代码运行时的状态,是我们非常关注的一个状态-程序运行状态,因为我们写代码就是为了运行,不运行的状态对我们是没什么吸引力的。说白了 Java 代码不外乎 <strong>数据 指令 控制</strong> 这三类型语句,所以我们将 JVM 运行时数据区可以划分为如下两大类:</p><ul><li>数据<ul><li>方法区</li><li>堆(Heap)</li></ul></li><li>指令<ul><li>虚拟机栈</li><li>本地方法栈</li><li>程序计数器</li></ul></li></ul><h4 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h4><p><strong>定义</strong>:指向<strong>当前线程正在执行</strong>的字节码指令的<strong>地址</strong> 也就是<strong>行号</strong>。</p><p><strong>注意</strong>:我们需要思考一个问题,我的当前线程本身已经在执行了,为什么还要找个寄存器把他的执行行号记录下来呢?</p><p>因为我们程序执行的最小单位是线程,而线程在 CPU 上执行的时候是抢占式的,这样的话就存在线程被挂起的情况,例如:有 A B 两个线程,如果 A 线程执行过程被 B 线程抢占了 CPU,则需要把挂起的 A 线程 当前执行到的行号存储下来,等到 A 重新获得 CPU 时间片执行权的时候去程序计数器获得上一次执行的行号以便于继续执行这个程序。</p><p>所以,每个线程都有自己的 程序计数器,而且是互不干扰的,属于<strong>线程私有区域</strong></p><ul><li>如果执行的是一个 Java 方法,计数器记录的是正在执行的虚拟机字节码指令的地址</li><li>如果执行的是一个 Native 方法,计数器的值则为空(undefined)</li></ul><h4 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h4><p><strong>定义</strong>:存储当前线程运行方法所需要的数据、指令和返回地址,生命周期与线程相同,同样属于<strong>线程私有区域</strong>。</p><p>每个 Java 方法在执行的同时都会创建一个栈帧用于存储局部变量、操作数栈、方法出口等信息,</p><p>如下所示,这个栈帧会存储的信息包括:</p><ul><li><p>局部变量表</p></li><li><p>操作数栈</p></li><li><p>动态链接</p></li><li><p>出口</p></li><li><p>… …</p></li></ul><p>每一个方法从调用直至执行完成的过程,其实真正对应的是一个栈帧在虚拟机栈中入栈到出栈的过程。</p><p>其中局部变量表存放了编译器可知的各种基本数据类型、引用对象等。需要注意的是因为局部变量表空间长度只有 32 位,如果是 long 和 double 类型的话会占用 2 个局部变量表空间,其他数据类型只占用 1 个。</p><p><strong>注意</strong>:局部变量表所需的内存空间在编译期间就会车队分配完成,因为在进入一个方法时,这个方法需要在栈帧中分配多大的局部空间是完全确定的,方法运行期间局部变量大小是不会改变的。</p><h3 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h3><p>和虚拟机栈类似,只不过他存储的是当前线程调用的本地方法所需要的数据、指令和返回地址等,本地方法时标识有 Native 关键字的方法,此处就不展开描述了,参考上述虚拟机栈的介绍。</p><p>另外,根据《深入理解 Java 虚拟机》这本书的介绍,有些虚拟机(如 Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一了。</p><h3 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h3><p>这块区域属于线程共享群与,主要存储的信息包括已被虚拟机你加载的类信息(类的元信息)、常量、静态变量、JIT(编译器编译后的代码)等数据。</p><p>方法区有一块区域我们称之为 <strong>运行时常量池</strong>,存放编译期生成的各种字面量和符号引用,运行时常量池有一个重要特征是具备动态性,也就是说在运行期间依然可以将新的常量放入池中,我们开发常用的有 String 类的 intern() 方法</p><h3 id="堆(Heap)"><a href="#堆(Heap)" class="headerlink" title="堆(Heap)"></a>堆(Heap)</h3><p>属于线程共享区域,在虚拟机启动时创建,是虚拟机管理的内存中最大的一块。它的唯一作用就是存放对象实例。</p><p>根据虚拟机规范的描述是:所有的对象实例及数组都要在堆上分配。当然随着现在技术的发展优化这个也变得没有那么绝对,后续会进行分享。</p><p>这块区域也是垃圾收集器管理的主要区域,现如今流行的垃圾回收器基本都采用的是分代收集算法,所以也就衍生了一些分代方式,</p><p>比如对于内存模型的划分,在 JDK1.8 以前的版本基本是这样的:</p><p><img src="F:/Study/LearningPunch/JVM/assets/1552365212876.png" alt="1552365212876"></p><ul><li>新生代<ul><li>Eden</li><li>s0</li><li>s1</li></ul></li><li>老年代</li><li>永久代</li></ul><p>在 JDK 1.8 以后的版本:</p><p><img src="F:/Study/LearningPunch/JVM/assets/1552365244555.png" alt="1552365244555"></p><ul><li>新生代</li><li>老年代</li><li>Meta Space</li></ul><p>此处小提一下,之所以在 JDK 1.8 以后 有了 Meta Space,其设计的目的在于规避永久代溢出的问题,因为 Meta Space 是可以自动扩容的,就跟 Java 中的集合一样。</p><p>以上种种的划分方式,都是为了更好地回收内存或者分配内存,从下一篇开始就开始学习内存分配及垃圾回收相关算法啦!</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li>JVM 负责软件层面的机器码翻译,可以把我们写的 .java 文件翻译成机器可以识别的机器码</li><li>JVM 负责内存管理</li><li>JVM 的运行时数据区包括方法区、堆、虚拟机栈、本地方法栈和程序计数器</li><li>JVM 中的方法区和堆区是所有线程共享的,其他区域都是线程独享的</li></ul>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> Java </tag>
<tag> JVM </tag>
</tags>
</entry>
<entry>
<title>只用自己项目中的经验来面试,估计很难通过啊</title>
<link href="/2019/03/06/zhi-yong-zi-ji-xiang-mu-zhong-de-jing-yan-lai-mian-shi-gu-ji-hen-nan-tong-guo/"/>
<url>/2019/03/06/zhi-yong-zi-ji-xiang-mu-zhong-de-jing-yan-lai-mian-shi-gu-ji-hen-nan-tong-guo/</url>
<content type="html"><![CDATA[<p>在上周,我密集面试了若干位 Java 后端的候选人,工作经验在 3 到 5 年间。我的标准其实不复杂:第一能干活,第二 Java 基础要好,第三最好熟悉些分布式框架,我相信其它公司招开发人员时,应该也照着这个标准来面的。</p><p>我也知道,不少候选人能力其实不差,但面试时没准备或不会说,这样的人可能在进团队干活后确实能达到期望,但可能就无法通过面试,但面试官总是只根据面试情况来判断。</p><p>但现实情况是,大多数人可能面试前没准备,或准备方法不得当。要知道,我们平时干活更偏重于业务,不可能大量接触到算法,数据结构,底层代码这类面试必问的问题点,换句话说,面试准备点和平时工作要点匹配度很小。</p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181125183205560-1269369262.png" alt="img"></p><p>作为面试官,我只能根据候选人的回答来决定面试结果。不过,与人方便自己方便,所以我在本文里,将通过一些常用的问题来介绍面试的准备技巧。大家在看后一定会感叹:只要方法得当,准备面试第一不难,第二用的时间也不会太多。</p><h4 id="1-框架是重点,但别让人感觉你只会山寨别人的代码"><a href="#1-框架是重点,但别让人感觉你只会山寨别人的代码" class="headerlink" title="1 框架是重点,但别让人感觉你只会山寨别人的代码"></a>1 <strong>框架是重点,但别让人感觉你只会山寨别人的代码</strong></h4><p>在面试前,我会阅读简历以查看候选人在框架方面的项目经验,在候选人的项目介绍的环节,我也会着重关注候选人最近的框架经验,目前比较热门的是 SSM。</p><p>不过,一般工作在 3 年内的候选人,大多仅仅是能“山寨”别人的代码,也就是说能在现有框架的基础上,照着别人写的流程,扩展出新的功能模块。比如要写个股票挂单的功能模块,是会模仿现有的下单流程,然后从前端到后端再到数据库,依样画葫芦写一遍,最多把功能相关的代码点改掉。</p><p> <img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181126071536784-128425691.png" alt="img"></p><p> 其实我们每个人都这样过来的,但在面试时,如果你仅仅表现出这样的能力,就和大多数人的水平差不多了,在这点就没法体现出你的优势了。</p><p>我们知道,如果单纯使用 SSM 框架,大多数项目都会有痛点。比如数据库性能差,或者业务模块比较复杂,并发量比较高,用 Spring MVC 里的 Controller 无法满足跳转的需求。所以我一般还会主动问:你除了依照现有框架写业务代码时,还做了哪些改动?</p><p>我听到的回答有:增加了 Redis 缓存,以避免频繁调用一些不变的数据。或者,在 MyBitas 的 xml 里,select 语句 where 条件有 isnull,即这个值有就增加一个 where 条件,对此,会对任何一个 where 增加一个不带 isnull 的查询条件,以免该语句当传入参数都是 null 时,做全表扫描。或者,干脆说,后端异步返回的数据量很大,时间很长,我在项目里就调大了异步返回的最大时间,或者对返回信息做了压缩处理,以增加网络传输性能。</p><p>对于这个问题,我不在乎听到什么回答,我只关心回答符不符逻辑。一般只要答对,我就会给出“在框架层面有自己的体会,有一定的了解”,否则,我就只会给出“只能在项目经理带领下编写框架代码,对框架本身了解不多”。</p><p>其实,在准备面试时,归纳框架里的要点并不难,我就不信所有人在做项目时一点积累也没,只要你说出来,可以说,这方面你就碾压了将近 7 成的竞争者。 </p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181125183410909-1886241292.png" alt="img"></p><h4 id="2-别单纯看单机版的框架,适当了解些分布式"><a href="#2-别单纯看单机版的框架,适当了解些分布式" class="headerlink" title="2 别单纯看单机版的框架,适当了解些分布式"></a><strong>2 别单纯看单机版的框架,适当了解些分布式</strong></h4><p> 此外,在描述项目里框架技术时,最好你再带些分布式的技术。下面我列些大家可以准备的分布式技术。</p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181126071640412-1267277776.png" alt="img"></p><p> 1. 反向代理方面,nginx 的基本配置,比如如何通过 lua 语言设置规则,如何设置 session 粘滞。如果可以,再看些 nginx 的底层,比如协议,集群设置,失效转移等。</p><p> 2. 远程调用dubbo方面,可以看下 dubbo 和 zookeeper 整合的知识点,再深一步,了解下 dubbo 底层的传输协议和序列化方式。</p><p> 3. 消息队列方面,可以看下kafka或任意一种组件的使用方式,简单点可以看下配置,工作组的设置,再深入点,可以看下Kafka集群,持久化的方式,以及发送消息是用长连接还是短拦截。</p><p>以上仅仅是用 3 个组件举例,大家还可以看下 Redis 缓存,日志框架,MyCAT 分库分表等。准备的方式有两大类,第一是要会说怎么用,这比较简单,能通过配置文件搭建成一个功能模块即可,第二是可以适当读些底层代码,以此了解下协议,集群和失效转移之类的高级知识点。 </p><p>如果能在面试中侃侃而谈分布式组件的底层,那么得到的评价就会比较好了,比如“深入了解框架底层”,或“框架经验丰富”,这样就算去面试架构师也行了,更何况是高级开发。</p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181125183621383-254647591.png" alt="img"></p><h4 id="3-数据库方面,别就知道增删改查,得了解性能优化"><a href="#3-数据库方面,别就知道增删改查,得了解性能优化" class="headerlink" title="3 数据库方面,别就知道增删改查,得了解性能优化"></a><strong>3 数据库方面,别就知道增删改查,得了解性能优化</strong></h4><p>在实际项目里,大多数程序员用到的可能仅仅是增删改查,当我们用 Mybatis 时,这个情况更普遍。不过如果你面试时也这样表现,估计你的能力就和其它竞争者差不多了。</p><p>这方面,你可以准备如下的技能。</p><p> 1. SQL 高级方面,比如 group by, having,左连接,子查询(带 in),行转列等高级用法。</p><p> 2. 建表方面,你可以考虑下,你项目是用三范式还是反范式,理由是什么?</p><p> 3 .尤其是优化,你可以准备下如何通过执行计划查看 SQL 语句改进点的方式,或者其它能改善 SQL 性能的方式(比如建索引等)。</p><p> 4. 如果你感觉有能力,还可以准备些 MySQL 集群,MyCAT 分库分表的技能。比如通过 LVS+Keepalived 实现MySQL 负载均衡,MyCAT 的配置方式。同样,如果可以,也看些相关的底层代码。</p><p>哪怕你在前三点表现一般,那么至少也能超越将近一般的候选人,尤其当你在 SQL 优化方面表现非常好,那么你在面试高级开发时,数据库层面一定是达标的,如果你连第四点也回答非常好,那么恭喜你,你在数据库方面的能力甚至达到了初级架构的级别。</p><p> <img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181125183919965-416889857.png" alt="img"></p><h4 id="4-Java核心方面,围绕数据结构和性能优化准备面试题"><a href="#4-Java核心方面,围绕数据结构和性能优化准备面试题" class="headerlink" title="4 Java核心方面,围绕数据结构和性能优化准备面试题"></a><strong>4 Java核心方面,围绕数据结构和性能优化准备面试题</strong></h4><p> Java 核心这块,网上的面试题很多,不过在此之外,大家还应当着重关注集合(即数据结构)和多线程并发这两块,在此基础上,大家可以准备些设计模式和虚拟机的说辞。</p><p> 下面列些我一般会问的部分问题:</p><p> 1. String a = “123”; String b = “123”; a==b 的结果是什么? 这包含了内存,String 存储方式等诸多知识点。</p><p> 2. HashMap 里的 hashCode 方法和 equal 方法什么时候需要重写?如果不重写会有什么后果?对此大家可以进一步了解 HashMap(甚至ConcurrentHashMap)的底层实现。</p><p> 3. ArrayList 和 LinkedList 底层实现有什么差别?它们各自适用于哪些场合?对此大家也可以了解下相关底层代码。</p><p> 4. volatile 关键字有什么作用?由此展开,大家可以了解下线程内存和堆内存的差别。</p><p> 5. CompletableFuture,这个是 JDK1.8 里的新特性,通过它怎么实现多线程并发控制?</p><p> 6. JVM里,new 出来的对象是在哪个区?再深入一下,问下如何查看和优化 JVM 虚拟机内存。</p><p> 7. Java的静态代理和动态代理有什么差别?最好结合底层代码来说。</p><p>通过上述的问题点,我其实不仅仅停留在“会用”级别,比如我不会问如何在 ArrayList 里放元素。大家可以看到,上述问题包含了“多线程并发”,“JVM优化”,“数据结构对象底层代码”等细节,大家也可以举一反三,通过看一些高级知识,多准备些其它类似面试题。</p><p>我们知道,目前 Java 开发是以 Web 框架为主,那么为什么还要问 Java 核心知识点呢?我这个是有切身体会的。</p><p>之前在我团队里,我见过两个人,一个是就会干活,具体表现是会用 Java 核心基本的 API,而且也没有深入了解的意愿(估计不知道该怎么深入了解),另一位平时专门会看些 Java 并发,虚拟机等的高级知识。过了半年以后,后者的能力快速升级到高级开发,由于对 JAVA 核心知识点了解很透彻,所以看一些分布式组件的底层实现没什么大问题。 而前者,一直在重复劳动,能力也只一直停留在“会干活”的层面。 </p><p>而在现实的面试中,如果不熟悉 Java 核心知识点,估计升高级开发都难,更别说是面试架构师级别的岗位了。 </p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181125184219103-1719510580.png" alt="img"></p><h4 id="5-Linux方面,至少了解如何看日志排查问题"><a href="#5-Linux方面,至少了解如何看日志排查问题" class="headerlink" title="5 Linux方面,至少了解如何看日志排查问题"></a><strong>5 Linux方面,至少了解如何看日志排查问题</strong></h4><p>如果候选人能证明自己有“排查问题”和“解决问题”的能力,这绝对是个加分项,但怎么证明?</p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181126072830196-1645619688.png" alt="img"></p><p>目前大多数的互联网项目,都是部署在 Linux 上,也就是说,日志都是在 Linux,下面归纳些实际的 Linux 操作。</p><p> 1. 能通过 less 命令打开文件,通过 Shift+G 到达文件底部,再通过 ? +关键字的方式来根据关键来搜索信息。</p><p> 2. 能通过 grep 的方式查关键字,具体用法是, grep 关键字 文件名,如果要两次在结果里查找的话,就用 grep 关键字 1 文件名 | 关键字 2 –color。最后 –color 是高亮关键字。</p><p> 3. 能通过 vi 来编辑文件。</p><p> 4. 能通过 chmod 来设置文件的权限。</p><p>当然,还有更多更实用的 Linux 命令,但在实际面试过程中,不少候选人连一条 Linux 命令也不知道。还是这句话,你哪怕知道些很基本的,也比一般人强了。 </p><h4 id="6-通读一段底层代码,作为加分项"><a href="#6-通读一段底层代码,作为加分项" class="headerlink" title="6 通读一段底层代码,作为加分项"></a><strong>6 通读一段底层代码,作为加分项</strong></h4><p>如何证明自己对一个知识点非常了解?莫过于能通过底层代码来说明。我在和不少工作经验在 5 年之内的程序员沟通时,不少人认为这很难?确实,如果要通过阅读底层代码了解分布式组件,那难度不小,但如果如下部分的底层代码,并不难懂。</p><p><img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181126072022177-387218375.png" alt="img"></p><p> 1. ArrayList,LinkedList 的底层代码里,包含着基于数组和链表的实现方式,如果大家能以此讲清楚扩容,“通过枚举器遍历“等方式,绝对能证明自己。</p><p> 2. HashMap直接对应着 Hash 表这个数据结构,在 HashMap 的底层代码里,包含着 hashCode 的 put,get 等的操作,甚至在 ConcurrentHashMap 里,还包含着 Lock 的逻辑。我相信,如果大家在面试中,看看而言ConcurrentHashMap,再结合在纸上边说边画,那一定能征服面试官。</p><p> 3. 可以看下静态代理和动态代理的实现方式,再深入一下,可以看下 Spring AOP 里的实现代码。</p><p> 4. 或许 Spirng IOC 和 MVC 的底层实现代码比较难看懂,但大家可以说些关键的类,根据关键流程说下它们的实现方式。 </p><p>其实准备的底层代码未必要多,而且也不限于在哪个方面,比如集合里基于红黑树的 TreeSet,基于 NIO 的开源框架,甚至分布式组件的 Dubbo,都可以准备。而且准备时未必要背出所有的底层(事实上很难做到),你只要能结合一些重要的类和方法,讲清楚思路即可(比如讲清楚 HashMap 如何通过 hashCode 快速定位)。</p><p>那么在面试时,如何找到个好机会说出你准备好的上述底层代码?在面试时,总会被问到集合,Spring MVC 框架等相关知识点,你在回答时,顺便说一句,“我还了解这块的底层实现”,那么面试官一定会追问,那么你就可以说出来了。</p><p>不要小看这个对候选人的帮助,一旦你讲了,只要意思到位,那么最少能得到个“肯积极专业“的评价,如果描述很清楚,那么评价就会升级到“熟悉 Java 核心技能(或 Spring MVC),且基本功扎实”。要知道,面试中,很少有人能讲清楚底层代码,所以你抛出了这个话题,哪怕最后没达到预期效果,面试官也不会由此对你降低评价。所以说,准备这块绝对是“有百利而无一害”的挣钱买卖。</p><h4 id="7-一切的一切,把上述技能嵌入到你做过的项目里"><a href="#7-一切的一切,把上述技能嵌入到你做过的项目里" class="headerlink" title="7 一切的一切,把上述技能嵌入到你做过的项目里"></a><strong>7 一切的一切,把上述技能嵌入到你做过的项目里</strong></h4><p>在面试过程中,我经常会听到一些比较遗憾的回答,比如候选人对 SQL 优化技能讲得头头是道,但最后得知,这是他平时自学时掌握的,并没用在实际项目里。</p><p>当然这总比不说要好,所以我会写下“在平时自学过 SQL 优化技能”,但如果在项目里实践过,那么我就会写下“有实际数据库 SQL 优化的技能”。大家可以对比下两者的差别,一个是偏重理论,一个是直接能干活了。其实,很多场景里,我就不信在实际项目里一定没有实践过SQL优化技能。</p><p>从这个案例中,我想告诉大家的是,你之前费了千辛万苦(其实方法方向得到,也不用费太大精力)准备的很多技能和说辞,最后应该落实到你的实际项目里。</p><p>比如你有过在 Linux 日志里查询关键字排查问题的经验,在描述时你可以带一句,在之前的项目里我就这样干的。又如,你通过看底层代码,了解了 TreeSet 和 HashSet 的差别以及它们的适用范围,那么你就可以回想下你之前做的项目,是否有个场景仅仅适用于 TreeSet?如果有,那么你就可以适当描述下项目的需求,然后说,通过读底层代码,我了解了两者的差别,而且在这个实际需求里,我就用了 TreeSet,而且我还专门做了对比性试验,发现用 TreeSet 比 HashSet 要高 xx 个百分点。</p><p>请记得,“实践经验”一定比“理论经验”值钱,而且大多数你知道的理论上的经验,一定在你的项目里用过。所以,如果你仅仅让面试官感觉你只有“理论经验”,那就太亏了。</p><p> <img src="https://img2018.cnblogs.com/blog/1226172/201811/1226172-20181126072347776-324172954.png" alt="img"></p><h4 id="8-小结:本文更多讲述的准备面试的方法"><a href="#8-小结:本文更多讲述的准备面试的方法" class="headerlink" title="8 小结:本文更多讲述的准备面试的方法"></a><strong>8 小结:本文更多讲述的准备面试的方法</strong></h4><p>本文给出的面试题并不多,但本文并没有打算给出太多的面试题。从本文里,大家更多看到的是面试官发现的诸多候选人的痛点。</p><p>本文的用意是让大家别再重蹈别人的覆辙,这还不算,本文还给出了不少准备面试的方法。你的能力或许比别人出众,但如果你准备面试的方式和别人差不多,或者就拿你在项目里干的活来说事,而没有归纳出你在项目中的亮点,那么面试官还真的会看扁你。</p><p>本文里提到的方法和技能,如果能对大家有所帮助,请大家帮忙转发,或者通过评论来参与讨论。</p>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 猿说 </tag>
<tag> 程序人生 </tag>
</tags>
</entry>
<entry>
<title>我只是一个平庸程序员</title>
<link href="/2019/03/06/wo-zhi-shi-yi-ge-ping-yong-cheng-xu-yuan/"/>
<url>/2019/03/06/wo-zhi-shi-yi-ge-ping-yong-cheng-xu-yuan/</url>
<content type="html"><![CDATA[<blockquote><p>阅读本文大概需要8分钟</p></blockquote><p>我个人认为有一些程序员就是天才,他们可以轻而易举地创造一些了不起的软件产品。因为这群天才的存在,我们对这个行业充满了期待。但是有一个悲伤的事实是:不是每一个人都是大师级的程序员。</p><p>实际上这就是我,一个平庸的程序员。</p><p>这篇文章将指导你,作为一个非天才程序员,如何在这个行业中生存。</p><h5 id="我一直用google搜索最简单的技术"><a href="#我一直用google搜索最简单的技术" class="headerlink" title="我一直用google搜索最简单的技术"></a>我一直用google搜索最简单的技术</h5><p>我记不住很多东西。比如,标准库里的函数和方法,参数的位置,依赖的包名,样板代码等等。</p><p>所以,我需要用google搜索,每天如此。我也从旧的项目里复用代码,有时也从<strong>StackOverflow</strong>或者<strong>GitHub</strong>上复制别人的代码。是的,我是一个<a href="https://link.juejin.im?target=https%3A%2F%2Fmeta.stackoverflow.com%2Fquestions%2F361904%2Fwhat-is-stack-overflow-driven-development" target="_blank" rel="noopener">面向<em>StackOverflow</em>编程</a>的程序员。</p><p>但我不是一个人在战斗,很多很多程序员都像我一样。<strong>Ruby on Rails</strong>的作者曾经发过一个很火的<a href="https://link.juejin.im?target=https%3A%2F%2Ftwitter.com%2Fdhh%2Fstatus%2F834146806594433025" target="_blank" rel="noopener">twitter</a>。</p><p><img src="https://user-gold-cdn.xitu.io/2019/3/3/1694410512edeac5?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><p>这样子写代码有什么不好呢?有如下几点坏处:</p><ul><li>你可能从别人那拷贝的,是糟糕的设计或者很烂的代码。</li><li>容易形成一个坏的心态:如果不能从网上搜索到你想到的,那么就是“休斯顿,我们遇到麻烦了”。</li><li>如果没有网了,那么你就无法工作了。</li></ul><p>但是,我并不认为这是一个大问题。它甚至可以作为你的秘密武器。我有几点建议减轻这些负面影响。</p><h5 id="生存法则-1"><a href="#生存法则-1" class="headerlink" title="生存法则 1"></a>生存法则 1</h5><ul><li>使用IDE的代码自动补全和提示,你就不用去搜索语言的基础用法了。</li><li>记住你在什么地方或使用什么方法解决了这个问题。下一次遇到同样的问题,找出来看一下就可以了。</li><li>你提交到项目中的所有代码,都应该在之后进行分析、重构和评审。这样做,就不会用糟糕的代码降低项目的质量,而是帮助它获得快速的解决方案。</li></ul><h6 id="保存事情的简单性"><a href="#保存事情的简单性" class="headerlink" title="保存事情的简单性"></a>保存事情的简单性</h6><p>我们说什么,机器做什么。有时候,机器做了错误的事情,仅仅是因为我们下了错误的指令。因此软件开发中的主要问题,不是机器,而是开发人员的思维能力。这种能力是有限的。所以,我们作为一个平庸的程序员,不要浪费脑子去创建复杂的抽象设计、编写晦涩的算法或不可读的长代码块。</p><p>然而,我们怎么区分这段代码是简单的还是复杂的?我们需要使用 WTFs/分钟 的方法去衡量代码质量。(译者注:WTF = What the Fu**)</p><p><img src="https://user-gold-cdn.xitu.io/2019/3/3/169441055d1725e0?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><p>这条规则非常简单易懂。你发现代码中有一些你看不懂的东西,那它就是复杂的。你应该怎么做?</p><ul><li>重写代码,让人看起来清晰</li><li>提供文档</li><li>在最难懂的地方添加注释。但是记住,过多的注释本身,就是代码的坏味道。(译者注:参见<a href="https://link.juejin.im?target=https%3A%2F%2Fblog.csdn.net%2FLoveLion%2Farticle%2Fdetails%2F9301691" target="_blank" rel="noopener">22种代码味道</a>)</li></ul><h5 id="生存法则-2"><a href="#生存法则-2" class="headerlink" title="生存法则 2"></a>生存法则 2</h5><ul><li>使用正确的变量名、函数名和类名</li><li>确保你代码每一部分只做一件事件</li><li>优先使用纯函数,而不是常规函数</li><li>优先使用常规函数,而不是类</li><li>只在非常必要的情况下,才使用回调</li></ul><h6 id="我不相信我自己"><a href="#我不相信我自己" class="headerlink" title="我不相信我自己"></a>我不相信我自己</h6><p>一些开发者已经证明他们能提交高质量的代码。像下面这位女神:Margaret Hamilton,阿波罗计划的首席软件工程师。这张图里,她旁边的等身高的纸,就是为登月任务编写的代码。</p><p><img src="https://user-gold-cdn.xitu.io/2019/3/3/169441055d2cab60?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><p>不过,但于我而言,无论我编写任何代码,我不相信我自己。即使是做项目里最简单的部分,我也能把事件搞得非常糟糕,可能包括:</p><ul><li>语言错误</li><li>逻辑错误</li><li>设计错误</li><li>演示错误</li><li>安全性错误</li><li>WTF错误(我最喜欢的)</li></ul><p>世界上并没有一本关于“如何编写无bug代码”的魔法师,所以这些错误都是正常的。所有的软件都有bug,处理掉它就是了。</p><p>实际上,任何人都不允许编写带有<strong>明显错误</strong>的代码。所以至少我们应该尝试做到这一点。我应该怎样保护我自己的项目呢?下面有几条建议。</p><h5 id="生存法则3"><a href="#生存法则3" class="headerlink" title="生存法则3"></a>生存法则3</h5><ul><li>编写测试用例,编写大量的测试用例。大到集成测试,小到单元测试。在每次拉取请求前执行CI持续集成,这将减少你的一些逻辑错误。</li><li>使用静态数据类型或者可选静态类型。例如,我们在python中使用mypy,在javascript中使用flow(译者注:现在应该使用Typescript)。这样做的好处是:清晰的设计和编译时类型检查。</li><li>使用自动样式检测工具。每种语言都有大量的样式检查工具。</li><li>使用质量检测工具。有些工具在你的代码库上运行一些复杂的启发式算法来检测不同的问题,比如这行内部逻辑太多,不需要这个类,这个函数太复杂。</li><li>检阅你的代码。在合并到主分支之前代码,有时候在合并之后也需要review。</li><li><a href="https://link.juejin.im?target=https%3A%2F%2Fwemake.services%2Fmeta%2Frsdp%2Faudits%2F" target="_blank" rel="noopener">花钱让别人审核你的代码</a>。这样做有相当大的好处,因为当别的程序员第一次看你的代码时,很容易看出不一致的地方和糟糕的代码设计。</li></ul><h6 id="不应该只在我的电脑上有效"><a href="#不应该只在我的电脑上有效" class="headerlink" title="不应该只在我的电脑上有效"></a>不应该只在我的电脑上有效</h6><p><img src="https://user-gold-cdn.xitu.io/2019/3/3/169441055dd0b65c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><p>差不多十年前,当我的团队开发完第一个大型软件项目时,我们将其作为java源文件发布。在我们呈现给客户前的几个小时,它在目标服务器上编译失败了。这算是个大事故。虽然最终我们修复好了并运行起来,但这是个终身难忘的经历。</p><p>这是因为在构建管道里,有着大量的配置和大量的复杂性。我们没有能力去正确管理该系统的复杂性。从那天开始,为了减少这一步的复杂性,我尝试将程序打包在独立的环境中,并在实际部署之前在此环境中进行测试。</p><p>这几年,随着<strong>docker</strong>(以及一般的容器)的兴起,这件事情开始变得简单起来。<strong>docker</strong>允许你在完全相同的独立环境下进行开发、测试和生产上线。采用这种方式,你不会遗留任何重要的事情。</p><p>不好吗?说说我自己,在搭建服务、初始化配置或者链接一些东西的时候,我总会遗漏掉一部分。因为有许多东西需要记住。幸运的是,我们仍然可以实现自动化。有许多很棒的工具可以进行自动化部署。如:<a href="https://link.juejin.im?target=https%3A%2F%2Fwww.terraform.io%2F" target="_blank" rel="noopener">terraform</a>, <a href="https://link.juejin.im?target=https%3A%2F%2Fwww.ansible.com%2F" target="_blank" rel="noopener">ansible</a>, and <a href="https://link.juejin.im?target=https%3A%2F%2Fwww.packer.io%2F" target="_blank" rel="noopener">packer</a>。查看他们的文档,找到适合你的工具。</p><p>我也尝试设置CI/CD进行持续集成和持续部署。当在测试和部署的自动化构建失败时,我会收到报告通知。</p><h5 id="生存法则4"><a href="#生存法则4" class="headerlink" title="生存法则4"></a>生存法则4</h5><ol><li>一切使用自动化部署</li><li>使用<strong>docker</strong>作为开发、测试和生产环境</li><li>使用部署工具</li></ol><h6 id="在部署应用后,我仍然不相信我自己"><a href="#在部署应用后,我仍然不相信我自己" class="headerlink" title="在部署应用后,我仍然不相信我自己"></a>在部署应用后,我仍然不相信我自己</h6><p>最后,我的应用已经在生产环境上线了,它已经在运行了。我可以打个小盹儿了,什么事儿都不会发生。等一下,不,一切都将崩溃。是的,一切。</p><p>实际上,有一些工具可以很容易的发现和修复现在问题。</p><ol><li>Sentry : 任何一个你的用户产生异常时,你都会收到通知。Sentry已经支持几乎所有的开发语言。</li><li>各式各样的<a href="https://link.juejin.im?target=https%3A%2F%2Fpapertrailapp.com%2F" target="_blank" rel="noopener">服务</a>和<a href="https://link.juejin.im?target=https%3A%2F%2Fwww.elastic.co%2Fcn%2Fproducts%2Fkibana" target="_blank" rel="noopener">工具</a>,可以将多个程序的日志收集到一个地方。</li><li>服务监控 :你可以对CPU、硬盘、网络和存储器配置监控。你甚至可以在用户实际压垮你的服务之前,确定需要进行服务扩容的时间。</li></ol><p>简单来说,我们需要在生产环境上进行监控。有的时候你需要上述所有工具,有的时候你只需要一部分。要根据自己的情况进行判断。</p><h5 id="持续学习"><a href="#持续学习" class="headerlink" title="持续学习"></a>持续学习</h5><p>哇,有好多需要学的东西。但这就是我的生存方式。如果我们想写好代码,我们就需要持续学习。成功路上没有捷径,你需要做的就是学习如何一天比一天好。</p><p>总结来说,我们需要理解两个基本原则:</p><ol><li>每个人都会遇到问题。最关键的是,我们对这些问题,准备好了吗,准备到什么程度。</li><li>我们可以把问题的根源降低到可接受的程度。</li></ol><p>这与你的思维能力或心态无关。</p><blockquote><p>英文原文链接:<a href="https://dev.to/sobolevn/i-am-a-mediocre-developer--30hn" target="_blank" rel="noopener">https://dev.to/sobolevn/i-am-a-mediocre-developer--30hn</a></p><p>译文原文链接 :<a href="https://juejin.im/post/5c7beb9af265da2d9d1cc8cb" target="_blank" rel="noopener">https://juejin.im/post/5c7beb9af265da2d9d1cc8cb</a></p><p>译 | 年糕霸霸</p></blockquote><p><img src="https://mmbiz.qpic.cn/mmbiz_png/3wiaHiab86pHHibDCf8v3BASxwT5jhJt2DFK7Zu9IVYYcTSDmUuMaz62JIWicP8l9nJ4Sib1MMf11cjGcGsSCqb0q6w/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt=""></p>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 程序人生 </tag>
</tags>
</entry>
<entry>
<title>请注意,简历上不是所有GitHub都可以加分</title>
<link href="/2019/03/06/qing-zhu-yi-jian-li-shang-bu-shi-suo-you-github-du-ke-yi-jia-fen/"/>
<url>/2019/03/06/qing-zhu-yi-jian-li-shang-bu-shi-suo-you-github-du-ke-yi-jia-fen/</url>
<content type="html"><![CDATA[<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/DUkHON5M6v2ic8A8uf3h49Iy0HVm50F9nMXEcyXckzbVibwKcb5wNHicXMHRgXJes7HSbkPIibJTibqT5uUyc4jdwfQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt="img"></p><blockquote><p>阅读文本大概需要 8 分钟。</p></blockquote><p> 本文授权转载于 公众号 果汁简历 公号ID juice_resume</p><h2 id="为什么-Github-可以加分"><a href="#为什么-Github-可以加分" class="headerlink" title="为什么 Github 可以加分"></a><strong>为什么 Github 可以加分</strong></h2><p>很多招聘描述上面都会备注 <code>github加分项</code>,那么为什么它是加分项呢? 停,如果看到这里你还不知道 <code>Github</code> 是什么,可以看一下 <code>Phodal</code>的手记 <a href="https://github.com/phodal/github" target="_blank" rel="noopener">https://github.com/phodal/github</a> (手动粘贴或者阅读原文)</p><p>社区光环,众所众知,Github 是开发者的“同性交友社区”。Google, FB, Alibaba 都在通过它来贡献自己的开源项目。如果你留心关注,公司越大 <code>Github</code>的贡献度越高,那么如果你想进入大的互联网公司,是不是也提前融入一下这个氛围?同时对于求知的小伙伴儿想阅读源码没有什么比它来的更直接了。</p><p>代码工具,基本上 <code>90%</code> 以上的公司都使用 <code>Git</code> 系工具(Gitlab,Gitee,Github)做项目源码管理,你的提交记录是最好的证明你的 <code>Git</code> 基本功,要比在简历上写熟练使用 <code>Git</code> 更有说服力。那么一个题外话,如果你也热衷于维护 <code>StackOverflow</code>账号,写上也是一种加分。</p><p>编程习惯,Talk is cheap, show me the code., 这句话想必大家都知道吧。 <code>Github</code> 上面的代码能够最直观的看到你的编程风格和能力,面试官在背调的时候可以通过你的代码结构,更深入的了解你,好的编程规范会在面试前就留下一个很好的印象。大多数面试官希望看到这种能够真正展示你能力的东西,而不是只是在简历上写会什么什么。作为面试官,大多数是来看人们的编码习惯,不管人家写这个东西是为了什么的。</p><p>技术热情,如果你钟情于购物你第一想到的可能是小红书,如果你钟情于阅读第一想到的可能“蜗牛读书”(笔者在用很不错的读书 App),如果你钟情于看新闻第一想到的可能是腾讯新闻。然而如果你钟情于技术第一想到的可能是 <code>Github</code>。只有一个钟情技术的人,才会想像朋友圈一样每天关注 Github,维护 Github。所以你“绿油油”的 Github 是你的技术热情的最好体现。你看到下图会动心吗?</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/zoa6DXqcuUibxt9jny38gApsKiawJQwGnI7FZXEZGLvKvrlTVRSufiawuYJpHW3WPNjYrbMggIfqiawOqNvYlZ53Kw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt="img"></p><blockquote><p>PS: 曾经一个技术很不错的小伙伴终面的时候挂了,面试系统中的备注是:“看不到他对技术的热情”。所以这也会作为面试的一个考量。</p></blockquote><p>英文水平,常用 <code>Github</code> 的朋友可能不觉得,但是对于普遍互联网公司的朋友们,英语都是致命的伤痛。虽然 <code>Github</code> 简单的操作英文并不是那么难,但是想把它玩透,还是需要一些功底的。如果你没用那么好的英语底子也能玩透,无论是你的英文熟悉能力还是适应能力都是可以肯定的。对了,还有你的 <code>repository</code> 的命名。这不比写上四六级证书来的实际?</p><blockquote><p>PS: 我们其实也都知道,当年的四六级是怎么就过了。[坏笑]</p></blockquote><p>涉猎领域,你的圈子决定了你的水平,一样的道理。你的 <code>Github</code>的 <code>follow</code>, <code>star</code>, <code>followers</code>, <code>repository</code>, <code>pull request</code> 还有你获得 <code>star</code>,都是在展示你的涉猎,关注和掌握程度。你的一个 <code>ElasticSearch</code> 的 <code>PullRequest</code> 被 <code>Merge</code> 远比你的一句“精通 ElasticSearch”要更有说服力。</p><p>情投意合,村子里面的阿姨每天就聊聊哪个市场的白菜最新鲜,那么你如果有好的 star 或者好的开源贡献,这会让面试官在面试你的时候有东西可聊。如果他喜欢上你的 <code>repository</code>而侃侃而谈,那么结果会怎么样呢?</p><p>开源贡献,开源贡献不一定非要有一个几K的star,而是你对于开源的那份热忱。可以是好的资源整理,也可以是自己找到了痛点做的一个很小的工具,或者是使用别人的工具提的 <code>pull request</code>。都是你对开源的那一份热忱。你从开源一直索取,适当的给予更能体现一个人的心智。</p><p>与众不同,虽然上面说了 <code>Github</code> 的好处多多,但是国内的技术圈子能持续坚持 <code>Github</code>的人还是太少了,所以变成了如果你有一个不错的 <code>Github</code>写到简历上面也算是一道风景线了。</p><blockquote><p>PS: 笔者每天阅读近百份简历,能有不错 <code>Github</code> 的人寥寥无几。</p></blockquote><h2 id="为什么没有亮点反而减分"><a href="#为什么没有亮点反而减分" class="headerlink" title="为什么没有亮点反而减分"></a><strong>为什么没有亮点反而减分</strong></h2><p>僵尸粉,一进来 Github 主页只有几个空项目,名字很奇怪也没有 star,点击进去杂乱的项目目录,提交记录和中文提交备注。那么这样的 Github 不仅没有提分的亮点,而且还会大大折扣面试官的印象。</p><p>零参与,别人都是“绿油油”的草地,你这是撒哈拉沙漠,那么一点也体现不出来你对技术的热情,持续的学习。</p><p>短而小,如果都是 <code>HelloWorld</code> 一样的 <code>demo</code> 是完全没有说服力的。</p><p>乱投机,自己没有贡献代码,只是因为提交了 <code>issue</code>可以把别人的 <code>repository</code> 固定到自己的 <code>GithubProfile</code> 首页。这样不但不能加分反而减分。这样就好比随意粘贴来别人辛苦写的公众号文章作为自己的原创一样。</p><p>乱命名,这几天在收到的简历里面发现了好多github命名方式千奇百怪,比如 X00000000X,LZ00000000,ZUOYE20170909。这可是你的用户名,一来方便别人访问和记住,二来你这个命名都这么不规范,何况是项目,类,方法呢?</p><p>不专业,切记,想提高自己也好,想开源小东西也好,切记不能再上班时间整理自己的代码,如果面试官一看你的所有提交记录都是周一到周五的上班时间,那么你想他还会聘请你吗?我的意思不是说让你工作日写完,周末去提交,而是 Github 是体现你业余时间的一个贡献度。</p><h2 id="怎么样让自己的-Github-加分"><a href="#怎么样让自己的-Github-加分" class="headerlink" title="怎么样让自己的 Github 加分"></a><strong>怎么样让自己的 Github 加分</strong></h2><p>习惯,良好的使用习惯,就像是每天来看朋友圈一样,不一定每天都有东西要提交,但是一定要一直有一个 <code>repository</code> 在维护,持续的提交代码。同时也要注意自己的 <code>repository</code>的命名,提交代码保证一次的功能完备,提交备注要清晰明了。</p><p>持久,可以试着翻译外文,写博客,或者是自己做一个有意思的小程序,每天坚持编写代码,提交代码。但是一定要注意每次提交的功能完整性和备注。</p><p>贡献,很多方式可以贡献开源社区,有一个话说的非常好,我们做程序不是总知道提出问题,而是提出问题并有解决方案。那么你可以提交 <code>issue</code>给你觉得有问题的软件,如果没有理睬自己修改,然后提交 <code>pull request</code>。或者自己做一些脚手架,多少有一些实际的功能,当然我们不希望重复制造轮子,你在创造的时候还是提前去检索一番。不过这个时候你需要了解一下开源协议,合理的使用别人的源码和开源自己的源码。</p><blockquote><p>PS: 阮一峰老师的文章写得深入浅出(手动粘贴或者阅读原文) <a href="http://www.ruanyifeng.com/blog/2011/05/howtochoosefreesoftware_licenses.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2011/05/howtochoosefreesoftware_licenses.html</a></p></blockquote><p>文档,开发人员处理编码,更重要的是能写出完备的文档,所以如果你开源了项目,一定要详细的写出说明问题。这不仅体现出你的编写文档能力,还能体现你的逻辑能力。</p><p>关注,原来你可能使用 Google 来解决问题,告诉你一个新方法,遇到问题可以按照自己提炼的关键字来 Github 搜索,没准能找到意想不到的结果。而后呢 Fork 或者 Star 这个你得到的结果,慢慢的你就真正的把 Github 用起来了。</p><p>英文,试着不要给 <code>repository</code>起拼音名字,试着用英文写 <code>readme</code>,试着每一个提交都是英文,并每次都要斟酌。</p><p>博客,一个 <code>GithubPages</code> 的博客对于有技术热忱的极客来说,比每天在 CSDN 写博客要专业很多。可以没有自己的域名,自带的域名也可以带飞。不过这里需要补充一点,不是一拥有了一个博客就加分,而是你是不是在持续的维护这个博客。 PS: juice-resume.github.io 看起来是不是有点极客感?</p><p>简历,写一份 <code>Markdown</code> 的简历也是一个不错的选择,极客一般都喜欢使用 <code>Markdown</code> 写文章,写文档,有的时候就连周报也用。那么你写一个 <code>Markdown</code> 的简历也是一个亮点诶。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/3wiaHiab86pHHibDCf8v3BASxwT5jhJt2DFK7Zu9IVYYcTSDmUuMaz62JIWicP8l9nJ4Sib1MMf11cjGcGsSCqb0q6w/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt=""></p>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 简历 </tag>
<tag> 面试 </tag>
</tags>
</entry>
<entry>
<title>你觉得过年是什么样的?</title>
<link href="/2019/02/01/ni-jue-de-guo-nian-shi-shi-me-yang-de/"/>
<url>/2019/02/01/ni-jue-de-guo-nian-shi-shi-me-yang-de/</url>
<content type="html"><![CDATA[<blockquote><p>所有过去的都未曾过去,所有失去的都未曾失去,它都一件不落的留在了我们的生命里,变成了我们人生长河中璀璨的星辰,永远熠熠生辉。 </p></blockquote><p>提起春节,对于一个有这10几亿人口的国家来说,有一个事情不得不提,那就是春运!</p><p>以前看书知道,每年6月左右,非洲坦桑尼亚大草原到了旱季,青草和食物减少,为了追赶青草和水源,数百万只角马开始了3000多公里的长途跋涉,上演地球上最壮观的动物大迁徙。途中不仅要穿越狮子、猎豹埋伏的草原,还要提防随时有可能出没的豺狗以及在狭窄的马拉河两畔聚集的尼罗鳄。虽一路惊险,但仍义无反顾,年复一年。</p><p>每到春运时节我都会不自然地想到那遥远的非洲迁徙,两者有太多的相似,同样的规模庞大、同样的过程艰辛、同样的脚步坚定、同样的周而复始。草原的动物为了追逐食物、为了生存,而我们追逐的又是什么?</p><p>依稀还记得儿时的春节,是满怀期待的,因为除夕的饭菜最丰盛,各种形状的饺子… …饺子熟了上桌之后,我们是不能先吃的,必须先听父亲讲话:“这一年,我们家里的收获是……来年我们的目标是……你们这一年的收获是什么,哪些地方做得不好,来年怎么做……”父亲说他的话,我们的眼睛只顾盯着平日里难得见到的饺子流口水。</p><p>“我今年评上了三好学生,期末考了双百,不足之处就是我的小白死了,明年我再养一只……”我的眼里湿湿的,我忍住了,我知道过年是不能掉眼泪的。</p><p>父亲抚摸了我的头:“孩子,不错,给老爸争气了,明天继续努力啊!至于小白兔,明年再养一只,爸爸不杀它。”</p><p>“嗯嗯”我只顾应声,他每个除夕都要说的话我都背了下来。</p><p>窗外的爆竹像炸了锅一样响了起来,湮没父亲的训话,也要赶走了我的痛。</p><p>为了吃到包饺子的时候母亲放的钢镚儿,我们吃得肚子溜圆,看着每一个饺子都像装上了钢镚儿,结果又被它们的表象所欺骗,但寻找目标的快乐也是无比的。</p><p>剩下的饺子放在了锅里,母亲说来年一定会越来越好。</p><p>洗脚水端了上来,除夕夜必须每人都要洗一洗脚,用父亲的话说:“过年这天晚上洗脚脚最长,无论什么好事都能赶得上。”</p><p>……</p><p><strong>时至今日,儿时已经走远,消失在了生命的另一个国度里。那些过时发生的那些事,刀一样刻在了心上,历经岁月的打磨,愈发璀璨晶莹。</strong></p><p>现在只觉得回家过年一方面是为了那一份心底的乡情,为了看望思念已久的父母,给自己一点安慰。也是为了给家人报一声平安,告诉他们自己长漂在外一切安好。</p><p>同样,回家过年对有些人是一种纠结。我们总想向家人朋友展现体面的自己,小心翼翼地藏起生活的艰辛,而实际却要面对关心追问的烦恼。回家过年,见到了父母,却又自责努力的脚步太慢,父母老去的速度经不起等待。</p><p>想起电影《后来的我们》中的一个片断,男女主角是一对恋人,是挣扎打拼的北漂一族。一次过年回家,为了显得体面,租了汽车,又买了一车礼物,花光了全年工资。在家乡小镇的同学朋友眼里,在大北京工作的那是有头有脸有钱、见过世面的人物。为了不在朋友面前丟面子,男主角豪爽地请吃请喝请唱K,俨然成了同学们眼中的大款,付款时卡内余额不足被当场揭穿,如今是公务员的小学同学最后付了账单,男主人公用大醉掩盖尴尬,结束了饭局。回到家把情绪全渲泄给年迈的父亲,最后留给父亲一句话“以后再也不回这个家了”。</p><p>这是一部好电影,因为它太过真实。每个人都有虚荣心,都有自尊心,当被戳穿的那一刻,剩下的只有心酸。电影中有没有看到自己回家过年的影子和心情?</p><p><strong>过年,不识愁滋味的孩子们看到的都是快乐。</strong></p><p><strong>这快乐,关乎得到,关乎失去,关乎不同的人生体验。</strong></p><p><strong>年关已近,放下虚荣,放下负担,赶快订票,只为一个原因:父母在家等你。</strong></p>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 猿说 </tag>
</tags>
</entry>
<entry>
<title>集体宿舍,将3000万大学生拉入深渊</title>
<link href="/2019/02/01/ji-ti-su-she-jiang-3000-wan-da-xue-sheng-la-ru-shen-yuan/"/>
<url>/2019/02/01/ji-ti-su-she-jiang-3000-wan-da-xue-sheng-la-ru-shen-yuan/</url>
<content type="html"><![CDATA[<p><img src="http://images.zhoudl.top/%E5%B1%B1%E4%B8%9C%E7%9C%81%E5%9C%B0%E6%96%B9.png" alt=""></p><h3 id="1-小明的故事"><a href="#1-小明的故事" class="headerlink" title="1 小明的故事"></a>1 小明的故事</h3><p>毁掉一个人最好的两种方法,<strong>一是强迫他做毫无意义的重复性工作,另一种是让他过集体主义生活。</strong></p><p>小明从高中开始就将这句话奉为真理,虽然他不知道这是出自哪位夫斯基的名言。他受够了整整九年的集体宿舍,憋着一股劲要考上大学,就是想摆脱这种困境,过上独居生活。</p><p>上大学后,他才发现自己住在了12人间的宿舍里,而且宿舍楼是一栋上世纪50年代苏联援建的文物。</p><p>空厕浴阳是铁定没有了,12个人挤在6张架子床上,过道中间摆着简陋的长桌子,丝毫挡不住天南地北的舍友齐聚一堂,享受着文化融洽的欢喜。</p><p>不能忍也只能忍着,就当做是提前体验就业的艰辛了,小明的内心逐渐意识到,毕业后,这种集体的生活状态将会继续上演。</p><p>就这样,在这种宿舍环境下生活了几年,有几大未解之谜让他困惑不已:</p><p><strong>下铺的床为何总在摇动? 空气中为何总有沁人心脾的脚臭气味? 桌上的零食为何空空如也? 衣柜里的内裤为何离奇失窃? 深夜连环打鼾声,究竟是何人所为? 宿舍的门经常忘锁,究竟是人是鬼? 厕所门长期紧闭,背后又隐藏着什么?</strong></p><p>这些事情,小明都能忍,但他不能忍的一点是:<strong>舍友的烟瘾</strong>。几个人在宿舍人凑在一起吞云吐雾,使得小明长期笼罩在烟雾弥漫中,每天被呛得咳嗽不停。</p><p>既然劝不了,小明只能默默忍受着二手烟,并时刻担心自己的健康状况。有一天,他发现自己胸口很闷,呼吸不畅,癌症的念头突然涌上心头,内心的恐慌令他如临末日。</p><p>去了医院检查后,他颤巍巍地接过检查报告,才发现自己的肺很健康,但却得了咽炎。</p><p>他垂头丧气地回到宿舍,看着舍友嘻嘻哈哈地边抽烟边玩游戏,心里就一阵悲凉。</p><p>他依靠在窗边,像极了王小波话里的场景:</p><p>“<strong>傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。</strong>”</p><h3 id="2-小天的故事"><a href="#2-小天的故事" class="headerlink" title="2 小天的故事"></a>2 小天的故事</h3><p>小天的宿舍有着异曲同工之妙,舍友是一群游戏迷,每晚都在熬夜打游戏,和死神一起赛跑。</p><p>有的人玩游戏还会外放电音,吵闹之极,让小天的心情格外烦躁,买来的耳塞已经用完一整盒了。</p><p>他不明白学校口口声声说要建设高标准一流大学,每年花重资挖教授建新校区,却连学生宿舍的基本修缮都难以达到,更别指望在铁板烧宿舍能享受空调的待遇了。</p><p>他也不明白为何在考试的前一晚,舍友的精力会那么旺盛,丝毫不介意挂科的危险,更不明白为何监考老师会睁眼闭眼,让这群学渣堂而皇之地抄袭试卷。</p><p>小天和舍友没有什么共同话题,彼此之间陌如路人。他去看心理老师,挤出了内心话:“<strong>我很想搬离宿舍,真适应不了集体宿舍,太难受了</strong>。”</p><p>心理老师抚慰一笑,她遇到太多这类学生,早已总结出一套无懈可击的措辞来引导:</p><p><strong>小天啊,有时候呢,要反思自己,为何融入不进集体生活呢?现在你还年轻,对宿舍条件要求不能太高,要多想想,为什么别人都能适应,而你不行呢?</strong></p><p>是啊,为什么自己不行呢?小天浑浑噩噩地从心理室走出来,强制说服自己要勇于合群,一切的心理问题只是自己的矫情和性格缺陷所作怪,脱离集体就是在张扬个性,要不得。</p><p><strong>这种论调就好像劝和不劝离一样,面临家暴的威胁,大家只会让女方忍忍,迁就一下,一切都会过去的。</strong></p><p>等小天回到宿舍,发现隔壁宿舍发生了口角,辅导员在找学生谈话。</p><p>“<strong>这点小事情就别烦我了,你们私下解决,互相都包容一点。</strong>”辅导员显得很不耐烦,转身就走。有个男生低声说了一句话:“<strong>当年马加爵也是私下解决的。</strong>”</p><p>这句话直接惊动了校领导,找男生进行深入地谈话。</p><p><strong>当类似“为什么别人就能适应而你就不行”的话甚嚣尘上时,社会却看不到大学生的心理问题日益严重。</strong></p><h3 id="3-小美的故事"><a href="#3-小美的故事" class="headerlink" title="3 小美的故事"></a>3 小美的故事</h3><p>曾有一个段子说,<strong>一个女生宿舍会核裂变为十几个微信群</strong>。小美的宿舍没那么夸张,8人同住,只建了三个群,分别是8人群,7人群和3人群。</p><p>小美有两个同吃饭、同上课和同玩耍的舍友,三人组成一个群。尽管表面上相处得不错,但是小美信不过大学的友情,仅把她俩当成普通朋友,难以交心。3人会在群里经常聊各种八卦趣闻,也会讨论某个舍友的是非。</p><p>8人群也称为宿舍群,除了某个舍友过生日外,平时基本没人聊天。而宿舍还有一个7人群,一致将同宿舍的A排除在外,并在群里评论A的是非。</p><p>因为A整天在外面胡混,晚上泡吧总是夜不归宿,也不通知大家一声,导致宿舍门不敢锁上。</p><p>不锁门怕小偷进来;锁门了,半夜又会被吵醒去开门,大家为此怨声载道,A也不思悔改。</p><p>有一次,A不经舍友同意就将男朋友带回宿舍过夜。宿舍的人非常生气,小美更是一气之下把男生赶出宿舍,因此也跟A闹翻了。</p><p>自那次翻脸以后,宿舍的饮水机被闲置起来,再也没有人敢喝里面的水。</p><h3 id="4-小刚的故事"><a href="#4-小刚的故事" class="headerlink" title="4 小刚的故事"></a>4 小刚的故事</h3><p>马基雅维利说过:“<strong>如果将兵法引入生活,无疑将生活引入地狱</strong>”。</p><p>小刚的宿舍为10人间,一切词汇都不足以形容他宿舍的破烂。但他从小在农村长大,特别能吃苦,对恶劣环境的忍耐力很强,唯独不能融入集体的生活。</p><p>在这个宿舍,有他无话不谈的朋友,也有一起打球的伙伴,相处得很好。但10个人挤在十几平方米的狭窄空间,即使相处得再好,也会有矛盾。</p><p>尽管小刚在刻意避免,也无济于事,他算是明白了,其实<strong>大家都不坏,就是合不到一起,性格上以及地域的差异,很多时候是难以融合的</strong>。</p><p>小刚想找舍管换宿舍,不批准,找辅导员也不行。后来他直接找到校领导,也被拒绝了。领导觉得他不能吃苦,要学会适应,融入集体社会。</p><p>小刚当场反驳:<strong>冒昧地说,换做是当年,其实你们也吃不了苦</strong>。</p><p>领导咳嗽一声,缓缓说道:<strong>自古人们就提倡男子汉四海为家,大丈夫志在四方。现在你连宿舍环境都不能忍耐,以后还怎么在社会活下去?</strong></p><p><strong>为何君子要慎独?当你独居就知道了,人的本性是贪婪和懒惰,独居会剥离一个人的社会属性,让人沉浸在舒适感中,难以培养好的习惯。</strong></p><p><strong>在集体宿舍里,大家互相鼓励,督促你奋斗、上进和坚持。你再看看那些宅男,整天只会躺在家里没有了上进心,那怎么行呢?</strong></p><p>小刚哑口无言,总觉得哪里不太对劲,却不知道如何反驳。他看了看办公室的优良环境,突然顿悟了。</p><p>当处于社会地位顶端,并掌握着核心资源和话语权的人,一旦站在道德制高点上去倡导吃苦耐劳,那根本毫无意义。</p><p>除了点头示意,小刚别无他法。</p><h3 id="5-阿杰的故事"><a href="#5-阿杰的故事" class="headerlink" title="5 阿杰的故事"></a>5 阿杰的故事</h3><p>阿杰每天从床上醒来,第一件事是将床上掉落的石灰扫走。他每晚睡觉已经很小心了,但是身体依然刮到墙壁,导致许多灰掉落下来。</p><p>宿舍环境一如既往地差,老鼠蟑螂横行,宿舍也被经常偷东西,他已经不想分辨到底是外人还是舍友所干的了。</p><p>此时此刻,他只想找个人好好聊天,将内心的郁闷诉说出来。在学校里,阿杰有一位华裔留学生朋友,他本想将内心的烦恼诉说出来,得到这位朋友的认可,哪知对方开始了长篇大论:</p><p><strong>中国目前现状如此,每个高校都在扩招,导致住宿资源紧张。如果每个人都想匹配单人宿舍,需要多少无谓的资源?而且,学校的宿舍价格明显比外面租房便宜多了,就别得寸进尺。</strong></p><p><strong>学校宿舍最起码能看到阳光,你去看看北京的地下室,那些香港的棺材房,哪个不辛苦?</strong></p><p>阿杰看着远处那栋陈旧不堪的宿舍楼,叹了一声:“<strong>所以高校扩招,要我们学生来背锅?</strong>”</p><p>“<strong>阿杰,要永远觉得祖国的土地是稳固地在你脚下,要与集体一起生活,要记住,是集体教育了你。那一天你若和集体脱离,那便是末路的开始,这是奥斯特洛夫斯基的名言!</strong>”</p><p>阿杰直接反驳他的名言:“<strong>可是《死屋手记》也说了,苦役生活中,除了失掉自由、强迫劳动以外,还有一种痛苦要比其他一切痛苦都更加强烈,这就是:被迫过集体生活。</strong>”</p><p>华裔留学生被阿杰的话呛到了,他皱眉道:“<strong>你要真有钱就去租房,这里没人拦着你。学校是学习的地方,是提升能力的场所,而不是生活的地方。</strong>”</p><p>话语刚落,留学生转身回到自己的公寓,留下他怔在原地。阿杰忽然想起,学校给每个留学生都配了单人间,听说还带有客厅、电视以及空调等设施。</p><h3 id="6-小方的故事"><a href="#6-小方的故事" class="headerlink" title="6 小方的故事"></a>6 小方的故事</h3><p>环境心理学曾得出一个结论,<strong>大学宿舍如果缺乏私密的空间,很容易产生心理问题</strong>。</p><p>小方知道自己的心理肯定出问题了,他上知乎一搜类似的问题,竟然找到许多深受其扰的答主。</p><p>他不喜欢听到“<strong>敌军还有三十秒到达战场</strong>”的声音,也不想每次出宿舍门都被当成外卖小哥,更不愿意听到下铺兄弟的各种感情纠纷。</p><p>他不想看电影或者看书的时候,总有人凑过头来打断自己的思路;也不想强制性合群,一定要和舍友同上课同吃饭;有时候舍友睡懒觉,还要帮舍友签到,为此小方还练就了左右手互换的绝招,并学会在几个椅子上腾挪,接受老师迷之异样的眼神。</p><p>他觉得这样的生活真累,他没办法接受这样的生活。每个人都有不同的习惯差异,他不想试图合群,只有距离才能避免学生之间的矛盾摩擦。</p><p><strong>最可怕的是同化,整个宿舍的氛围正在慢慢趋同,一人懒散,整个宿舍就集体睡懒觉;一人打游戏就变成整个宿舍一起开黑。</strong>最近小方的舍友还买了遮光窗帘,黑漆漆的环境让大家更适合睡眠了。</p><p>有一次,小方去别的学校,真切体会到学霸宿舍的差距:<strong>别的宿舍集体鼓励考研,而自己的宿舍却集体去挂科重修。</strong></p><p>太多长辈教育小方要去适应大学生活,并被指责“<strong>为什么别人生活得好,只有你这么矫情</strong>”等等,让他一度怀疑是自我的要求太高。</p><p>后来他觉得错不在自己,因为<strong>独居并不是屏蔽社交</strong>,但集体宿舍却要求24小时强迫性交流,强迫被影响,这是个性被剥夺的体现。</p><p><strong>所谓的集体生活,错不在大学生,只不过是对教育资源稀缺的妥协,让他们更早地学会如何低头忍受,如何去奉承世俗罢了。</strong></p><p>最重要的是,小方在心里呐喊出来:<strong>睡觉的地方怎么就成了社交场所啊!</strong></p><blockquote><p>作者:智_先生</p><p>链接:<a href="https://www.jianshu.com/p/b37597bf0086" target="_blank" rel="noopener">https://www.jianshu.com/p/b37597bf0086</a></p></blockquote>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 程序员成长 </tag>
</tags>
</entry>
<entry>
<title>高性能MySQL读书笔记分享</title>
<link href="/2019/02/01/gao-xing-neng-mysql-du-shu-bi-ji-fen-xiang/"/>
<url>/2019/02/01/gao-xing-neng-mysql-du-shu-bi-ji-fen-xiang/</url>
<content type="html"><![CDATA[<h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><blockquote><p>很开心终于更新完了MySQL进阶学习系列文章,其中有原创内容,也有转载你内容,可能有人会问,为什么不全部原创?其实我是写好原自己的文章了(同步更新到了我的博客),但是通过找一些资料,发现一些文章确实要比我写的内容好上很多,为了在公众号呈现更好的内容给大家,也就取其精华去其糟粕,不重复造轮子,所以有些我不擅长,掌握的不好的方面选择了文章转载,例如最后一篇就是阿飞大神所写 <img src="https://res.wx.qq.com/mpres/htmledition/images/icon/common/emotion_panel/smiley/smiley_79.png" alt="img"></p></blockquote><p>为了大家更方便的观看,在本文对MySQL系列文章做一个梳理:</p><h3 id="读书笔记汇总"><a href="#读书笔记汇总" class="headerlink" title="读书笔记汇总"></a>读书笔记汇总</h3><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484107&idx=1&sn=6788f758401294ade9f6cf1efa097080&chksm=e9ccdc87debb5591e6f43cb922a49d6badaa9271e9a79250d6730acd9214b81788f8026134ec&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(一)| 开发人心里都该有的那颗B树</a><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484107&idx=1&sn=6788f758401294ade9f6cf1efa097080&chksm=e9ccdc87debb5591e6f43cb922a49d6badaa9271e9a79250d6730acd9214b81788f8026134ec&scene=21#wechat_redirect" target="_blank" rel="noopener">】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484112&idx=1&sn=0dc02de1ef7da83f5b717cb811273fa2&chksm=e9ccdc9cdebb558addb8b8f94b2d5e775650e8bc529a9ca2b8a3315b56e26b11d5c99bc47eb6&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(二)| MySQL索引机制】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484138&idx=1&sn=77f222160f8f26cbb868eb7a4547e7f1&chksm=e9ccdca6debb55b0e7e565aa2b69ca6b05e5f965f84a0317fa6c26191a229c00aa03f1757228&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(三)| 五分钟搞清楚MySQL事务隔离级别】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484201&idx=1&sn=2212704ce7a901d972d44b34b5a653bd&chksm=e9ccdd65debb54732caa0ff29759f3f380bd7b0bad09f6a42c50fdd12b78dfc6bf099200588a&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(四)| 五分钟搞清楚InnoDB锁机制】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484256&idx=1&sn=ad21e6bd2a60495424c7f57d5a6d0af2&chksm=e9ccdd2cdebb543a5bd01b44491aeb2b24f3e00a9d10c77104450a6a9cfa9633c44e7acf357c&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(五)| 五分钟搞清楚MVCC机制】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484267&idx=1&sn=553aeb84efab031a50b7d778367a5a7e&chksm=e9ccdd27debb5431621f7d7e9ecbf788b3c65c73916d62555992f5d4e961e6e9b1d32bfadce1&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(六)| 详细分析MySQL事务日志redo log】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484273&idx=1&sn=223e6018e7158876feb4fcc998fd2c81&chksm=e9ccdd3ddebb542bbc0774f8f4acbd377a908219bc458fa8775999ce2692a93c746b57691940&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(七)| 详细分析MySQL事务日志undo log】</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzI1Mzg4OTMxNQ==&mid=2247484277&idx=1&sn=7ada50a204bdb5e85700f6f6b77d5cc6&chksm=e9ccdd39debb542fce91885b917ef5b887ef6f1f0caeab64202a8b55b1b83cdce257d07d4e27&scene=21#wechat_redirect" target="_blank" rel="noopener">【MySQL(八)| 深入InnoDB空间及索引页文件结构】</a></p><h3 id="公众号介绍"><a href="#公众号介绍" class="headerlink" title="公众号介绍"></a>公众号介绍</h3><p>欢迎大家关注我的个人公众号,期待着你的到来,我们共同进步!</p><p><img src="https://img-blog.csdn.net/20180925183656761?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pCeWxhbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="个人公众号"></p>]]></content>
<categories>
<category> MySQL </category>
</categories>
<tags>
<tag> MySQL </tag>
<tag> 读书笔记 </tag>
<tag> 数据库优化 </tag>
</tags>
</entry>
<entry>
<title>利用Redis实现分布式锁</title>
<link href="/2019/02/01/li-yong-redis-shi-xian-fen-bu-shi-suo/"/>
<url>/2019/02/01/li-yong-redis-shi-xian-fen-bu-shi-suo/</url>
<content type="html"><![CDATA[<p>作者:燕少༒江湖<br>原文:<a href="https://blog.csdn.net/qq_31289187/article/details/84399880" target="_blank" rel="noopener">https://blog.csdn.net/qq_31289187/article/details/84399880</a> </p><h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><p>大多数互联网系统都是分布式部署的,分布式部署确实能带来性能和效率上的提升,但为此,我们就需要多解决一个分布式环境下,数据一致性的问题。</p><p>当某个资源在多系统之间,具有共享性的时候,为了保证大家访问这个资源数据是一致的,那么就必须要求在同一时刻只能被一个客户端处理,不能并发的执行,否者就会出现同一时刻有人写有人读,大家访问到的数据就不一致了。</p><h3 id="我们为什么需要分布式锁?"><a href="#我们为什么需要分布式锁?" class="headerlink" title="我们为什么需要分布式锁?"></a>我们为什么需要分布式锁?</h3><p>在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行加锁,当使用完资源之后,再解锁,其它线程就可以接着使用了。例如,在JAVA中,甚至专门提供了一些处理锁机制的一些API(synchronize/Lock等)。</p><p>但是到了分布式系统的时代,这种线程之间的锁机制,就没作用了,系统可能会有多份并且部署在不同的机器上,这些资源已经不是在线程之间共享了,而是属于进程之间共享的资源。</p><p>因此,为了解决这个问题,我们就必须引入「分布式锁」。</p><p>分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问。</p><p>分布式锁要满足哪些要求呢?</p><ul><li>排他性:在同一时间只会有一个客户端能获取到锁,其它客户端无法同时获取</li><li>避免死锁:这把锁在一段有限的时间之后,一定会被释放(正常释放或异常释放)</li><li>高可用:获取或释放锁的机制必须高可用且性能佳</li></ul><p>讲完了背景和理论,那我们接下来再看一下分布式锁的具体分类和实际运用。</p><h3 id="分布式锁的实现方式有哪些?"><a href="#分布式锁的实现方式有哪些?" class="headerlink" title="分布式锁的实现方式有哪些?"></a>分布式锁的实现方式有哪些?</h3><p>目前主流的有三种,从实现的复杂度上来看,从上往下难度依次增加:</p><ul><li>基于数据库实现</li><li>基于<code>Redis</code>实现</li><li>基于<code>ZooKeeper</code>实现</li></ul><p>无论哪种方式,其实都不完美,依旧要根据咱们业务的实际场景来选择。</p><p>接下来着重介绍下基于redis的分布式锁实现</p><ol><li>分布锁一般通过<code>redis</code>实现,主要通过<code>setnx</code>函数向<code>redis</code>保存一个<code>key,value</code>等于保存时的时间戳,并设置过期时间,然后返回<code>true</code>;</li><li>当获得锁超过等待时间返回<code>false</code>;</li><li>通过<code>key</code>获取<code>redis</code>保存的时间戳,如果<code>value</code>不为空,并且当前时间戳减去<code>value</code>值超过锁过期时间返回<code>false</code>;</li><li>如果一次没有获得锁,则每隔一定时间(<code>10ms</code>或者<code>20ms</code>)再获取一次,直到超过等待时间返回<code>false</code>。</li></ol><h3 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h3><p>所需<code>jar</code>包如下:</p><pre class=" language-xml"><code class="language-xml"><span class="token prolog"><?xml version="1.0" encoding="UTF-8"?></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>project</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://maven.apache.org/POM/4.0.0<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>xsi</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/XMLSchema-instance<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xsi:</span>schemaLocation</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>modelVersion</span><span class="token punctuation">></span></span>4.0.0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>modelVersion</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>com.cn.dl<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>springboot-aop-test<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>0.0.1-SNAPSHOT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>packaging</span><span class="token punctuation">></span></span>jar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>packaging</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>name</span><span class="token punctuation">></span></span>springboot-aop-test<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>name</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>description</span><span class="token punctuation">></span></span>Demo project for Spring Boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>description</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>parent</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-parent<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>2.1.0.RELEASE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>relativePath</span><span class="token punctuation">/></span></span> <span class="token comment" spellcheck="true"><!-- lookup parent from repository --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>parent</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>properties</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>project.build.sourceEncoding</span><span class="token punctuation">></span></span>UTF-8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>project.build.sourceEncoding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>project.reporting.outputEncoding</span><span class="token punctuation">></span></span>UTF-8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>project.reporting.outputEncoding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>java.version</span><span class="token punctuation">></span></span>1.8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>java.version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>properties</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependencies</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-web<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-test<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>scope</span><span class="token punctuation">></span></span>test<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>scope</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-aop<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-expression<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>5.0.0.RELEASE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-data-redis<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.projectlombok<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>lombok<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>commons-lang<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>commons-lang<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>2.6<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>com.alibaba<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>fastjson<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>1.2.47<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependencies</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>build</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-maven-plugin<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>build</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>project</span><span class="token punctuation">></span></span></code></pre><p><code>RedisLockTestAnnotation</code></p><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>annotation<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>ElementType<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Retention<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RetentionPolicy<span class="token punctuation">;</span><span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Target<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by yanshao on 2018/11/23. */</span><span class="token annotation punctuation">@Target</span><span class="token punctuation">(</span>ElementType<span class="token punctuation">.</span>METHOD<span class="token punctuation">)</span><span class="token annotation punctuation">@Retention</span><span class="token punctuation">(</span>RetentionPolicy<span class="token punctuation">.</span>RUNTIME<span class="token punctuation">)</span><span class="token keyword">public</span> @<span class="token keyword">interface</span> <span class="token class-name">RedisLockTestAnnotation</span> <span class="token punctuation">{</span> String <span class="token function">redisKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><p><code>RedisLockTestAspectUtils</code></p><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>utils<span class="token punctuation">;</span><span class="token keyword">import</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RedisLockTestAnnotation<span class="token punctuation">;</span><span class="token keyword">import</span> lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span>Slf4j<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>ProceedingJoinPoint<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Around<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Aspect<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Component<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by yanshao on 2018/11/23. */</span><span class="token annotation punctuation">@Aspect</span><span class="token annotation punctuation">@Component</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RedisLockTestAspectUtils</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Autowired</span> RedisLock redisLockUtil<span class="token punctuation">;</span> <span class="token annotation punctuation">@Around</span><span class="token punctuation">(</span><span class="token string">"@annotation(redisLock)"</span><span class="token punctuation">)</span> <span class="token keyword">public</span> Object <span class="token function">redisLockTest</span><span class="token punctuation">(</span>ProceedingJoinPoint point<span class="token punctuation">,</span> RedisLockTestAnnotation redisLock<span class="token punctuation">)</span><span class="token punctuation">{</span> String lockKey <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token keyword">boolean</span> flag <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//根据</span> String paramterIndex <span class="token operator">=</span> redisLock<span class="token punctuation">.</span><span class="token function">redisKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>redisLock<span class="token punctuation">.</span><span class="token function">redisKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">"#"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> index <span class="token operator">=</span> Integer<span class="token punctuation">.</span><span class="token function">parseInt</span><span class="token punctuation">(</span>paramterIndex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//获取添加注解方法中的参数列表</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> args <span class="token operator">=</span> point<span class="token punctuation">.</span><span class="token function">getArgs</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//生成redis的key:</span> <span class="token comment" spellcheck="true">// TODO: 2018/11/23 根据固定为:REDIS_TEST_#数字,必须是参数列表对应的下表,从0开始,并且小于参数列表的长度</span> lockKey <span class="token operator">=</span> redisLock<span class="token punctuation">.</span><span class="token function">redisKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">"#"</span><span class="token operator">+</span>paramterIndex<span class="token punctuation">,</span>args<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"redis key:{}"</span><span class="token punctuation">,</span>lockKey<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//set到redis</span> flag <span class="token operator">=</span> redisLockUtil<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span>lockKey<span class="token punctuation">,</span>args<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"redis save result:{}"</span><span class="token punctuation">,</span>flag<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//执行添加了注解的方法并返回</span> <span class="token keyword">if</span><span class="token punctuation">(</span>flag<span class="token punctuation">)</span><span class="token punctuation">{</span> Object result <span class="token operator">=</span> point<span class="token punctuation">.</span><span class="token function">proceed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span><span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> throwable<span class="token punctuation">)</span> <span class="token punctuation">{</span> throwable<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">finally</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//最后在finally中删除</span> <span class="token keyword">if</span><span class="token punctuation">(</span>flag<span class="token punctuation">)</span><span class="token punctuation">{</span> redisLockUtil<span class="token punctuation">.</span><span class="token function">unlock</span><span class="token punctuation">(</span>lockKey<span class="token punctuation">,</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> null<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p><code>RedisLock</code></p><pre><code>package com.cn.dl.utils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import java.util.concurrent.TimeUnit;/** * Created by yanshao on 2018/9/30. */@Component@Slf4jpublic class RedisLock { @Autowired StringRedisTemplate redisTemplate; private static final long EXPIRE = 60 * 1000L; private static final long TIMEOUT = 10 * 1000L; public boolean lock(String key,String value){ log.info("获取锁 kye:{},value:{}",key,value); //请求锁时间 long requestTime = System.currentTimeMillis(); while (true){ //等待锁时间 long watiTime = System.currentTimeMillis() - requestTime; //如果等待锁时间超过10s,加锁失败 if(watiTime > TIMEOUT){ log.info("等待锁超时 kye:{},value:{}",key,value); return false; } if(redisTemplate.opsForValue().setIfAbsent(key,String.valueOf(System.currentTimeMillis()))){ //获取锁成功 log.info("获取锁成功 kye:{},value:{}",key,value); //设置超时时间,防止解锁失败,导致死锁 redisTemplate.expire(key,EXPIRE, TimeUnit.MILLISECONDS); return true; } String valueTime = redisTemplate.opsForValue().get(key); if(! StringUtils.isEmpty(valueTime) && System.currentTimeMillis() - Long.parseLong(valueTime) > EXPIRE){ //加锁时间超过过期时间,删除key,防止死锁 log.info("锁超时, key:{}, value:{}", key, value); try{ redisTemplate.opsForValue().getOperations().delete(key); }catch (Exception e){ log.info("删除锁异常 key:{}, value:{}", key, value); e.printStackTrace(); } return false; } //获取锁失败,等待20毫秒继续请求 try { log.info("等待20 nanoSeconds key:{},value:{}",key,value); TimeUnit.NANOSECONDS.sleep(20); } catch (InterruptedException e) { log.info("等待20 nanoSeconds 异常 key:{},value:{}",key,value); e.printStackTrace(); } } } /** * 分布式加锁 * @param key * @param value * @return * */ public boolean secKilllock(String key,String value){ /** * setIfAbsent就是setnx * 将key设置值为value,如果key不存在,这种情况下等同SET命令。 * 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写 * */ if(redisTemplate.opsForValue().setIfAbsent(key,value)){ //加锁成功返回true return true; } String currentValue = redisTemplate.opsForValue().get(key); /** * 下面这几行代码的作用: * 1、防止死锁 * 2、防止多线程抢锁 * */ if(! StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){ String oldValue = redisTemplate.opsForValue().getAndSet(key,value); if(! StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)){ return true; } } return false; } /** * 解锁 * @param key * @param value * */ public void unlock(String key,String value){ try{ redisTemplate.opsForValue().getOperations().delete(key); }catch (Exception e){ e.printStackTrace(); } }}</code></pre><p><code>RedisConfig</code></p><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>config<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by yanshao on 2018/11/23. */</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">RedisConfig</span> <span class="token punctuation">{</span> String REDIS_LOCK <span class="token operator">=</span> <span class="token string">"'REDIS_LOCK_'"</span><span class="token punctuation">;</span> String REDIS_TEST <span class="token operator">=</span> <span class="token string">"REDIS_TEST_"</span><span class="token punctuation">;</span> <span class="token keyword">long</span> REDIS_EXPIRE_TIME <span class="token operator">=</span> 60L <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><p><code>RestConfiguration</code></p><blockquote><p>加这个配置的原因是:保存到redis的key和value乱码问题,就是因为没有序列化!!!</p></blockquote><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>config<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Bean<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Configuration<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>core<span class="token punctuation">.</span>RedisTemplate<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>serializer<span class="token punctuation">.</span>GenericJackson2JsonRedisSerializer<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>serializer<span class="token punctuation">.</span>StringRedisSerializer<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by yanshao on 2018/11/23. */</span><span class="token annotation punctuation">@Configuration</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RestConfiguration</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Autowired</span> <span class="token keyword">private</span> RedisTemplate redisTemplate<span class="token punctuation">;</span> <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> RedisTemplate <span class="token function">redisKeyValueSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//redis key和value值序列化,不序列话发现查到的key和value乱码</span> redisTemplate<span class="token punctuation">.</span><span class="token function">setKeySerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">StringRedisSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> redisTemplate<span class="token punctuation">.</span><span class="token function">setValueSerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">GenericJackson2JsonRedisSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> redisTemplate<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p><code>StudentController</code></p><pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>controller<span class="token punctuation">;</span><span class="token keyword">import</span> com<span class="token punctuation">.</span>alibaba<span class="token punctuation">.</span>fastjson<span class="token punctuation">.</span>JSONObject<span class="token punctuation">;</span><span class="token keyword">import</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RedisLockTestAnnotation<span class="token punctuation">;</span><span class="token keyword">import</span> com<span class="token punctuation">.</span>cn<span class="token punctuation">.</span>dl<span class="token punctuation">.</span>config<span class="token punctuation">.</span>RedisConfig<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>PostMapping<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RequestMapping<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RequestParam<span class="token punctuation">;</span><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RestController<span class="token punctuation">;</span><span class="token comment" spellcheck="true">/** * Created by yanshao on 2018/11/23. */</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/student"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StudentController</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"update"</span><span class="token punctuation">)</span> <span class="token annotation punctuation">@RedisLockTestAnnotation</span><span class="token punctuation">(</span>redisKey <span class="token operator">=</span> RedisConfig<span class="token punctuation">.</span>REDIS_TEST <span class="token operator">+</span> <span class="token string">"#0"</span><span class="token punctuation">)</span> <span class="token keyword">public</span> JSONObject <span class="token function">sutdentInfoUpdate</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">"studentId"</span><span class="token punctuation">)</span> String studentId<span class="token punctuation">,</span> <span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">"age"</span><span class="token punctuation">)</span> <span class="token keyword">int</span> age<span class="token punctuation">)</span><span class="token punctuation">{</span> JSONObject result <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JSONObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> result<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"update"</span><span class="token punctuation">,</span><span class="token string">"success"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p><code>application.properties</code></p><pre class=" language-properties"><code class="language-properties"><span class="token attr-name">server.port</span><span class="token punctuation">=</span><span class="token attr-value">7555</span><span class="token comment" spellcheck="true">#redis</span><span class="token attr-name">spring.redis.host</span><span class="token punctuation">=</span><span class="token attr-value">127.0.0.1</span><span class="token attr-name">spring.redis.port</span><span class="token punctuation">=</span><span class="token attr-value">6379</span><span class="token attr-name">spring.redis.password</span><span class="token punctuation">=</span><span class="token attr-value">*****</span></code></pre><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>以上的实现方式,没有在所有场合都是完美的,包括文章刚开始提及的其他几种分布式锁实现方式,各有优缺点,所以,应根据不同的应用场景选择最适合的实现方式。</p><p>在分布式环境中,对资源进行上锁有时候是很重要的,比如抢购某一资源,这时候使用分布式锁就可以很好地控制资源。 当然,在具体使用中,还需要考虑很多因素,比如超时时间的选取,获取锁时间的选取对并发量都有很大的影响,上述实现的分布式锁也只是一种简单的实现,主要是一种思想,以上包括文中的代码,只做入门参考!</p>]]></content>
<categories>
<category> 分布式 </category>
</categories>
<tags>
<tag> 分布式锁 </tag>
<tag> Redis </tag>
<tag> SpringBoot </tag>
</tags>
</entry>
<entry>
<title>Alibaba Cloud Toolkit工具一键部署本地应用到ECS服务器</title>
<link href="/2019/01/31/alibaba-cloud-toolkit-gong-ju-yi-jian-bu-shu-ben-di-ying-yong-dao-ecs-fu-wu-qi/"/>
<url>/2019/01/31/alibaba-cloud-toolkit-gong-ju-yi-jian-bu-shu-ben-di-ying-yong-dao-ecs-fu-wu-qi/</url>
<content type="html"><![CDATA[<h2 id="Alibaba-Cloud-Toolkit工具一键部署本地应用到ECS服务器"><a href="#Alibaba-Cloud-Toolkit工具一键部署本地应用到ECS服务器" class="headerlink" title="Alibaba Cloud Toolkit工具一键部署本地应用到ECS服务器"></a>Alibaba Cloud Toolkit工具一键部署本地应用到ECS服务器</h2><p><img src="https://img-blog.csdnimg.cn/20181209133458931.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1lDbGltYg==,size_16,color_FFFFFF,t_70" alt="Alibaba Cloud Toolkit"></p><h3 id="什么是-Alibaba-Cloud-Toolkit"><a href="#什么是-Alibaba-Cloud-Toolkit" class="headerlink" title="什么是 Alibaba Cloud Toolkit"></a>什么是 Alibaba Cloud Toolkit</h3><p>Alibaba Cloud Toolkit (后文简称 Cloud Toolkit)是阿里云针对 IDE 平台为开发者提供的一款插件,用于帮助开发者高效开发并部署适合在云端运行的应用。在本地完成应用程序的开发、调试和测试后,可以使用在 IED (如 Eclipse 或 IntelliJ)中安装的 Cloud Toolkit 插件,通过图形配置的方式连接到云端部署环境并将应用程序快如部署到云端。</p><blockquote><p>说明:目前 Cloud Toolkit 仅支持 Eclipse、Intellij 等其它开发环境开发中,请您持续关注 Cloud Tookit 动态。</p></blockquote><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>官方有提供的文档说明:</p><pre><code>https://help.aliyun.com/product/29966.html</code></pre><h3 id="使用IDEA安装和配置Cloud-Toolkit"><a href="#使用IDEA安装和配置Cloud-Toolkit" class="headerlink" title="使用IDEA安装和配置Cloud Toolkit"></a>使用IDEA安装和配置Cloud Toolkit</h3><h4 id="在idea上安装Alibaba-Cloud-Toolkit插件"><a href="#在idea上安装Alibaba-Cloud-Toolkit插件" class="headerlink" title="在idea上安装Alibaba Cloud Toolkit插件"></a>在idea上安装Alibaba Cloud Toolkit插件</h4><p>到 Idea 插件库中进行下载,如下图:</p><p><img src="http://pkon92vqd.bkt.clouddn.com/1.png" alt=""></p><p>如果插件下载速度比较慢,稍等一会,因为是国外网站,下载完成后需要重启 idea 应用后生效。</p><h4 id="配置Alibaba-Cloud-Toolkit的Account信息"><a href="#配置Alibaba-Cloud-Toolkit的Account信息" class="headerlink" title="配置Alibaba Cloud Toolkit的Account信息"></a>配置Alibaba Cloud Toolkit的Account信息</h4><p><img src="http://pkon92vqd.bkt.clouddn.com/2.png" alt=""></p><p><img src="http://pkon92vqd.bkt.clouddn.com/3.png" alt=""></p><p>其中 Access Key 和 Access Key Secret 信息可以到阿里云控制台查询获得</p><p><img src="http://pkon92vqd.bkt.clouddn.com/3.5.png" alt=""></p><p>创建完成并配置好Account后,在 ECS on Alibaba Cloud 视图中可以看到,会检索到你的实例</p><p><img src="http://pkon92vqd.bkt.clouddn.com/4.png" alt=""></p><h4 id="开始部署项目到ECS服务器"><a href="#开始部署项目到ECS服务器" class="headerlink" title="开始部署项目到ECS服务器"></a>开始部署项目到ECS服务器</h4><p>鼠标选中你的项目,然后右键,找到 Alibaba Cloud 选项</p><p><img src="http://pkon92vqd.bkt.clouddn.com/5.png" alt=""></p><p>然后填写其他信息,项目部署位置等,如果你的Account配置没有问题,则会自动账户显示对应的ECS服务器,在发布时,需要手动选择某台服务器,一定要选择哦!</p><p>对于Command的编写,可以参考官方文档</p><pre><code>https://yq.aliyun.com/articles/665693</code></pre><p><img src="http://pkon92vqd.bkt.clouddn.com/6.png" alt=""></p><p>配置成功后,可以点击Run运行程序,此时会自动为我们编译并上传到阿里云服务器中,发布到地址就是上图中的Deploy Location中的路径,发布前如果需要Maven执行,一定不要忘记配置上图中Maven的命令,中间的Command是在上传到服务器成功后执行的命令,主要用于应用的启动停止重启等。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>好了,到此如果没有其他问题的话,你就会已经发布成功了,从此失去手动打包、上传再发布的繁琐过程了,这个插件针对个人开发用户来说是极其友好的,因为个人开发者一般不会像企业级开发一样部署 Jekins 等大型部署工具,而这个时候如果使用 Alibaba Cloud Toolkit ,在信息配置好的情况可以说基本就是一键部署!</p><p>再贴一下官网地址:</p><pre><code>https://www.aliyun.com/product/cloudtoolkit</code></pre><p>官网介绍的更加详细,也有官方钉钉群,大家感兴趣的可以加入进行深入交流。</p><p><img src="https://yqfile.alicdn.com/5685e931e06cd61faa41dee0ad46bf251fe56837.png" alt="5685e931e06cd61faa41dee0ad46bf251fe56837"></p>]]></content>
<categories>
<category> 工具 </category>
</categories>
<tags>
<tag> 工具 </tag>
<tag> 服务器 </tag>
</tags>
</entry>
<entry>
<title>如何在面试中介绍自己的项目经验</title>
<link href="/2019/01/29/ru-he-zai-mian-shi-zhong-jie-shao-zi-ji-de-xiang-mu-jing-yan/"/>
<url>/2019/01/29/ru-he-zai-mian-shi-zhong-jie-shao-zi-ji-de-xiang-mu-jing-yan/</url>
<content type="html"><![CDATA[<h3 id="如何在面试中介绍自己的项目经验"><a href="#如何在面试中介绍自己的项目经验" class="headerlink" title="如何在面试中介绍自己的项目经验"></a>如何在面试中介绍自己的项目经验</h3><blockquote><p>在面试时,经过寒暄后,一般面试官会让介绍项目经验 。常见的问法是,说下你最近的(或最拿得出手的)一个项目。<br>根据我们的面试经验,发现有不少候选人对此没准备,说起来磕磕巴巴,甚至有人说出项目经验从时间段或技术等方面和简历上的不匹配,这样就会造成如下的后果。<br> 1 第一印象就不好了,至少会感觉该候选人表述能力不强。<br> 2 一般来说,面试官会根据候选人介绍的项目背景来提问题,假设面试时会问10个问题,那么至少有5个问题会根据候选人所介绍的项目背景来问,候选人如果没说好,那么就没法很好地引导后继问题了,就相当于把提问权完全交给面试官了。</p></blockquote><p>面试时7份靠能力,3份靠技能,而刚开始时的介绍项目又是技能中的重中之重,所以本文将从“介绍”和“引导”两大层面告诉大家如何准备面试时的项目介绍。<br>好了,如下是正文内容。</p><hr><h3 id="1-在面试前准备项目描述,别害怕,因为面试官什么都不知道"><a href="#1-在面试前准备项目描述,别害怕,因为面试官什么都不知道" class="headerlink" title="1 在面试前准备项目描述,别害怕,因为面试官什么都不知道"></a>1 在面试前准备项目描述,别害怕,因为面试官什么都不知道</h3><p>面试官是人,不是神,拿到你的简历的时候,是没法核实你的项目细节的(一般公司会到录用后,用背景调查的方式来核实)。更何况,你做的项目是以月为单位算的,而面试官最多用30分钟来从你的简历上了解你的项目经验,所以你对项目的熟悉程度要远远超过面试官,所以你一点也不用紧张。如果你的工作经验比面试官还丰富的话,甚至还可以控制整个面试流程(笔者在面试方面成精后也经常干这种事情,大家一定也能行)。</p><table><thead><tr><th></th><th><strong>你</strong></th><th><strong>面试官</strong></th></tr></thead><tbody><tr><td>对你以前的项目和技能</td><td>很了解</td><td>只能听你说,只能根据你说的内容做出判断</td></tr><tr><td>在面试过程中的职责</td><td>在很短的时间内防守成功即可</td><td>如果找不出漏洞,就只能算你以前做过</td></tr><tr><td>准备时间</td><td>面试前你有充足的时间准备</td><td>一般在面试前用30分钟阅读你的简历</td></tr><tr><td>沟通过程</td><td>你可以出错,但别出关键性的错误</td><td>不会太为难你,除非你太差</td></tr><tr><td>技巧</td><td>你有足够的技巧,也可以从网上找到足够多的面试题</td><td>其实就问些通用的有规律的问题</td></tr></tbody></table><p>既然面试官无法了解你的底细,那么他们怎么来验证你的项目经验和技术?下面总结了一些常用的提问方式。</p><table><thead><tr><th><strong>提问方式</strong></th><th><strong>目的</strong></th></tr></thead><tbody><tr><td>让你描述工作经验和项目(极有可能是最近的),看看你说的是否和简历上一致</td><td>看你是否真的做过这些项目</td></tr><tr><td>看你简历上项目里用到的技术,比如框架、数据库,然后针对这些技术提些基本问题</td><td>还是验证你是否做过项目,同时看你是否了解这些技术,为进一步提问做准备</td></tr><tr><td>针对某个项目,不断深入地问一些技术上的问题,或者从不同侧面问一些技术实现,看你前后回答里面是否有矛盾</td><td>深入核实你的项目细节</td></tr><tr><td>针对某技术,问些项目里一定会遇到的问题,比如候选人说做过数据库,那么就会问索引方面的问题</td><td>通过这类问题,核实候选人是否真的有过项目经验(或者还仅仅是学习经验)</td></tr></tbody></table><h3 id="2-准备项目的各种细节,一旦被问倒了,就说明你没做过"><a href="#2-准备项目的各种细节,一旦被问倒了,就说明你没做过" class="headerlink" title="2 准备项目的各种细节,一旦被问倒了,就说明你没做过"></a>2 准备项目的各种细节,一旦被问倒了,就说明你没做过</h3><p> 一般来说,在面试前,大家应当准备项目描述的说辞,自信些,因为这部分你说了算,流利些,因为你经过充分准备后,可以知道你要说些什么。而且这些是你实际的项目经验(不是学习经验,也不是培训经验),那么一旦让面试官感觉你都说不上来,那么可信度就很低了。</p><p>不少人是拘泥于“项目里做了什么业务,以及代码实现的细节”,这就相当于把后继提问权直接交给面试官。下表列出了一些不好的回答方式。</p><table><thead><tr><th><strong>回答方式</strong></th><th><strong>后果</strong></th></tr></thead><tbody><tr><td>我在XX软件公司做了XX门户网站项目,这个项目做到了XX功能,具体是XX和XX模块,各模块做了XX功能,客户是XX,最后这个项目挣了XX钱</td><td>直接打断,因为业务需求我不需要了解,我会直接问他项目里的技术</td></tr><tr><td>(需要招聘一个Java后端开发,会Spring MVC)最近一个项目我是用C#(或其他非Java技术)实现的,实现了……或者我最近做的不是开发,而是测试……或者我最近的项目没有用到Spring MVC</td><td>提问,你最近用到SSH技术的项目是什么时候,然后在评语上写:最近XX时间没接触过SSH</td></tr><tr><td>在毕业设计的时候(或者在读书的时候,在学习的时候,在XX培训学校,在XX实训课程中),……</td><td>直接打断,提问你这个是否是商业项目,如果不是,你有没有其他的商业经验。如果没商业项目经验,除非是校招,否则就直接结束面试</td></tr><tr><td>描述项目时,一些关键要素(比如公司、时间、所用技术等)和简历上的不匹配</td><td>我们会深究这个不一致的情况,如果是简历造假,那么可能直接中断面试,如果真的是笔误,那么就需要提供合理的解释</td></tr></tbody></table><p>在避免上述不好的回答的同时,大家可以按下表所给出的要素准备项目介绍。如果可以,也请大家准备一下用英语描述。其实刚毕业的学生,或者工作经验较少的人,英语能力都差不多,但你说了,这就是质的进步。 </p><table><thead><tr><th>要素</th><th>样式</th></tr></thead><tbody><tr><td>控制在1分钟里面,讲出项目基本情况,比如项目名称,背景,给哪个客户做,完成了基本的事情,做了多久,项目规模多大,用到哪些技术,数据库用什么,然后酌情简单说一下模块。重点突出背景,技术,数据库和其他和技术有关的信息。</td><td>我在XX公司做了XX外汇保证金交易平台,客户是XX银行,主要完成了挂盘,实盘成交,保证金杠杆成交等功能,数据库是Oracle,前台用到JS等技术,后台用到Java的SSH,几个人做了X个月。不需要详细描述各功能模块,不需要说太多和业务有关但和技术无关的。如果面试官感兴趣,等他问。</td></tr><tr><td>要主动说出你做了哪些事情,这部分的描述一定需要和你的技术背景一致。</td><td>我做了外汇实盘交易系统,挂单成交系统,XXX模块,做了X个月</td></tr><tr><td>描述你在项目里的角色</td><td>我主要是做了开发,但在开发前,我在项目经理的带领下参与了业务调研,数据库设计等工作,后期我参与了测试和部署工作。</td></tr><tr><td>可以描述用到的技术细节,特别是你用到的技术细节,这部分尤其要注意,你说出口的,一定要知道,因为面试官后面就根据这个问的。你如果做了5个模块,宁可只说你能熟练说上口的2个。</td><td>用到了Java里面的集合,JDBC,…等技术,用到了Spring MVC等框架,用技术连接数据库。</td></tr><tr><td>这部分你风险自己承担,如果可以,不露声色说出一些热门的要素,比如Linux,大数据,大访问压力等。但一旦你说了,面试官就会直接问细节。</td><td>这个系统里,部署在Linux上,每天要处理的数据量是XX,要求是在4小时,1G内存是的情况下处理完5千万条数据。平均访客是每分钟XXX。</td></tr></tbody></table><p>面试前,你一定要准备,一定要有自信,但也要避免如下的一些情况。 </p><table><thead><tr><th>要避免的情况</th><th>正确的做法</th><th>原因</th></tr></thead><tbody><tr><td>回答很简单。问什么答什么,往往就用一句话回答</td><td>把你知道的都说出来,重点突出你知道的思想,框架</td><td>问:你SSH用过吗?答:用过。问:在什么项目里用到?答:一个保险项目问:你做了哪方面的事情?答:开发我直接不问了</td></tr><tr><td>说得太流利</td><td>适当停顿,边思考边说</td><td>让面试官感觉你在背准备的东西,这样后面问题就很难</td></tr><tr><td>项目介绍时什么都说,</td><td>就说些刚才让准备的一些,而且要有逻辑地说</td><td>会让面试官感觉你思路太乱</td></tr><tr><td>别太多介绍技术细节,就说你熟悉的技术</td><td>技术面点到为止,等面试官来问</td><td>你说到的所有技术要点,都可能会被深问。面试官一般会有自己的面试节奏,如果你在介绍时就太多说技术细节,很有可能被打断,从而没法说出你准备好的亮点。</td></tr></tbody></table><h3 id="3-不露痕迹地说出面试官爱听的话"><a href="#3-不露痕迹地说出面试官爱听的话" class="headerlink" title="3 不露痕迹地说出面试官爱听的话"></a>3 不露痕迹地说出面试官爱听的话</h3><p> 在项目介绍的时候(当然包括后继的面试),面试官其实很想要听一些关键点,只要你说出来,而且回答相关问题比较好,这绝对是加分项。我在面试别人的时候,一旦这些关键点得到确认,我是绝对会在评语上加上一笔的。</p><p>下面列些面试官爱听的关键点和对应的说辞。</p><table><thead><tr><th>关键点</th><th>说辞</th></tr></thead><tbody><tr><td>能考虑到代码的扩展性,有参与框架设计的意识</td><td>我的项目XX保险项目,用到SSH技术,数据库是Oracle,(这个是铺垫),开发的时候,我会先和项目经理一起设计框架,并参与了框架的构建,连接数据库的时候,我们用到了DAO,这样做的理由是,把SQL语句封装到DAO层,一旦要扩展功能模块,就可以不用做太多的改动。</td></tr><tr><td>有调优意识,能通过监控发现问题点,然后解决</td><td>在开发阶段,我就注意到内存的性能问题和SQL运行的时间问题,在压力测试阶段,我会通过xx工具来监控内存和数据库,发现待提升的代码点,然后通过查资料来优化。最后等项目上线后,我们会部署监控系统,一旦发现内存和数据库问题,我们会第一时间解决。</td></tr><tr><td>动手能力很强,肯干活,会的东西比较多,团队合作精神比较好</td><td>在项目里,我不仅要做开发的工作,而且需要自己测试,需要自己根据一些日志的输出到数据库或Java端去debug,当我开好一个模块时,需要自己部署到Linux上测试。或者,一旦遇到问题,如果是业务方面的,我会及时和项目经理沟通,如果是技术方面的,我会自己查资料,如果是测试方面的,我会及时和测试的人沟通。</td></tr><tr><td>责任心比较强,能适应大压力的环境</td><td>被问“你如果在项目里遇到问题怎么办?”回答:遇到问题我先查资料,如果实在没法解决,不会拖,会及时问相关的人,即使加班,也会在规定的时间内解决。</td></tr><tr><td>有主见,能不断探索新的知识</td><td>在项目里,我会在保证进度的前提下和项目经理说我的想法,提出我的解决方案。在开发过程中,我会先思考一下,用一种比较好的方式,比如效率最高的方法实现。另外你要找机会说出:平时我会不断看一些新技术(比如大数据Hadoop),会不断深入了解一些框架和技术的实现底层。</td></tr></tbody></table><h3 id="4-一定要主动,面试官没有义务挖掘你的亮点"><a href="#4-一定要主动,面试官没有义务挖掘你的亮点" class="headerlink" title="4 一定要主动,面试官没有义务挖掘你的亮点"></a>4 一定要主动,面试官没有义务挖掘你的亮点</h3><p> 我这样问已经是处于角色错位了,作为面试者,应当主动说出,而不是等着问,但请注意,说的时候要有技巧,找机会说,通常是找一些开放性的问题说。</p><p>比如:在这个项目里用到了什么技术?你除了说一些基本的技术,比如Spring MVC,Hibernate,还有数据库方面的常规技术时,还得说,用到了Java内存管理,这样能减少对虚拟机内存的压力,或者说用到了大数据处理技术等。也就是说,得找一切机会说出你拿得出手的而且当前也非常热门的技术。</p><p>或者找个相关的问题做扩展性说明,比如被问到:你有没有用到过一对多和多对多?你除了说基本知识点以外,还可以说,一般我还会根据需求适当地设置cascade和inverse关键字,随后通过一个实际的案例来说明合理设计对你项目的帮助,这样就能延伸性地说明你的技能了。相反如果你不说,面试话一定会认为你只会简单的一对一和一对多操作。</p><p> 面试的时候,如果候选人回答问题很简单,有一说一,不会扩展,或者用非常吝啬的语句来回答我的问题,那么我一般会给机会让他们深入讲述(但我不敢保证不是每个面试官都会深入提问),如果回答再简洁,那么也会很吝啬地给出好的评语。</p><p> 记住:面试官不是你的亲戚,面试官很忙,能挖掘出你的亮点的面试官很少,而说出你的亮点是你的义务。</p><p> 我在面试别人过程中,根据不同的情况一般会给出如下的评语。</p><p> 1 回答很简单,但回答里能证明出他对框架等技术确实是做过,我会在评语里些“对框架了解一般,不知道一些深层次的知识(我都问了多次了你都回答很简答,那么对不起了,我只能这么写,或许你确实技术很强,那也没办法,谁让你不肯说呢?)”,同时会加一句“表达能力很一般,沟通能力不强”,这样即使他通过技术面试,后面的面试他也会很吃力。</p><p> 2 回答很简单,通过回答我没法验证他是在项目里做过这个技术,还是仅仅在平时学习中学过这个技术。我就会写“在简历中说用过XX技术,但对某些细节说不上来,没法看出在项目里用到这个技术”,如果这个技术是职务必需点,那么他通过面试的可能性就非常小。</p><p> 3 回答很简单,而且只通过嗯啊之类的虚词回答,经过提醒还这样,我会敷衍几句结束面试,直接写“技术很薄弱,没法通过面试”。</p><p> 4 虽然通过回答能很好地展示自己的技能,但逻辑调理不清晰,那么我会让他通过技术面试,但会写上“技能很好,但表达能力一般(或有待提高),请后继面试经理斟酌”。这样通过后继综合面试的机会就一般了,毕竟综合面试会着重考察表达能力交往能力等非技术因素。</p><p><strong>不管怎样,一旦回答简单,不主动说出你的擅长点,或没有条理很清楚地说出你的亮点,就算我让你通过面试,也不会写上“框架细节了解比较深,数据库应用比较熟练”等之类的好评语,你即使通过技术和后面的综合面试,工资也是比较低的。</strong></p><h3 id="5-一旦有低级错误,可能会直接出局"><a href="#5-一旦有低级错误,可能会直接出局" class="headerlink" title="5 一旦有低级错误,可能会直接出局"></a>5 一旦有低级错误,可能会直接出局</h3><p> 面试过程中有些方面你是绝对不能出错,所以你在准备过程中需要尤其注意如下的因素。下面列了些会导致你直接出局的错误回答。</p><table><thead><tr><th>错误类型</th><th>导致的后果</th></tr></thead><tbody><tr><td>前后矛盾,后面的回答无法证明你的项目描述,比如一开始说用到了Spring MVC,后面没法说出最基本的实现,比如不知道Spring有哪些类,或者没法说出项目的细节。</td><td>我会怀疑这个项目的真实性,我就会进一步问:数据库用什么,数据量多少?多少人做了多少时间,一旦再出现明显漏洞,比如一个小项目用到非常多的时间,那么就不仅仅是技术问题,而是在面试过程中企图“蒙混过关”的性质了。</td></tr><tr><td>项目里一定会用到的基本概念性问题都回答不上,Spring的依赖注入概念是什么,怎么用的,或者Hibernate的一对多怎么实现</td><td>一旦被我发现概念不知道,我就会通过更多问题确认,如果被我确认很弱,这就相当严重,因为技术能力差和技术没用过是两个截然不同的状况,技术没用过会导致直接出局。</td></tr><tr><td>面试时说出的工作经验和简历上的不一致</td><td>我会直接怀疑简历是编的,我会让候选人解释,即使是说简历写错了,我也会问比较深入的问题来核实他的技能和能力。</td></tr><tr><td>简历上的技能描述和回答出来的明显不一致,比如明明是只会简单的Linux,但吹得天花乱坠</td><td>我会通过一些比较深的问题核实其他技能,找出其他方面吹嘘的水分。所以建议,你可以适当夸张,但别过分,比如你在项目里没搭建框架但平时学习时搭建过,你可以写“XX项目的框架是你搭建的”,但你不能说你是一个架构师,非常了解项目的底层。</td></tr><tr><td>让面试官感觉你不稳定,很浮躁,比如说话不庄重,或者面试时打扮非常不正规,就穿背心来。</td><td>即使你技术再好,这个会可能导致你直接出局。我对油嘴滑舌的候选人一般会直接写上不好的评语,这样很难过后面项目经理的面试。我还遇到一个人,简历上工作是半年一换,我问他为什么经常换,他直接说是待遇问题,这个人我是直接Fail掉。</td></tr><tr><td>明说不能加班,不能出差</td><td>其实虽然有这一问,但公司里未必真的会加班会出差。但听到这类回答,说明这个人不能承受大压力的工作,或者责任心不强,大多数公司是不会要这种人的。</td></tr></tbody></table><h3 id="6-引导篇:准备些加分点,在介绍时有意提到,但别说全"><a href="#6-引导篇:准备些加分点,在介绍时有意提到,但别说全" class="headerlink" title="6 引导篇:准备些加分点,在介绍时有意提到,但别说全"></a>6 引导篇:准备些加分点,在介绍时有意提到,但别说全</h3><p> 在做项目介绍的时候,你可以穿插说出一些你的亮点,但请记得,不论在介绍项目还是在回答问题,你当前的职责不是说明亮点而是介绍项目,一旦你详细说,可能会让面试官感觉你跑题了。</p><p> 所以这时你可以一笔带过,比如你可以说,“我们的项目对数据要求比较大,忙的时候平均每小时要处理几十万条数据”,这样就可以把面试官引入“大数据”的方向。</p><p> 你在面试前可以根据职位的需求,准备好这种“一笔带过”的话。比如这个职位的需求点是Spring MVC框架,大数据高并发,要有数据库调优经验,那么介绍以往项目时,你就最好突出这些方面你的实际技能。</p><p> 再给大家举个例子,比如Java虚拟机内存管理和数据库优化是绝大多数项目都要遇到的两大问题,大家都可以在叙述项目经验时说,在这个项目里,我们需要考虑内存因素,因为我们的代码只允许在2G内存环境中运行,而且对数据库性能要求比较高,所以我们经常要监控优化内存和数据库里的SQL语句。这样当面试官深入提问时,就能抛出自己准备好的虚拟机内存优化和数据库优化方面的说辞。</p><p>实在不行,你也可以说“我除了做开发,也做了了解需求,测试和部署的工作,因为这个项目人手比较少,压力比较大”,这样你也能展示你有过独挡一面的经历。</p><p>我在面试过程中,一旦听到有亮点,就会等到他说好当前问题后,顺口去问,一般技术面试最多办半小时,你把时间用在回答准备好的问题点上的时候,被问其他问题的时间就会少了。 </p><h3 id="7-你可以引导,但不能自说自话"><a href="#7-你可以引导,但不能自说自话" class="headerlink" title="7 你可以引导,但不能自说自话"></a>7 你可以引导,但不能自说自话</h3><p> 我面试的时候,也会遇到些有准备的人,其实如果你真的想应聘的话,一定要事先准备,这点我能理解,甚至赞同,你只要别露出太明显的痕迹,我不会写上“似乎有准备,没法考察真实技能”这种话,更何况未必每个面试官都能感觉出你准备过。 但你不能凭着有准备而太强势,毕竟面试是面试官主导的。</p><p> 我遇到个别面试的人,他们说话太多,一般会主动扩展,比如我问他数据库用什么,他不仅回答数据库是什么,自己做了什么,甚至顺便会把大数据处理技术都说出来。</p><p> 其实过犹不及,我就会重点考察你说的每个细节,因为我怀疑你说的都是你从网上看的,而不是你项目中用到的,我甚至会直接威胁:“你先和我说实话这个技术你真在项目里用到,我后面会重点考察,一旦被认为你项目里没做,这个性质就是蒙混过关了”,往往这些人会主动坦白。</p><p>不过话说回来,他如果仅仅说,数据量比较大,但点到为止,不继续说后面的话,我就会深入去问,他自然有机会表达。同时请注意,一般在面试过程中,一旦你亮出加分点,但面试官没接嘴,这个加分点可能就不是项目必备的,也不是他所关注的,当前你就可以别再说了,或者等到你提问题的时候再说。 </p><h3 id="8-不是结尾的总结"><a href="#8-不是结尾的总结" class="headerlink" title="8 不是结尾的总结"></a>8 不是结尾的总结</h3><p> 到这里,我们已经给出了介绍项目的一些技巧,第一,面试前一定要准备,第二,本文给出是的方法,不是教条,大家可以按本文给出的方向结合自己的项目背景做准备,而不是死记硬背本文给出的一些说辞。</p><p> 当大家介绍好项目背景后,面试才刚刚开始,哪怕你说得再好,哪怕你把问题引导到你准备的范围里,这也得应付Java Web(比如Spring MVC,ORM等)、Java Core(多线程、集合、JDBC等)和数据库等方面的问题。</p><p> 那么本文的价值体现在哪呢?如果引导不好,你根本没机会展示自己的能力。这就是本文给出的方法价值所在。说句自夸的话,本文给出的一些方法和说辞不是拍脑袋想出来的,而是从面试上百个候选人的经历中抽取出来的,其中有不少血泪,也有不少人成功的途径,这篇文章多少对大家(尤其是经验不满3年的初级程序员)有帮助。</p>]]></content>
<categories>
<category> 面试 </category>
</categories>
<tags>
<tag> 项目经验介绍 </tag>
<tag> 程序员成长 </tag>
</tags>
</entry>
<entry>
<title>如何健壮你的后端服务</title>
<link href="/2019/01/29/ru-he-jian-zhuang-ni-de-hou-duan-fu-wu/"/>
<url>/2019/01/29/ru-he-jian-zhuang-ni-de-hou-duan-fu-wu/</url>
<content type="html"><![CDATA[<p>对每一个程序员而言,故障都是悬在头上的达摩克利斯之剑,都唯恐避之不及,如何避免故障是每一个程序员都在苦苦追寻希望解决的问题。对于这一问题,大家都可以从需求分析、架构设计 、代码编写、测试、code review、上线、线上服务运维等各个视角给出自己的答案。本人结合自己两年有限的互联网后端工作经验,从某几个视角谈谈自己对这一问题的理解,不足之处,望大家多多指出。</p><p> 我们大部分服务都是如下的结构,既要给使用方使用,又依赖于他人提供的第三方服务,中间又穿插了各种业务、算法、数据等逻辑,这里面每一块都可能是故障的来源。如何避免故障?我用一句话概括,“<strong>怀疑第三方,防备使用方,做好自己</strong>”。</p><p><img src="https://images0.cnblogs.com/blog2015/522490/201508/232008015194182.png" alt="img"></p><h3 id="1-怀疑第三方"><a href="#1-怀疑第三方" class="headerlink" title="1 怀疑第三方"></a>1 怀疑第三方</h3><p> 坚持一条信念:“所有第三方服务都不可靠”,不管第三方什么天花乱坠的承诺。基于这样的信念,我们需要有以下行动。</p><h4 id="1-1-有兜底,制定好业务降级方案"><a href="#1-1-有兜底,制定好业务降级方案" class="headerlink" title="1.1 有兜底,制定好业务降级方案"></a>1.1 有兜底,制定好业务降级方案</h4><p> 如果第三方服务挂掉怎么办?我们业务也跟着挂掉?显然这不是我们希望看到的结果,如果能制定好降级方案,那将大大提高服务的可靠性。举几个例子以便大家更好的理解。</p><p> 比如我们做个性化推荐服务时,需要从用户中心获取用户的个性化数据,以便代入到模型里进行打分排序,但如果用户中心服务挂掉,我们获取不到数据了,那么就不推荐了?显然不行,我们可以在cache里放置一份热门商品以便兜底;</p><p> 又比如做一个数据同步的服务,这个服务需要从第三方获取最新的数据并更新到mysql中,恰好第三方提供了两种方式:1)一种是消息通知服务,只发送变更后的数据;2)一种是http服务,需要我们自己主动调用获取数据。我们一开始选择消息同步的方式,因为实时性更高,但是之后就遭遇到消息迟迟发送不过来的问题,而且也没什么异常,等我们发现一天时间已过去,问题已然升级为故障。合理的方式应该两个同步方案都使用,消息方式用于实时更新,http主动同步方式定时触发(比如1小时)用于兜底,即使消息出了问题,通过主动同步也能保证一小时一更新。</p><p> 有些时候第三方服务表面看起来正常,但是返回的数据是被污染的,这时还有什么方法兜底吗?有人说这个时候除了通知第三方快速恢复数据,基本只能干等了。举个例子,我们做移动端的检索服务,其中需要调用第三方接口获取数据来构建倒排索引,如果第三方数据出错,我们的索引也将出错,继而导致我们的检索服务筛选出错误的内容。第三方服务恢复数据最快要半小时,我们构建索引也需要半小时,即可能有超过1个多小时的时间检索服务将不能正常使用,这是不可接受的。如何兜底呢?我们采取的方法是每隔一段时间保存全量索引文件快照,一旦第三方数据源出现数据污染问题,我们先按下停止索引构建的开关,并快速回滚到早期正常的索引文件快照,这样尽管数据不是很新(可能1小时之前),但是至少能保证检索有结果,不至于对交易产生特别大的影响。</p><h4 id="1-2-遵循快速失败原则,一定要设置超时时间"><a href="#1-2-遵循快速失败原则,一定要设置超时时间" class="headerlink" title="1.2 遵循快速失败原则,一定要设置超时时间"></a>1.2 遵循快速失败原则,一定要设置超时时间</h4><p> 某服务调用的一个第三方接口正常响应时间是50ms,某天该第三方接口出现问题,大约有15%的请求响应时间超过2s,没过多久服务load飙高到10以上,响应时间也非常缓慢,即第三方服务将我们服务拖垮了。</p><p> 为什么会被拖垮?没设置超时!我们采用的是同步调用方式,使用了一个线程池,该线程池里最大线程数设置了50,如果所有线程都在忙,多余的请求就放置在队列里中。如果第三方接口响应时间都是50ms左右,那么线程都能很快处理完自己手中的活,并接着处理下一个请求,但是不幸的是如果有一定比例的第三方接口响应时间为2s,那么最后这50个线程都将被拖住,队列将会堆积大量的请求,从而导致整体服务能力极大下降。</p><p> 正确的做法是和第三方商量确定个较短的超时时间比如200ms,这样即使他们服务出现问题也不会对我们服务产生很大影响。</p><h4 id="1-3-适当保护第三方,慎重选择重试机制"><a href="#1-3-适当保护第三方,慎重选择重试机制" class="headerlink" title="1.3 适当保护第三方,慎重选择重试机制"></a>1.3 适当保护第三方,慎重选择重试机制</h4><p> 需要结合自己的业务以及异常来仔细斟酌是否使用重试机制。比如调用某第三方服务,报了个异常,有些同学就不管三七二十一就直接重试,这样是不对的,比如有些业务返回的异常表示业务逻辑出错,那么你怎么重试结果都是异常;又如有些异常是接口处理超时异常,这个时候就需要结合业务来判断了,有些时候重试往往会给后方服务造成更大压力,启到雪上加霜的效果。</p><h3 id="2-防备使用方"><a href="#2-防备使用方" class="headerlink" title="2 防备使用方"></a>2 防备使用方</h3><p> 这里又要坚持一条信念:“所有的使用方都不靠谱”,不管使用方什么天花乱坠的保证。基于这样的信念,我们需要有以下行动。 </p><h4 id="2-1-设计一个好的api(RPC、Restful),避免误用"><a href="#2-1-设计一个好的api(RPC、Restful),避免误用" class="headerlink" title="2.1 设计一个好的api(RPC、Restful),避免误用"></a>2.1 设计一个好的api(RPC、Restful),避免误用</h4><p> 过去两年间看过不少故障,直接或间接原因来自于糟糕的接口。如果你的接口让很多人误用,那要好好反思自己的接口设计了,接口设计虽然看着简单,但是学问很深,建议大家好好看看Joshua Bloch的演讲《How to Design a Good API & Why it Matters(如何设计一个好的API及为什么这很重要)》</p><p> 下面简单谈谈我的经验。</p><p><strong>a)</strong> <strong>遵循接口最少暴露原则</strong></p><p> 使用方用多少接口我们就提供多少,因为提供的接口越多越容易出现乱用现象,言多必失嘛。此外接口暴露越多自己维护成本就越高。</p><p><strong>b)</strong> <strong>不要让使用方做接口可以做的事情</strong></p><p> 如果使用方需要调用我们接口多次才能进行一个完整的操作,那么这个接口设计就可能有问题。比如获取数据的接口,如果仅仅提供getData(int id);接口,那么使用方如果要一次性获取20个数据,它就需要循环遍历调用我们接口20次,不仅使用方性能很差,也无端增加了我们服务的压力,这时提供getDataList(List<integer> idList);接口显然是必要的。</integer></p><p><strong>c</strong>)<strong>避免长时间执行的接口</strong></p><p> 还是以获取数据方法为例:getDataList(List<integer> idList); 假设一个用户一次传1w个id进来,我们的服务估计没个几秒出不来结果,而且往往是超时的结果,用户怎么调用结果都是超时异常,那怎么办?限制长度,比如限制长度为100,即每次最多只能传100个id,这样就能避免长时间执行,如果用户传的id列表长度超过100就报异常。</integer></p><p> 加了这样限制后,必须要让使用方清晰地知道这个方法有此限制。之前就遇到误用的情况,某用户一个订单买了超过100个商品,该订单服务需要调用商品中心接口获取该订单下所有商品的信息,但是怎么调用都失败,而且异常也没打出什么有价值的信息,后来排查好久才得知是商品中心接口做了长度限制。</p><p> 怎么才能做到加了限制,又不让用户误用呢?</p><p> 两种思路:1)接口帮用户做了分割调用操作,比如用户传了1w个id,接口内部分割成100个id列表(每个长度100),然后循环调用,这样对使用方屏蔽了内部机制,对使用方透明;2)让用户自己做分割,自己写循环显示调用,这样需要让用户知道我们方法做了限制,具体方法有:1)改变方法名,比如getDataListWithLimitLength(List<integer> idList); ;2)增加注释;3)如果长度超过 100,很明确地抛出异常,很直白地进行告知。</integer></p><p><strong>d</strong>)<strong>参数易用原则</strong></p><p> 避免参数长度太长,一般超过3个后就较难使用,那有人说了我参数就是这么多,那怎么办?写个参数类嘛!</p><p> 此外避免连续的同类型的参数,不然很容易误用。</p><p> 能用其它类型如int等的尽量不要用String类型,这也是避免误用的方法。</p><p><strong>e)</strong> <strong>异常</strong></p><p> 接口应当最真实的反应出执行中的问题,更不能用聪明的代码做某些特别处理。经常看到一些同学接口代码里一个try catch,不管内部抛了什么异常,捕获后返回空集合。</p><pre class=" language-java"><code class="language-java">`<span class="token keyword">public</span>` `List<span class="token operator"><</span>Integer<span class="token operator">></span> <span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>`` ``<span class="token keyword">try</span>` `<span class="token punctuation">{</span>`` ``<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>`` ``<span class="token punctuation">}</span> ``<span class="token keyword">catch</span>` `<span class="token punctuation">(</span>Exception e<span class="token punctuation">)</span> <span class="token punctuation">{</span>`` ``<span class="token keyword">return</span>` `Collections<span class="token punctuation">.</span><span class="token function">emptyList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>`` ``<span class="token punctuation">}</span>`` ``<span class="token punctuation">}</span>`</code></pre><p> 这让使用方很无奈,很多时候不知道是自己参数传的问题,还是服务方内部的问题,而一旦未知就可能误用了。</p><h4 id="2-2-流量控制,按服务分配流量,避免滥用"><a href="#2-2-流量控制,按服务分配流量,避免滥用" class="headerlink" title="2.2 流量控制,按服务分配流量,避免滥用"></a><strong>2.2 流量</strong>控制,按服务分配流量,避免滥用</h4><p> 相信很多做过高并发服务的同学都碰到类似事件:某天A君突然发现自己的接口请求量突然涨到之前的10倍,没多久该接口几乎不可使用,并引发连锁反应导致整个系统崩溃。</p><p> 为什么会涨10倍,难道是接口被外人攻击了,以我的经验看一般内部人“作案”可能性更大。之前还见过有同学mapreduce job调用线上服务,分分钟把服务搞死。</p><p> 如何应对这种情况?生活给了我们答案:比如老式电闸都安装了保险丝,一旦有人使用超大功率的设备,保险丝就会烧断以保护各个电器不被强电流给烧坏。同理我们的接口也需要安装上“保险丝”,以防止非预期的请求对系统压力过大而引起的系统瘫痪,当流量过大时,可以采取拒绝或者引流等机制。</p><h3 id="3-做好自己"><a href="#3-做好自己" class="headerlink" title="3 做好自己"></a>3 做好自己</h3><p> 做好自己是个非常大的话题,从需求分析、架构设计 、代码编写、测试、code review、上线、线上服务运维等阶段都可以重点展开介绍,这次简单分享下架构设计、代码编写上的几条经验原则。</p><h4 id="3-1-单一职责原则"><a href="#3-1-单一职责原则" class="headerlink" title="3.1 单一职责原则"></a>3.1 单一职责原则</h4><p> 对于工作了两年以上的同学来说,设计模式应该好好看看,我觉得各种具体的设计模式其实并不重要,重要的是背后体现的原则。比如单一职责原则,在我们的需求分析、架构设计、编码等各个阶段都非常有指导意义。</p><p> 在需求分析阶段,单一职责原则可以界定我们服务的边界,如果服务边界如果没界定清楚,各种合理的不合理的需求都接,最后导致服务出现不可维护、不可扩展、故障不断的悲哀结局。</p><p> 对于架构来讲,单一职责也非常重要。比如读写模块放置在一起,导致读服务抖动非常厉害,如果读写分离那将大大提高读服务的稳定性(读写分离);比如一个服务上同时包含了订单、搜索、推荐的接口,那么如果推荐出了问题可能影响订单的功能,那这个时候就可以将不同接口拆分为独立服务,并独立部署,这样一个出问题也不会影响其他服务(资源隔离);又比如我们的图片服务使用独立域名、并放置到cdn上,与其它服务独立(动静分离)。</p><p> 从代码角度上讲,一个类只干一件事情,如果你的类干了多个事情,就要考虑将他分开。这样做的好处是非常清晰,以后修改起来非常方便,对其它代码的影响就很小。再细粒度看类里的方法,一个方法也只干一个事情,即只有一个功能,如果干两件事情,那就把它分开,因为修改一个功能可能会影响到另一个功能。</p><h4 id="3-2-控制资源的使用"><a href="#3-2-控制资源的使用" class="headerlink" title="3.2 控制资源的使用"></a>3.2 控制资源的使用</h4><p> 写代码脑子一定要绷紧一根弦,认知到我们所在的机器资源是有限的。机器资源有哪些?cpu、内存、网络、磁盘等,如果不做好保护控制工作,一旦某一资源满负荷,很容易导致出现线上问题。</p><h5 id="3-2-1-CPU资源怎么限制?"><a href="#3-2-1-CPU资源怎么限制?" class="headerlink" title="3.2.1 CPU资源怎么限制?"></a>3.2.1 CPU资源怎么限制?</h5><p><strong>a)计算算法优化</strong></p><p> 如果服务需要进行大量的计算,比如推荐排序服务,那么务必对你的计算算法进行优化。</p><p><strong>b)锁</strong></p><p> 对于很多服务而言,没有那么多耗费计算资源的算法,但cpu使用率也很高,这个时候需要看看锁的使用情况,我的建议是如无必要,尽量不用显式使用锁。</p><p><strong>c) 习惯问题</strong></p><p> 比如写循环的时候,千万要检查看看是否能正确退出,有些时候一不小心,在某些条件下就成为死循环,很著名的案例就是《<a href="http://www.cnblogs.com/ITtangtang/p/3966467.html" target="_blank" rel="noopener">多线程下HashMap的死循环问题</a>》。比如集合遍历时候使用性能较差的遍历方式、String +检查,如果有超过多个String相加,是否使用StringBuffer.append?</p><p><strong>d)尽量使用线程池</strong></p><p> 通过线程池来限制线程的数目,避免线程过多造成的线程上下文切换的开销。</p><p><strong>e)jvm参数调优</strong></p><p> jvm参数也会影响cpu的使用</p><h5 id="3-2-2-内存资源怎么限制?"><a href="#3-2-2-内存资源怎么限制?" class="headerlink" title="3.2.2 内存资源怎么限制?"></a>3.2.2 内存资源怎么限制?</h5><p><strong>a)Jvm参数设置</strong></p><p> 通过JVM参数的设置来限制内存使用,jvm参数调优比较靠经验</p><p><strong>b)初始化java集合类大小</strong></p><p> 使用java集合类的时候尽量初始化大小,在长连接服务等耗费内存资源的服务中这种优化非常重要;</p><p><strong>c)使用内存池/对象池</strong></p><p><strong>d)使用线程池的时候一定要设置队列的最大长度</strong></p><p> 之前看过好多起故障都是由于队列最大长度没有限制最后导致内存溢出。</p><p><strong>e)如果数据较大避免使用本地缓存</strong></p><p> 如果数据量较大,可以考虑放置到分布式缓存如redis、tair等,不然gc都可能把自己服务卡死;</p><p><strong>f)对缓存数据进行压缩</strong></p><p> 比如之前做推荐相关服务时,需要保存用户偏好数据,如果直接保存可能有12G,后来采用短文本压缩算法直接压缩到6G,不过这时一定要考虑好压缩解压缩算法的cpu使用率、效率与压缩率的平衡,一些压缩率很高但是性能很差的算法,也不适合线上实时调用。</p><p> 有些时候直接使用probuf来序列化之后保存,这样也能节省内存空间。</p><p><strong>g)清楚第三方软件实现细节,精确调优</strong></p><p> 在使用第三方软件时,只有清楚细节后才知道怎么节约内存,这点我在实际工作中深有体会,比如之前在阅读过lucene的源码后发现我们的索引文件原来是可以压缩的,而这在说明文档中都找不到。</p><h5 id="3-2-3-网络资源怎么限制?"><a href="#3-2-3-网络资源怎么限制?" class="headerlink" title="3.2.3 网络资源怎么限制?"></a>3.2.3 <strong>网络资源怎么限制?</strong></h5><p><strong>a)减少调用的次数</strong> </p><p> 减少调用的次数?经常看到有同学在循环里用redis/tair的get,如果意识到这里面的网络开销的话就应该使用批量处理;又如在推荐服务中经常遇到要去多个地方去取数据,一般采用多线程并行去取数据,这个时候不仅耗费cpu资源,也耗费网络资源,一种在实际中常常采用的方法就是先将很多数据离线存储到一块 ,这时候线上服务只要一个请求就能将所有数据获取。</p><p><strong>b)减少传输的数据量</strong></p><p> 一种方法是压缩后传输,还有一种就是按需传输,比如经常遇到的getData(int id),如果我们返回该id对应的Data所有信息,一来人家不需要,二来数据量传输太大,这个时候可以改为getData(int id, List<string> fields),使用方传输相应的字段过来,服务端只返回使用方需要的字段即可。</string></p><h5 id="3-2-4-磁盘资源怎么限制?"><a href="#3-2-4-磁盘资源怎么限制?" class="headerlink" title="3.2.4 磁盘资源怎么限制?"></a>3.2.4 <strong>磁盘资源怎么限制?</strong></h5><pre><code> 打日志要控制量,并定期清理。1)只打印关键的异常日志;2)对日志大小进行监控报警。我有一次就遇到了第三方服务挂了,然后我这边就不断打印调用该第三方服务异常的日志,本来我的服务有降级方案,如果第三方服务挂了会自动使用其它服务,但是突然收到报警说我服务挂了,登上机器一看才知道是磁盘不够导致的崩溃;3)定期对日志进行清理,比如用crontab,每隔几天对日志进行清理;4)打印日志到远端,对于一些比较重要的日志可以直接将日志打印到远端HDFS文件系统里;</code></pre><h4 id="3-3-避免单点"><a href="#3-3-避免单点" class="headerlink" title="3.3 避免单点"></a>3.3 避免单点</h4><p> 不要把鸡蛋放在一个篮子上!从大层次上讲服务可以多机房部署、异地多活;从自己设计角度上讲,服务应该能做到水平扩展。</p><p> 对于很多无状态的服务,通过nginx、zookeeper能轻松实现水平扩展;</p><p> 对数据服务来说,怎么避免单点呢?简而言之、可以通过分片、分层等方式来实现,后面会有个博文总结。</p><h3 id="4-小结"><a href="#4-小结" class="headerlink" title="4 小结"></a>4 小结</h3><p> 如何避免故障?我的经验浓缩为一句:“<strong>怀疑第三方,防备使用方,做好自己</strong>”,大家也可以思考、总结并分享下自己的经验。</p><p>原文地址:<a href="http://www.cnblogs.com/LBSer" target="_blank" rel="noopener">http://www.cnblogs.com/LBSer</a></p>]]></content>
<categories>
<category> 架构 </category>
</categories>
<tags>
<tag> 系统设计 </tag>
<tag> 架构 </tag>
</tags>
</entry>
<entry>
<title>如果你在大学一开始就看了这篇文章,离开时已经是高级码农呢</title>
<link href="/2019/01/29/ru-guo-ni-zai-da-xue-yi-kai-shi-jiu-kan-liao-zhe-pian-wen-zhang-chi-kai-shi-yi-jing-shi-gao-ji-ma-nong-ni/"/>
<url>/2019/01/29/ru-guo-ni-zai-da-xue-yi-kai-shi-jiu-kan-liao-zhe-pian-wen-zhang-chi-kai-shi-yi-jing-shi-gao-ji-ma-nong-ni/</url>
<content type="html"><![CDATA[<h2 id="如果大学里你一开始就看了这篇文,离开时已经是高级码农"><a href="#如果大学里你一开始就看了这篇文,离开时已经是高级码农" class="headerlink" title="如果大学里你一开始就看了这篇文,离开时已经是高级码农"></a>如果大学里你一开始就看了这篇文,离开时已经是高级码农</h2><p>作者知乎ID:三级狗</p><p>原文地址:<a href="https://zhuanlan.zhihu.com/p/38338888" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/38338888</a></p><blockquote><p><strong>如果嫌篇幅太长的,可以直接翻到最后看总结,那里言简意赅</strong></p></blockquote><p>先简单介绍一下我的情况:大概去年的这个时候从学校毕业,二本A软件工程,现在在北上广深之一的某卫星城从事互联网相关工作,月薪勉强养活自己。看上去一份很没说服力的简历,希望我下面的话,不会让你有这个感觉。</p><p>对于如何提升自己的编程能力。其他的回答中很多人都说了,这没什么捷径,就是多练,问题是并没有人说怎么练?一天敲50遍Hello Word算多练嘛?当然,各路大佬自然是知道该怎么练的,只是懒得在逼乎上浪费时间。我属于比较爱扯淡的,就在这里长篇大论的扯一波儿,不喜勿喷。</p><p><strong>首先、什么算你所谓的编程能力?</strong></p><p>我们对一项技能的掌握程度往往很难量化,对于编程能力的考量可能比较抽象,我们来类比比较直观的其他技能。比如说什么叫会弹吉他?我们说一个人吉他玩的好,这个人会弹吉他,是指他会弹《小星星》?还是会弹岸部真明的《time travel》?(力荐,好听!)恐怕都不是,我们对于会弹吉他的认知,应当是随手给他一个不熟悉的谱子,你也能很快的用吉他精彩的演奏,我们才说这个人吉他玩的真6。那编程也是,我们所希望的编程能力,指的是会写双向链表还是会写二叉树?恐怕都不是,<strong>我们所指的会编程,是指他在应对各种不同的业务需求时,都能很快的将业务逻辑转化成编程逻辑,并且编码实现的能力。</strong></p><p><strong>那么、如何来锻炼这种能力?</strong></p><p>前段时间在罗胖的《得到app》上听的一篇精品课,非常受启发。一位老师讲如何高效地学习一项技能,他用两年的时间就从零基础达到了专业级的弹指吉他大师的水平,他所使用的方法很值得借鉴。</p><p>内容大概是这样:</p><blockquote><p>他一开始接触吉他,没有从基础开始练,而是直接挑战难度极高的世界名曲开始演奏。可想而知这难度是极大的,没有任何基础的他,很多和弦都压不住。尤其对与刚玩吉他的人,十指连心啊,压弦的那只手是钻心的疼。一开始一句完整的都弹不下来,更别提什么扫弹,闷音,切音的技巧。就这样一节一节地弹,经过不懈的努力,他把这首曲子拿了下来。巨大的成就感是自然的,但对于优秀的渴望使得这位大佬感到仍然不能满足。怎么办呢?请教名师!这时候老师告诉他:“一禅呐!所谓知之者不如好之者、好之者不如乐之者,你现在已经能够从弹奏吉他中获得喜悦,现在,请再回过头,从基础开始学起。”这下子他才开始从最基础的乐理开始,什么叫节奏、什么叫旋律、什么叫音阶、什么 C 调 G 调 F 调。</p></blockquote><ul><li>原来之前练到手指快疼死的的指法叫 F 和弦?</li><li>原来之前的曲子里变调是这个意思?</li></ul><p>和上去就啃吉他基础教学不一样,这波儿基础的学习让他任督二脉蹭的一下就通了,仿佛杨过一身雄厚的内劲得黄药师点化一番,实力大增。不但能将那首世界名曲演绎的更加纯熟,对于其他没有演奏过乐曲,只要稍加练习,就能够德芙般顺滑地弹奏下来。</p><p>同样的方法,映射到编程上,就是我想说的学习方法。我很不建议一开始就从基础开始啃,有多少人从大一刚入学立志将来做一个IT大佬,抱着一本《C++ Primer》开始啃,最后啃不到 200 页就去 LOL 上分冲段位了。所以我的建议是,一开始只要会点儿基础语法,就定一个小目标去实现就好了,不必强求每一行代码都是亲自手写。遇到问题就查,百度也可以查书也可以,我一开始写个五子棋小游戏的时候,连数组的声明语法都是查书的。</p><blockquote><p><strong>把你遇到的问题从业务逻辑定位成代码逻辑,然后知道从哪儿可以找到想要的答案</strong></p></blockquote><p>这个能力在未来的工作、编程和面试中非常重要。一两个完整的程序做下来之后,再回过头来从变量、语法、表达式、流程控制、函数….重新去学习这门编程语言,这时候你会不断地发现原来这个地方这么写的原因是这样?原来这个地方是这么实现的,那个地方我还可以这么写。一本枯燥的语法书籍你会很流畅地读下来,甚至还可能读出快感和兴趣,这样一顿操作之后,你可以算真正掌握了一门编程语言,有了自己的理解在里面,并且有对应的应用经验,未来的面试中也可以讲的头头是道。</p><p><strong>这就引出下一个问题,选择什么样的程序作为上手项目</strong></p><ol><li>首先!是你做出来的程序,一定要好看!</li></ol><p>和语言没关系,不管是HTML+CSS还是Python还是C。我承认我是颜控,但这和个人癖好没什么关系。一个酷炫的UI设计将在很大程度上提升你的成就感以及你程序的品质。这里说的是品质,也就是逼格,用户体验好了,逼格上去了,自己的成就感油然而生,对于编程的兴趣也就上来了。下面上一波儿图来佐证一下我的观点:</p><ul><li>大一的时候用纯C语言做的五子棋,比较遗憾的是AI算法是整合网上别人的:</li></ul><p><img src="http://images.zhoudl.top/study_share/1.jpg" alt=""></p><p><img src="http://images.zhoudl.top/study_share/2.jpg" alt=""></p><p>是纯C你没听错,有一个简单易用的函数库,叫做<code>easyx</code>可以了解一波。就按官网教程看几篇下来做成这样没有丝毫问题。当时课程设计,同学们看到这个程序佩服的和关老爷一样,老师也对我赞不绝口,毕竟在大家都用控制台画界面的时候,我具有划时代意义地用上了鼠标。只有我自己知道这其中只不过是些花拳绣腿。</p><ul><li>这个年代比较近了,使用了<code>C++ Qt</code>,王者荣耀风格的连连看:</li></ul><p><img src="http://images.zhoudl.top/study_share/31.jpg" alt=""></p><p><img src="http://images.zhoudl.top/study_share/4.jpg" alt=""></p><p><img src="http://images.zhoudl.top/study_share/5.jpg" alt=""></p><p><img src="http://images.zhoudl.top/study_share/6.jpg" alt=""></p><ul><li>最后这个更亮了,得意之作也是毕业设计,准备擦亮眼:</li></ul><p>播放器主界面</p><p><img src="http://images.zhoudl.top/study_share/7.jpg" alt=""></p><p>桌面歌词</p><p><img src="http://images.zhoudl.top/study_share/8.jpg" alt=""></p><p>手机遥控,同wifi下就可以遥控,还能定时关机</p><p><img src="http://images.zhoudl.top/study_share/9.jpg" alt=""></p><p>音乐魔方,旁边是酷狗的音乐魔方。有对比,才显的我更有逼格。</p><p><img src="http://images.zhoudl.top/study_share/10.jpg" alt=""></p><p>设置页面,很尴尬。这里都是假图,左侧五个按钮点击,右侧是五张图片来回换。-_-||</p><p><img src="http://images.zhoudl.top/study_share/11.jpg" alt=""></p><p>除了这还支持全局热键、播放MV、系统托盘等等。</p><p>如果你张大嘴哇了出来,就说明这些图片确实震撼到你了,试想如果你也把经手的程序做成这个样子,谁还会说对编程没兴趣?兴趣有了,动力就有了,项目搞完回过头来系统地学习语法基础,不但不再枯燥,而且理解也会更加深刻。</p><p>所以、做程序的时候想办法把程序做的漂亮一些,自己会P图就自己搞素材,自己不会就去网上下漂亮的UI素材整合到你的程序里,相信我、这些花拳绣腿不用花多大功夫就可以做到。</p><p>2.刚才说的是开发上手程序的建议的第一点,要好看。还有非常值得推荐的一点:把你的程序当做产品来看待。</p><p>当你把程序作为产品来看待的时候,你就会考虑到一个非常重要的因素:用户体验。假设你的产品要上线,是要给别人来用的一个程序。用户体验会倒逼你对你的程序作出一些调整和优化。有些调整优化力所能及,有些难度高到可能你实现不了,没关系,这对于你来说都将是成长很大的一步。在调整和优化的过程中,你的代码可能越改越乱,最后改不下去了,你会发现你的代码需要从底向上重新整理编写才能满足需求,恭喜你,这是编程能力提升的又一个重要阶段:Code refactoring 代码重构。无论重构的效果如何,一定会比上一个版本有所进步,这就是提升和成长。也将成为你编程经验中可贵的积累。</p><p>就以我刚才的音乐播放器为例,作为毕业设计已经绰绰有余,但是如果以产品的眼光来定位,它还存在着很多的问题,我大致记录如下:</p><p><img src="http://images.zhoudl.top/study_share/12.jpg" alt=""></p><p>随着我编程能力的提升,这些问题我在解决的过程中就发现,我的代码简直是一塌糊涂!现在存在的问题几乎到了不重构无法解决的地步。所以这个程序的重构后来被正式提上议程,并且因为我的懒惰成功搁浅了长达一年之久,现在还凉着… …</p><p><strong>学校学的东西到底还有没有用?</strong></p><p>编程能力的提升说完了,你可能会有这样的疑问了,学校学的东西还有用吗?好像按照我上面的办法自己学也能小有所成。</p><p>答案是毋庸置疑的,学校学的东西必然有用!</p><p>先来看看大学都学了什么?高数、大物、英语、C语言、数据结构、算法、计算机网络、计算机组成原理等等。显然,都是些既枯燥又复杂的知识,后来做了开发的大家都知道,这些东西很难直接应用在生产实际中,相反,很多互联网公司招聘,要求的是熟练使用各种框架,前端要vue,React,java要ssh,python要Django,C++要Qt等等,大学里不教,企业招聘又要,怎么办?这不是多了雨后春笋般的培训机构么…</p><p>甚至有人因此放出豪言壮语了:学校学的东西根本没卵用,不如早早学个流行的框架出去找工作。<br>难道说大学讲的东西真没用?既然这些东西我们工作中可能用不到,为什么学校还要讲?而不讲我们用得到的呢?我先来举几个实例:</p><p>1、你看到的大多数互联网公司招聘都要求熟悉这那的框架,但是越是大型互联网公司,比如BAT这种体量,学历要求是研究生到博士,数据结构和算法要求是精通,英语要求是6级。框架?不存在的,我招你进来很可能就是要参与开发我们自己的框架呢,你会用别人的框架用的再6也不是我想要的。</p><p>2、如果你研究人工智能领域的一些技术,机器学习、深度学习、神经网络什么的,一些似曾相识的名词开始映入眼帘:贝叶斯?逻辑回归?支持向量机?拉格朗日乘子法?数学让我寸步难行…</p><p>3、如果你学习前百度首席科学家吴恩达的全套机器学习课程,通篇没有说过一个中国字儿的,英语让我寸步难行…</p><p>4、答主之前接触过一款C++游戏引擎叫Cocos2d-x,设置一个小人跑酷,需要小人可以跑可以跳,自己会下落,可以碰撞检测。算坐标算的太麻烦,引入了一个叫Box2D的物理引擎。从此,小人的像前移动,变成了给小人设置一个像前的初速度。小人的自由掉落,变成了设置全局的重力加速度。小人的跳跃,变成了向上给一个初速度。碰撞检测变成了设置小人和物块为刚体,物理让我寸步难行。。。<br>5、答主一个朋友<br><a href="https://www.zhihu.com/people/67c546ed9d310fdc581e3cac66ffdafa" target="_blank" rel="noopener">@探索之言</a><br>在游戏行业做引擎优化和图像处理的,有一天跟我打电话,问我记不记得求空间中两个几何体相交截面的公式?我回了个黑人问号脸,后来他又打电话说起最近在做GPU编程,我为了不失逼格仍然不懂装懂的聊,他听出我啥也不懂,之后就再没给我打过电话了<br>(这里的应用场景是很常见的,在很多3D游戏的物体的投影,不全是投在地上的,有些投影会投在其他物体上,那这个时候显示出来的投影的样子,就是刚刚他问我的横截面。CS游戏中的喷漆也是这个原理。)</p><p>6、前段时间写一个贪吃蛇的小游戏,蛇的身体我封装成了一个类,每一个类放了另一个身体节点的指针,巧妙地实现了贪吃蛇的走步方式,我沾沾自喜地找同事炫耀我靠大脑创造出的高科技算法,被同事怼了回来:“这特么不是链表么?!”。</p><p>从上面的实例你应该已经能看出来了,<strong>说大学知识没用的那些人,不是因为大学知识真的没用,而是因为以他的能耐和眼光,还看不到大学知识用在什么地方。</strong>编程达到一些深度,开始需要自己造一些轮子用的时候,数据结构和算法是不掌握不行的,一些高精尖领域的研究,比如图像识别、大数据、人工智能等等,玩的全是数学。而我们所说的这框架那框架,全部是基于我们大学里学的知识基础封装出来的,当你对编程研究到一定深度的时候,大学里落下的基础,一样也跑不了。</p><p>这就是为什么说大学的东西不见得用得到,而大学还一定要讲,因为这些都是最基础最核心的东西。坦白讲,<strong>框架、编程语言都是用来解决问题的工具,工具用的好坏只能决定你跑的多块,而基础有多么牢固,才能决定你跑的多远。</strong></p><p><strong>未来你要成为怎样的程序员?</strong><br>这一点跟你的题目没太大关系了,作为程序员分享出来与大家共勉。</p><p><strong>这个世界上有两种人才,一种是所有的事情都知道一些,另一种是知道一些事情的所有,而这两者兼具的人,就成了我们口中的业界精英、行业翘楚。</strong></p><p>当然,成为翘楚光靠努力不够,还要一些运气和天赋,但这并不代表我们普通人不能向翘楚看齐,即使成不了精英,成为大佬还是有可能实现的。所以,对未来的规划,我的建议是从深度与广度两个维度来成长,定义好几个时间节点,在这些时间里就向这既定的目标来努力,如果中途感觉跑偏了,就回望初心,重新把自己划回正规。</p><p>就以我为例,我的大学基本是凉凉了,编程能力不错,但是成绩真的是有些不忍直视。我对自己明确的规划也是在快毕业的时候才有,那时候刚22岁,目标是在25岁之前,打造自己的广度,争取各个领域都有所涉猎有所了解,在25岁到28岁,选择一个自己喜欢的领域深入研究,打造自己的深度,争取在30岁之前,成为一个深度与广度兼具的大佬。在这期间,薪资不是我考虑的主要因素,薪资只是你个人能力的附属品(这话俺妈讲的)考虑的第一因素是成长。</p><p>扯得哲学点就是:<strong>不要着急花更多的时间去赚钱,去花时间,让自己的时间更值钱。</strong>当然,现在的我还差的很远。</p><p>鸡汤写完了,以上只是我的个人观点以及经验之谈,希望能对你,对其他在校的在职的程序猿有或多或少的帮助。<br>最后,还有很重要的一点,善于总结。定期回过头来看看,自己都学到了什么掌握了什么欠缺什么,也许你能从中淬炼出一些很有价值的东西,比如今天这篇鸡汤,总结如下:</p><ol><li><strong>知之者不如好之者,好之者不如乐之者。尝试引导自己对编程产生兴趣,从中获取成就感。</strong></li><li><strong>尝试从实践出发然后再回归到理论,就像上面提到的学吉他的方式方法。</strong></li><li><strong>做出来东西一定要好看,会更有成就感,更容易感兴趣。</strong></li><li><strong>用产品的眼光去定位你的作品,会得到更加意想不到的成长和进步。</strong></li><li><strong>会用工具,能决定你跑的多块,但基础多么牢靠,才决定你能跑多远!</strong></li><li><strong>这个世界上有两种人才,一种是所有的事情都知道一些,另一种是知道一些事情的所有,而这两者兼具的人,就成了我们口中的业界精英、行业翘楚。</strong></li><li><strong>不要着急花更多的时间去赚钱,去花时间,让自己的时间更值钱。</strong></li><li><strong>自己从实践中总结的才能叫真理,看别人说的只能叫鸡汤。</strong></li></ol>]]></content>
<categories>
<category> 猿说 </category>
</categories>
<tags>
<tag> 程序员成长 </tag>
<tag> 如何学习 </tag>
</tags>
</entry>
<entry>
<title>Quartz原理解密</title>
<link href="/2019/01/29/quartz-yuan-li-jie-mi/"/>
<url>/2019/01/29/quartz-yuan-li-jie-mi/</url>
<content type="html"><![CDATA[<h1 id="Quartz原理解密"><a href="#Quartz原理解密" class="headerlink" title="Quartz原理解密"></a>Quartz原理解密</h1><blockquote><p>Author: Dorae<br>原文链接:<a href="https://juejin.im/post/5c3bf24951882523d3201c54?utm_source=gold_browser_extension" target="_blank" rel="noopener">https://juejin.im/post/5c3bf24951882523d3201c54?utm_source=gold_browser_extension</a></p></blockquote><h2 id="一、quartz概述"><a href="#一、quartz概述" class="headerlink" title="一、quartz概述"></a>一、quartz概述</h2><p>quartz是一个用java实现的开源任务调度框架,可以用来创建简单或者复杂的任务调度,并且可以提供许多企业级的功能,比如JTA以及集群等,是当今比较流行的JAVA任务调度框架。</p><h3 id="1-可以用来做什么"><a href="#1-可以用来做什么" class="headerlink" title="1. 可以用来做什么"></a>1. 可以用来做什么</h3><p>Quartz是一个任务调度框架,当遇到以下问题时:</p><ul><li>想在每月25号,自动还款;</li><li>想在每年4月1日给当年自己暗恋的女神发一封匿名贺卡;</li><li>想每隔1小时,备份一下自己的各种资料。</li></ul><p>那么总结起来就是,在一个有规律的时间点做一些事情,并且这个规律可以非常复杂,复杂到了需要一个框架来帮助我们。Quartz的出现就是为了解决这个问题,定义一个触发条件,那么其负责到了特定的时间点,触发相应的job干活。</p><h3 id="2-特点"><a href="#2-特点" class="headerlink" title="2. 特点"></a>2. 特点</h3><ul><li>强大的调度功能,例如丰富多样的调度方法,可以满足各种常规和特殊需求;</li><li>灵活的应用方式,比如支持任务调度和任务的多种组合,支持数据的多种存储(DB,RAM等;</li><li>支持分布式集群,在被Terracotta收购之后,在原来基础上进行了进一步的改造。</li></ul><h2 id="二、quartz基本原理"><a href="#二、quartz基本原理" class="headerlink" title="二、quartz基本原理"></a>二、quartz基本原理</h2><h3 id="1-核心元素"><a href="#1-核心元素" class="headerlink" title="1. 核心元素"></a>1. 核心元素</h3><p>Quartz核心要素有Scheduler、Trigger、Job、JobDetail,其中trigger和job、jobDetail为元数据,而Scheduler为实际进行调度的控制器。</p><ul><li>Trigger</li></ul><p>Trigger用于定义调度任务的时间规则,在Quartz中主要有四种类型的Trigger:SimpleTrigger、CronTrigger、DataIntervalTrigger和NthIncludedTrigger。</p><ul><li>Job&Jodetail</li></ul><p>Quartz将任务分为Job、JobDetail两部分,其中Job用来定义任务的执行逻辑,而JobDetail用来描述Job的定义(例如Job接口的实现类以及其他相关的静态信息)。对Quartz而言,主要有两种类型的Job,StateLessJob、StateFulJob</p><ul><li>Scheduler</li></ul><p>实际执行调度逻辑的控制器,Quartz提供了DirectSchedulerFactory和StdSchedulerFactory等工厂类,用于支持Scheduler相关对象的产生。</p><h3 id="2-核心元素间关系"><a href="#2-核心元素间关系" class="headerlink" title="2. 核心元素间关系"></a>2. 核心元素间关系</h3><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a64cd7144c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h3 id="3-主要线程"><a href="#3-主要线程" class="headerlink" title="3. 主要线程"></a>3. 主要线程</h3><p>在Quartz中,有两类线程,也即执行线程和调度线程,其中执行任务的线程通常用一个线程池维护。线程间关系如图所示。</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a64b28e2da?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><p>在quartz中,Scheduler调度线程主要有两个:regular Scheduler Thread(执行常规调度)和Misfire Scheduler Thread(执行错失的任务)。其中Regular Thread 轮询Trigger,如果有将要触发的Trigger,则从任务线程池中获取一个空闲线程,然后执行与改Trigger关联的job;Misfire Thraed则是扫描所有的trigger,查看是否有错失的,如果有的话,根据一定的策略进行处理。</p><h3 id="4-数据存储"><a href="#4-数据存储" class="headerlink" title="4. 数据存储"></a>4. 数据存储</h3><p>Quartz中的trigger和job需要存储下来才能被使用。Quartz中有两种存储方式:RAMJobStore,JobStoreSupport,其中RAMJobStore是将trigger和job存储在内存中,而JobStoreSupport是基于jdbc将trigger和job存储到数据库中。RAMJobStore的存取速度非常快,但是由于其在系统被停止后所有的数据都会丢失,所以在集群应用中,必须使用JobStoreSupport。其中表结构如表所示。</p><table><thead><tr><th>Table name</th><th>Description</th></tr></thead><tbody><tr><td>QRTZ_CALENDARS</td><td>存储Quartz的Calendar信息</td></tr><tr><td>QRTZ_CRON_TRIGGERS</td><td>存储CronTrigger,包括Cron表达式和时区信息</td></tr><tr><td>QRTZ_FIRED_TRIGGERS</td><td>存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息</td></tr><tr><td>QRTZ_PAUSED_TRIGGER_GRPS</td><td>存储已暂停的Trigger组的信息</td></tr><tr><td>QRTZ_SCHEDULER_STATE</td><td>存储少量的有关Scheduler的状态信息,和别的Scheduler实例</td></tr><tr><td>QRTZ_LOCKS</td><td>存储程序的悲观锁的信息</td></tr><tr><td>QRTZ_JOB_DETAILS</td><td>存储每一个已配置的Job的详细信息</td></tr><tr><td>QRTZ_SIMPLE_TRIGGERS</td><td>存储简单的Trigger,包括重复次数、间隔、以及已触的次数</td></tr><tr><td>QRTZ_BLOG_TRIGGERS</td><td>Trigger作为Blob类型存储</td></tr><tr><td>QRTZ_TRIGGERS</td><td>存储已配置的Trigger的信息</td></tr><tr><td>QRTZ_SIMPROP_TRIGGERS</td></tr></tbody></table><h2 id="三、quartz集群原理"><a href="#三、quartz集群原理" class="headerlink" title="三、quartz集群原理"></a>三、quartz集群原理</h2><p>一个Quartz集群中的每个节点是一个独立的Quartz应用,它又管理着其他的节点。这就意味着你必须对每个节点分别启动或停止。Quartz集群中,独立的Quartz节点并不与另一其的节点或是管理节点通信,而是通过相同的数据库表来感知到另一Quartz应用的,如图所示。</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a64b5a2943?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h2 id="四、quartz主要流程"><a href="#四、quartz主要流程" class="headerlink" title="四、quartz主要流程"></a>四、quartz主要流程</h2><h3 id="1-启动流程"><a href="#1-启动流程" class="headerlink" title="1. 启动流程"></a>1. 启动流程</h3><p>若quartz是配置在spring中,当服务器启动时,就会装载相关的bean。SchedulerFactoryBean实现了InitializingBean接口,因此在初始化bean的时候,会执行afterPropertiesSet方法,该方法将会调用SchedulerFactory(DirectSchedulerFactory 或者 StdSchedulerFactory,通常用StdSchedulerFactory)创建Scheduler。SchedulerFactory在创建quartzScheduler的过程中,将会读取配置参数,初始化各个组件,关键组件如下:</p><ol><li><p><strong>ThreadPool</strong>:一般是使用SimpleThreadPool,SimpleThreadPool创建了一定数量的WorkerThread实例来使得Job能够在线程中进行处理。WorkerThread是定义在SimpleThreadPool类中的内部类,它实质上就是一个线程。在SimpleThreadPool中有三个list:workers-存放池中所有的线程引用,availWorkers-存放所有空闲的线程,busyWorkers-存放所有工作中的线程; 线程池的配置参数如下所示:</p><p>org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=3 org.quartz.threadPool.threadPriority=5</p></li><li><p><strong>JobStore</strong>:分为存储在内存的RAMJobStore和存储在数据库的JobStoreSupport(包括JobStoreTX和JobStoreCMT两种实现,JobStoreCMT是依赖于容器来进行事务的管理,而JobStoreTX是自己管理事务),若要使用集群要使用JobStoreSupport的方式;</p></li><li><p><strong>QuartzSchedulerThread</strong>:用来进行任务调度的线程,在初始化的时候paused=true,halted=false,虽然线程开始运行了,但是paused=true,线程会一直等待,直到start方法将paused置为false;</p></li></ol><p>另外,SchedulerFactoryBean还实现了SmartLifeCycle接口,因此初始化完成后,会执行start()方法,该方法将主要会执行以下的几个动作:</p><ol><li>创建ClusterManager线程并启动线程:该线程用来进行集群故障检测和处理,将在下文详细讨论;</li><li>创建MisfireHandler线程并启动线程:该线程用来进行misfire任务的处理,将在下文详细讨论;</li><li>置QuartzSchedulerThread的paused=false,调度线程才真正开始调度;</li></ol><p>Quartz的整个启动流程如图所示。</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a64b6a1c91?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h3 id="2-QuartzSchedulerThread线程"><a href="#2-QuartzSchedulerThread线程" class="headerlink" title="2. QuartzSchedulerThread线程"></a>2. QuartzSchedulerThread线程</h3><p>QuartzSchedulerThread线程是实际执行任务调度的线程,其中主要代码如下。</p><pre class=" language-java"><code class="language-java"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>halted<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> availThreadCount <span class="token operator">=</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getThreadPool</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">blockForAvailableThreads</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> triggers <span class="token operator">=</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getJobStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">acquireNextTriggers</span><span class="token punctuation">(</span>now <span class="token operator">+</span> idleWaitTime<span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">(</span>availThreadCount<span class="token punctuation">,</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getMaxBatchSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getBatchTimeWindow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">long</span> triggerTime <span class="token operator">=</span> triggers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getNextFireTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">long</span> timeUntilTrigger <span class="token operator">=</span> triggerTime <span class="token operator">-</span> now<span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>timeUntilTrigger <span class="token operator">></span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> now <span class="token operator">=</span> System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> timeUntilTrigger <span class="token operator">=</span> triggerTime <span class="token operator">-</span> now<span class="token punctuation">;</span> <span class="token punctuation">}</span> List<span class="token operator"><</span>TriggerFiredResult<span class="token operator">></span> bndle <span class="token operator">=</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getJobStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">triggersFired</span><span class="token punctuation">(</span>triggers<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> res<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> JobRunShell shell <span class="token operator">=</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getJobRunShellFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">createJobRunShell</span><span class="token punctuation">(</span>bndle<span class="token punctuation">)</span><span class="token punctuation">;</span> shell<span class="token punctuation">.</span><span class="token function">initialize</span><span class="token punctuation">(</span>qs<span class="token punctuation">)</span><span class="token punctuation">;</span> qsRsrcs<span class="token punctuation">.</span><span class="token function">getThreadPool</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">runInThread</span><span class="token punctuation">(</span>shell<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><ol><li>先获取线程池中的可用线程数量(若没有可用的会阻塞,直到有可用的);</li><li>获取30m内要执行的trigger(即acquireNextTriggers): 获取trigger的锁,通过select …for update方式实现;获取30m内(可配置)要执行的triggers(需要保证集群节点的时间一致),若@ConcurrentExectionDisallowed且列表存在该条trigger则跳过,否则更新trigger状态为ACQUIRED(刚开始为WAITING);插入firedTrigger表,状态为ACQUIRED;(注意:在RAMJobStore中,有个timeTriggers,排序方式是按触发时间nextFireTime排的;JobStoreSupport从数据库取出triggers时是按照nextFireTime排序);</li><li>等待直到获取的trigger中最先执行的trigger在2ms内;</li><li>triggersFired:<ol><li>更新firedTrigger的status=EXECUTING;</li><li>更新trigger下一次触发的时间;</li><li>更新trigger的状态:无状态的trigger->WAITING,有状态的trigger->BLOCKED,若nextFireTime==null ->COMPLETE;</li><li>commit connection,释放锁;</li></ol></li><li>针对每个要执行的trigger,创建JobRunShell,并放入线程池执行:<ol><li>execute:执行job</li><li>获取TRIGGER_ACCESS锁</li><li>若是有状态的job:更新trigger状态:BLOCKED->WAITING,PAUSED_BLOCKED->BLOCKED</li><li>若@PersistJobDataAfterExecution,则updateJobData</li><li>删除firedTrigger</li><li>commit connection,释放锁</li></ol></li></ol><p>调度线程的执行流程如图所示。</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a64b434d22?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><p>调度过程中Trigger状态变化如图所示。</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a64cff820c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h3 id="3-MisfireHandler线程"><a href="#3-MisfireHandler线程" class="headerlink" title="3. MisfireHandler线程"></a>3. MisfireHandler线程</h3><p>下面这些原因可能造成 misfired job:</p><ol><li>系统因为某些原因被重启。在系统关闭到重新启动之间的一段时间里,可能有些任务会被 misfire;</li><li>Trigger 被暂停(suspend)的一段时间里,有些任务可能会被 misfire;</li><li>线程池中所有线程都被占用,导致任务无法被触发执行,造成 misfire;</li><li>有状态任务在下次触发时间到达时,上次执行还没有结束;为了处理 misfired job,Quartz 中为 trigger 定义了处理策略,主要有下面两种:<ul><li>MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:针对 misfired job 马上执行一次;</li><li>MISFIRE_INSTRUCTION_DO_NOTHING:忽略 misfired job,等待下次触发;默认是MISFIRE_INSTRUCTION_SMART_POLICY,该策略在CronTrigger中=MISFIRE_INSTRUCTION_FIRE_ONCE_NOW线程默认1分钟执行一次;在一个事务中,默认一次最多recovery 20个;</li></ul></li></ol><p>执行流程:</p><ol><li>若配置(默认为true,可配置)成获取锁前先检查是否有需要recovery的trigger,先获取misfireCount;</li><li>获取TRIGGER_ACCESS锁;</li><li>hasMisfiredTriggersInState:获取misfired的trigger,默认一个事务里只能最大20个misfired trigger(可配置),misfired判断依据:status=waiting,next_fire_time < current_time-misfirethreshold(可配置,默认1min)</li><li>notifyTriggerListenersMisfired</li><li>updateAfterMisfire:获取misfire策略(默认是MISFIRE_INSTRUCTION_SMART_POLICY,该策略在CronTrigger中=MISFIRE_INSTRUCTION_FIRE_ONCE_NOW),根据策略更新nextFireTime;</li><li>将nextFireTime等更新到trigger表;</li><li>commit connection,释放锁8.如果还有更多的misfired,sleep短暂时间(为了集群负载均衡),否则sleep misfirethreshold时间,后继续轮询;</li></ol><p>misfireHandler线程执行流程如图所示:</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a66aa45772?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h3 id="4-ClusterManager集群管理线程"><a href="#4-ClusterManager集群管理线程" class="headerlink" title="4. ClusterManager集群管理线程"></a>4. ClusterManager集群管理线程</h3><p>初始化:</p><p>failedInstance=failed+self+firedTrigger表中的schedulerName在scheduler_state表中找不到的(孤儿)</p><p>线程执行:</p><p>每个服务器会定时(org.quartz.jobStore.clusterCheckinInterval这个时间)更新SCHEDULER_STATE表的LAST_CHECKIN_TIME,若这个字段远远超出了该更新的时间,则认为该服务器实例挂了;</p><p>注意:每个服务器实例有唯一的id,若配置为AUTO,则为hostname+current_time</p><p>线程执行的具体流程:</p><ol><li>检查是否有超时的实例failedInstances;</li><li>更新该服务器实例的LAST_CHECKIN_TIME; 若有超时的实例:</li><li>获取STATE_ACCESS锁;</li><li>获取超时的实例failedInstances;</li><li>获取TRIGGER_ACCESS锁;</li><li>clusterRecover:<ul><li>针对每个failedInstances,通过instanceId获取每个实例的firedTriggers;</li><li>针对每个firedTrigger:<ul><li>更新trigger状态:<ul><li>BLOCKED->WAITING</li><li>PAUSED_BLOCKED->PAUSED</li><li>ACQUIRED->WAITING</li></ul></li><li>若firedTrigger不是ACQUIRED状态(在执行状态),且jobRequestRecovery=true: 创建一个SimpleTrigger,存储到trigger表,status=waiting,MISFIRE_INSTR=MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY.</li><li>删除firedTrigger</li></ul></li></ul></li></ol><p>clusterManager线程执行时序图如图1-8所示:</p><p><img src="https://user-gold-cdn.xitu.io/2019/1/14/1684a2a66eee57d6?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h2 id="五、注意问题"><a href="#五、注意问题" class="headerlink" title="五、注意问题"></a>五、注意问题</h2><ol><li><strong>时间同步问题</strong></li></ol><p>Quartz实际并不关心你是在相同还是不同的机器上运行节点。当集群放置在不同的机器上时,称之为水平集群。节点跑在同一台机器上时,称之为垂直集群。对于垂直集群,存在着单点故障的问题。这对高可用性的应用来说是无法接受的,因为一旦机器崩溃了,所有的节点也就被终止了。对于水平集群,存在着时间同步问题。</p><p>节点用时间戳来通知其他实例它自己的最后检入时间。假如节点的时钟被设置为将来的时间,那么运行中的Scheduler将再也意识不到那个结点已经宕掉了。另一方面,如果某个节点的时钟被设置为过去的时间,也许另一节点就会认定那个节点已宕掉并试图接过它的Job重运行。最简单的同步计算机时钟的方式是使用某一个Internet时间服务器(Internet Time Server ITS)。</p><ol><li><strong>节点争抢Job问题</strong></li></ol><p>因为Quartz使用了一个随机的负载均衡算法,Job以随机的方式由不同的实例执行。Quartz官网上提到当前,还不存在一个方法来指派(钉住) 一个 Job 到集群中特定的节点。</p><ol><li><strong>从集群获取Job列表问题</strong></li></ol><p>当前,如果不直接进到数据库查询的话,还没有一个简单的方式来得到集群中所有正在执行的Job列表。请求一个Scheduler实例,将只能得到在那个实例上正运行Job的列表。Quartz官网建议可以通过写一些访问数据库JDBC代码来从相应的表中获取全部的Job信息。</p>]]></content>
<categories>
<category> 框架 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> Quartz </tag>
</tags>
</entry>
<entry>
<title>日志脱敏</title>
<link href="/2019/01/29/ri-zhi-tuo-min/"/>
<url>/2019/01/29/ri-zhi-tuo-min/</url>
<content type="html"><![CDATA[<blockquote><p> 本文首发于一个朋友的公众号: java界的小学生</p></blockquote><h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><p>在日常工作中,日志处理是我们每一个程序员必备的素质,但是在有些场景下客户信息敏感,需要进行某些字段,或者某部分字段的脱敏处理。接到需求我们开始操刀!</p><h3 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h3><p>处理字段的方式多种多样,如何方便,高效才是关键,众所周知在java中最好的处理方式就是封装,即,对程序员暴露出的最好是一个统一的<code>API</code>,不关心具体的处理逻辑,能拿到想要的返回值就好。</p><h3 id="实现第一版"><a href="#实现第一版" class="headerlink" title="实现第一版"></a>实现第一版</h3><p>由于在RPC调用过程当中,大部分接口的参数封装数据类型都是<code>Map</code>,所以在此先针对Map形式实现日志脱敏功能</p><h3 id="实现思路:"><a href="#实现思路:" class="headerlink" title="实现思路:"></a>实现思路:</h3><p>有两种实现方法:</p><pre class=" language-tex"><code class="language-tex">第一种:写死配置第二种:使用注解驱动由于写死配置的扩展性实在是差,所以我们本次实现主要是注解驱动</code></pre><h3 id="定义注解"><a href="#定义注解" class="headerlink" title="定义注解"></a>定义注解</h3><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: DesensitizedAnnotation * @Description: 注解类 * @Author: 尚先生 * @CreateDate: 2019/1/24 17:42 * @Version: 1.0 */</span><span class="token annotation punctuation">@Target</span><span class="token punctuation">(</span><span class="token punctuation">{</span>ElementType<span class="token punctuation">.</span>METHOD<span class="token punctuation">,</span>ElementType<span class="token punctuation">.</span>FIELD<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Retention</span><span class="token punctuation">(</span>RetentionPolicy<span class="token punctuation">.</span>RUNTIME<span class="token punctuation">)</span><span class="token annotation punctuation">@Inherited</span><span class="token annotation punctuation">@Documented</span><span class="token keyword">public</span> @<span class="token keyword">interface</span> <span class="token class-name">DesensitizedAnnotation</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/*脱敏数据类型(规则)*/</span> TypeEnum <span class="token function">type</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/*判断注解是否生效,暂时没有用到*/</span> String <span class="token function">isEffictiveMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">default</span> <span class="token string">""</span><span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><h3 id="引入枚举"><a href="#引入枚举" class="headerlink" title="引入枚举"></a>引入枚举</h3><p>主要是便于统一处理同类型的字段</p><pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">enum</span> TypeEnum <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">/**客户名称**/</span> PERSON_NAME<span class="token punctuation">,</span> <span class="token comment" spellcheck="true">/**客户证件号**/</span> PERSON_CERT_NO<span class="token punctuation">,</span> <span class="token comment" spellcheck="true">/**客户手机号**/</span> PERSON_PHONE_NO<span class="token punctuation">,</span> <span class="token comment" spellcheck="true">/**客户银行卡名称**/</span> PERSON_BANK_NAME<span class="token punctuation">,</span> <span class="token comment" spellcheck="true">/**客户银行卡号**/</span> PERSON_BANK_NO<span class="token punctuation">,</span> <span class="token comment" spellcheck="true">/**密码**/</span> PASSWORD<span class="token punctuation">,</span><span class="token punctuation">}</span></code></pre><h3 id="定义基本数据模板类"><a href="#定义基本数据模板类" class="headerlink" title="定义基本数据模板类"></a>定义基本数据模板类</h3><p>主要作用是定义待过滤字段集合</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: BaseInfo * @Description: 日志过滤字段基类 * @Author: 尚先生 * @CreateDate: 2019/1/24 17:38 * @Version: 1.0 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">BaseInfo</span> <span class="token keyword">implements</span> <span class="token class-name">Serializable</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> serialVersionUID <span class="token operator">=</span> 1L<span class="token punctuation">;</span> <span class="token annotation punctuation">@DesensitizedAnnotation</span><span class="token punctuation">(</span>type <span class="token operator">=</span> TypeEnum<span class="token punctuation">.</span>PERSON_NAME<span class="token punctuation">)</span> <span class="token keyword">private</span> String custName<span class="token punctuation">;</span> <span class="token annotation punctuation">@DesensitizedAnnotation</span><span class="token punctuation">(</span>type <span class="token operator">=</span> TypeEnum<span class="token punctuation">.</span>PERSON_CERT_NO<span class="token punctuation">)</span> <span class="token keyword">private</span> String certNo<span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><h3 id="定义处理工具类"><a href="#定义处理工具类" class="headerlink" title="定义处理工具类"></a>定义处理工具类</h3><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: DesensitizedUtils * @Description: 日志脱敏工具类 * @Author: 尚先生 * @CreateDate: 2019/1/24 17:52 * @Version: 1.0 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DesensitizedUtils</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Logger logger <span class="token operator">=</span> LoggerFactory<span class="token punctuation">.</span><span class="token function">getLogger</span><span class="token punctuation">(</span>DesensitizedUtils<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> TypeEnum<span class="token operator">></span> annotationMaps <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/** * 类加载时装配待脱敏字段 */</span> <span class="token keyword">static</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> clazz <span class="token operator">=</span> Class<span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span>BaseInfo<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Field<span class="token punctuation">[</span><span class="token punctuation">]</span> fields <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getDeclaredFields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> fields<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fields<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">setAccessible</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> DesensitizedAnnotation annotation <span class="token operator">=</span> fields<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getAnnotation</span><span class="token punctuation">(</span>DesensitizedAnnotation<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>annotation <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span> TypeEnum type <span class="token operator">=</span> annotation<span class="token punctuation">.</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> String name <span class="token operator">=</span> fields<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//name为注解字段名称,value为注解类型。方便后续根据注解类型扩展</span> annotationMaps<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> type<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"类加载时装配待脱敏字段异常,异常信息:[{}]"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span>e<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 脱敏处理方法 * * @param object * @return */</span> <span class="token keyword">public</span> <span class="token keyword">static</span> String <span class="token function">getConverent</span><span class="token punctuation">(</span>Map<span class="token operator"><</span>String<span class="token punctuation">,</span>Object<span class="token operator">></span> object<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 1.处理Map数据类型</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>object <span class="token keyword">instanceof</span> <span class="token class-name">Map</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> HashMap<span class="token operator"><</span>String<span class="token punctuation">,</span> Object<span class="token operator">></span> reqMap <span class="token operator">=</span> <span class="token punctuation">(</span>HashMap<span class="token punctuation">)</span> object<span class="token punctuation">;</span> Iterator<span class="token operator"><</span>String<span class="token operator">></span> iterator <span class="token operator">=</span> annotationMaps<span class="token punctuation">.</span><span class="token function">keySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> iterator<span class="token punctuation">.</span><span class="token function">forEachRemaining</span><span class="token punctuation">(</span>annotationName <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>reqMap<span class="token punctuation">.</span><span class="token function">keySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">doconverentForMap</span><span class="token punctuation">(</span>reqMap<span class="token punctuation">,</span> annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>reqMap<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"日志脱敏处理失败,回滚,详细信息:[{}]"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span>e<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 脱敏数据源为Map时处理方式 * * @param reqMap * @param annotationName * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">doconverentForMap</span><span class="token punctuation">(</span>HashMap<span class="token operator"><</span>String<span class="token punctuation">,</span> Object<span class="token operator">></span> reqMap<span class="token punctuation">,</span> String annotationName<span class="token punctuation">)</span> <span class="token punctuation">{</span> String value <span class="token operator">=</span> String<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>reqMap<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>StringUtils<span class="token punctuation">.</span><span class="token function">isNotEmpty</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token function">doConverentByType</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> reqMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 根据不同注解类型处理不同字段 * * @param value * @param annotationName * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> String <span class="token function">doConverentByType</span><span class="token punctuation">(</span>String value<span class="token punctuation">,</span> String annotationName<span class="token punctuation">)</span> <span class="token punctuation">{</span> TypeEnum typeEnum <span class="token operator">=</span> annotationMaps<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>typeEnum<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> PERSON_NAME<span class="token operator">:</span> value <span class="token operator">=</span> <span class="token function">getStringByLength</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> PERSON_CERT_NO<span class="token operator">:</span> value <span class="token operator">=</span> <span class="token function">getStringByLength</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">default</span><span class="token operator">:</span> value <span class="token operator">=</span> <span class="token function">getStringByLength</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 根据value长度取值(切分) * * @param value * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> String <span class="token function">getStringByLength</span><span class="token punctuation">(</span>String value<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> length <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"*"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">==</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"*"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">></span> <span class="token number">3</span> <span class="token operator">&&</span> length <span class="token operator"><=</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"**"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">></span> <span class="token number">5</span> <span class="token operator">&&</span> length <span class="token operator"><=</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"***"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">></span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"*****"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="定义测试类"><a href="#定义测试类" class="headerlink" title="定义测试类"></a>定义测试类</h3><p>测试第一版实现的针对Map处理的脱敏操作</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: TestDeaensitized * @Description: 日志脱敏测试类 * @Author: 尚先生 * @CreateDate: 2019/1/24 18:27 * @Version: 1.0 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TestDeaensitized</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> HashMap<span class="token operator"><</span>String<span class="token punctuation">,</span> Object<span class="token operator">></span> hashMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hashMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"custName"</span><span class="token punctuation">,</span> <span class="token string">"小妮儿"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hashMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"certNo"</span><span class="token punctuation">,</span> <span class="token string">"12345678909876543"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hashMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"phone"</span><span class="token punctuation">,</span> <span class="token string">"12345678909"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"脱敏前:"</span> <span class="token operator">+</span> hashMap<span class="token punctuation">)</span><span class="token punctuation">;</span> String converent1 <span class="token operator">=</span> DesensitizedUtils<span class="token punctuation">.</span><span class="token function">getConverent</span><span class="token punctuation">(</span>hashMap<span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"脱敏后:"</span> <span class="token operator">+</span> converent1<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="第一版实现测试结果"><a href="#第一版实现测试结果" class="headerlink" title="第一版实现测试结果"></a>第一版实现测试结果</h3><pre class=" language-tex"><code class="language-tex">针对Map实现的脱敏结果脱敏前:{certNo=12345678909876543, phone=12345678909, custName=小妮儿}脱敏后:{"certNo":"123*****543","phone":"12345678909","custName":"小*儿"}</code></pre><p>至此第一版功能实现顺利完成。</p><h3 id="实现第二版"><a href="#实现第二版" class="headerlink" title="实现第二版"></a>实现第二版</h3><p>由于在<code>RPC</code>调用过程当中,大部分接口的参数封装数据类型都是<code>Map</code>,但是部分接口还是使用<code>Java Bean</code>所以在此针对<code>Java Bean</code>形式实现日志脱敏功能</p><h3 id="实现思路:-1"><a href="#实现思路:-1" class="headerlink" title="实现思路:"></a>实现思路:</h3><pre class=" language-tex"><code class="language-tex">根据不同的数据类型进行不同判断,屏蔽上层调用者的可见度,在底层动态实现分情况处理在结果处理完之后,统一返回调用者序列化完成的数据信息</code></pre><p>在第一版实现的基础之上,我们开始第二版的实现</p><h3 id="添加实体类"><a href="#添加实体类" class="headerlink" title="添加实体类"></a>添加实体类</h3><p>主要是为了封装模拟RPC调用过程中参数实体的属性</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: Person * @Description: Person实体类 * @Author: 尚先生 * @CreateDate: 2019/1/24 17:50 * @Version: 1.0 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Person</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> String custName<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">int</span> idNo<span class="token punctuation">;</span> <span class="token keyword">private</span> String certNo<span class="token punctuation">;</span> <span class="token keyword">public</span> String <span class="token function">getCustName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> custName<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setCustName</span><span class="token punctuation">(</span>String custName<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>custName <span class="token operator">=</span> custName<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">getIdNo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> idNo<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setIdNo</span><span class="token punctuation">(</span><span class="token keyword">int</span> idNo<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>idNo <span class="token operator">=</span> idNo<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> String <span class="token function">getCertNo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> certNo<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setCertNo</span><span class="token punctuation">(</span>String certNo<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>certNo <span class="token operator">=</span> certNo<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> String <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"Person{"</span> <span class="token operator">+</span> <span class="token string">"custName='"</span> <span class="token operator">+</span> custName <span class="token operator">+</span> <span class="token string">''</span>' <span class="token operator">+</span> <span class="token string">", idNo="</span> <span class="token operator">+</span> idNo <span class="token operator">+</span> <span class="token string">", certNo='"</span> <span class="token operator">+</span> certNo <span class="token operator">+</span> <span class="token string">''</span>' <span class="token operator">+</span> <span class="token string">'}'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="改造处理工具类"><a href="#改造处理工具类" class="headerlink" title="改造处理工具类"></a>改造处理工具类</h3><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: DesensitizedUtils * @Description: 日志脱敏工具类 * @Author: 尚先生 * @CreateDate: 2019/1/24 17:52 * @Version: 1.0 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DesensitizedUtils</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Logger logger <span class="token operator">=</span> LoggerFactory<span class="token punctuation">.</span><span class="token function">getLogger</span><span class="token punctuation">(</span>DesensitizedUtils<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> TypeEnum<span class="token operator">></span> annotationMaps <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">/** * 类加载时装配待脱敏字段 */</span> <span class="token keyword">static</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> clazz <span class="token operator">=</span> Class<span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span>BaseInfo<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Field<span class="token punctuation">[</span><span class="token punctuation">]</span> fields <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getDeclaredFields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> fields<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fields<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">setAccessible</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> DesensitizedAnnotation annotation <span class="token operator">=</span> fields<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getAnnotation</span><span class="token punctuation">(</span>DesensitizedAnnotation<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>annotation <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span> TypeEnum type <span class="token operator">=</span> annotation<span class="token punctuation">.</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> String name <span class="token operator">=</span> fields<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//name为注解字段名称,value为注解类型。方便后续根据注解类型扩展</span> annotationMaps<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> type<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"类加载时装配待脱敏字段异常,异常信息:[{}]"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span>e<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 脱敏处理方法 * * @param object * @return */</span> <span class="token keyword">public</span> <span class="token keyword">static</span> String <span class="token function">getConverent</span><span class="token punctuation">(</span>Object object<span class="token punctuation">)</span> <span class="token punctuation">{</span> String objClassName <span class="token operator">=</span> object<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">// 1.处理Map数据类型</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>object <span class="token keyword">instanceof</span> <span class="token class-name">Map</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> HashMap<span class="token operator"><</span>String<span class="token punctuation">,</span> Object<span class="token operator">></span> reqMap <span class="token operator">=</span> <span class="token punctuation">(</span>HashMap<span class="token punctuation">)</span> object<span class="token punctuation">;</span> Iterator<span class="token operator"><</span>String<span class="token operator">></span> iterator <span class="token operator">=</span> annotationMaps<span class="token punctuation">.</span><span class="token function">keySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> iterator<span class="token punctuation">.</span><span class="token function">forEachRemaining</span><span class="token punctuation">(</span>annotationName <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>reqMap<span class="token punctuation">.</span><span class="token function">keySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">doconverentForMap</span><span class="token punctuation">(</span>reqMap<span class="token punctuation">,</span> annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>reqMap<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">// 2.处理Object数据类型</span> Object val <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> objClazz <span class="token operator">=</span> Class<span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span>objClassName<span class="token punctuation">)</span><span class="token punctuation">;</span> Field<span class="token punctuation">[</span><span class="token punctuation">]</span> declaredFields <span class="token operator">=</span> objClazz<span class="token punctuation">.</span><span class="token function">getDeclaredFields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator"><</span> declaredFields<span class="token punctuation">.</span>length<span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Iterator<span class="token operator"><</span>String<span class="token operator">></span> iterator <span class="token operator">=</span> annotationMaps<span class="token punctuation">.</span><span class="token function">keySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>iterator<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> String annotationName <span class="token operator">=</span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>declaredFields<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> declaredFields<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">setAccessible</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> val <span class="token operator">=</span> declaredFields<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment" spellcheck="true">//获取属性后现在默认处理的是String类型,其他类型数据可扩展</span> String value <span class="token operator">=</span> <span class="token function">doconverentForObject</span><span class="token punctuation">(</span>val<span class="token punctuation">,</span> annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> declaredFields<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>object<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"日志脱敏处理失败,回滚,详细信息:[{}]"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span>e<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 脱敏数据源为Object时处理方式 * * @param val * @param annotationName * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> String <span class="token function">doconverentForObject</span><span class="token punctuation">(</span>Object val<span class="token punctuation">,</span> String annotationName<span class="token punctuation">)</span> <span class="token punctuation">{</span> String value <span class="token operator">=</span> String<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>StringUtils<span class="token punctuation">.</span><span class="token function">isNotEmpty</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token function">doConverentByType</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 脱敏数据源为Map时处理方式 * * @param reqMap * @param annotationName * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">doconverentForMap</span><span class="token punctuation">(</span>HashMap<span class="token operator"><</span>String<span class="token punctuation">,</span> Object<span class="token operator">></span> reqMap<span class="token punctuation">,</span> String annotationName<span class="token punctuation">)</span> <span class="token punctuation">{</span> String value <span class="token operator">=</span> String<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>reqMap<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>StringUtils<span class="token punctuation">.</span><span class="token function">isNotEmpty</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token function">doConverentByType</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> reqMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 根据不同注解类型处理不同字段 * * @param value * @param annotationName * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> String <span class="token function">doConverentByType</span><span class="token punctuation">(</span>String value<span class="token punctuation">,</span> String annotationName<span class="token punctuation">)</span> <span class="token punctuation">{</span> TypeEnum typeEnum <span class="token operator">=</span> annotationMaps<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>annotationName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>typeEnum<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> PERSON_NAME<span class="token operator">:</span> value <span class="token operator">=</span> <span class="token function">getStringByLength</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> PERSON_CERT_NO<span class="token operator">:</span> value <span class="token operator">=</span> <span class="token function">getStringByLength</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">default</span><span class="token operator">:</span> value <span class="token operator">=</span> <span class="token function">getStringByLength</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment" spellcheck="true">/** * 根据value长度取值(切分) * * @param value * @return */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> String <span class="token function">getStringByLength</span><span class="token punctuation">(</span>String value<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> length <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"*"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">==</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"*"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">></span> <span class="token number">3</span> <span class="token operator">&&</span> length <span class="token operator"><=</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"**"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">></span> <span class="token number">5</span> <span class="token operator">&&</span> length <span class="token operator"><=</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"***"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">></span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">{</span> value <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"*****"</span> <span class="token operator">+</span> value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>length <span class="token operator">-</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> value<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="定义测试类-1"><a href="#定义测试类-1" class="headerlink" title="定义测试类"></a>定义测试类</h3><p>测试第二版实现的针对Object处理的脱敏操作</p><pre class=" language-java"><code class="language-java"><span class="token comment" spellcheck="true">/** * @ClassName: TestDeaensitized * @Description: 日志脱敏测试类 * @Author: 尚先生 * @CreateDate: 2019/1/24 18:27 * @Version: 1.0 */</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TestDeaensitized</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> HashMap<span class="token operator"><</span>String<span class="token punctuation">,</span> Object<span class="token operator">></span> hashMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token operator"><</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hashMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"custName"</span><span class="token punctuation">,</span> <span class="token string">"小妮儿"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hashMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"certNo"</span><span class="token punctuation">,</span> <span class="token string">"12345678909876543"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hashMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"phone"</span><span class="token punctuation">,</span> <span class="token string">"12345678909"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"脱敏前:"</span> <span class="token operator">+</span> hashMap<span class="token punctuation">)</span><span class="token punctuation">;</span> String converent1 <span class="token operator">=</span> DesensitizedUtils<span class="token punctuation">.</span><span class="token function">getConverent</span><span class="token punctuation">(</span>hashMap<span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"脱敏后:"</span> <span class="token operator">+</span> converent1<span class="token punctuation">)</span><span class="token punctuation">;</span> Person person <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> person<span class="token punctuation">.</span><span class="token function">setCertNo</span><span class="token punctuation">(</span><span class="token string">"12345678909876541"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> person<span class="token punctuation">.</span><span class="token function">setCustName</span><span class="token punctuation">(</span><span class="token string">"小妮儿真可爱!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"脱敏前:"</span> <span class="token operator">+</span> person<span class="token punctuation">)</span><span class="token punctuation">;</span> String converent2 <span class="token operator">=</span> DesensitizedUtils<span class="token punctuation">.</span><span class="token function">getConverent</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"脱敏后:"</span> <span class="token operator">+</span> converent2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre><h3 id="第二版实现测试结果"><a href="#第二版实现测试结果" class="headerlink" title="第二版实现测试结果"></a>第二版实现测试结果</h3><pre class=" language-tex"><code class="language-tex">针对Map实现的脱敏结果脱敏前:{certNo=12345678909876543, phone=12345678909, custName=小妮儿}脱敏后:{"certNo":"123*****543","phone":"12345678909","custName":"小*儿"}针对Object实现的脱敏结果脱敏前:Person{custName='小妮儿真可爱!', idNo=0, certNo='12345678909876541'}脱敏后:{"certNo":"123*****541","custName":"小妮***爱!","idNo":0}</code></pre><p>至此所有功能实现顺利完成。</p><blockquote><p>完整代码请参考Github</p><pre><code>https://github.com/dwyanewede/project-learn/tree/master/src/main/java/com/learn/demo/desensitization</code></pre><p>博客链接:</p><pre><code>https://blog.csdn.net/shang_xs/article/details/86632071</code></pre></blockquote>]]></content>
<categories>
<category> 日志 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> SpringBoot </tag>
<tag> 自定义注解 </tag>
</tags>
</entry>
<entry>
<title>EveryThing如何用到极致?</title>
<link href="/2019/01/29/everything-ru-he-yong-dao-ji-zhi/"/>
<url>/2019/01/29/everything-ru-he-yong-dao-ji-zhi/</url>
<content type="html"><![CDATA[<p><img src="https://upload-images.jianshu.io/upload_images/2346551-cf440b8ac3c91878.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000/format/webp" alt="Everything是国民最爱软件"></p><hr><p>摘要:Everything几乎是每个职场人必备的效率工具,但同事们都只用它的一两个基本功能,并没有发挥出该软件的真正效率。实际上,把Everything的功能用到极致能够成倍的提升我们的工作效率,本文尝试详述那些藏在角落里的功能,帮您把它的潜力发挥到极致。</p><hr><h2 id="0-Everything的前世今生"><a href="#0-Everything的前世今生" class="headerlink" title="0. Everything的前世今生"></a>0. Everything的前世今生</h2><p>Everything是澳大利亚人David Carpenter开发的免费文件检索工具,自从问世以来,因其占用内存小,搜索迅捷,获得了全世界windows用户的追捧,是职场同仁们必备的利器。拿我自己来说吧,我的电脑里有60万个文件夹和文件,如果没有everything,我不知道找到需要的文件要多么痛苦。</p><p>下面我们就按使用场景说说Everything的用法。</p><h2 id="1-Everything的使用场景"><a href="#1-Everything的使用场景" class="headerlink" title="1. Everything的使用场景"></a>1. Everything的使用场景</h2><h3 id="1-1-搜索包含某个关键词的文件名,怎么办?"><a href="#1-1-搜索包含某个关键词的文件名,怎么办?" class="headerlink" title="1.1 搜索包含某个关键词的文件名,怎么办?"></a>1.1 搜索包含某个关键词的文件名,怎么办?</h3><p>这个是同学们最常用的功能了,即在搜索框输入你要查询的关键字,例如,我想查询包含coffee到文件名,就直接在搜索框里输入coffee,就可以了,效果如下。</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-9a40981c21bca250.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/848/format/webp" alt="搜索包含coffee的文件名"></p><h3 id="1-2-要搜索的文件名中同时包含多个关键词怎么办?"><a href="#1-2-要搜索的文件名中同时包含多个关键词怎么办?" class="headerlink" title="1.2 要搜索的文件名中同时包含多个关键词怎么办?"></a>1.2 要搜索的文件名中同时包含多个关键词怎么办?</h3><p>如果想搜索的文件名中同时包含多个关键词,可以在搜索框中顺序敲入那几个关键词,中间用空格分开,例如包含hpe和win的文件,可以输入hpe然后跟着一个空格,然后输入win,结果如下图所示:</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-2c11dab4b349e415.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/844/format/webp" alt="搜索包含两个关键词的文件名"></p><p>但这种方法查出来的结果文件,名字中的关键词出现的顺序不一定是你输入的关键词的顺序。如果你想查询的结果必须按照你输入的顺序,可以使用<em>关键词</em>关键词<em>的方式。例如,</em>hpe<em>win</em>,你看,下面出现的结果,都是hpe在前,win在后的文件名。</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-e0ba4b2e70cbe26d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/845/format/webp" alt=""></p><p>另一种输入多关键词的方法,可以确保结果中关键词的顺序</p><h3 id="1-3-想要搜索的文件名中包含多个关键词中的一个就可以,怎么办?"><a href="#1-3-想要搜索的文件名中包含多个关键词中的一个就可以,怎么办?" class="headerlink" title="1.3 想要搜索的文件名中包含多个关键词中的一个就可以,怎么办?"></a>1.3 想要搜索的文件名中包含多个关键词中的一个就可以,怎么办?</h3><p>如果我想查询的文件名中,包含关键词1,<strong>或</strong>,包含关键词二,可以在两个关键词中间加竖线(注意竖线前后都有空格),例如coffee | orange</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-95f7f5b6385d0833.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/860/format/webp" alt=""></p><p>两个关键词满足一个就可以,使用“或”的关系</p><h3 id="1-4-只搜索目录名,不想要文件名怎么办?"><a href="#1-4-只搜索目录名,不想要文件名怎么办?" class="headerlink" title="1.4 只搜索目录名,不想要文件名怎么办?"></a>1.4 只搜索目录名,不想要文件名怎么办?</h3><p>如果只想显示符合条件的目录,鼠标点选右上角的下拉框,把显示范围从Everything(全部),改为folder(文件夹)</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-f7bade6adac99e16.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/849/format/webp" alt="只显示符合条件的文件夹"></p><h3 id="1-5-只搜索某目录下的文件怎么办?"><a href="#1-5-只搜索某目录下的文件怎么办?" class="headerlink" title="1.5 只搜索某目录下的文件怎么办?"></a>1.5 只搜索某目录下的文件怎么办?</h3><p>如果只想搜索某目录下的文件,则可以用目录加反斜杠,加空格,加关键词的方式进行搜索,例如:downloads\ 华为</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-cf50517c83645f63.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/844/format/webp" alt="只搜索某目录下的满足条件的文件"></p><h3 id="1-6-只搜索某种类型的文件怎么办?"><a href="#1-6-只搜索某种类型的文件怎么办?" class="headerlink" title="1.6 只搜索某种类型的文件怎么办?"></a>1.6 只搜索某种类型的文件怎么办?</h3><p>如果想搜索某种特定类型的文件,可以使用<em>.加文件类型后缀的方式,例如downloads\ </em>.pdf,就只显示该目录下的pdf文件,其他文件就不显示了。</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-11568ee05e6f2d7c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/846/format/webp" alt="搜索某种特定类型的文件"></p><p>搜索某种特定类型的文件,使用*.后缀就可以了</p><h3 id="1-7-如何使用通配符?"><a href="#1-7-如何使用通配符?" class="headerlink" title="1.7 如何使用通配符?"></a>1.7 如何使用通配符?</h3><p>刚才我们已经举了两种使用通配符<em>的例子,简单讲</em>代表任意字符,问号(?)是代表一个字符(任意字符),例如:downloads\ *.p??,会显示downloads目录下的pdf文件,ppt文件等等,但不会显示pptx文件</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-d14ce197df94dd58.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/841/format/webp" alt="通配符?能够匹配任意一个字符"></p><p>通配符?能够匹配任意一个字符</p><h3 id="1-8-搜索不包含某关键词的文件名怎么办?"><a href="#1-8-搜索不包含某关键词的文件名怎么办?" class="headerlink" title="1.8 搜索不包含某关键词的文件名怎么办?"></a>1.8 搜索不包含某关键词的文件名怎么办?</h3><p>不包含由!代表,例如,我们想搜索downloads目录下不是pdf文件的其他文件,可以这样写:downloads\ !<em>.pdf !</em>.docx</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-1aed361c72869189.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/843/format/webp" alt="叹号代表否定"></p><p>叹号代表否定</p><h3 id="1-9-搜索的关键词中包含空格怎么办?"><a href="#1-9-搜索的关键词中包含空格怎么办?" class="headerlink" title="1.9 搜索的关键词中包含空格怎么办?"></a>1.9 搜索的关键词中包含空格怎么办?</h3><p>上面你可能已经看出来了,查询关键字中的空格,代表“且”的意思,即两个关键词中间如果有空格的话,代表同时包含两个关键词。但如果我想查询的关键词本身包含空格怎么办呢? 可以用双引号把它们括起来,这样everything就会把它看待成一个词了。例如,我想查询downloads目录下包含university of bath的文件,可以写: downloads\ “university of”</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-d08a02722dc1166e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/848/format/webp" alt=""></p><p>如果查询的关键字本身包含空格,应该用双引号把它引起来</p><h2 id="2-Everything的web服务器"><a href="#2-Everything的web服务器" class="headerlink" title="2. Everything的web服务器"></a>2. Everything的web服务器</h2><p>好了,everything的功能介绍完了,希望大家都能把它的功能用到极致,也不枉作者好心开发这么好的工具,免费放出来给大家用,也不枉那么那么多的小伙伴在网路上推荐它呀</p><p>最后,Everything还有一个功能提一下,就是可以从手机或平板上通过浏览器访问它,这样的话,您就可以从手机上搜索自己电脑上的电影看了。打开everything的网站功能:从菜单Tools(工具)-Options(选项)的对话框,选择Http Server(Http服务器),然后勾选第一个勾选框,下面第三行有一个端口号要记住,假设是10000,那么你在手机浏览器的地址栏输入http://电脑IP:10000就可以访问了。</p><p><img src="https://upload-images.jianshu.io/upload_images/2346551-be12b5e8eeab5f9d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/885/format/webp" alt=""></p><p>打开Everything的web服务器可以从手机上访问您的文件哦</p>]]></content>
<categories>
<category> Windows软件 </category>
</categories>
<tags>
<tag> 工具 </tag>
<tag> 猿说 </tag>
</tags>
</entry>
<entry>
<title>Hello World</title>
<link href="/2019/01/29/hello-world/"/>
<url>/2019/01/29/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><pre class=" language-bash"><code class="language-bash">$ hexo new <span class="token string">"My New Post"</span></code></pre><p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><pre class=" language-bash"><code class="language-bash">$ hexo server</code></pre><p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><pre class=" language-bash"><code class="language-bash">$ hexo generate</code></pre><p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><pre class=" language-bash"><code class="language-bash">$ hexo deploy</code></pre><p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p>]]></content>
</entry>
</search>