Repository navigation
Expand file tree
/
Copy pathatom.xml
More file actions
547 lines (299 loc) · 372 KB
/
Copy pathatom.xml
File metadata and controls
547 lines (299 loc) · 372 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Hexo</title>
<link href="/atom.xml" rel="self"/>
<link href="https://jinlongchen.com/"/>
<updated>2020-07-02T18:56:45.507Z</updated>
<id>https://jinlongchen.com/</id>
<author>
<name>Jerome Chan</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Hello World</title>
<link href="https://jinlongchen.com/posts/hello-world/"/>
<id>https://jinlongchen.com/posts/hello-world/</id>
<published>2020-07-02T18:56:45.507Z</published>
<updated>2020-07-02T18:56:45.507Z</updated>
<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><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><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><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><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><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><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><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html" target="_blank" rel="noopener">Deployment</a></p>]]></content>
<summary type="html">
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
</summary>
<category term="分类1" scheme="https://jinlongchen.com/categories/%E5%88%86%E7%B1%BB1/"/>
<category term="标签1" scheme="https://jinlongchen.com/tags/%E6%A0%87%E7%AD%BE1/"/>
</entry>
<entry>
<title>透过Hexo静态站点探讨Serverless架构(一)</title>
<link href="https://jinlongchen.com/posts/2020/06/23/hexo-best-practice/"/>
<id>https://jinlongchen.com/posts/2020/06/23/hexo-best-practice/</id>
<published>2020-06-22T16:00:00.000Z</published>
<updated>2020-07-02T18:56:45.507Z</updated>
<content type="html"><![CDATA[<h4 id="一、静态站点的概念与使用场景"><a href="#一、静态站点的概念与使用场景" class="headerlink" title="一、静态站点的概念与使用场景"></a>一、静态站点的概念与使用场景</h4><p>在开始介绍静态站点之前,我们来先聊一聊动态站点,而动态站点中最为经典的莫过于CS架构。<br>关于CS架构的站点及其用途,其覆盖面毋庸置疑,日常技术开发工作也已经司空见惯,各类电商网站,移动App,微信平台小程序等应用均离不开客户端与服务端,客户端负责提供统一用户体验,而服务端则负责处理交互的数据流转与处理。<br>尽管CS架构的应用如此广泛,但并不代表着CO架构的站点没有立足之地。</p><p>静态站点,即该站点是纯静态的,不具备Server端数据请求与用户交互特性,本篇文章对于该类站点称之为CO(Client Only)架构的站点。<br>个人博客、企业简介官网等展示类的站点,其主要功能与站点目的只是将信息po到网络上,使其公开、可视与共享,而不涉及负责的产品、订单、留言等交互功能。针对这类场景应用,纯静态站点具有其天生的优势:</p><ul><li>内容编辑与维护便捷,所见即所得;</li><li>省去了Client到Server端的网络流转,站点内容加载速度快;</li></ul><p>在开源的世界里,关于静态站点的技术框架应用较为广泛的属Jekyll与Hexo,二者均可直接托管于GitHub容器内,为用户省去了服务器租用费用的开销。<br>Jekyll是一款Ruby生态圈衍生出来的静态站点生成框架,Hexo则是Node.js生态圈的产物,二者要求的底层技术知识有所区别。在选用的时候,请选择自己熟悉的擅长的就好,二者在生产出的静态站点成品上对于普通用户而言,并无太大区别。<br>Jekyll与Hexo在主题定制方面都拥有着不错的社区,用的时候可以自己去对应的社区挑选自己喜爱的主题作为二次定制开发的基础。</p><p>本篇文章主要Focus在Hexo的应用上,而在过去的日子里,对于Jekyll已经有了一定成品研究,如果感兴趣可以前往本人GitHub上的项目进行观摩、留言与交流:<br>GitHub项目:<a href="https://github.com/jeromechan/hyde-for-jekyll-v3" target="_blank" rel="noopener">hyde-for-jekyll-v3</a><br>这是一个不错的,可定制性很强的一个主题。在原项目poole/hyde的基础上,本人作了版本升级与功能定制开发,更符合国内用户的使用习惯。</p><h4 id="二、准备Hexo开发环境与工具"><a href="#二、准备Hexo开发环境与工具" class="headerlink" title="二、准备Hexo开发环境与工具"></a>二、准备Hexo开发环境与工具</h4><p>正所谓工欲善其事,必先利其器。我们先来说说折腾Hexo静态站点生成器所需要准备的各类资源,其中包含环境配置、工具安装、资料阅读等方面。本篇文章主要基于macOS环境开展讲解,同样也适用于Windows平台上的运用。</p><ul><li>Hexo官方网站:<a href="https://hexo.io/zh-cn/" target="_blank" rel="noopener">https://hexo.io/zh-cn/</a><br>学习如何安装Hexo、执行Hexo站点部署与调试的基本操作。</li><li>Hexo官方主题:<a href="https://hexo.io/themes/" target="_blank" rel="noopener">https://hexo.io/themes/</a><br>很多很赞的官方主题供你随意挑选。</li><li>Node.js官方网站:<a href="https://nodejs.org/zh-cn/" target="_blank" rel="noopener">https://nodejs.org/zh-cn/</a><br>这是Hexo框架的底层依赖,必须先安装好才便于更好地使用Hexo。</li><li>EJS模板引擎:<a href="https://ejs.co/" target="_blank" rel="noopener">https://ejs.co/</a><br>这是示例中pure主题所使用到的JS模板引擎,基础的标签和模板语言需要简要学习一番。</li><li>TAWK.to插件:<a href="https://www.tawk.to/" target="_blank" rel="noopener">https://www.tawk.to/</a><br>tawk.to是一个在线聊天插件,可以运用于静态站点,场景可以是提供用户文章留言、意见反馈、在线聊天等。</li><li>cofess/hexo-theme-pure主题(案例):<a href="https://github.com/cofess/hexo-theme-pure" target="_blank" rel="noopener">https://github.com/cofess/hexo-theme-pure</a><br>适用于最新版本Hexo的hexo-theme-pure主题,本篇文章的讲述基于该主题逐步开展。该主题也是我个人博客的基础主题。</li></ul><h4 id="三、Hexo静态站点搭建步骤"><a href="#三、Hexo静态站点搭建步骤" class="headerlink" title="三、Hexo静态站点搭建步骤"></a>三、Hexo静态站点搭建步骤</h4><ol><li><p>安装node.js<br>如下图,下载安装文件并进行安装。建议下载LTS版本的进行安装,LTS即Long-term support长期维护版本,相较其它版本的稳定性更佳。<br><img src="/images/2020-06-23-hexo-best-practice/15937078276185.jpg" alt="-w811"></p></li><li><p>安装Hexo框架<br>打开terminal,敲入命令进行安装。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-cli -g</span><br></pre></td></tr></table></figure></li><li><p>初始化一个Hexo静态站点</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo init blog</span><br><span class="line"><span class="built_in">cd</span> blog</span><br><span class="line">npm install</span><br></pre></td></tr></table></figure></li><li><p>下载并安装cofess/hexo-theme-pure主题<br>在<code>blog</code>目录下,执行如下命令:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/cofess/hexo-theme-pure.git themes/pure</span><br></pre></td></tr></table></figure><p>然后,修改hexo根目录<code>/blog</code>下的<code>blog/_config.yml</code>文件,修改配置为<code>theme: pure</code>。<br>额外地,如果需要完全使用pure主题所具备的特性,如下插件建议安装,如下命令:</p></li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># hexo-wordcount</span></span><br><span class="line">npm install hexo-wordcount --save</span><br><span class="line"></span><br><span class="line"><span class="comment"># hexo-generator-json-content</span></span><br><span class="line">npm install hexo-generator-json-content --save</span><br><span class="line"></span><br><span class="line"><span class="comment"># hexo-generator-feed</span></span><br><span class="line">npm install hexo-generator-feed --save</span><br><span class="line"></span><br><span class="line"><span class="comment"># hexo-generator-sitemap</span></span><br><span class="line">npm install hexo-generator-sitemap --save</span><br><span class="line"></span><br><span class="line"><span class="comment"># hexo-generator-baidu-sitemap</span></span><br><span class="line">npm install hexo-generator-baidu-sitemap --save</span><br></pre></td></tr></table></figure><p>关于pure主题更多的优化点,请参考:<a href="https://github.com/cofess/hexo-theme-pure#blog-optimization" target="_blank" rel="noopener">https://github.com/cofess/hexo-theme-pure#blog-optimization</a></p><ol start="5"><li>启动Hexo静态站点<code>blog</code><br>在<code>blog</code>目录下执行命令,启动静态站点本地调试环境。<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo server --debug</span><br></pre></td></tr></table></figure></li></ol><h4 id="四、cofess-hexo-theme-pure改造思路与定制性开发"><a href="#四、cofess-hexo-theme-pure改造思路与定制性开发" class="headerlink" title="四、cofess/hexo-theme-pure改造思路与定制性开发"></a>四、cofess/hexo-theme-pure改造思路与定制性开发</h4><ol><li><p>修复分页功能显示源代码问题<br>原因系<code>paginator()</code>方法内对传入的html源代码作了转义,而在页面渲染之时却未恢复为原样,导致页面渲染出现了html源代码。<br><img src="/images/2020-06-23-hexo-best-practice/15937097798901.jpg" alt="-w913"></p></li><li><p>移除冗余的篇章末签名功能块<br>(1)增加_config.yml配置</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># post page configurations</span></span><br><span class="line"><span class="attr">postConfig:</span></span><br><span class="line"> <span class="attr">signature:</span> <span class="literal">false</span> <span class="comment">#是否在post底部展示站点作者签名</span></span><br></pre></td></tr></table></figure><p>(2)修改copyright.ejs,实现篇章末的前面功能块被隐藏<br>if判断条件新增条件<code>&& theme.postConfig.signature</code>。<br><img src="/images/2020-06-23-hexo-best-practice/15937102869214.jpg" alt="-w946"></p></li><li><p>新增与配置gitalk评论模块<br>(1)注册GitHub OAuth application<br><a href="https://github.com/settings/applications/new" target="_blank" rel="noopener">https://github.com/settings/applications/new</a><br><img src="/images/2020-06-23-hexo-best-practice/15937109150632.jpg" alt="-w742"></p></li></ol><p>(2)配置pure主题的_config.yml</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">gitalk:</span> <span class="comment"># gitalk. https://gitalk.github.io/</span></span><br><span class="line"> <span class="attr">owner:</span> <span class="string">jeromechan</span> <span class="comment">#必须. GitHub repository 所有者,可以是个人或者组织。</span></span><br><span class="line"> <span class="attr">admin:</span> <span class="string">jeromechan</span> <span class="comment">#必须. GitHub repository 的所有者和合作者 (对这个 repository 有写权限的用户)。</span></span><br><span class="line"> <span class="attr">repo:</span> <span class="string">jeromechan.github.io</span> <span class="comment">#必须. GitHub repository.</span></span><br><span class="line"> <span class="attr">ClientID:</span> <span class="string">ecf46d673fc8c48a8f6f</span> <span class="comment">#必须. GitHub Application Client ID.</span></span><br><span class="line"> <span class="attr">ClientSecret:</span> <span class="string">268a6dcde075fd5cc3e2f3e676c93cbde3868c3e</span> <span class="comment">#必须. GitHub Application Client Secret.</span></span><br></pre></td></tr></table></figure><ol start="4"><li><p>修复豆瓣书单openAPI过期失效问题<br>(1)原豆瓣openAPI已失效,改到新的接口调用上<br><img src="/images/2020-06-23-hexo-best-practice/15937112195049.jpg" alt="-w839"><br>(2)更换pure主题_config.yml配置中的豆瓣openAPI请求秘钥</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">douban:</span></span><br><span class="line"> <span class="attr">user:</span> <span class="number">205387900</span> <span class="comment"># 豆瓣用户名</span></span><br><span class="line"> <span class="attr">start:</span> <span class="number">0</span> <span class="comment"># 从哪一条记录开始</span></span><br><span class="line"> <span class="attr">count:</span> <span class="number">100</span> <span class="comment"># 获取豆瓣书单数据条数</span></span><br><span class="line"> <span class="attr">apikey:</span> <span class="string">0df993c66c0c636e29ecbb5344252a4a</span> <span class="comment"># 网络流传 ApiKey 0df993c66c0c636e29ecbb5344252a4a 0b2bdeda43b5688921839c8ecb20399b</span></span><br></pre></td></tr></table></figure></li><li><p>新增tawk.to插件的支持<br>(1)新增tawk-to.ejs文件<br><img src="/images/2020-06-23-hexo-best-practice/15937115176825.jpg" alt="-w836"></p></li></ol><p>(2)将tawk-to.ejs内容进行动态加载<br>修改<code>themes/pure/layout/_common/script.ejs</code>,新增加载<code>tawk-to.ejs</code>文件内容</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><%- partial('_script/tawk-to') %></span><br></pre></td></tr></table></figure><ol start="6"><li><p>更改菜单“关于”的超链接至LinkedIn<br>(1)修改pure主题下的_config.yml配置如下<br><img src="/images/2020-06-23-hexo-best-practice/15937120365118.jpg" alt="-w811"><br>(2)更改header.ejs,完善内容的呈现逻辑<br>判断若文本内容为https或http开头,则以超链接形式进行<code><a></code>标签渲染,否则直接渲染文本。<br><img src="/images/2020-06-23-hexo-best-practice/15937121567220.jpg" alt="-w870"></p></li><li><p>更改博文url pattern配置<br>修改项目<code>blog</code>目录下的<code>_config.yml</code>配置。<br><img src="/images/2020-06-23-hexo-best-practice/15937118312500.jpg" alt="-w794"></p></li></ol><h4 id="五、在Hexo静态站点DevOps自动化方面做的一些改善"><a href="#五、在Hexo静态站点DevOps自动化方面做的一些改善" class="headerlink" title="五、在Hexo静态站点DevOps自动化方面做的一些改善"></a>五、在Hexo静态站点DevOps自动化方面做的一些改善</h4><p>关于如何将Hexo静态站点托管到GitHub Pages上的操作流程,以及如何使用GitHub Pages实现Https加密配置,使用搜索引擎Google可以得到非常多的Tips和博文,这里就不再赘述。这里会额外讲述一下Hexo静态站点项目在DevOps自动化部署上的一些新的尝试。</p><p>GitHub原生支持Jekyll站点项目,无需额外生成全部静态站点文件即可部署使用。与Jekyll不同,Hexo需完全编译并生成全部静态站点文件才可部署到GitHub Pages上使用。故而Hexo项目的部署流程略复杂于Jekyll。<br><img src="/images/2020-06-23-hexo-best-practice/15937136700715.jpg" alt="-w525"></p><p>针对<code>Core Operations: gen&sync</code>,通过shell脚本完成其自动化部署流程数最为便捷的实践方案。其实现细节与流程步骤如下源码所示:<br>源代码:<a href="https://github.com/jeromechan/jeromechan.github.io/blob/source-hexo/deploy.sh" target="_blank" rel="noopener">https://github.com/jeromechan/jeromechan.github.io/blob/source-hexo/deploy.sh</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"执行开始"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># A script to build a Hexo site</span></span><br><span class="line">REPO_NAME=<span class="string">"jeromechan.github.io"</span></span><br><span class="line">BRANCH_SOURCE=<span class="string">"source-hexo"</span></span><br><span class="line">BRANCH_MASTER=<span class="string">"master"</span></span><br><span class="line">CURRENT_DIR=<span class="variable">${PWD}</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 0. 完善当前构建环境的依赖</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"0. 完善当前构建环境的依赖,执行yarn install"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">yarn install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 切换至source-hexo分支下,执行hexo clean & hexo generate</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"1. 切换至source-hexo分支下,执行hexo clean & hexo generate"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">git checkout <span class="variable">$BRANCH_SOURCE</span></span><br><span class="line">hexo clean</span><br><span class="line">hexo generate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 执行git add * & git commit -am 'Auto update file using hexo-deploy.sh script.' & git push</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"2. 执行git add * & git commit -am 'Auto update file using hexo-deploy.sh script.' & git push"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">git add *</span><br><span class="line">git commit -am <span class="string">'Auto update file using hexo-deploy.sh script.'</span></span><br><span class="line">git push</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 将public文件夹内容拷贝至/tmp/jeromechan.github.io,额外包含项目目录下的"CNAME"文件</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"3. 将public文件夹内容拷贝至/tmp/jeromechan.github.io,额外包含项目目录下的'CNAME'文件"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">rm -rf /tmp/<span class="variable">$REPO_NAME</span></span><br><span class="line">cp -r ./public/ /tmp/<span class="variable">$REPO_NAME</span></span><br><span class="line">cp ./CNAME /tmp/<span class="variable">$REPO_NAME</span>/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 切换至master分支下,删除所有非隐藏的项目文件,然后执行git commit -am 'Auto reset master files using hexo-deploy.sh script.'</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"4. 切换至master分支下,删除所有非隐藏的项目文件,然后执行git commit -am 'Auto reset master files using hexo-deploy.sh script.'"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">git checkout <span class="variable">$BRANCH_MASTER</span></span><br><span class="line">rm -r <span class="variable">$CURRENT_DIR</span>/*</span><br><span class="line">git commit -am <span class="string">'Auto reset master files using hexo-deploy.sh script.'</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 将/tmp/jeromechan.github.io文件内容全部拷贝至master分支下</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"5. 将/tmp/jeromechan.github.io文件内容全部拷贝至master分支下"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">cp -r /tmp/<span class="variable">$REPO_NAME</span>/* ./</span><br><span class="line">rm -rf /tmp/<span class="variable">$REPO_NAME</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 执行git add * & git commit -am 'Refresh and add master files using hexo-deploy.sh script.' & git push</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"6. 执行git add * & git commit -am 'Refresh and add master files using hexo-deploy.sh script.' & git push"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">git add *</span><br><span class="line">git commit -am <span class="string">'Refresh and add master files using hexo-deploy.sh script.'</span></span><br><span class="line">git push</span><br><span class="line"></span><br><span class="line"><span class="comment"># 7. 切换至source-hexo分支下</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"7. 切换至source-hexo分支下"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line">git checkout <span class="variable">$BRANCH_SOURCE</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"执行完成"</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"###########################################################"</span></span><br></pre></td></tr></table></figure><h4 id="六、篇末总结"><a href="#六、篇末总结" class="headerlink" title="六、篇末总结"></a>六、篇末总结</h4><p>以上实现细节均可参考项目源码实现:<br>GitHub项目地址:<a href="https://github.com/jeromechan/jeromechan.github.io/tree/source-hexo" target="_blank" rel="noopener">https://github.com/jeromechan/jeromechan.github.io/tree/source-hexo</a></p><p>透过以上Hexo静态站点的实践,我们可以了解到静态站点的优缺点,如何快速运用Hexo静态站点来构建自己的个人博客站点。同时通过在Hexo主题部分的定制化优化与改造,快速上手与提高Hexo开发技能。相信通过这篇文章,大家都可以学习到自己需要的知识点。后续还将撰写续篇,通过Hexo架构深入探讨当前业界盛行的Serverless架构设计。</p>]]></content>
<summary type="html">
分享Hexo静态站点的构建步骤以及主题定制之旅,同时延伸探讨当前盛行的Serverless架构。
</summary>
<category term="架构设计" scheme="https://jinlongchen.com/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
<category term="Hexo" scheme="https://jinlongchen.com/tags/Hexo/"/>
<category term="Serverless" scheme="https://jinlongchen.com/tags/Serverless/"/>
</entry>
<entry>
<title>Programming Principles(编程准则)</title>
<link href="https://jinlongchen.com/posts/2017/05/16/programming-principles/"/>
<id>https://jinlongchen.com/posts/2017/05/16/programming-principles/</id>
<published>2017-05-16T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.507Z</updated>
<content type="html"><</li><li><a href="#健壮性原则">健壮性原则</a></li></ul><h3 id="Module-Class"><a href="#Module-Class" class="headerlink" title="Module/Class"></a>Module/Class</h3><h3 id="模块-类"><a href="#模块-类" class="headerlink" title="模块/类"></a>模块/类</h3><ul><li><a href="#maximise-cohesion">Maximise Cohesion</a></li><li><a href="#高内聚">高内聚</a></li><li><a href="#liskov-substitution-principle">Liskov Substitution Principle</a></li><li><a href="#里氏替换原则">里氏替换原则</a></li><li><a href="#openclosed-principle">Open/Closed Principle</a></li><li><a href="#开闭原则">开闭原则</a></li><li><a href="#single-responsibility-principle">Single Responsibility Principle</a></li><li><a href="#单一职责原则">单一职责原则</a></li><li><a href="#hide-implementation-details">Hide Implementation Details</a></li><li><a href="#隐藏实现细节">隐藏实现细节</a></li><li><a href="#curlys-law">Curly’s Law</a></li><li><a href="#科里定律">科里定律</a></li><li><a href="#encapsulate-what-changes">Encapsulate What Changes</a></li><li><a href="#封装性">封装性</a></li><li><a href="#interface-segregation-principle">Interface Segregation Principle</a></li><li><a href="#接口隔离原则">接口隔离原则</a></li><li><a href="#command-query-separation">Command Query Separation</a></li><li><a href="#命令查询职责分离">命令查询职责分离</a></li></ul><h2 id="KISS"><a href="#KISS" class="headerlink" title="KISS"></a>KISS</h2><h2 id="KISS准则"><a href="#KISS准则" class="headerlink" title="KISS准则"></a>KISS准则</h2><p>Most systems work best if they are kept simple rather than made complex.</p><p>如果能够保持系统简单复杂性低,那么大多数的系统都可以运行的很好。</p><p>Why</p><p>为什么?</p><ul><li>Less code takes less time to write, has less bugs, and is easier to modify.</li><li>更少的代码可以花费更少的编码时间、更少的bugs、更容易去修改。</li><li>Simplicity is the ultimate sophistication.</li><li>简洁的就是最好最复杂的。</li><li>It seems that perfection is reached not when there is nothing left to add, but when there is nothing left to take away.</li><li>完美度不仅表示已经没有什么需要新增,而且也表示着没有什么需要剔除。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/KISS_principle" target="_blank" rel="noopener">KISS principle</a></li><li><a href="http://principles-wiki.net/principles:keep_it_simple_stupid" target="_blank" rel="noopener">Keep It Simple Stupid (KISS)</a></li></ul><h2 id="YAGNI"><a href="#YAGNI" class="headerlink" title="YAGNI"></a>YAGNI</h2><h2 id="YAGNI准则"><a href="#YAGNI准则" class="headerlink" title="YAGNI准则"></a>YAGNI准则</h2><p>YAGNI stands for “you aren’t gonna need it”: don’t implement something until it is necessary.</p><p>YAGNI英文描述为 “you aren’t gonna need it”:直到使用之前都不要去实现它。</p><p>Why</p><p>为什么?</p><ul><li>Any work that’s only used for a feature that’s needed tomorrow, means losing effort from features that need to be done for the current iteration.</li><li>任何只用于明天所需要的功能的工作,意味着从当前所需要的功能中减去相应的工作量。</li><li>It leads to code bloat; the software becomes larger and more complicated.</li><li>意味着代码变得臃肿,软件变得更大更复杂。</li></ul><p>How</p><p>怎么办?</p><ul><li>Always implement things when you actually need them, never when you just foresee that you need them.</li><li>只有在你真正需要的时候才去实现它,而不是根据你对于未来的估算和预测。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://c2.com/xp/YouArentGonnaNeedIt.html" target="_blank" rel="noopener">You Arent Gonna Need It</a></li><li><a href="http://www.xprogramming.com/Practices/PracNotNeed.html" target="_blank" rel="noopener">You’re NOT gonna need it!</a></li><li><a href="http://en.wikipedia.org/wiki/You_ain' target="_blank" rel="noopener"t_gonna_need_it">You ain’t gonna need it</a></li></ul><h2 id="Do-The-Simplest-Thing-That-Could-Possibly-Work"><a href="#Do-The-Simplest-Thing-That-Could-Possibly-Work" class="headerlink" title="Do The Simplest Thing That Could Possibly Work"></a>Do The Simplest Thing That Could Possibly Work</h2><h2 id="做最简单最可能实现的事情"><a href="#做最简单最可能实现的事情" class="headerlink" title="做最简单最可能实现的事情"></a>做最简单最可能实现的事情</h2><p>Why</p><p>为什么?</p><ul><li>Real progress against the real problem is maximized if we just work on what the problem really is.</li><li>如果我们努力解决问题的真正原因,那么对真正问题的积极进展就会被最大化。</li></ul><p>How<br>怎么办?</p><ul><li>Ask yourself: “What is the simplest thing that could possibly work?”</li><li>常常询问自己:“最简单最根本的事情是什么?”</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://c2.com/xp/DoTheSimplestThingThatCouldPossiblyWork.html" target="_blank" rel="noopener">Do The Simplest Thing That Could Possibly Work</a></li></ul><h2 id="Separation-of-Concerns"><a href="#Separation-of-Concerns" class="headerlink" title="Separation of Concerns"></a>Separation of Concerns</h2><h2 id="关注点分离"><a href="#关注点分离" class="headerlink" title="关注点分离"></a>关注点分离</h2><p>Separation of concerns is a design principle for separating a computer program into distinct sections, such that each section addresses a separate concern. For example the business logic of the application is a concern and the user interface is another concern. Changing the user interface should not require changes to business logic and vice versa.</p><p>关注点分离,是将计算机程序分成不同部分的设计原则,使每个部分都涉及到单独的问题。 例如,应用程序的业务逻辑是一个问题,用户界面是另一个问题。 更改用户界面不应要求对业务逻辑进行更改,反之亦然。</p><p>Quoting <a href="https://en.wikipedia.org/wiki/Edsger_W._Dijkstra" target="_blank" rel="noopener">Edsger W. Dijkstra</a> (1974):</p><p>引证 <a href="https://en.wikipedia.org/wiki/Edsger_W._Dijkstra" target="_blank" rel="noopener">Edsger W. Dijkstra</a> (1974):</p><blockquote><p>It is what I sometimes have called “the separation of concerns”, which, even if not perfectly possible, is yet the only available technique for effective ordering of one’s thoughts, that I know of. This is what I mean by “focusing one’s attention upon some aspect”: it does not mean ignoring the other aspects, it is just doing justice to the fact that from this aspect’s point of view, the other is irrelevant.<br>通常意义上所谈到的“关注点分离”,或许不是完全可能,我所知道的是它依然更多地依赖于个人对于事情场景的观点和想法。其实我所更想提及的是“把一个人的焦点放在某一个方面”:这并不意味着要忽略其他的方面,只是更多地期望能在关注的方面或者说角度上,找到根本的原因和事实,而不是其它不相关的部分。</p></blockquote><p>Why</p><p>为什么?</p><ul><li>Simplify development and maintenance of software applications.</li><li>简化软件应用的开发和维护工作。</li><li>When concerns are well-separated, individual sections can be reused, as well as developed and updated independently.</li><li>当关注点得到很好的分离,单独的部分可以被重用,那么开发和更新工作也将是相互独立的。</li></ul><p>How</p><p>怎么办?</p><ul><li>Break program functionality into separate modules that overlap as little as possible.</li><li>将程序按照功能拆分为不同的模块,使之具备更低的重复率。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="https://en.wikipedia.org/wiki/Separation_of_concerns" target="_blank" rel="noopener">Separation of Concerns</a></li></ul><h2 id="Keep-things-DRY"><a href="#Keep-things-DRY" class="headerlink" title="Keep things DRY"></a>Keep things DRY</h2><h2 id="DRY原则"><a href="#DRY原则" class="headerlink" title="DRY原则"></a>DRY原则</h2><p>Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.</p><p>在一个系统内,每一项知识点的表现必须是单一的,明确无歧义的,具备权威性的。</p><p>Each significant piece of functionality in a program should be implemented in just one place in the source code. Where similar functions are carried out by distinct pieces of code, it is generally beneficial to combine them into one by abstracting out the varying parts.</p><p>源代码中的每一项重要的程序功能只应该出现在一个地方。当我们遇到不同的代码段实现了相类似的功能,通常地,我们把不同的部分抽象出来,然后把相同的部分放在公共层,以实现更高的复用性。</p><p>Why</p><p>为什么?</p><ul><li>Duplication (inadvertent or purposeful duplication) can lead to maintenance nightmares, poor factoring, and logical contradictions.</li><li>重复代码(有意或无意的重复)会引发维护上的噩梦、较差的因果关系、逻辑矛盾。</li><li>A modification of any single element of a system does not require a change in other logically unrelated elements.</li><li>一个系统内单一元素的修改不涉及其他逻辑关联的元素的变更。</li><li>Additionally, elements that are logically related all change predictably and uniformly, and are thus kept in sync.</li><li>另,逻辑相关的元素的变化需要具有可预见性和一致性的,即需要保持同步性。</li></ul><p>How</p><p>怎么办?</p><ul><li>Put business rules, long expressions, if statements, math formulas, metadata, etc. in only one place.</li><li>将业务规则、长表达式、条件语句、数学公式、元数据等放置到同一个地方。</li><li>Identify the single, definitive source of every piece of knowledge used in your system, and then use that source to generate applicable instances of that knowledge (code, documentation, tests, etc).</li><li>在系统的识别出每个知识点的单一来源,然后将这些知识点并应用于相同场景的实例当中(代码、文档、测试用例等)。</li><li>Apply the <a href="http://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)" target="_blank" rel="noopener">Rule of three</a>.</li><li>遵循规则<a href="http://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)" target="_blank" rel="noopener">Rule of three</a>。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://c2.com/cgi/wiki?DontRepeatYourself" target="_blank" rel="noopener">Dont Repeat Yourself</a></li><li><a href="http://en.wikipedia.org/wiki/Don' target="_blank" rel="noopener"t_repeat_yourself">Don’t repeat yourself</a></li><li><a href="http://programmer.97things.oreilly.com/wiki/index.php/Don' target="_blank" rel="noopener"t_Repeat_Yourself">Don’t Repeat Yourself</a></li></ul><p>Related</p><p>引用</p><ul><li><a href="http://en.wikipedia.org/wiki/Abstraction_principle_(computer_programming)" target="_blank" rel="noopener">Abstraction principle</a></li><li><a href="http://c2.com/cgi/wiki?OnceAndOnlyOnce" target="_blank" rel="noopener">Once And Only Once</a> is a subset of DRY (also referred to as the goal of refactoring).</li><li><a href="http://en.wikipedia.org/wiki/Single_Source_of_Truth" target="_blank" rel="noopener">Single Source of Truth</a></li><li>A violation of DRY is <a href="http://thedailywtf.com/articles/The-WET-Cart" target="_blank" rel="noopener">WET</a> (Write Everything Twice)</li></ul><h2 id="Code-For-The-Maintainer"><a href="#Code-For-The-Maintainer" class="headerlink" title="Code For The Maintainer"></a>Code For The Maintainer</h2><h2 id="编写可维护的代码"><a href="#编写可维护的代码" class="headerlink" title="编写可维护的代码"></a>编写可维护的代码</h2><p>Why</p><p>为什么?</p><ul><li>Maintenance is by far the most expensive phase of any project.</li><li>维护阶段是一个项目生命周期当中最为昂贵的阶段。</li></ul><p>How</p><p>怎么办?</p><ul><li><em>Be</em> the maintainer.</li><li><em>成为</em>一个维护者。</li><li>Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.</li><li>常常作出假设,假设你的代码维护者是一个有精神暴力倾向的并且知道你住处的人。</li><li>Always code and comment in such a way that if someone a few notches junior picks up the code, they will take pleasure in reading and learning from it.</li><li>在编码和注释的时候,时长以阅读者的角度去考虑,考虑他们是否会爱上阅读和学习你的成果。</li><li><a href="http://www.sensible.com/dmmt.html" target="_blank" rel="noopener">Don’t make me think</a>.</li><li>以常识和简洁的角度去说明事情关键,不要让读者去想太多。<a href="http://www.sensible.com/dmmt.html" target="_blank" rel="noopener">Don’t make me think</a>.</li><li>Use the <a href="http://en.wikipedia.org/wiki/Principle_of_least_astonishment" target="_blank" rel="noopener">Principle of Least Astonishment</a>.</li><li>遵循准则<a href="http://en.wikipedia.org/wiki/Principle_of_least_astonishment" target="_blank" rel="noopener">Principle of Least Astonishment</a>.</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://c2.com/cgi/wiki?CodeForTheMaintainer" target="_blank" rel="noopener">Code For The Maintainer</a></li><li><a href="http://blog.codinghorror.com/the-noble-art-of-maintenance-programming/" target="_blank" rel="noopener">The Noble Art of Maintenance Programming</a></li></ul><h2 id="Avoid-Premature-Optimization"><a href="#Avoid-Premature-Optimization" class="headerlink" title="Avoid Premature Optimization"></a>Avoid Premature Optimization</h2><h2 id="避免过度过早优化"><a href="#避免过度过早优化" class="headerlink" title="避免过度过早优化"></a>避免过度过早优化</h2><p>Quoting <a href="http://en.wikiquote.org/wiki/Donald_Knuth" target="_blank" rel="noopener">Donald Knuth</a>:</p><p>引用 <a href="http://en.wikiquote.org/wiki/Donald_Knuth" target="_blank" rel="noopener">唐纳德·克努斯</a>:</p><blockquote><p>Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.<br>程序员花费极大的时间去思考、担心哪些程序里非关键部位的运行速度,而这种种在调试、维护上的工作效能方面常常引发了负面的影响。我们应该摒弃那些花费了97%时间去做的低效能的事情。过早的优化是所有邪恶的根源。不管怎样,我们不能放弃那3%关键部分的机会。</p></blockquote><p>Understanding what is and isn’t “premature” is critical of course.</p><p>对于是否属于“过早的优化”,这是一项关键的课程。</p><p>Why</p><p>为什么?</p><ul><li>It is unknown upfront where the bottlenecks will be.</li><li>过早优化会引发未来不可知的瓶颈的发生。</li><li>After optimization, it might be harder to read and thus maintain.</li><li>优化之后,它或许变得更加难以阅读和维护。</li></ul><p>How</p><p>怎么办?</p><ul><li><a href="http://c2.com/cgi/wiki?MakeItWorkMakeItRightMakeItFast" target="_blank" rel="noopener">Make It Work Make It Right Make It Fast</a></li><li><a href="http://c2.com/cgi/wiki?MakeItWorkMakeItRightMakeItFast" target="_blank" rel="noopener">使之具备可行性、正确性、快速性</a></li><li>Don’t optimize until you need to, and only after profiling you discover a bottleneck optimise that.</li><li>只有在你需要的时候才去优化,只有在做了瓶颈分析之后再去做实际的优化。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Program_optimization" target="_blank" rel="noopener">Program optimization</a></li><li><a href="http://c2.com/cgi/wiki?PrematureOptimization" target="_blank" rel="noopener">Premature Optimization</a></li></ul><h2 id="Boy-Scout-Rule"><a href="#Boy-Scout-Rule" class="headerlink" title="Boy-Scout Rule"></a>Boy-Scout Rule</h2><h2 id="美国童子军准则"><a href="#美国童子军准则" class="headerlink" title="美国童子军准则"></a>美国童子军准则</h2><p>The Boy Scouts of America have a simple rule that we can apply to our profession: “Leave the campground cleaner than you found it”. The boy-scout rule states that we should always leave the code cleaner than we found it.</p><p>美国童子军有一项简单的准则:“离开野营区之时保证要比自己来的时候更加干净”。我们可以将这一准则应用在计算机方面:“时常迫使代码比我们发现它的时候更整洁更清晰”。</p><p>Why</p><p>为什么?</p><ul><li>When making changes to an existing codebase the code quality tends to degrade, accumulating technical debt. Following the boyscout rule, we should mind the quality with each commit. Technical debt is resisted by continuous refactoring, no matter how small.</li><li>当我们对现有代码库作修改之时,现有代码库质量低并且积累了许多的技术债务。根据美国童子军准则,我们理应关心每一次提交的代码质量,而技术债务也应该在持续的重构过程中逐渐变少。</li></ul><p>How</p><p>怎么办?</p><ul><li>With each commit make sure it does not degrade the codebase quality.</li><li>保证每一次的提交不会造成代码库的代码质量的降级。</li><li>Any time someone sees some code that isn’t as clear as it should be, they should take the opportunity to fix it right there and then.</li><li>任何时间,任何人,只要发现了代码可以变得更为清晰和整洁,那么他就应该寻找机会去实现它。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://martinfowler.com/bliki/OpportunisticRefactoring.html" target="_blank" rel="noopener">Opportunistic Refactoring</a></li></ul><h2 id="Minimise-Coupling"><a href="#Minimise-Coupling" class="headerlink" title="Minimise Coupling"></a>Minimise Coupling</h2><h2 id="低耦合"><a href="#低耦合" class="headerlink" title="低耦合"></a>低耦合</h2><p>Coupling between modules/components is their degree of mutual interdependence; lower coupling is better. In other words, coupling is the probability that code unit “B” will “break” after an unknown change to code unit “A”.</p><p>耦合度指的是模块/组件之间的相互依赖程度。耦合度越低则越好。换句话说,耦合度高会导致代码单元“B”因为代码单元“A”的一个未知变更而被打断。</p><p>Why</p><p>为什么?</p><ul><li>A change in one module usually forces a ripple effect of changes in other modules.</li><li>一个模块的变更会迫使和波及其他的模块也需要发生相应的变更。</li><li>Assembly of modules might require more effort and/or time due to the increased inter-module dependency.</li><li>模块集成过程中会涉及更多的修改、更多的时间,正是因为模块内的相互依赖复杂度的升高。</li><li>A particular module might be harder to reuse and/or test because dependent modules must be included.</li><li>一个指定的模块将变得难以重用和测试,正是因为其依赖的其他模块也必须被包含进来。</li><li>Developers might be afraid to change code because they aren’t sure what might be affected.</li><li>开发者会害怕修改代码,因为他们不确定具体有多少方面会被涉及。</li></ul><p>How</p><p>怎么办?</p><ul><li>Eliminate, minimise, and reduce complexity of necessary relationships.</li><li>尽可能多地,甚至消除复杂必须的关联关系。</li><li>By hiding implementation details, coupling is reduced.</li><li>隐藏了实现的细节之后,耦合性往往会被降低。</li><li>Apply the <a href="#law-of-demeter">Law of Demeter</a>.</li><li>遵循迪米特法则<a href="#law-of-demeter">Law of Demeter</a>.</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Coupling_(computer_programming)" target="_blank" rel="noopener">Coupling</a></li><li><a href="http://c2.com/cgi/wiki?CouplingAndCohesion" target="_blank" rel="noopener">Coupling And Cohesion</a></li></ul><h2 id="Law-of-Demeter"><a href="#Law-of-Demeter" class="headerlink" title="Law of Demeter"></a>Law of Demeter</h2><h2 id="迪米特法则"><a href="#迪米特法则" class="headerlink" title="迪米特法则"></a>迪米特法则</h2><p>Don’t talk to strangers.</p><p>不要告诉陌生人。</p><p>Why</p><p>为什么?</p><ul><li>It usually tightens coupling</li><li>依赖往往会增加耦合度。</li><li>It might reveal too much implementation details</li><li>依赖会露出过多的实现细节。</li></ul><p>How</p><p>怎么办?</p><p>A method of an object may only call methods of:</p><p>一个对象的一个方法,只能调用如下内容:</p><ol><li>The object itself.</li><li>对象本身。</li><li>An argument of the method.</li><li>方法参数。</li><li>Any object created within the method.</li><li>方法内所创建的对象。</li><li>Any direct properties/fields of the object.</li><li>对象内直接包含的属性、字段。</li></ol><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Law_of_Demeter" target="_blank" rel="noopener">Law of Demeter</a></li><li><a href="http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx/" target="_blank" rel="noopener">The Law of Demeter Is Not A Dot Counting Exercise</a></li></ul><h2 id="Composition-Over-Inheritance"><a href="#Composition-Over-Inheritance" class="headerlink" title="Composition Over Inheritance"></a>Composition Over Inheritance</h2><h2 id="组合优于继承"><a href="#组合优于继承" class="headerlink" title="组合优于继承"></a>组合优于继承</h2><p>Why</p><p>为什么?</p><ul><li>Less coupling between classes.</li><li>降低类之间的耦合。</li><li>Using inheritance, subclasses easily make assumptions, and break LSP.</li><li>使用继承、子类会易于出现断言,并且打破里氏替换原则。</li></ul><p>How</p><p>怎么办?</p><ul><li>Test for LSP (substitutability) to decide when to inherit.</li><li>根据里氏替换原则,判断什么时候使用继承。</li><li>Compose when there is a “has a” (or “uses a”) relationship, inherit when “is a”.</li><li>若是”has a” (或者”uses a”)关系的时候使用组合,若是”is a”关系则使用继承。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://blogs.msdn.com/b/thalesc/archive/2012/09/05/favor-composition-over-inheritance.aspx" target="_blank" rel="noopener">Favor Composition Over Inheritance</a></li></ul><h2 id="Orthogonality"><a href="#Orthogonality" class="headerlink" title="Orthogonality"></a>Orthogonality</h2><h2 id="正交性"><a href="#正交性" class="headerlink" title="正交性"></a>正交性</h2><blockquote><p>The basic idea of orthogonality is that things that are not related conceptually should not be related in the system.<br>正交性的基本定义是,系统内概念不相同的部分,不应该形成关联和依赖。</p></blockquote><p>Source: <a href="http://www.artima.com/intv/dry3.html" target="_blank" rel="noopener">Be Orthogonal</a></p><p>引用: <a href="http://www.artima.com/intv/dry3.html" target="_blank" rel="noopener">Be Orthogonal</a></p><blockquote><p>It is associated with simplicity; the more orthogonal the design, the fewer exceptions. This makes it easier to learn, read and write programs in a programming language. The meaning of an orthogonal feature is independent of context; the key parameters are symmetry and consistency.<br>它与简单性有关,设计的时候正交性越高,则异常发生地越少。这通常会使得代码变得更加容易学习、阅读和编写,无论你使用的是哪一种编程语言。正交性的一个典型特征是上下文之间互相没有依赖,核心参数之间会保持对称性和一致性。</p></blockquote><p>Source: <a href="http://en.wikipedia.org/wiki/Orthogonality_(programming)" target="_blank" rel="noopener">Orthogonality</a></p><p>引用: <a href="http://en.wikipedia.org/wiki/Orthogonality_(programming)" target="_blank" rel="noopener">Orthogonality</a></p><h2 id="Robustness-Principle"><a href="#Robustness-Principle" class="headerlink" title="Robustness Principle"></a>Robustness Principle</h2><h2 id="健壮性原则"><a href="#健壮性原则" class="headerlink" title="健壮性原则"></a>健壮性原则</h2><blockquote><p>Be conservative in what you do, be liberal in what you accept from others<br>对内保持保守,对外保持自由。</p></blockquote><p>Collaborating services depend on each others interfaces. Often the interfaces need to evolve causing the other end to receive unspecified data. A naive implementation refuses to collaborate if the received data does not strictly follow the specification. A more sophisticated implementation will still work ignoring the data it does not recognize.</p><p>协作服务依赖于各式各样的接口。通常地,接口的演变会致使另外一端接收到未指定的数据。倘若数据结构没有遵循规范,那么一个简单的不健壮的实现会遭到拒绝。一个健壮的实现会主动忽略不可识别的数据,继而持续工作。</p><p>Why</p><p>为什么?</p><ul><li>In order to be able to evolve services you need to ensure that a provider can make changes to support new demands while causing minimal breakage to their existing clients.</li><li>为了使得程序服务得以演进,你需要确保提供者程序可以支持新的指令,尽管这些指令可能会对现有的客户端所导致的小面积破坏。</li></ul><p>How</p><p>怎么办?</p><ul><li>Code that sends commands or data to other machines (or to other programs on the same machine) should conform completely to the specifications, but code that receives input should accept non-conformant input as long as the meaning is clear.</li><li>发送给其他机器(或者相同机器的其他程序)的指令或者数据的代码理应完全遵循指定的规范,但是接收方需要能够接收和处理不符合规范,但含义清晰的数据或指令。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="https://en.wikipedia.org/wiki/Robustness_principle" target="_blank" rel="noopener">Robustness Principle in Wikipedia</a></li><li><a href="http://martinfowler.com/bliki/TolerantReader.html" target="_blank" rel="noopener">Tolerant Reader</a></li></ul><h2 id="Maximise-Cohesion"><a href="#Maximise-Cohesion" class="headerlink" title="Maximise Cohesion"></a>Maximise Cohesion</h2><h2 id="高内聚"><a href="#高内聚" class="headerlink" title="高内聚"></a>高内聚</h2><p>Cohesion of a single module/component is the degree to which its responsibilities form a meaningful unit; higher cohesion is better.</p><p>一个单一模块/组件的内聚性指的是它的职责形成了有具体含义的独立单元。内聚性自然越高也好。</p><p>Why</p><p>为什么?</p><ul><li>Increased difficulty in understanding modules.</li><li>增加了模块理解的难度。</li><li>Increased difficulty in maintaining a system, because logical changes in the domain affect multiple modules, and because changes in one module require changes in related modules.</li><li>增加了维护系统的成本消耗。因为一个方面的逻辑变更会引发其它多个关联模块需要同时发生变更。</li><li>Increased difficulty in reusing a module because most applications won’t need the random set of operations provided by a module.</li><li>增加了程序重用的难度。因为大多数的程序不需要模块所提供的随机操作。</li></ul><p>How</p><p>怎么办?</p><ul><li>Group related functionalities sharing a single responsibility (e.g. in a class).</li><li>一组相似的功能共享使用一个单一的职责(例如在一个类里)。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Cohesion_(computer_science)" target="_blank" rel="noopener">Cohesion</a></li><li><a href="http://c2.com/cgi/wiki?CouplingAndCohesion" target="_blank" rel="noopener">Coupling And Cohesion</a></li></ul><h2 id="Liskov-Substitution-Principle"><a href="#Liskov-Substitution-Principle" class="headerlink" title="Liskov Substitution Principle"></a>Liskov Substitution Principle</h2><h2 id="里氏替换原则"><a href="#里氏替换原则" class="headerlink" title="里氏替换原则"></a>里氏替换原则</h2><p>The LSP is all about expected behavior of objects:</p><p>里氏替换原则是关于对象预期行为方面的描述:</p><blockquote><p>Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.<br>程序内的对象理应可以被它的子类所无缝替换,此间不需要作任何的程序订正操作。</p></blockquote><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Liskov_substitution_principle" target="_blank" rel="noopener">Liskov substitution principle</a></li><li><a href="http://freshbrewedcode.com/derekgreer/2011/12/31/solid-javascript-the-liskov-substitution-principle/" target="_blank" rel="noopener">The Liskov Substitution Principle</a></li></ul><h2 id="Open-Closed-Principle"><a href="#Open-Closed-Principle" class="headerlink" title="Open/Closed Principle"></a>Open/Closed Principle</h2><h2 id="开闭原则"><a href="#开闭原则" class="headerlink" title="开闭原则"></a>开闭原则</h2><p>Software entities (e.g. classes) should be open for extension, but closed for modification. I.e. such an entity can allow its behavior to be modified without altering its source code.</p><p>软件实体(例如类)理应对扩展开放,对修改关闭。例如一个实体可以允许它的动作不需要经过修改源码而发生的演变。</p><p>Why</p><p>为什么?</p><ul><li>Improve maintainability and stability by minimizing changes to existing code.</li><li>在现有代码发生最小化改变的前提下,改善和提高可维护性和稳定性。</li></ul><p>How</p><p>怎么办?</p><ul><li>Write classes that can be extended (as opposed to classes that can be modified).</li><li>编写可以被扩展的类(而不是类文件被修改)。</li><li>Expose only the moving parts that need to change, hide everything else.</li><li>只暴露那些需要修改以支持扩展的部分,其余的部分都隐藏。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Open/closed_principle" target="_blank" rel="noopener">Open Closed Principle</a></li><li><a href="http://freshbrewedcode.com/derekgreer/2011/12/19/solid-javascript-the-openclosed-principle/" target="_blank" rel="noopener">SOLID JavaScript: The Open/Closed Principle</a></li></ul><h2 id="Single-Responsibility-Principle"><a href="#Single-Responsibility-Principle" class="headerlink" title="Single Responsibility Principle"></a>Single Responsibility Principle</h2><h2 id="单一职责原则"><a href="#单一职责原则" class="headerlink" title="单一职责原则"></a>单一职责原则</h2><p>A class should never have more than one reason to change.</p><p>一个类不应该包含多于一个职责。</p><p>Long version: Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. Responsibility can be defined as a reason to change, so a class or module should have one, and only one, reason to change.</p><p>长文字版本:每一个类都应该有一个职责,而这个职责应该被这个类所内聚。职责被定义为发生改变的一个原因,即一个类或者模块有且仅有一个原因会致使其发生演变。</p><p>Why</p><p>为什么?</p><ul><li>Maintainability: changes should be necessary only in one module or class.</li><li>可维护性:变更只应该出现在一个模块或者一个类当中。</li></ul><p>How</p><p>怎么办?</p><ul><li>Apply <a href="#Curly-s-Law">Curly’s Law</a>.</li><li>遵循<a href="#科里定律">科里定律</a>.</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Single_responsibility_principle" target="_blank" rel="noopener">Single responsibility principle</a></li></ul><h2 id="Hide-Implementation-Details"><a href="#Hide-Implementation-Details" class="headerlink" title="Hide Implementation Details"></a>Hide Implementation Details</h2><h2 id="隐藏实现细节"><a href="#隐藏实现细节" class="headerlink" title="隐藏实现细节"></a>隐藏实现细节</h2><p>A software module hides information (i.e. implementation details) by providing an interface, and not leak any unnecessary information.</p><p>一个软件模块应该隐藏一些信息(例如实现细节),仅提供一个接口即可,以确保不泄露任何不必要的信息。</p><p>Why</p><p>为什么?</p><ul><li>When the implementation changes, the interface clients are using does not have to change.</li><li>当实现细节需要发生变更之时,接口消费方可以无缝使用而不需要作出任何变更。</li></ul><p>How</p><p>怎么办?</p><ul><li>Minimize accessibility of classes and members.</li><li>最小化类及其成员的可访问性。</li><li>Don’t expose member data in public.</li><li>不要暴露成员数据在public域。</li><li>Avoid putting private implementation details into a class’s interface.</li><li>杜绝将私有的实现细节放置到类的接口层上。</li><li>Decrease coupling to hide more implementation details.</li><li>降低模块间耦合性,隐藏更多的不必要的实现细节。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://en.wikipedia.org/wiki/Information_hiding" target="_blank" rel="noopener">Information hiding</a></li></ul><h2 id="Curly’s-Law"><a href="#Curly’s-Law" class="headerlink" title="Curly’s Law"></a>Curly’s Law</h2><h2 id="科里定律"><a href="#科里定律" class="headerlink" title="科里定律"></a>科里定律</h2><p>Curly’s Law is about choosing a single, clearly defined goal for any particular bit of code: Do One Thing.</p><p>科里定律是关于如何为任一特定代码段选择一项单一的、定义明确的目标的一项法则:只作一件事。</p><ul><li><a href="http://blog.codinghorror.com/curlys-law-do-one-thing/" target="_blank" rel="noopener">Curly’s Law: Do One Thing</a></li><li><a href="http://fortyplustwo.com/2008/09/06/the-rule-of-one-or-curlys-law/" target="_blank" rel="noopener">The Rule of One or Curly’s Law</a></li></ul><h2 id="Encapsulate-What-Changes"><a href="#Encapsulate-What-Changes" class="headerlink" title="Encapsulate What Changes"></a>Encapsulate What Changes</h2><h2 id="封装性"><a href="#封装性" class="headerlink" title="封装性"></a>封装性</h2><p>A good design identifies the hotspots that are most likely to change and encapsulates them behind an API. When an anticipated change then occurs, the modifications are kept local.</p><p>一项好的设计意味着某些很有可能被修改和内聚的热点部分被隐藏在API的后方。当预期的变更出现时,这些变更也只需要在内部作出调整即可。</p><p>Why</p><p>为什么?</p><ul><li>To minimize required modifications when a change occurs</li><li>当变更发生时,可以最小化变更的涉及面。</li></ul><p>How</p><p>怎么办?</p><ul><li>Encapsulate the concept that varies behind an API</li><li>在API内部封装多样化的概念。</li><li>Possibly separate the varying concept into its own module</li><li>尽可能地从多样化的概念中,分隔出独立的单一模块。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="http://principles-wiki.net/principles:encapsulate_the_concept_that_varies" target="_blank" rel="noopener">Encapsulate the Concept that Varies</a></li><li><a href="http://blogs.msdn.com/b/steverowe/archive/2007/12/26/encapsulate-what-varies.aspx" target="_blank" rel="noopener">Encapsulate What Varies</a></li><li><a href="https://en.wikipedia.org/wiki/Information_hiding" target="_blank" rel="noopener">Information Hiding</a></li></ul><h2 id="Interface-Segregation-Principle"><a href="#Interface-Segregation-Principle" class="headerlink" title="Interface Segregation Principle"></a>Interface Segregation Principle</h2><h2 id="接口隔离原则"><a href="#接口隔离原则" class="headerlink" title="接口隔离原则"></a>接口隔离原则</h2><p>Reduce fat interfaces into multiple smaller and more specific client specific interfaces. An interface should be more dependent on the code that calls it than the code that implements it. </p><p>降低接口的粒度,使得接口更小更具体,更接近调用方的现实需要。一个接口的定义更多地应该依赖于调用方的代码,而不是实现方可以实现的细节。</p><p>Why</p><p>为什么?</p><ul><li>If a class implements methods that are not needed the caller needs to know about the method implementation of that class. For example if a class implements a method but simply throws then the caller will need to know that this method shouldn’t actually be called.</li><li>如果一个类的实现方法已经被废弃,那么调用方理应需要知晓这个类的实现方法的具体实现细节。例如,假设一个类的实现方法仅仅是抛出了一个异常,那么调用方需要知道这个方法不应该再被调用。</li></ul><p>How</p><p>怎么办?</p><ul><li>Avoid fat interfaces. Classes should never have to implement methods that violate the <a href="#single-responsibility-principle">Single responsibility principle</a>.</li><li>杜绝肥胖的接口存在。一个类的实现方法的设计不应该违背<a href="#single-responsibility-principle">单一职责原则</a>.</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="https://en.wikipedia.org/wiki/Interface_segregation_principle" target="_blank" rel="noopener">Interface segregation principle</a></li></ul><h2 id="Command-Query-Separation"><a href="#Command-Query-Separation" class="headerlink" title="Command Query Separation"></a>Command Query Separation</h2><h2 id="命令查询职责分离(也称Command-Query-Responsibility-Segregation,CQRS)"><a href="#命令查询职责分离(也称Command-Query-Responsibility-Segregation,CQRS)" class="headerlink" title="命令查询职责分离(也称Command Query Responsibility Segregation,CQRS)"></a>命令查询职责分离(也称Command Query Responsibility Segregation,CQRS)</h2><p>The Command Query Separation principle states that each method should be either a command that performs an action or a query that returns data to the caller but not both. Asking a question should not modify the answer.</p><p>命令查询职责分离准则的具体阐述可以参见资料所引述到的Martin Fowler的文章。命令查询职责分离原则指出,每个方法应该是执行一个操作的命令(命令是void的,不返回任何结果,但会改变对象的状态)或者一个查询(返回结果,但是不会改变对象的状态,对系统没有副作用),它将数据返回给调用者,而不是两者。提出问题不应该修改答案。</p><p>With this principle applied the programmer can code with much more confidence. The query methods can be used anywhere and in any order since they do not mutate the state. With commands one has to be more careful.</p><p>使用这条准则,程序员在编码之时可以更加自信,查询方法可以在任何地方以任意次序使用,因为它不会影响到对象状态。而命令则需要更加小心谨慎。</p><p>Why</p><p>为什么?</p><ul><li>By clearly separating methods into queries and commands the programmer can code with additional confidence without knowing each method’s implementation details.</li><li>基于清晰地将方法分隔为查询和命令两大阵营之后,程序员在编码方面更加自信了,因为他们可以不需要去了解每个方法的实现具体细节。</li></ul><p>How</p><p>怎么办?</p><ul><li>Implement each method as either a query or a command</li><li>方法设计之时,要么是查询,要么是命令,不可二者兼备。</li><li>Apply naming convention to method names that implies whether the method is a query or a command</li><li>针对方法名的命名上作约定和规范,使得方法名上可以直接表露出它具体是查询还是命令。</li></ul><p>Resources</p><p>更多资料</p><ul><li><a href="https://en.wikipedia.org/wiki/Command%E2%80%93query_separation" target="_blank" rel="noopener">Command Query Separation in Wikipedia</a></li><li><a href="http://martinfowler.com/bliki/CommandQuerySeparation.html" target="_blank" rel="noopener">Command Query Separation by Martin Fowler</a></li><li><a href="http://www.cnblogs.com/yangecnu/p/Introduction-CQRS.html" target="_blank" rel="noopener">Introduction CQRS</a></li></ul>]]></content>
<summary type="html">
编程准则,programming principles
</summary>
<category term="架构设计" scheme="https://jinlongchen.com/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
<category term="编程准则" scheme="https://jinlongchen.com/tags/%E7%BC%96%E7%A8%8B%E5%87%86%E5%88%99/"/>
<category term="principles" scheme="https://jinlongchen.com/tags/principles/"/>
</entry>
<entry>
<title>并发场景实践方案优劣评析</title>
<link href="https://jinlongchen.com/posts/2016/12/19/parallel-of-buying-scenario/"/>
<id>https://jinlongchen.com/posts/2016/12/19/parallel-of-buying-scenario/</id>
<published>2016-12-19T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.507Z</updated>
<content type="html"><![CDATA[<p>近期实践了高并发场景下,如何去实现抢购系统的研究。虽然方案并不算成功,但是本次能将以往只在资料中学习过的知识付诸于现实,也让自己畅快淋漓了一番。本篇文章所阐述的内容都比较基础,重在于将实践过程中所得到的经验和教训分享出来给大家。</p><p>以下将分为五个方面来回顾下本次高并发的实践经历。</p><ul><li>1.抢购场景特点</li><li>2.技术选型</li><li>3.方案思路</li><li>4.优劣势辨析</li><li>5.小结一下</li></ul><h4 id="一、抢购场景的特点"><a href="#一、抢购场景的特点" class="headerlink" title="一、抢购场景的特点"></a>一、抢购场景的特点</h4><p>在日常生活中,电子商务营销手段中,抢购、促销是最为常用的提高销售的手段。最熟悉不过的当然就是天猫“双十一”购物节了。既然是“抢购”,用通俗的话来概括,那就是<strong>手快有手慢无</strong>。以下我们做了一些细分:</p><p><strong>1.1 限时限量</strong></p><ul><li>限用户样本</li><li>限用户可购买量</li><li>限商品总量</li><li>限抢购时间</li></ul><p><strong>1.2 防伪防恶意</strong></p><ul><li>防虚假用户购买</li><li>防恶意系统攻击</li></ul><p>而广大群众并不一定都跟着你的步伐走,可能他们会跟抢12306火车票的手段一样,在你的抢购活动中付诸于实践,往往这个会给系统带来四面楚歌般的无形压力,轻者响应延迟,重者宕机。</p><p>写到这里,复杂度已经可见一斑,内容涉及了各个方面,例如并发编程、安全、缓存、关系型数据存储、网络流量、防火墙、服务容器、连接池、IO通信。虽说内容很多,但是我们可以从中根据自己所面临的场景,选择其中一些合适的技术手段即可,不必面面俱到追求完美,它们都只是你实现目标的工具。</p><h4 id="二、技术选型"><a href="#二、技术选型" class="headerlink" title="二、技术选型"></a>二、技术选型</h4><p>了解基本的抢购场景知识之后,就是考虑怎么去解决场景中所遇到的实际问题了。对于抢购场景的特点,我们给每个特点归归类:</p><ul><li><strong>限用户样本、限抢购时间,这2项可以算是参加抢购的前置条件,不满足则拒绝请求。</strong> </li></ul><p>对于用户样本的限定,普通的条件性编码可以满足这项需要,因为这项属于前置条件,条件数据的属性当确定活动用户样本之后,它们就成为了静态数据,没有并发的意外风险。<br>对于抢购时间,这项条件可以借鉴业界对这方面的设计思路,将静态代码(html等静态页面内容)部署到CDN节点上,而仅保留一项js文件(该文件中包含了判断抢购开始的请求编码,当然里面可以包含一项随机token来保障代码所发出的请求的有效性)执行动态加载,以保障抢购可以准时开始。<br>对于抢购页面CDN静态之后所出现的无效点击、提前知道抢购页面等问题,这部分可以使用包含随机token串的页面url,用以保证抢购页面的有效性和请求的合法性。</p><ul><li><strong>限用户可购买量、限商品总量,这2项是整个抢购场景中的核心前提,即“不可超售”的业务特点。</strong></li></ul><p>对于用户可购买量、商品总量的限制,这部分可以考虑使用编程锁的实现,如果考虑到多机部署的分布式需要,可以考虑使用分布式锁,同时加以“分而治之”的思路,可以提高请求的并发量。<br>关于锁的知识,我们常见的锁有悲观锁和乐观锁,而锁的实现方式可以是中间件、缓存、JDK自带同步锁、关系型数据库等方式。</p><ul><li><strong>防虚假用户购买,这一特点旨在于系统如何去辨析真正的消费用户,而非临时创建的僵尸用户。</strong></li></ul><p>这部分比较难以实现,并不是简单的判别某个单位时间同一来源请求上限值、验证码校验这么一些常见的指标。如果要根本性解决这方面,可能需要使用一些机器学习方面的知识,分析出那类请求可以判别为“疑似攻击”,同时还需要准确率,这是一门深奥的学问,目前自己还没有太多的涉及就不深入谈论了。</p><ul><li><strong>防恶意系统攻击,这项属于系统部署层面上需要考量的架构问题。如若不能及时识别出这是一次攻击而非正常消费请求,那么系统会面临宕机的风险。</strong></li></ul><p>关于恶意系统攻击,与前面谈及的“防虚假用户购买”有相同之处,“虚假用户购买”的量达到了一定的阈值,那么就可以归类为“恶意系统攻击”了。在WEB安全领域上,有许多常见的网络攻击都是需要去规避和解决的,无论是编码上还是运维部署层面上,例如DDoS、SQL注入、信息伪造等手段。</p><p>本次实践过程中,对于这一块并没有太多的涉及,只是通过一些流量设置上的控制,避免了服务容器由于请求压力过高而产生的宕机风险。这一块待日后自己的知识库成熟之后,再po文分享下自己的实践感触。</p><h4 id="三、方案思路"><a href="#三、方案思路" class="headerlink" title="三、方案思路"></a>三、方案思路</h4><h5 id="3-1-基本思路和考量点"><a href="#3-1-基本思路和考量点" class="headerlink" title="3.1 基本思路和考量点"></a>3.1 基本思路和考量点</h5><ul><li>不可超售,需要使用到线程安全、线程锁、本地线程、事务性、原子CAS特性、阻塞/非阻塞队列等知识;</li><li>分而治之,将商品可销售总量分为多条管道同时进行销售,由负载均衡算法代理路由的逻辑;</li><li>存储介质上,考量可持久化缓存、关系型数据库;</li><li>在组件的配置方面,考量服务容器的并行连接池配置、缓存的连接池配置、关系型数据库连接池配置、编码线程池配置、JVM启动参数(例如JVM PermSize、Xms、Xmx内存项配置);</li><li>在流程实现上,将一些有效判断节点分布在真实消费商品的逻辑之前,提高核心逻辑的有效执行占比,提高访问效率;</li><li>在消费数据的持久化措施上,采用“生产者-消费者”的队列实现模式,异步的策略将消费数据尽可能快地持久化到日常使用的关系型数据库中,便于查看和其他业务系统的接口读取;</li></ul><h5 id="3-2-逻辑流程图"><a href="#3-2-逻辑流程图" class="headerlink" title="3.2 逻辑流程图"></a>3.2 逻辑流程图</h5><p><img src="/images/2016-12-19-parallel-of-buying-scenario/14821328103721.jpg" alt="1"></p><h5 id="3-3-实现过程中可能需要额外关注的部分"><a href="#3-3-实现过程中可能需要额外关注的部分" class="headerlink" title="3.3 实现过程中可能需要额外关注的部分"></a>3.3 实现过程中可能需要额外关注的部分</h5><blockquote><p>Redis使用方面</p></blockquote><ul><li>1.系统Redis的连接池配置,最大可用连接<code>maxTotal</code>要小于Redis服务配置<code>redis.conf</code>中<code>maxclients</code>数值;</li><li>2.系统Redis的连接超时时长<code>maxWaitMillis</code>要尽可能地根据系统可承载压力进行设置,一般不可超过5s,高并发的场景下建议在2s以下;</li><li>3.为保障系统Redis客户端从池子中获取的连接不是broken的,参数<code>testOnBorrow => true</code>一定要加上;</li><li>4.Jedis连接池的空闲释放算法采用的是apache common pool作的实现,GenericObjectPool是通过“驱逐者线程Evictor”管理“空闲池对象”的。在高并发场景下,选用<code>LIFO</code>可以更快地通过Evictor驱逐者任务将空闲无占用连接及时释放掉,而避免采用<code>FIFO</code>致使部分idle连接处在<code>starvation</code>状态下久久得不到释放,从而造成leaks of redis resources;</li><li>5.如果你使用的不是Spring框架对Redis客户端进行<code>scope="singleton"</code>的集成,那么在创建连接池之时,也需要避免瞬间并发而导致的池子被多建的场景发生,因为每一个池子都会由于配置<code>minIdle</code>而固定占用着redis的连接数量;</li><li>6.如果你使用的是分布式集群Redis,那么可以对连接池配置多机,如此可有效提高连接数和Redis服务性能;</li><li>7.如果你使用的是Spring+Jedis(注:最受欢迎的Redis Client之一)的Redis编码方案,请留意<code>ShardedJedisPool</code>和<code>JedisPool</code>的区别,后者所取得的连接可以执行事务、多key批量等操作,而前者是为Redis分布式集群而生,每一次所获取的连接并不保证相同和唯一来源;</li></ul><p>这里给出一个简单的Spring+Jedis的连接池配置例子:</p><ul><li>Jedis连接池基础参数配置</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"jedisConfig"</span> <span class="attr">class</span>=<span class="string">"redis.clients.jedis.JedisPoolConfig"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"maxTotal"</span> <span class="attr">value</span>=<span class="string">"10000"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"maxIdle"</span> <span class="attr">value</span>=<span class="string">"10000"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"minIdle"</span> <span class="attr">value</span>=<span class="string">"200"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"testOnBorrow"</span> <span class="attr">value</span>=<span class="string">"true"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"TestOnReturn"</span> <span class="attr">value</span>=<span class="string">"true"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"maxWaitMillis"</span> <span class="attr">value</span>=<span class="string">"300"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"testWhileIdle"</span> <span class="attr">value</span>=<span class="string">"true"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"timeBetweenEvictionRunsMillis"</span> <span class="attr">value</span>=<span class="string">"20000"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"minEvictableIdleTimeMillis"</span> <span class="attr">value</span>=<span class="string">"10000"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"lifo"</span> <span class="attr">value</span>=<span class="string">"true"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">bean</span>></span></span><br></pre></td></tr></table></figure><ul><li>ShardedJedisPool连接池</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"shardedJedisPool"</span> <span class="attr">class</span>=<span class="string">"redis.clients.jedis.ShardedJedisPool"</span> <span class="attr">scope</span>=<span class="string">"singleton"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"0"</span> <span class="attr">ref</span>=<span class="string">"jedisConfig"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"1"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">list</span>></span></span><br><span class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">class</span>=<span class="string">"redis.clients.jedis.JedisShardInfo"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"0"</span> <span class="attr">value</span>=<span class="string">"127.0.0.1"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"1"</span> <span class="attr">value</span>=<span class="string">"6379"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"2"</span> <span class="attr">value</span>=<span class="string">"300"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">bean</span>></span></span><br><span class="line"><span class="tag"></<span class="name">list</span>></span></span><br><span class="line"><span class="tag"></<span class="name">constructor-arg</span>></span></span><br><span class="line"><span class="tag"></<span class="name">bean</span>></span></span><br></pre></td></tr></table></figure><ul><li>JedisPool连接池</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"jedisPool"</span> <span class="attr">class</span>=<span class="string">"redis.clients.jedis.JedisPool"</span> <span class="attr">scope</span>=<span class="string">"singleton"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"0"</span> <span class="attr">ref</span>=<span class="string">"jedisConfig"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"1"</span> <span class="attr">value</span>=<span class="string">"127.0.0.1"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"2"</span> <span class="attr">value</span>=<span class="string">"6379"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">"3"</span> <span class="attr">value</span>=<span class="string">"300"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">bean</span>></span></span><br></pre></td></tr></table></figure><blockquote><p>服务容器的运行保障方面(本次所使用到的是Tomcat)</p></blockquote><p>为了降低服务容器因为过载而造成的请求堵塞、容器停滞、宕机等风险,针对Tomcat的连接数方面做了些配置上的调整,以确保可控可监听:</p><ul><li>Tomcat Executor连接线程池</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">Executor</span> <span class="attr">name</span>=<span class="string">"tomcatThreadPool"</span> // 线程池名称</span></span><br><span class="line"><span class="tag"><span class="attr">namePrefix</span>=<span class="string">"catalina-exec-"</span> // 线程名称前缀</span></span><br><span class="line"><span class="tag"> <span class="attr">maxThreads</span>=<span class="string">"3000"</span> // 最大连接线程数</span></span><br><span class="line"><span class="tag"> <span class="attr">minSpareThreads</span>=<span class="string">"500"</span> // 最小空闲线程数,初始化会达到该数值</span></span><br><span class="line"><span class="tag"> <span class="attr">maxIdleTime</span>=<span class="string">"30000"</span> // 最大线程空闲时长</span></span><br><span class="line"><span class="tag"> /></span></span><br></pre></td></tr></table></figure><ul><li>Tomcat Connector连接配置</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">Connector</span> <span class="attr">executor</span>=<span class="string">"tomcatThreadPool"</span> // 线程池引用</span></span><br><span class="line"><span class="tag"> <span class="attr">port</span>=<span class="string">"8080"</span> // 请求访问端口</span></span><br><span class="line"><span class="tag"> <span class="attr">protocol</span>=<span class="string">"org.apache.coyote.http11.Http11NioProtocol"</span> // 启用<span class="attr">NIO</span>协议</span></span><br><span class="line"><span class="tag"> <span class="attr">connectionTimeout</span>=<span class="string">"5000"</span> // 创建连接时长</span></span><br><span class="line"><span class="tag"> <span class="attr">keepAliveTimeout</span>=<span class="string">"10000"</span> // 连接活跃保持时长</span></span><br><span class="line"><span class="tag"> <span class="attr">redirectPort</span>=<span class="string">"8443"</span> // <span class="attr">SSL</span>转发端口</span></span><br><span class="line"><span class="tag"> <span class="attr">acceptCount</span>=<span class="string">"2000"</span> // 额外的线程队列</span></span><br><span class="line"><span class="tag"> <span class="attr">maxConnections</span>=<span class="string">"3000"</span> // 最大连接线程数</span></span><br><span class="line"><span class="tag"></span></span><br><span class="line"><span class="tag"> <span class="attr">compression</span>=<span class="string">"on"</span> <span class="attr">compressionMinSize</span>=<span class="string">"2048"</span> <span class="attr">compressableMimeType</span>=<span class="string">"text/html,text/xml,text/plain,application/octet-stream"</span> // 启用响应报文压缩<span class="attr">gzip</span></span></span><br><span class="line"><span class="tag"> /></span></span><br></pre></td></tr></table></figure><p>与此同时,我们还对服务容器所占用的内存空间进行了分配和调整:</p><ul><li>Tomcat Startup Properties启动参数</li></ul><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Configurations of JVM properties.</span></span><br><span class="line"><span class="comment"># In order to increase JVM heap memory.</span></span><br><span class="line"><span class="comment"># </span></span><br><span class="line"><span class="comment"># Edited by chenjinlong 20161216</span></span><br><span class="line"><span class="comment"># </span></span><br><span class="line"><span class="attr">JAVA_OPTS</span>=<span class="string">"</span></span><br><span class="line"><span class="meta">-Xms512m</span> <span class="string">// Java Heap堆初始大小,默认是物理内存的1/64</span></span><br><span class="line"><span class="meta">-Xmx1024m</span> <span class="string">// Java Heap堆初始大小,建议可设为物理内存的一半,不可超过物理内存</span></span><br><span class="line"><span class="meta">-XX</span>:<span class="string">MaxNewSize=128m // 新生成的池的最大大小,默认32M</span></span><br><span class="line"><span class="meta">-XX</span>:<span class="string">NewSize=64m // 新生成的池的初始大小,默认2M</span></span><br><span class="line"><span class="meta">-XX</span>:<span class="string">MaxPermSize=256m // 内存的永久保存区最大大小,默认64M</span></span><br><span class="line"><span class="meta">-XX</span>:<span class="string">PermSize=128m // 内存的永久保存区初始大小,默认64M</span></span><br><span class="line"></span><br><span class="line"><span class="meta">-Djava.rmi.server.hostname</span>=<span class="string">127.0.0.1 // JMX监听主机设置</span></span><br><span class="line"><span class="meta">-Dcom.sun.management.jmxremote.port</span>=<span class="string">32123 // JMX监听主机端口</span></span><br><span class="line"><span class="meta">-Dcom.sun.management.jmxremote.ssl</span>=<span class="string">false // 是否开启SSL开关</span></span><br><span class="line"><span class="meta">-Dcom.sun.management.jmxremote.authenticate</span>=<span class="string">false" // 是否需要认证的开关</span></span><br></pre></td></tr></table></figure><p>在实践过程中发现,单个Tomcat实例在并行负载线程达到>3000之时,会使用到<code>acceptCount</code>所设置的数值创建队列,将后进的线程排队到该队列内。<br>但是同时也发现,当并行负载线程>3000之后,CPU和Head占用会大幅升高,响应时长也在爬高并超过实践过程中预设的1s(注:低负载情况下请求响应总时长为200-300ms);<br>当并行负载线程>5000之后,Tocmat发生了假死状况(注:这里所说的假死,是处于一种Tomcat进程仍然存活,但是请求已不再执行和处理),Heap内存下降,CPU下降,此时的Tomcat其实已经和宕机无异。这些情况所发生的原因,其中有一点毋庸置疑的那就是,你增加Tomcat准入线程数的同时,也在增加CPU对于容器上下文切换频率,就会增加CPU的资源消耗,自然地,请求的响应时间也就越来越慢。所以一个合理的线程连接数是如此的关键。 </p><p>根据网络查询资料,许多IT从业者给出了近似的一些结论:如果需要考虑Tomcat在支撑并发数>1000以上,则可以引入分布式方案了。确实,系统稳定性是非常重要的。</p><h4 id="四、优劣势辨析"><a href="#四、优劣势辨析" class="headerlink" title="四、优劣势辨析"></a>四、优劣势辨析</h4><p>本次实践过程中采用的是“分布式事务 + CAS原子性操作函数”所实现的乐观锁解决方案。在阐述其中优劣势之前,我们先来了解下关于抢购系统的外观设计办法有哪些。</p><ul><li>方案一:抢购结果滞后反馈</li></ul><p>这一方案很典型地采用了异步处理的机制,虽然可以很大层面上解决访问压力的问题,但是会给人一种“暗箱操作”、“用户体验极其不好”的感觉。如下图所示:</p><p><img src="/images/2016-12-19-parallel-of-buying-scenario/14821468543527.jpg" alt=""></p><ul><li>方案二:抢购结果即刻响应</li></ul><p>如方案标题所阐述的那样,即刻给用户反馈抢购操作的结果。该方案有很好的用户体验,但是对于系统也同样有着很高的考验。例如如何杜绝商品超售超发等问题的发生,因为一旦发生了超售超发,也就意味着系统的问题正在引发经济上的亏损。</p><p><img src="/images/2016-12-19-parallel-of-buying-scenario/14821477756314.jpg" alt=""></p><p>本次实践中采用的是方案二的做法,如文章前面所述的流程图一样,采用的是乐观锁的实现思路。那么相对应的是否还有其他的实现思路,同样可以实现方案二呢?毋庸置疑,当然是有的。</p><ul><li>思路一:悲观锁思路</li></ul><p>这里无论你采用的是分布式锁(Distributed lock)还是监视器锁(Monitor lock),他们有一个共通的特点,那就是Mutex(互斥性)。在同一时间只允许一个访问者可以访问共享资源,其他访问者必须等到加锁的访问者释放锁之后才可进入临界区进行资源访问。</p><p><img src="/images/2016-12-19-parallel-of-buying-scenario/14821488683883.jpg" alt=""><br>在上图形势下,当并发请求上升之时,每个请求都需要请求锁,而线程之间也存在着线程调度的情况,自然也会出现starvation的现象发生,驻留了很多优先级低的线程资源未能及时处理和释放。随着并发时间的流逝,服务容器一定会出现请求阻塞,响应时间延迟,容器资源占用率高,Heap内存溢出等问题的发生。</p><ul><li>思路二:队列的思路</li></ul><p>如果你对JDK中并发包有一定了解,这一思路一定非常熟悉,就是在阻塞性队列实现过程中,常常所需要用到的“生产者-消费者”的并发设计模式。</p><p><img src="/images/2016-12-19-parallel-of-buying-scenario/14821495463736.jpg" alt=""></p><p>如图,这一思路优势是可以很快地处理并响应用户的抢购请求,只需要执行一些必要的过滤逻辑即可,舍去了关系型存储和其他存储的连接资源的请求和读写。劣势当然是有的,它过度依赖队列驻留在内存中的数据,一旦tomcat发生阻塞或者宕机,那么数据将永久丢失,这是灾难性的结果。另外,我们所应对的是并发的场景,队列的长度虽然可以设置为无上限,但是服务容器的资源是有限的,当消费速度小于请求速度之时,系统就逐渐迈向queue内存溢出的悬崖。</p><ul><li>思路三:乐观锁思路</li></ul><p>本次实践的正是乐观锁思路。所谓乐观,自然与悲观的理念是相反的。悲观的思路会将访问线程用锁这一介质的作用,使得线程进入阻塞和等待。乐观的思路,就是临界区的共享资源不需要锁的介入就可以随意访问和修改,没有任何阻塞性质的限定。在并发级别的分类当中,有一种并发的级别叫做“无等待”,这是终极乐观锁的一种体现。<br>基本实现流程如下图所示:</p><p><img src="/images/2016-12-19-parallel-of-buying-scenario/14821559358332.jpg" alt=""></p><p>很显然,乐观锁的思路,是一种无锁无等待的一种并发实现方式。可以使用事务CAS原子的特性来保障数据的准确性,避免了“超售超发”的问题发生。可以在请求线程并发过程中,无阻塞地进行抢购逻辑的处理。但是,与此同时引入了另外一个问题,它不可避免的会出现操作被事务性回退的现象,放在抢购场景下解释,就是一个请求进入事务之时,数据的无误可占用和消费的,但是当线程执行到提交事务一步时,数据版本出现了不一致(也就是其他的线程将商品占用并成功提交了事务),从而被定性为该请求抢购失败,即无效抢购。 </p><p>和前面所提及的队列的思路比较起来,乐观锁的思路所实现的结果是无序的。假设你需要去保证按时序进行分配抢购的产品,那么乐观锁并不适合这一诉求,有序队列的实现思路可能更为合适。</p><h4 id="五、小结一下"><a href="#五、小结一下" class="headerlink" title="五、小结一下"></a>五、小结一下</h4><p>似乎也不需要总结太多,上文已经把整个方案过程中所涉及的考量和技术点都一一覆盖到了,可能会有些片面,或者有些阐述地比较粗糙,但毕竟这是一篇分享实现思路的文章而并非技术方案细节的文档,所以可以根据上述思路自行发散,有所参考,有所改进。</p><p>如果你有更好的建议、方案或者是优化策略,不妨在留言板上写下你的想法。</p><h4 id="六、参考资料"><a href="#六、参考资料" class="headerlink" title="六、参考资料"></a>六、参考资料</h4><p>本篇文章有原创体会,也有网络资料参考,国内外均涉及,这里就不一一罗列了,可以自行根据“抢购”、“分布式锁(distributed lock)”、“高并发(concurrency/parallel computing)”、“阻塞队列(blocking queue)”等关键词进行检索和进一步学习。</p><p>在此感谢伟大的Tim Berners-Lee、RMS(Richard Matthew Stallman)等业界大神,给世界带来了互联网,创造了开源的氛围和精神,让我们的信息得以共享。</p>]]></content>
<summary type="html">
parallel computing practice about buying scenario
</summary>
<category term="架构设计" scheme="https://jinlongchen.com/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
<category term="parallel computing" scheme="https://jinlongchen.com/tags/parallel-computing/"/>
<category term="concurrency" scheme="https://jinlongchen.com/tags/concurrency/"/>
</entry>
<entry>
<title>Git协作之合并代码</title>
<link href="https://jinlongchen.com/posts/2016/12/06/git-fetch-among-branch/"/>
<id>https://jinlongchen.com/posts/2016/12/06/git-fetch-among-branch/</id>
<published>2016-12-06T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.506Z</updated>
<content type="html"><![CDATA[<p>继上一次撰写GIT相关文章已经数月有余,半年来在团队内实践GIT的过程中,遇到了很多个人项目所不会接触到的疑问和问题,最为典型的无异于分支、代码之间的合并操作。本篇我们来讲一讲如何在本地仓库中做好多人协作过程中的分支代码合并。</p><h4 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h4><ul><li><strong>Git知识回顾</strong></li></ul><p>在开始之前,如果你对于GIT的基础知识已经有些遗忘了,可以参考以下这几篇文章,知识都比较基础:</p><ul><li><a href="http://aboutcoder.com/2014/06/29/git-basic-knowledge-1/" target="_blank" rel="noopener">GIT基础知识讲解(一)</a></li><li><a href="http://aboutcoder.com/2014/06/29/git-basic-knowledge-2/" target="_blank" rel="noopener">GIT基础知识讲解(二)</a></li></ul><p>如果你想将GIT流程应用到研发工作当中去,GIT流程是你必须要知道的:</p><ul><li><a href="http://aboutcoder.com/2015/11/16/work-in-git/" target="_blank" rel="noopener">Git软件开发过程</a></li></ul><p>希望上面几篇文章可以帮助到你学习和回顾GIT应用知识。</p><ul><li><strong>冲突产生的原因以及合并操作的原则</strong></li></ul><p>如果你对Git有了以上方面的了解,那么在学习如何进行冲突解决之前,可以先了解为什么会产生冲突?<br>冲突产生一般可分为如下四个方面:</p><ol><li>workdirectory的修改</li><li>index的修改</li><li>merge来源的修改</li><li>merge之前的修改(Been committed)</li></ol><p>在merge manual中已经给出了一条合并操作的原则:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// Command: git merge --help</span><br><span class="line">Warning: Running git merge with non-trivial uncommitted changes is discouraged: while possible, it may leave you in a state that is hard to back out of in the case of a conflict.</span><br><span class="line">// 简而言之,有未提交修改情况下,不要执行可能会冲突的操作!</span><br></pre></td></tr></table></figure><p>在执行合并操作之前,保持workdirectory、index区域的clean特性,是非常必要的。当然,满足了这一前提,也并不代表就可以杜绝了冲突的可能。遵循该原则之后,解决的是1、2两点所产生的冲突可能,而前面所提及的3、4两点,是由于合并的操作所引入的冲突。<br>其实很多操作都可能会产生冲突,但最根本原因都是因为<code>merge</code>和<code>patch</code>,这两者是冲突的直接来源。例如如下操作都可能产生冲突,它们过程中也都包含了合并的性质: </p><ul><li>rebase:重新设置基准,然后应用补丁</li><li>pull:会自动merge</li><li>repo sync:会自动rebase</li><li>cherry-pick:会应用补丁</li><li>…</li></ul><p>如何保持workdirectory、index区域的clean特性,可以参考上文文章已经阐述到的<code>stash</code>操作。在<code>merge</code>过程中一旦发生冲突,往往会停滞在merging状态中,这个时候,我们可以使用: </p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// 中止merge过程,该命令会尽可能恢复到merge操作之前的状态,但也可能恢复失败。</span><br><span class="line">git merge --abort</span><br><span class="line"></span><br><span class="line">// merge manual原文是这么解释的:</span><br><span class="line">--abort</span><br><span class="line">Abort the current conflict resolution process, and try to reconstruct the pre-merge state.</span><br><span class="line">If there were uncommitted worktree changes present when the merge started, git merge --abort will in some cases be unable to reconstruct these changes.</span><br><span class="line">It is therefore recommended to always commit or stash your changes before running git merge.</span><br><span class="line">git merge --abort is equivalent to git reset --merge when MERGE_HEAD is present.</span><br></pre></td></tr></table></figure><h4 id="二、合并代码之前,需要理解的概念"><a href="#二、合并代码之前,需要理解的概念" class="headerlink" title="二、合并代码之前,需要理解的概念"></a>二、合并代码之前,需要理解的概念</h4><p><strong>1. FETCH_HEAD</strong></p><p><code>FETCH_HEAD</code>可以视为一个版本链接,或者说是版本commit-id的履历文件,该文件的路径在Git项目下的<code>.git/FETCH_HEAD</code>文件中。<br>当运行<code>git fetch remote_repo src_branch</code>类似的命令之时,将从远程repo获取分支的末端版本信息,记录到<code>.git/FETCH_HEAD</code>文件中:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ /jeromechan/project git:(project) ✗>git fetch origin <span class="built_in">test</span></span><br><span class="line">remote: Counting objects: 334, <span class="keyword">done</span>.</span><br><span class="line">remote: Compressing objects: 100% (211/211), <span class="keyword">done</span>.</span><br><span class="line">remote: Total 334 (delta 177), reused 167 (delta 51)</span><br><span class="line">Receiving objects: 100% (334/334), 96.42 KiB | 0 bytes/s, <span class="keyword">done</span>.</span><br><span class="line">Resolving deltas: 100% (177/177), completed with 70 <span class="built_in">local</span> objects.</span><br><span class="line">From https://github.com/jeromechan/project</span><br><span class="line"> * branch <span class="built_in">test</span> -> FETCH_HEAD</span><br><span class="line"> f5a095a..18b49aa <span class="built_in">test</span> -> origin/<span class="built_in">test</span></span><br></pre></td></tr></table></figure><p>然后,我们看下<code>.git/FETCH_HEAD</code>文件内容:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">➜ /jeromechan/project git:(project) ✗>vim .git/FETCH_HEAD</span><br><span class="line"></span><br><span class="line"><span class="comment">## 文件内容如下</span></span><br><span class="line">18b49aaa008731ff17768d6b18905fc5865b3aa7 branch <span class="string">'test'</span> of https://github.com/jeromechan/project</span><br></pre></td></tr></table></figure><p><strong>2. git fetch</strong></p><p>该命令可以将远程repo所包含的所有分支(或者指定分支)的最新commit-id记录到<code>.git/FETCH_HEAD</code>文件中。</p><p><strong>3. git pull</strong></p><p>该命令是GIT更新local repo和working copy的文件。在之前的文章中,提及过<code>git pull = git fetch + git merge FETCH_HEAD(by default)</code>,这里就不再重复提及了,如果想复习了解,请看“前言”中列出的几篇GIT基础知识文章。</p><p><strong>4. commit-id</strong></p><p>commit-id指的是每一次<code>git commit</code>操作所生成的每一个版本号,是一个能唯一标识一个版本的序列号。在使用git push后,这个序列号还会同步到远程repo。</p><h4 id="三、如何执行合并操作"><a href="#三、如何执行合并操作" class="headerlink" title="三、如何执行合并操作"></a>三、如何执行合并操作</h4><p>这里举例两个常见的场景对合并操作进行解释。</p><p><strong>1. 将相同分支下,已经产生分支冲突的代码,合并到本地working copy中</strong></p><p>这类场景下,由于分支是相同的,产生冲突无异于因为差异版本之间,相同的代码区块发生了相同位置的变更,然后冲突就这样产生了。<br>这类冲突不难解决,可以使用交互式命令行<code>git rebase</code>、Git-GUI客户端工具的rebase-from的操作,指定来源分支,借用其中的交互式操作,可以逐个逐个地进行<code>commit-id</code>的代码合并。</p><p><strong>2. 将其他分支B的代码差异,合并到当前工作分支A的working copy中</strong></p><p>这类场景常常发生在多人协作的实际工作中。例如需要将工作分支A的代码提交到分支B上,但分支B与工作分支A出现了不可合并的代码冲突,此时就需要将分支B的差异代码合并到当前的工作分支A中,然后再做提交与合并。</p><p>这个时候,我们可以按照如下几个步骤进行:</p><p>(1)git checkout A</p><p>将当前工作分支切换到分支A。</p><p>(2)git fetch <a href="https://github.com/jeromechan/project" target="_blank" rel="noopener">https://github.com/jeromechan/project</a> B </p><p>将分支B的末端版本(HEAD)检出到FETCH_HEAD文件中。</p><p>(3)git merge FETCH_HEAD</p><p>基于本地<code>.git/FETCH_HEAD</code>记录,比对本地工作分支A的版本与FETCH_HEAD所记录的分支B末端版本信息,利用<code>git merge</code>将分支B的差异内容合并更新到工作分支A中。<br>此操作过程中如果出现conflicts,那么是需要开发者进行自助解决的。这一部分,如果是整体作accept合并,那么一定要留意“theirs”和“mine”的关系,“theirs”通常指的是来源代码分支(即当前示例所述的分支B),而“mine”便是你的本地分支A了。</p><p>(4)git commit</p><p>将当前合并下来的commit-id及其内容提交到A分支上。</p><p>(5)git push <a href="https://github.com/jeromechan/project" target="_blank" rel="noopener">https://github.com/jeromechan/project</a> HEAD</p><p>将提交的commit-id及其变更内容,推送到远程repo的分支A上。</p><p>本篇到这里已阐述完。以上所述内容,一定可以帮助各位解决许多的合并代码的问题了。</p>]]></content>
<summary type="html">
merge, fetch, 合并代码, 衍合
</summary>
<category term="工具环境" scheme="https://jinlongchen.com/categories/%E5%B7%A5%E5%85%B7%E7%8E%AF%E5%A2%83/"/>
<category term="Git分支合并" scheme="https://jinlongchen.com/tags/Git%E5%88%86%E6%94%AF%E5%90%88%E5%B9%B6/"/>
</entry>
<entry>
<title>几种方法调用思路的比较</title>
<link href="https://jinlongchen.com/posts/2016/12/02/funny-function-invoking-logic/"/>
<id>https://jinlongchen.com/posts/2016/12/02/funny-function-invoking-logic/</id>
<published>2016-12-02T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.506Z</updated>
<content type="html"><![CDATA[<p>编码过程中,每天都在实现的事情,不是对象就是方法。那么如何去封装一个方法的入参呢?如果入参很多,是否有比较不一样的思路可以提高代码的复用性和可读性呢?下面谈谈在这一方面,近期学习到的一点知识。</p><h4 id="一、参数逐个罗列"><a href="#一、参数逐个罗列" class="headerlink" title="一、参数逐个罗列"></a>一、参数逐个罗列</h4><p>假设有这么一个方法,方法参数很多,但都类似,我们可能需要把所有参数逐个逐个传入方法体内,就像这样:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * A common style of implementing a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> type1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> type2</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> type3</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">(Byte type1, Byte type2, Byte type3)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (type1 == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (type2 == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (type3 == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Test cases for explaining how to invoke a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> chenjinlong 20161202</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">invokerMethod</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Initial parameters</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">final</span> Byte type1 = <span class="number">1</span>, type2 = <span class="number">1</span>, type3 = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call with separated parameters</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> doSomething(type1, type2, type3);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="二、列表形式批量传入"><a href="#二、列表形式批量传入" class="headerlink" title="二、列表形式批量传入"></a>二、列表形式批量传入</h4><p>可能有的工程师觉得上面这种办法,会使得方法参数过多,不便于阅读和归类,列表的办法可能会更好,所以上面的逻辑也可以写成这样:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Another common style of implementing a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> typeList</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">(List<Byte> typeList)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (Byte type : typeList) {</span><br><span class="line"> <span class="keyword">switch</span> (type) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Test cases for explaining how to invoke a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> chenjinlong 20161202</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">invokerMethod</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Initial parameters</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">final</span> Byte type1 = <span class="number">1</span>, type2 = <span class="number">1</span>, type3 = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call with parameter list</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> List<Byte> typeList = <span class="keyword">new</span> ArrayList<Byte>(){{</span><br><span class="line"> add(type1);</span><br><span class="line"> add(type2);</span><br><span class="line"> add(type3);</span><br><span class="line"> }};</span><br><span class="line"> doSomething(typeList);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="三、对象VO的形式传入参数"><a href="#三、对象VO的形式传入参数" class="headerlink" title="三、对象VO的形式传入参数"></a>三、对象VO的形式传入参数</h4><p>可能还有的工程师觉得,塞个列表进来,其实也并不能将释义阐述清楚,每个列表值虽说都是byte,但是每个byte的含义都代表着不通的分支不通的含义。所以,对象的办法就引入进来了:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * An POJO-bean style of implementing a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> typeList</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">(TypeExplains typeExplains)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (typeExplains.getTypeOne() == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (typeExplains.getTypeTwo() == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (typeExplains.getTypeThree() == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Test cases for explaining how to invoke a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> chenjinlong 20161202</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">invokerMethod</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Initial parameters</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">final</span> Byte type1 = <span class="number">1</span>, type2 = <span class="number">1</span>, type3 = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call with parameter bean</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> TypeExplains typeExplains = <span class="keyword">new</span> TypeExplains();</span><br><span class="line"> typeExplains.setTypeOne(type1);</span><br><span class="line"> typeExplains.setTypeTwo(type2);</span><br><span class="line"> typeExplains.setTypeThree(type3);</span><br><span class="line"> doSomething(typeExplains);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="四、按位与-amp-按位或-的方式传入参数"><a href="#四、按位与-amp-按位或-的方式传入参数" class="headerlink" title="四、按位与&按位或 的方式传入参数"></a>四、按位与&按位或 的方式传入参数</h4><p>以上方式其实都可以达到相同的函数想要实现的目标,但是是否考虑过其他更为便捷的办法呢?最近就遇到这么一种比较创新的、特殊的、异类、不一样的、充分展现计算机知识学以致用的调用的模式:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Each of Keys must be strictly defined.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> TypeEnum {</span><br><span class="line"> TYPE_1(<span class="number">1</span>), <span class="comment">// equals to "0001"</span></span><br><span class="line"> TYPE_2(<span class="number">2</span>), <span class="comment">// equals to "0010"</span></span><br><span class="line"> TYPE_3(<span class="number">4</span>) <span class="comment">// equals to "0100"</span></span><br><span class="line"> ;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Integer key;</span><br><span class="line"></span><br><span class="line"> TypeEnum(Integer key) {</span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Integer <span class="title">getKey</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> key;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setKey</span><span class="params">(Integer key)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Using & and | features.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> type</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">(Byte type)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> ((type & TypeEnum.TYPE_1.getKey()) == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ((type & TypeEnum.TYPE_2.getKey()) == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ((type & TypeEnum.TYPE_3.getKey()) == <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> <span class="comment">// do some conditional processes.</span></span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Test cases for explaining how to invoke a function.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> chenjinlong 20161202</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">invokerMethod</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Initial parameters</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">final</span> Byte type1 = <span class="number">1</span>, type2 = <span class="number">1</span>, type3 = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call with & and |</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> doSomething((<span class="keyword">byte</span>)(type1 | type2 | type3));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这种按位或传入,按位与判断获取的办法,着实为传入参数提供了截然不同的参考。当然,这种办法也不是适用于所有场景的。<br>按位与按位或的办法,使用于需要传入参数很多的,方法体内针对每个参数会包含着不一样分支逻辑的场景。该方式简化了方法体外“或关系”判断的逻辑,但另一方面也确实降低了代码的易读性。</p><h4 id="五、总结"><a href="#五、总结" class="headerlink" title="五、总结"></a>五、总结</h4><p>其实以上谈到的四种模式中,没有哪一种是最好的。符合实践场景的、符合团队技术水平现实的、具备高内聚低耦合特点的,那就一定是最适合的。<br>后续日子里,会就代码内部模式的实践上,发散开来谈谈如何编写可读性强的代码,然后再多po几篇文章分享出来。</p>]]></content>
<summary type="html">
按位与,按位或,方法调用思路
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="方法调用" scheme="https://jinlongchen.com/tags/%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8/"/>
<category term="按位与" scheme="https://jinlongchen.com/tags/%E6%8C%89%E4%BD%8D%E4%B8%8E/"/>
<category term="按位或" scheme="https://jinlongchen.com/tags/%E6%8C%89%E4%BD%8D%E6%88%96/"/>
</entry>
<entry>
<title>[译]Feature Toggle - Martin Fowler</title>
<link href="https://jinlongchen.com/posts/2016/09/23/feature-toggle-translation/"/>
<id>https://jinlongchen.com/posts/2016/09/23/feature-toggle-translation/</id>
<published>2016-09-23T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.506Z</updated>
<content type="html"><![CDATA[<p>关于<a href="http://martinfowler.com/bliki/FeatureBranch.html" target="_blank" rel="noopener">FeatureBranch</a>的一个较为争论的问题是,特性分支(Feature Branch)因为待发布功能(pending features)的原因,拉长了单一发布周期(single release cycle)。可以想象一下,你每两个周发布一次生产版本,但是同时又需要去维护一个例如说三个月才能进行发布的特性分支。那么你该如何去保障团队成员们在开发主线(mainline)上的工作不受到衍生出来的、未完全实现的特性分支的影响呢?这是一个非常常见的问题,这个时候,feature toggle就是这么一个应对这种场景的便捷工具。</p><p><img src="/images/2016-09-23-feature-toggle-translation/14731341981641.jpg" alt=""></p><p>(曾经见过许多相同含义的概念、名词:feature bits, flags, flippers, switches等等。这在领域上似乎还没有一个统一的概念、名称。)</p><p>Feature toggle最基础的思路就是存在一个配置文件,配置文件里定义了许多的toggle,这些toggle都是待发布的特性功能。这一些toggle也正定义了哪一些features需要呈现,哪些并不需要。</p><p>这其中许多的toggle大多数都会应用在拥有用户界面的应用上,假设你构建一个网站,其中使用了jsp,你可能会使用一些jsp标签包围在需要待发布的特性功能代码前后。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">toggle</span> <span class="attr">name</span>=<span class="string">"petSurvey"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>Take our new <span class="tag"><<span class="name">a</span> <span class="attr">href</span> = <span class="string">'petSurvey'</span>></span>pet survey<span class="tag"></<span class="name">a</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"><span class="tag"></<span class="name">toggle</span>></span></span><br></pre></td></tr></table></figure><p>类似以上的代码,当你将配置toggle的开关打开之时,features的功能将会随之打开,否则将会继续忽略features功能代码。虽然不一样的UI技术实现上会存在差异,但是基础的原理都是一样的。</p><p>现实上往往还有一些特性功能,例如仅仅是引入一个新的价格算法,而这部分特性并没有对应的用户界面,纯粹是服务器端代码。对于这一情况,我们的toggle埋点可能会较为粗鲁和复杂,会直接将toggle codes耦合到系统应用层代码中,这将是比较忧虑的情况。</p><p>Toggle埋点应该以最少的埋点量,实现新特性的完全隐藏或者开放。如上图pet survey的特性,pet survey可能会有非常多的功能界面,但是入口只有一处,就是在首页的地方。也正是单一的入口,toggle的埋点只需要在该处即可,并不需要在所有相关涉及的代码各处都埋点。如此一来,之后移除toggle埋点的工作将会非常地高效。假设发现你移除toggle埋点的时间耗费的非常多,那么请关注,其中所涉及的toggle埋点是否太多太复杂了,代码结构是否已经足够规范和逻辑化。请记住,尽管简单的条件判断来设置toggle是一件非常容易的事情,但是也同时要记得使用一些类似多态替换的办法,去减少一些没有必要、过于复杂的toggle埋点的方案。</p><p>以往对于toggle的研究里,业界几乎都统称为“业务toggle”,而前面描述了那么多关于特性toggle的特点和办法,自此,可以将toggle分类为如下几种:</p><ol><li>发布型:出现在生产环境上,用于开放或者隐藏部分特性功能的toggle;</li><li>体验型:即A/B testing,用于区分A、B方案的实际使用情况;</li><li>操作型:提供给操作员工的一系列特殊操作;</li><li>权限型:使得部分指定范围内的用户可以体验到新特性功能,而并不是面向全部用户。</li></ol><p>大部分所见到的特性toggle都是在运行时设置的,当然,也有在编译时设置的。在编译时设置toggle,一个较小的优势是,它避免了toggle所涉及的特性功能代码被编译到生产发布的软件产品中。</p><p>Toggle不仅仅方便我们对于产品功能的管理,它也是有风险的。例如某位工程师忘了将界面UI的特性功能放置到toggle的tag闭包标签中,那么这一功能将会暴露在外,测试工作将难以进行,设置了toggle将元素隐藏的时候,并没有实际隐藏,开关功能已经处于不正确的状态下。如果这类情况在生产环境上出现,将是不可接受的。</p><p>谈到测试,大家比较关系的一个问题例如,如何对实现了toggle的系统进行测试验证?是否需要测试涉及toggle的所有场景?其实,对于一个发布型的toggle场景,只需要测试如下两类toggle使用情况:</p><ul><li>下一版本中期望实现的toggle埋点</li><li>所有toggle都打开的场景</li></ul><p>以上两点是发现多toggle集成bug的一个不错的好办法。</p><p>还有一点非常重要的,已经正式投产的toggle功能,一定要今早将toggle代码从生产环境中移除,其中不仅仅包括toggle的配置文件,还包括所有相涉及的系统toggle代码。如若不今早移除,会出现一种情况类似于,某些时候无人可以识别出某toggle是何种用途何时添加的细节情况了。一个较为清晰的案例就是,有一个需要二次编译linux内核的特殊场景,以使得可以获得更多的命令行开关的处理能力,恰巧当时就忘了这一场景,这是非常让人揪心的。</p><h5 id="发布型toggles是你最后需要去做的事情"><a href="#发布型toggles是你最后需要去做的事情" class="headerlink" title="发布型toggles是你最后需要去做的事情"></a>发布型toggles是你最后需要去做的事情</h5><p>发布型toggles是一个非常有用的技术手段,许多研发团队都在使用它。当你要将特性功能发布到生产之时,它可能是你最后需要去做的事情。</p><p>当特性功能上线后的第一步,你需要去按照你的实际需求,打开toggle开关,使之特性功能在生产环境得以显露和使用。这种技术手段的好处在于,不需要你去频繁发布系统产品,便可以控制功能的上线与下线,更有利于在区间时间内收集真实用户的使用体验,收集用户反馈,这对于后续的产品特性功能的后续发展或者优化,都有着至关重要的作用。</p><p>如果你的的确确需要隐藏一个局部的特性功能,最好的办法是将界面UI的功能入口关闭,直至最后一次UI代码的发布。对于纯服务端的代码,也可以采用这种方式,知道最后一次发布,再打开特性功能的可见入口。</p><p>这种办法是一种优秀的可持续迭代,并且适合密集发布周期的项目。但是依然会有一些不可避免的短柄事实。前后端兼备的应用中,如此一来,在最后一次UI发布,或者后台服务端发布之前,你都将无法通过UI或者服务端入口进行toggle的测试和功能验证。当然,你也可以通过一些特殊的办法或者途径去间接地进行toggle特性的测试,例如服务端单元测试,亦或者是前端UI特性功能的某一后门入口。自然,这是不被推荐的。</p><p>总结一下,当你不希望去密集性地为了发布或者下架特性功能而频繁上线的场景下,部署toggle场景将是一个非常不错的技术手段。</p><p><strong>进阶阅读材料</strong></p><p>如果你想要了解本篇文章图示的feature toggles的实际应用办法,可以参考这篇文章 <a href="http://martinfowler.com/articles/feature-toggles.html" target="_blank" rel="noopener">Pete Hodgson’s article</a></p><p><strong>致谢</strong></p><p>感谢Charles Bradley, Kent Beck and Christian Gruber通过tweets提醒我一些差点忘记的知识点。</p><p><strong>版本</strong></p><p>更新与2016-02-12,以适应于Pete Hodgson’s article所描述的最新细节。</p>]]></content>
<summary type="html">
feature toggle strategies using in development
</summary>
<category term="架构设计" scheme="https://jinlongchen.com/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
<category term="feature toggle" scheme="https://jinlongchen.com/tags/feature-toggle/"/>
</entry>
<entry>
<title>读写分离的原则</title>
<link href="https://jinlongchen.com/posts/2016/09/06/db-master-slave-pattern-rules/"/>
<id>https://jinlongchen.com/posts/2016/09/06/db-master-slave-pattern-rules/</id>
<published>2016-09-19T17:00:00.000Z</published>
<updated>2020-07-02T18:56:45.506Z</updated>
<content type="html"><![CDATA[<p><img src="/images/2016-09-20-db-master-slave-pattern-rules/14743556033765.jpg" alt=""></p><h4 id="一、读写分离是什么?"><a href="#一、读写分离是什么?" class="headerlink" title="一、读写分离是什么?"></a>一、读写分离是什么?</h4><p>简单了说,就是数据库一主一从/一主多从/多主多从的部署方式。先简单介绍下mysql的原生同步机制,其是将master的binlog期间的执行日志根据顺序,拷贝至slave的relaylog中,然后逐一解析和执行。</p><p>读写分离的好处在于:</p><ul><li>硬件处理能力得到了横向的扩增,拿机器和带宽换性能</li><li>降低了主库服务器的压力,提高并发性能</li><li>缓解读锁(S锁,共享锁,Shared Lock)和写锁(X锁,排他锁,Exclusive Lock)的竞争</li><li>系统运行性能得到间接的提升</li></ul><h4 id="二、如何判别是否需要读写分离?"><a href="#二、如何判别是否需要读写分离?" class="headerlink" title="二、如何判别是否需要读写分离?"></a>二、如何判别是否需要读写分离?</h4><p>谈及读写分离,必定要先了解怎样的应用层场景才需要使用到读写分离模式。以下是几点比较共性的场景特点:</p><ul><li>以读操作为主的应用或者服务</li><li>可接受时间上短暂的信息延迟</li><li>单一线程内同时涵盖读写操作的,使用主库</li></ul><h4 id="三、常见的读写分离的方式有哪些?"><a href="#三、常见的读写分离的方式有哪些?" class="headerlink" title="三、常见的读写分离的方式有哪些?"></a>三、常见的读写分离的方式有哪些?</h4><ol><li>中间件,例如java-amoeba</li><li>框架特性,例如spring-aop</li><li>自定义组件,例如在dal层的prepare过程,增加拦截代码,以执行数据源的路由逻辑。</li></ol><h4 id="四、读写分离简单原则"><a href="#四、读写分离简单原则" class="headerlink" title="四、读写分离简单原则"></a>四、读写分离简单原则</h4><p><img src="/images/2016-09-20-db-master-slave-pattern-rules/14787121481232.jpg" alt=""></p><h4 id="五、参考文章"><a href="#五、参考文章" class="headerlink" title="五、参考文章"></a>五、参考文章</h4><ul><li><a href="https://my.oschina.net/candiesyangyang/blog/203425" target="_blank" rel="noopener">为什么数据库读写分离可以提高性能</a></li></ul>]]></content>
<summary type="html">
How to use Master-Slave pattern for database.
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="Database" scheme="https://jinlongchen.com/tags/Database/"/>
</entry>
<entry>
<title>关于PhpStorm+MAMP+xdebug集成失败的处理办法</title>
<link href="https://jinlongchen.com/posts/2016/09/01/integrate-phpstorm-mamp-xdebug/"/>
<id>https://jinlongchen.com/posts/2016/09/01/integrate-phpstorm-mamp-xdebug/</id>
<published>2016-09-01T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.505Z</updated>
<content type="html"><![CDATA[<p>首先,阐述一下我的开发环境配置: </p><ul><li>MacOS v10.11.6</li><li>MAMP PRO v3.5</li><li>PHP v5.3.29</li><li>Xdebug v2.2.7</li><li>PhpStorm 2016.1</li></ul><p>近期对mamp作了次重装升级,之后遇到了xdebug无法正常启用的问题,在phpstorm中反馈的Event Log信息如下: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cannot accept external Xdebug connection: Cannot evaluate expression 'isset($_SERVER['PHP_IDE_CONFIG'])'</span><br></pre></td></tr></table></figure><p>看以上描述可以知道,是因为在建立Xdebug connection之时,无法找到变量<code>PHP_IDE_CONFIG</code>的定义。<br>谈到这里,大家来看看php.ini中Xdebug的参数配置先: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[xdebug]</span><br><span class="line">extension=xdebug.so</span><br><span class="line"></span><br><span class="line">[xdebug]</span><br><span class="line">MAMP_Xdebug_MAMP</span><br><span class="line"> xdebug.remote_enable=1</span><br><span class="line"> xdebug.remote_host=localhost</span><br><span class="line"> xdebug.remote_port=9000</span><br><span class="line"> xdebug.remote_autostart=1</span><br><span class="line"> ;xdebug.profiler_enable=0</span><br><span class="line"> ;xdebug.profiler_output_dir="/Applications/MAMP/tmp"</span><br></pre></td></tr></table></figure><h4 id="一、核实xdebug与phpstorm已建立关联关系"><a href="#一、核实xdebug与phpstorm已建立关联关系" class="headerlink" title="一、核实xdebug与phpstorm已建立关联关系"></a>一、核实xdebug与phpstorm已建立关联关系</h4><p>打开phpinfo()信息页面,xdebug扩展部分如下:</p><p><img src="/images/2016-09-01-integrate-phpstorm-mamp-xdebug/14727184774765.jpg" alt=""></p><p>也可以正常进行加载。但是问题来了,为什么phpstorm不能使用其进行debug过程呢?<br>不难发现,<code>IDE Key</code>上显示的是<code>no value</code>,这一方面说明了xdebug并没有正确地与phpstorm建立关联,即phpstorm即时发起xdebug connections,也无法正常连接并使用xdebug扩展。 </p><p>修正这个问题比较简单,增加xdebug扩展参数配置即可: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xdebug.idekey="PHPSTORM"</span><br></pre></td></tr></table></figure><h4 id="二、确认xdebug使用的是zend-extension引入方式"><a href="#二、确认xdebug使用的是zend-extension引入方式" class="headerlink" title="二、确认xdebug使用的是zend_extension引入方式"></a>二、确认xdebug使用的是zend_extension引入方式</h4><p>php的扩展库有很多,主要分为两大类:php extension和zend extension。</p><p>有的基于php module开发而成,有的基于zend engine开发而成(xdebug基于的就是zend引擎进行开发的),当然,也有的可以兼容两种引擎(memcache就是一个很好的兼容两种引擎的例子)。而这里要提到一个概念,php内核是基于zend engine的,zend engine从php3开始就在服务php内核的最后一道岗了。</p><blockquote><p>通常我们称php extension为“modules”,称zend extension为“extensions”。</p></blockquote><p>以下为大家扩展一下,php extension和zend extension的基本数据结构上的区别:</p><ul><li>php extension(modules)</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// php extension structures</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_module_entry</span> <span class="title">zend_module_entry</span>;</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_module_dep</span> <span class="title">zend_module_dep</span>;</span></span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">zend_module_entry</span> {</span></span><br><span class="line"><span class="keyword">unsigned</span> short <span class="built_in">size</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> zend_api;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> zend_debug;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> zts;</span><br><span class="line"><span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_ini_entry</span> *<span class="title">ini_entry</span>;</span></span><br><span class="line"><span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_module_dep</span> *<span class="title">deps</span>;</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *name;</span><br><span class="line"><span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_function_entry</span> *<span class="title">functions</span>;</span></span><br><span class="line"><span class="keyword">int</span> (*module_startup_func)(INIT_FUNC_ARGS);</span><br><span class="line"><span class="keyword">int</span> (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);</span><br><span class="line"><span class="keyword">int</span> (*request_startup_func)(INIT_FUNC_ARGS);</span><br><span class="line"><span class="keyword">int</span> (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);</span><br><span class="line"><span class="keyword">void</span> (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *version;</span><br><span class="line"><span class="keyword">size_t</span> globals_size;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> ZTS</span></span><br><span class="line">ts_rsrc_id* globals_id_ptr;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line"><span class="keyword">void</span>* globals_ptr;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"><span class="keyword">void</span> (*globals_ctor)(<span class="keyword">void</span> *global TSRMLS_DC);</span><br><span class="line"><span class="keyword">void</span> (*globals_dtor)(<span class="keyword">void</span> *global TSRMLS_DC);</span><br><span class="line"><span class="keyword">int</span> (*post_deactivate_func)(<span class="keyword">void</span>);</span><br><span class="line"><span class="keyword">int</span> module_started;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> type;</span><br><span class="line"><span class="keyword">void</span> *handle;</span><br><span class="line"><span class="keyword">int</span> module_number;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *build_id;</span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">zend_module_dep</span> {</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *name;<span class="comment">/* module name */</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *rel;<span class="comment">/* version relationship: NULL (exists), lt|le|eq|ge|gt (to given version) */</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *version;<span class="comment">/* version */</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> type;<span class="comment">/* dependency type */</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><ul><li>zend extension(extensions)</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// zend extension structures</span></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">message_handler_func_t</span>)</span><span class="params">(<span class="keyword">int</span> message, <span class="keyword">void</span> *arg)</span></span>;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">op_array_handler_func_t</span>)</span><span class="params">(zend_op_array *op_array)</span></span>;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">statement_handler_func_t</span>)</span><span class="params">(zend_op_array *op_array)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">fcall_begin_handler_func_t</span>)</span><span class="params">(zend_op_array *op_array)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">fcall_end_handler_func_t</span>)</span><span class="params">(zend_op_array *op_array)</span></span>;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">op_array_ctor_func_t</span>)</span><span class="params">(zend_op_array *op_array)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="keyword">op_array_dtor_func_t</span>)</span><span class="params">(zend_op_array *op_array)</span></span>;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_extension</span> {</span></span><br><span class="line"><span class="keyword">char</span> *name;</span><br><span class="line"><span class="keyword">char</span> *version;</span><br><span class="line"><span class="keyword">char</span> *author;</span><br><span class="line"><span class="keyword">char</span> *URL;</span><br><span class="line"><span class="keyword">char</span> *copyright;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">startup_func_t</span> startup;</span><br><span class="line"><span class="keyword">shutdown_func_t</span> <span class="built_in">shutdown</span>;</span><br><span class="line"><span class="keyword">activate_func_t</span> activate;</span><br><span class="line"><span class="keyword">deactivate_func_t</span> deactivate;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">message_handler_func_t</span> message_handler;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">op_array_handler_func_t</span> op_array_handler;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">statement_handler_func_t</span> statement_handler;</span><br><span class="line"><span class="keyword">fcall_begin_handler_func_t</span> fcall_begin_handler;</span><br><span class="line"><span class="keyword">fcall_end_handler_func_t</span> fcall_end_handler;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">op_array_ctor_func_t</span> op_array_ctor;</span><br><span class="line"><span class="keyword">op_array_dtor_func_t</span> op_array_dtor;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">int</span> (*api_no_check)(<span class="keyword">int</span> api_no);</span><br><span class="line"><span class="keyword">int</span> (*build_id_check)(<span class="keyword">const</span> <span class="keyword">char</span>* build_id);</span><br><span class="line"><span class="keyword">void</span> *reserved3;</span><br><span class="line"><span class="keyword">void</span> *reserved4;</span><br><span class="line"><span class="keyword">void</span> *reserved5;</span><br><span class="line"><span class="keyword">void</span> *reserved6;</span><br><span class="line"><span class="keyword">void</span> *reserved7;</span><br><span class="line"><span class="keyword">void</span> *reserved8;</span><br><span class="line"> </span><br><span class="line">DL_HANDLE handle;</span><br><span class="line"><span class="keyword">int</span> resource_number;</span><br><span class="line">} zend_extension;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">zend_extension_version_info</span> {</span></span><br><span class="line"><span class="keyword">int</span> zend_extension_api_no;</span><br><span class="line"><span class="keyword">char</span> *build_id;</span><br><span class="line">} zend_extension_version_info;</span><br></pre></td></tr></table></figure><p>从加载顺序上分析,zend extension要早于php extension进行注册(register),然而在激活(activate)阶段,却是php extension要早于zend extension被激活。<br>在两者的激活顺序上,有如下区别可以额外了解一下: </p><blockquote><p><strong>PHP extensions</strong> in an order that make the dependencies be activated in a specific order. But remember that <strong>Zend extensions</strong> are never sorted in any way. You must then declare them in the FIFO order in php.ini. The engine wont touch your declaration order.</p></blockquote><p>zend_extension注册是调用的函数<code>zend_register_extension()</code>,而php extension注册是调用的函数<code>zend_register_module_ex(zend_module_entry *module)</code>进行注册。</p><p>以下是两类扩展的生命周期图: </p><p><img src="/images/2016-09-01-integrate-phpstorm-mamp-xdebug/14727201625932.jpg" alt=""></p><p>前面谈及了许多的关于php两类扩展的一些内部实现问题,其实对于这部分仅仅为了让开发者们更加深刻记得一点:“确认xdebug使用的是zend_extension引入方式”。</p><p>这个时候,我们需要继续修改php.ini关于xdebug的引入配置:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">;1.移除默认引入xdebug.so的php extension的方式</span><br><span class="line">[xdebug]</span><br><span class="line">;extension=xdebug.so</span><br><span class="line"></span><br><span class="line">;2.更改为zend extension的方式对xdebug.so进行引入</span><br><span class="line">[xdebug]</span><br><span class="line">MAMP_Xdebug_MAMP</span><br><span class="line"> zend_extension="/Applications/MAMP/bin/php/php5.3.29/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"</span><br></pre></td></tr></table></figure><p>以上可能你会疑惑,为什么一个写的是绝对路径,而一个写的是相对路径?<br>其实原因很简单,php extension约定了在加载之时可以使用相对于php.ini公共配置项目 <code>extension_dir</code>的路径作为扩展路径;而zend extension则约定了使用全路径进行引入和加载。 </p><h4 id="三、配置PhpStorm项目PHP-Web-Application"><a href="#三、配置PhpStorm项目PHP-Web-Application" class="headerlink" title="三、配置PhpStorm项目PHP Web Application"></a>三、配置PhpStorm项目PHP Web Application</h4><p>这部分不在赘述,正如title中所提到的,配置好PhpStorm项目PHP Web Application,就可以进行debug调试你的php脚本代码了。以下就简单的附上几张图示吧,供各位参考。</p><ul><li>配置:Run/Debug Configuration</li></ul><p><img src="/images/2016-09-01-integrate-phpstorm-mamp-xdebug/14727221950526.jpg" alt=""></p><ul><li>配置:Servers</li></ul><p><img src="/images/2016-09-01-integrate-phpstorm-mamp-xdebug/14727223290574.jpg" alt=""></p><ul><li>操作:Start Listening Xdebug Connections</li></ul><p><img src="/images/2016-09-01-integrate-phpstorm-mamp-xdebug/14727235899205.jpg" alt=""></p><ul><li>Debug界面一览</li></ul><p><img src="/images/2016-09-01-integrate-phpstorm-mamp-xdebug/14727237053026.jpg" alt=""></p><p><em>备注:若各位看官有疑问或者建议,可以在文章末端的disqus留言,本人看到后会及时回复。感谢读者们阅读本篇文章。</em></p>]]></content>
<summary type="html">
PhpStorm+MAMP+xdebug集成失败,
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="php" scheme="https://jinlongchen.com/tags/php/"/>
<category term="xdebug" scheme="https://jinlongchen.com/tags/xdebug/"/>
</entry>
<entry>
<title>Git使用规范</title>
<link href="https://jinlongchen.com/posts/2016/08/27/git-protocol/"/>
<id>https://jinlongchen.com/posts/2016/08/27/git-protocol/</id>
<published>2016-08-27T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.505Z</updated>
<content type="html"><![CDATA[<blockquote><p>以下是关于源代码版本控制的一些共通的约定、协议。</p></blockquote><h4 id="一、管理仓库"><a href="#一、管理仓库" class="headerlink" title="一、管理仓库"></a>一、管理仓库</h4><ul><li>避免将自己开发环境中的内容添加到版本控制系统中;</li><li>每当merge完成之后,删除本地以及远程的相应的特性分支;</li><li>只在特性分支中完成编码工作;</li><li>经常性的执行Rebase,使得upstream的变更及时合并到本地内容中;</li><li>发起pull request,使得代码得以他人评审。</li></ul><h4 id="二、在特性分支工作"><a href="#二、在特性分支工作" class="headerlink" title="二、在特性分支工作"></a>二、在特性分支工作</h4><p>基于master,在本地创建特性分支。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git checkout master</span><br><span class="line">git pull</span><br><span class="line">git checkout -b <branch-name></span><br></pre></td></tr></table></figure><p>经常性的执行Rebase,使得upstream的变更及时合并到本地内容中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git fetch origin</span><br><span class="line">git rebase origin/master</span><br></pre></td></tr></table></figure><p>如果出现冲突,应在Rebase过程中及时解决。<br>当特性分支编码完成、通过测试之后,这个时候,需要将内容提交到index storage,即index暂存区。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add --all</span><br></pre></td></tr></table></figure><p>当你完成了内容的暂存,即以上的add操作,这个时候你便可以提交它了。 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br><span class="line">git commit --verbose</span><br></pre></td></tr></table></figure><p>这里有一个Tips,对阅读者友好的commit注释是非常必要的,例如以下,就是一则好的提交注释。 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Present-tense summary under 50 characters</span><br><span class="line"></span><br><span class="line">* More information about commit (under 72 characters).</span><br><span class="line">* More information about commit (under 72 characters).</span><br><span class="line"></span><br><span class="line">http://project.management-system.com/ticket/123</span><br></pre></td></tr></table></figure><p>如果你之前创建了多个commit,那么你应该使用交互式的Rebase操作,以更好地进行代码的合并,并且生成更为友好的提交注释。 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase -i origin/master</span><br></pre></td></tr></table></figure><p>代码提交完成之后,便是要将你提交的内容share给大家。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin <branch-name></span><br></pre></td></tr></table></figure><p>这个时候,便是向你的团队小伙伴们提交pull request的时候了,请团队成员们帮忙评审自己所编写的代码。</p><h4 id="三、评审代码"><a href="#三、评审代码" class="headerlink" title="三、评审代码"></a>三、评审代码</h4><p>邀请一个团队成员来评审自己的代码,是一个很好的发现缺陷issues的良策,还可以避免沟通上的遗漏和错误的传达。</p><p>通过pull request的评审,可以使得评审的工作不仅限于项目组的讨论会议。</p><p>另外,评审人员也可以自行作出代码变更和修改,只需要将变更内容检出到自己本地的分支。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git checkout <branch-name></span><br><span class="line">./bin/setup</span><br><span class="line">git diff staging/master..HEAD</span><br></pre></td></tr></table></figure><p>当评审人员在自己的branch上修改完毕,测试通过,提交commit并且push到远程origin分支中。<br>对所有变更满意之后,就可以在pull request中备注注释:<code>Ready to merge</code>。</p><h4 id="四、代码合并"><a href="#四、代码合并" class="headerlink" title="四、代码合并"></a>四、代码合并</h4><p>使用交互式的Rebase进行代码的衍合,在每一项衍合所产生的提交中,必要的时候将注释丰富,例如增加message<code>Fix whitespace</code>的备注。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git fetch origin</span><br><span class="line">git rebase -i origin/master</span><br></pre></td></tr></table></figure><p>倘若你force push你的commit(s)到master,倘若你所使用的平台是GitHub,那么GitHub将自动地关闭你的pull request,同时标记该pull request为”Merged”状态。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push --force-with-lease origin <branch-name></span><br></pre></td></tr></table></figure><p>说到这里,可能各位看官会疑惑,为什么这里引用了<code>--force-with-lease</code>这么一个git push的option呢? </p><p>下面介绍下这个参数是什么用途: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">--[no-]force-with-lease</span><br><span class="line">--force-with-lease=<refname></span><br><span class="line">--force-with-lease=<refname>:<expect></span><br><span class="line">Usually, "git push" refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it.</span><br><span class="line"></span><br><span class="line">This option overrides this restriction if the current value of the remote ref is the expected value. "git push" fails otherwise.</span><br><span class="line"></span><br><span class="line">Imagine that you have to rebase what you have already published. You will have to bypass the "must fast-forward" rule in order to replace the history you originally published with the rebased history. If somebody else built on top of your original history while you are rebasing, the tip of the branch at the remote may advance with her commit, and blindly pushing with --force will lose her work.</span><br><span class="line"></span><br><span class="line">This option allows you to say that you expect the history you are updating is what you rebased and want to replace. If the remote ref still points at the commit you specified, you can be sure that no other people did anything to the ref. It is like taking a "lease" on the ref without explicitly locking it, and the remote ref is updated only if the "lease" is still valid.</span><br><span class="line"></span><br><span class="line">--force-with-lease alone, without specifying the details, will protect all remote refs that are going to be updated by requiring their current value to be the same as the remote-tracking branch we have for them.</span><br><span class="line"></span><br><span class="line">--force-with-lease=<refname>, without specifying the expected value, will protect the named ref (alone), if it is going to be updated, by requiring its current value to be the same as the remote-tracking branch we have for it.</span><br><span class="line"></span><br><span class="line">--force-with-lease=<refname>:<expect> will protect the named ref (alone), if it is going to be updated, by requiring its current value to be the same as the specified value <expect> (which is allowed to be different from the remote-tracking branch we have for the refname, or we do not even have to have such a remote-tracking branch when this form is used).</span><br><span class="line"></span><br><span class="line">Note that all forms other than --force-with-lease=<refname>:<expect> that specifies the expected current value of the ref explicitly are still experimental and their semantics may change as we gain experience with this feature.</span><br><span class="line"></span><br><span class="line">"--no-force-with-lease" will cancel all the previous --force-with-lease on the command line.</span><br><span class="line"></span><br><span class="line">-f</span><br><span class="line">--force</span><br><span class="line">Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. Also, when --force-with-lease option is used, the command refuses to update a remote ref whose current value does not match what is expected.</span><br><span class="line"></span><br><span class="line">This flag disables these checks, and can cause the remote repository to lose commits; use it with care.</span><br><span class="line"></span><br><span class="line">Note that --force applies to all the refs that are pushed, hence using it with push.default set to matching or with multiple push destinations configured with remote.*.push may overwrite refs other than the current branch (including local refs that are strictly behind their remote counterpart). To force a push to only one branch, use a + in front of the refspec to push (e.g git push origin +master to force a push to the master branch). See the <refspec>... section above for details.</span><br></pre></td></tr></table></figure><p>以上大致的意思是指,在执行git push之时,增加<code>--force-with-lease</code>参数,可以避免覆盖其他开发者在你fetch了之后,对目标分支做了其他的更新,然后你简单的<code>git push -f</code>的操作强制覆盖了他人的commits内容。<br>如果想要了解<code>--force-with-lease</code>参数更多的相关知识,可以查看这里Stack Overflow的例子: <a href="http://stackoverflow.com/questions/5509543/how-do-i-properly-force-a-git-push" target="_blank" rel="noopener">How do I properly force a Git push?</a></p><p>想要查看新提交的commit(s)列表,想要查看变更的文件列表,以及合并开发分支代码到master,以下命令可以实现这些目的:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">git log origin/master..<branch-name></span><br><span class="line"></span><br><span class="line">git diff --stat origin/master</span><br><span class="line"></span><br><span class="line">git checkout master</span><br><span class="line">git merge <branch-name> --ff-only</span><br><span class="line">git push</span><br></pre></td></tr></table></figure><p>特性分支代码既然都已然成功合并到master上,这个时候我们需要去删除特性分支,无论是本地工程目录,还是远程仓库。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># Delete your remote feature branch.</span><br><span class="line">git push origin --delete <branch-name></span><br><span class="line"></span><br><span class="line"># Delete your local feature branch.</span><br><span class="line">git branch --delete <branch-name></span><br></pre></td></tr></table></figure><h4 id="五、参考资料"><a href="#五、参考资料" class="headerlink" title="五、参考资料"></a>五、参考资料</h4><ul><li><a href="https://github.com/thoughtbot/guides/tree/master/protocol/git" target="_blank" rel="noopener">Git Protocol</a></li><li><a href="http://stackoverflow.com/questions/5509543/how-do-i-properly-force-a-git-push" target="_blank" rel="noopener">How do I properly force a Git push?</a></li></ul>]]></content>
<summary type="html">
Git使用规范,Git协议
</summary>
<category term="工具环境" scheme="https://jinlongchen.com/categories/%E5%B7%A5%E5%85%B7%E7%8E%AF%E5%A2%83/"/>
<category term="protocol" scheme="https://jinlongchen.com/tags/protocol/"/>
<category term="specification" scheme="https://jinlongchen.com/tags/specification/"/>
</entry>
<entry>
<title>XRebel入门与实践</title>
<link href="https://jinlongchen.com/posts/2016/08/26/xrebel-tutorial/"/>
<id>https://jinlongchen.com/posts/2016/08/26/xrebel-tutorial/</id>
<published>2016-08-26T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.505Z</updated>
<content type="html"><![CDATA[<h4 id="一、XRebel简介"><a href="#一、XRebel简介" class="headerlink" title="一、XRebel简介"></a>一、XRebel简介</h4><p><img src="/images/2016-08-26-xrebel-tutorial/14721890028699.jpg" alt=""><br>XRebel是一款轻量级的性能优化器,可以帮助开发者们主动去发现和修复一些通用性的问题。XRebel提供了即时的方式,使得开发者可以针对每一个独立的请求进行分析、定位一些诸如DB查询、session变更细节等。<br>XRebel支持当前众多的Java企业开发平台,只需要耗费一点点的时间,便可以非常简洁地集成到开发环境当中去。</p><p>为什么开发者需要使用到XRebel呢?其实原因很简单。<br>许多开发者们都遇到过很多挣扎的事宜,例如为了寻找系统crash或者发现issues问题,不断地去确认编码逻辑,不断地去核实业务实现细节等等,是否有线程间通信安全问题,是否出现了未捕获异常等等。这些都是非常耗费时间精力的,而这些问题我们可以让其在整个软件开发过程中,暴露地更早。在软件开发周期中,问题暴露地越早,所花费的成本就会越低。</p><blockquote><p>The severity of an issue is higher(obviously) if it is later in the development cycle.</p></blockquote><p><img src="/images/2016-08-26-xrebel-tutorial/14721925857856.jpg" alt=""></p><p>XRebel正是为了让开发者们避免继续低效率地挣扎,遭遇节假日技术支持的困扰而诞生的性能优化工具。<br>它可以帮助开发者们在开发过程中,实时地检测所编程代码的效率参数和通信细节,XRebel只输出了开发者们所关心的关键监控参数细节,而一些无关的其他参数,它将智能地识别并且隐藏起来。<br>开发者们使用XRebel,并不需要频繁地切换系统程序和XRebel分析窗口,因为两者是分离开的。你可以在浏览器中的不同tab上打开对应的页面。这一点很好地提高了开发者的开发效率,方便了开发者在不同窗口内查看指定的数据。</p><p>相对于其他的性能优化器产品,XRebel实现了开发、生产环境在代码监测层面上的零差异。开发环境所监测到的sql、session、exception、code trace log等等,无论是在development环境,或是在product环境,都是一致的。 </p><p><img src="/images/2016-08-26-xrebel-tutorial/14721931961702.jpg" alt=""></p><p>而其他的一些产品,诸如JProfiler、APM,所监测到的数据大多基于CPU、Memory、Threads等方面,定位问题需要先找到对应的Thread,然后打开Thread Tree/Stack进行逐一排查,非常繁琐,并不是那么地简易操作。<br>恰恰XRebel解决了这个排查的技巧性问题。XRebel可以直接捕获到发生异常的代码行和exception detail,同时还可以查看发生该异常的请求之时,所涉及的sql、cache、queue、session等组件的交互情况,使得开发者更易于快速提高解决问题的效率。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14721926122030.jpg" alt=""></p><h4 id="二、XRebel安装使用教程"><a href="#二、XRebel安装使用教程" class="headerlink" title="二、XRebel安装使用教程"></a>二、XRebel安装使用教程</h4><p>前面介绍了关于XRebel的一些背景和特点,以下介绍XRebel的安装步骤及其Tips。</p><blockquote><p>首先要说明的一点,XRebel是一款收费工具,并非开源免费。<br>该工具值得你去购买,如果这笔款项不会影响你的生活质量。关于破解的方法,这里不做介绍,请自行Google。</p></blockquote><h5 id="下载与安装"><a href="#下载与安装" class="headerlink" title="下载与安装"></a>下载与安装</h5><p>1.从官网上下载XRebel压缩包(.zip)<a href="http://zeroturnaround.com/software/xrebel/download/" target="_blank" rel="noopener">http://zeroturnaround.com/software/xrebel/download/</a>。<br>如果你提前登陆了zeroturnaround.com,那么你所下载的zip包中就非常便捷地包含了你的帐号信息。</p><p>2.解压zip包到指定的目录中,zip包中包含的内容如下: </p><p><img src="/images/2016-08-26-xrebel-tutorial/14721938888446.jpg" alt=""></p><p>3.将<code>xrebel.jar</code>添加到应用服务容器的JVM参数中,添加时候确认路径是正确无误的。 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-javaagent:[path/to/]xrebel.jar</span><br></pre></td></tr></table></figure><p>4.启动应用服务容器,XRebel就会自动成功启动。<br>启动完成之后,打开浏览器,XRebel toolbar就会显示在你服务页面的左下角。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">2016-05-20 16:29:42 XRebel: #####################################################</span><br><span class="line">2016-05-20 16:29:42 XRebel:</span><br><span class="line">2016-05-20 16:29:42 XRebel: XRebel 3.1.1 (201605131009)</span><br><span class="line">2016-05-20 16:29:42 XRebel: (c) Copyright ZeroTurnaround AS, Estonia, Tallinn.</span><br><span class="line">2016-05-20 16:29:42 XRebel:</span><br><span class="line">2016-05-20 16:29:42 XRebel: For questions and support,</span><br><span class="line">2016-05-20 16:29:42 XRebel: contact xrebel@zeroturnaround.com</span><br><span class="line">2016-05-20 16:29:42 XRebel:</span><br><span class="line">2016-05-20 16:29:42 XRebel: #####################################################</span><br></pre></td></tr></table></figure><h5 id="License激活"><a href="#License激活" class="headerlink" title="License激活"></a>License激活</h5><p>使用XRebel必须先激活,毕竟是收费工具。当然,在你购买前,你会拥有15天的评估试用期。<br>怎么激活呢?其实很简单,在你安装完XRebel之后,首次打开浏览器,以下窗口便会自动弹跳出来,根据提示进行License注册便是。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14721942799287.jpg" alt=""></p><h5 id="将XRebel集成到你的Server中"><a href="#将XRebel集成到你的Server中" class="headerlink" title="将XRebel集成到你的Server中"></a>将XRebel集成到你的Server中</h5><p>这里会有两个方面可以实践。 </p><ul><li>IDE集成</li><li>Server容器集成</li></ul><p>以下逐一介绍。首先第一种:IDE集成。</p><p><strong>1.Eclipse</strong></p><blockquote><p>Tips: 插件<strong><a href="http://manuals.zeroturnaround.com/xrebel/install/eclipse.html#xrebel-eclipse-install" target="_blank" rel="noopener">XRebel for Eclipse</a></strong>已经在Marketplace提供下载。目前该插件仅支持Eclipse 3.6版本及以上。</p></blockquote><p>(1)打开Eclipse的 Run > Run Configurations…;<br>(2)选择Server服务容器,打开Arguments参数设置tab标签;<br>(3)将以下参数填写到VM arguments配置项目中; </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-javaagent:[path/to/]xrebel.jar</span><br></pre></td></tr></table></figure><p><img src="/images/2016-08-26-xrebel-tutorial/14722183417609.jpg" alt=""></p><p>(4)点击Apply应用并保存到当前的Run Configuration中。</p><p><strong>2.Intellij IDEA</strong></p><p>(1)打开IDEA的Run > Edit Configurations…;<br>(2)选择Server服务容器;<br>(3)将以下参数填写到VM arguments配置项目中; </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-javaagent:[path/to/]xrebel.jar</span><br></pre></td></tr></table></figure><p><img src="/images/2016-08-26-xrebel-tutorial/14722184711430.jpg" alt=""></p><p>(4)点击Apply应用并保存到当前的Run Configuration中。</p><p><strong>3.NetBeans</strong></p><p>(1)打开NetBeans的Tools > Servers;<br>(2)选择Server服务容器,打开Platform参数设置tab标签;<br>(3)将以下参数填写到VM arguments配置项目中; </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-javaagent:[path/to/]xrebel.jar</span><br></pre></td></tr></table></figure><p><img src="/images/2016-08-26-xrebel-tutorial/14722186661015.jpg" alt=""></p><p>(4)点击Close保存并关闭当前窗口。</p><p>下面我们介绍第二种:Server容器集成。</p><p>由于容器种类繁多,这里仅举例应用最为广泛的Tomcat上的应用,其他的容器配置可参考官方文章<a href="http://manuals.zeroturnaround.com/xrebel/install/index.html#adding-the-xrebel-parameter-without-an-ide" target="_blank" rel="noopener">Adding-the-xrebel-parameter-without-an-ide</a>。</p><p><strong>1.Tomcat on Windows Platform</strong></p><p>(1)找到安装Tomcat的目录(即Tomcat home folder);<br>(2)打开bin文件夹;<br>(3)新增一个可执行.bat文件,例如命名为<code>catalina-xrebel.bat</code>;<br>(4)将以下内容增加到第3步所创建好的.bat文件中; </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">@echo off</span><br><span class="line">set JAVA_OPTS=-javaagent:[path/to/]xrebel.jar %JAVA_OPTS%</span><br><span class="line">call "%~dp0\catalina.bat" %*</span><br></pre></td></tr></table></figure><p>(5)执行以上.bat文件,启动Tomcat。</p><p>PS: 需要将Tomcat以服务的方式启动着?可以打开tomcat包装器<code>tomcatXw.exe</code>,在tomcatXw包装器的设置项目Java > Java Options中,新增如下参数: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-javaagent:[path/to/]xrebel.jar</span><br></pre></td></tr></table></figure><p><strong>2.Tomcat on Mac OS and Linux</strong></p><p>(1)找到安装Tomcat的目录(即Tomcat home folder);<br>(2)打开bin文件夹;<br>(3)新增一个可执行.sh文件,例如命名为<code>catalina-xrebel.sh</code>;<br>(4)将以下内容增加到第3步所创建好的.sh文件中;</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">#!/bin/bash</span><br><span class="line">export JAVA_OPTS="-javaagent:[path/to/]xrebel.jar $JAVA_OPTS"</span><br><span class="line">`dirname $0`/catalina.sh $@</span><br></pre></td></tr></table></figure><p>(5)按照如下命令,执行以上.sh启动脚本,启动Tomcat。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./catalina-xrebel.sh run</span><br></pre></td></tr></table></figure><h4 id="三、XRebel应用"><a href="#三、XRebel应用" class="headerlink" title="三、XRebel应用"></a>三、XRebel应用</h4><p>经过前两部分的折腾,你应该已经可以成功启动XRebel了。下面我们来进一步介绍以下,如何去实际使用XRebel?XRebel到底给我们开发者提供了怎样的便捷特性?下面会逐一展开讲道。</p><p>以下主界面非常明显地呈现出了Toolbar的位置,也是XRebel主要的操作菜单功能区。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14722230804350.jpg" alt=""></p><p><strong>1.第一大功能:Access application profiling</strong></p><p><img src="/images/2016-08-26-xrebel-tutorial/14722233171431.jpg" alt=""></p><p><em>PS:为了书写方便,这里暂且称之为AAP功能。</em></p><p>AAP功能包含的是系统服务所接收到的请求列表,以及每一个请求的时间消耗细节监测数据。该数据的统计维度是定位到Method级别的。<br>请跟随以下图示,对AAP功能有一个现状的概览印象,具体事宜可以在真实实践的时候进一步逐一了解。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14722236434252.jpg" alt=""><br><img src="/images/2016-08-26-xrebel-tutorial/14722237721529.jpg" alt=""></p><blockquote><p><img src="/images/2016-08-26-xrebel-tutorial/14722238537773.jpg" alt=""><br>查看AAP功能的栈信息之时,留意符号的含义。该符号标识了一个异步的执行请求。</p></blockquote><p><strong>2.第二大功能:Review IO query</strong></p><p><em>PS:为了书写方便,这里暂且称之为RIQ功能。</em></p><p>RIQ功能可以支持但不仅限于jdbc的监测。例如RabbitMQ、JMS、Redis等框架的执行数据的监控。<br>如第3张图示,清晰地监测到了请求中所包含的执行SQL语句,这一点对于开发、测试过程都是及其有帮助的。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14722233844623.jpg" alt=""></p><p><img src="/images/2016-08-26-xrebel-tutorial/14722239944661.jpg" alt=""><br><img src="/images/2016-08-26-xrebel-tutorial/14722243069603.jpg" alt=""></p><p><strong>3.第三大功能:Find exceptions</strong></p><p><em>PS:为了书写方便,这里暂且称之为FE功能。</em></p><p>FE功能简单易懂,即异常日志的捕获。开发者们除了在slf4log、log4j等日志框架下定义好的路径中查看系统日志以外,还可以在系统程序运行时从XRebel中查看到即时的日志详情。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14722234443155.jpg" alt=""></p><p><img src="/images/2016-08-26-xrebel-tutorial/14722244480081.jpg" alt=""></p><p>以上介绍完了XRebel最为常用的三大功能点,关于session监控的部分,这里也截图一张说明一下,有需要的开发者可以自行研究,例如应用到CAS等用户登录相关场景之时,相信Session data这项功能是你非常好的监控助手。</p><blockquote><p>Session data的功能需要从XRebel的Settings中打开配置选项,默认是不勾选的。</p></blockquote><p><img src="/images/2016-08-26-xrebel-tutorial/14722252061448.jpg" alt=""></p><p>此外这里再补充一点,也就是主界面三大功能的右上角<code>Hidden items</code>选项。</p><p><img src="/images/2016-08-26-xrebel-tutorial/14722246072371.jpg" alt=""></p><p>将该选项打上勾,便可以查看到操作过程中,开发者们标记了hidden的条目,以及XRebel智能隐藏的其他条目。具体看应用场景的需要。</p><h4 id="四、高级配置选项"><a href="#四、高级配置选项" class="headerlink" title="四、高级配置选项"></a>四、高级配置选项</h4><p><strong>1.XRebel过滤名单</strong></p><p>XRebel允许配置一项黑名单列表,将不需要监测的class路径添加进去,即达到忽略的目的。<br>黑名单配置文件在这里: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~/.xrebel/traces-blacklist.txt</span><br></pre></td></tr></table></figure><p>XRebel还允许配置一项黑名单,以过滤<code>static</code>和<code>transient</code>属性的大小,使之不影响所在对象Object的大小计算。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~/.xrebel/session-blacklist.txt</span><br></pre></td></tr></table></figure><p><strong>2.XRebel启动参数</strong></p><ul><li><code>-Dxrebel.single_app_mode=true|false</code> (default value: true) – Consider all deployments to be part of a single application. This reports activity from different deployment units to one and the same toolbar.</li><li><code>-Dxrebel.injection.log_response=true|false</code> (default: false) – When true, the content of HTTP servlet responses will be logged to xrebel.log. This is meant to help debug cases when XRebel toolbar is not properly injected into the application’s HTML.</li><li><code>-Dxrebel.session.include_transient_fields=true|false</code> (default value: false) – When true, fields with the transient modifier will be considered when constructing the session graph. By default, all transient fields are ignored and objects reachable only through transient fields are not considered to be part of the session.</li><li><code>-Dxrebel.browser.console_log=true|false</code> (default value: false) – When true, the client side XRebel log will be written into the browser console.</li><li><code>-Dxrebel.injection=true|false</code> (default value: true) – Disables the XRebel toolbar injection into application HTML. Add /xrebel to your application URL to view the toolbar in a separate tab (this opens the alternative UI view).</li><li><code>-Dxrebel.traces.filter_resources=true|false</code> (default value: true) – Prevents displaying resource requests in the Application profiling view.</li><li><code>-Dxrebel.include_all_io=true|false</code> (default value: true) – Enables the display of all detected IO events. Uncategorized IO events are displayed as Unidentified.</li><li><code>-Dxrebel.sql.format=true|false</code> (default value: true) – Enables formatting of SQL queries.</li><li><code>-Dxrebel.traces.all=true|false</code> (default value: false) – Enables tracing for all contexts.<ul><li><code>-Dxrebel.traces.jms=true|false</code> (default value: false) – Enables tracing for JMS.</li><li><code>-Dxrebel.traces.scheduler=true|false</code> (default value: false) – Enables tracing for ScheduledExecutorService.</li><li><code>-Dxrebel.traces.quartz=true|false</code> (default value: false) – Enables tracing for Quartz.</li><li><code>-Dxrebel.traces.rabbitmq=true|false</code> (default value: false) – Enables tracing for RabbitMQ.</li></ul></li></ul><p><strong>3.XRebel特性Module配置参数</strong></p><ul><li><code>-Dxrebel.module.http.HttpURLConnection=true|false</code> (default value: true) – Disables collecting IQ queries from java.net.HttpURLConnection usage.</li><li><code>-Dxrebel.module.http.HttpCore=true|false</code> (default value: true) – Disables collecting IO queries from Apache HttpCore 4.x usage.</li><li><code>-Dxrebel.module.http.HttpClient3=true|false</code> (default value: true) – Disables collecting IO queries from Apache HttpClient 3.x usage.</li><li><code>-Dxrebel.module.mongodb=true|false</code> (default value: true) – Disables collecting IO queries from MongoDB driver usage.</li><li><code>-Dxrebel.module.hbase=true|false</code> (default value: true) – Disables collecting IO queries from HBase driver usage.</li><li><code>-Dxrebel.module.redis=true|false</code> (default value: true) – Disables collecting IO queries from Redis driver usage.</li><li><code>-Dxrebel.module.cassandra=true|false</code> (default value: true) – Disables collecting IO queries from Cassandra driver usage.</li><li><code>-Dxrebel.module.orm=true|false</code> (default value: true) – Disables collecting information about ORM queries, including Hibernate JPA queries.</li><li><code>-Dxrebel.module.equinox=true|false</code> (default value: true) – Disables support for Equinox class loaders (boot delegation).</li><li><code>-Dxrebel.module.rmi=true|false</code> (default value: true) – Disables collecting IO queries from RMI (java.rmi) remote object invocations.</li><li><code>-Dxrebel.module.traces=true|false</code> (default value: true) – Disables collecting all method calls during HTTP requests.</li><li><code>-Dxrebel.module.session=true|false</code> (default value: true) – Disables taking session snapshots.</li><li><code>-Dxrebel.module.springloaded=true|false</code> (default value: true) – Disables Spring Loaded agent when detected.</li><li><code>-Dxrebel.module.async=true|false</code> (default value: true) – Disables collecting IO queries from asynchronously executed threads.</li><li><code>-Dxrebel.module.quartz=true|false</code> (default value: true) – Disables collecting IO queries within a Quartz job context.</li><li><code>-Dxrebel.module.jms=true|false</code> (default value: true) – Disables collecting IO queries within JMS MessageListener.onMessage() context.</li><li><code>-Dxrebel.module.scheduler=true|false</code> (default value: true) – Disables collecting IO queries for ScheduledThreadPoolExecutor tasks.</li><li><code>-Dxrebel.module.rabbitmq=true|false</code> (default value: true) – Disables collecting IO events within RabbitMQ consumer.handleDelivery() context and outgoing messages.</li><li><code>-Dxrebel.module.remote_events=true|false</code> (default value: true) – Disables collecting and displaying data from remote XRebel-enabled web services (microservices).</li><li><code>-Dxrebel.module.jdbc=true|false</code> (default value: true) – Disable collecting IO queries from JDBC usage.<ul><li><code>-Dxrebel.module.jdbc.sql=true|false</code> (default value: true) – Disables collecting IO queries from relational database JDBC usage. Requires -Dxrebel.module.jdbc to be true.</li><li><code>-Dxrebel.module.jdbc.neo4j=true|false</code> (default value: true) – Disables collecting IO queries from Neo4j JDBC usage. Requires -Dxrebel.module.jdbc to be true.</li><li><code>-Dxrebel.module.jdbc.cassandra=true|false</code> (default value: true) – Disables collecting IO queries from Cassandra JDBC usage. Requires -Dxrebel.module.jdbc to be true.</li><li><code>-Dxrebel.module.jdbc.phoenix=true|false</code> (default value: true) – Disables collecting IO queries from Apache Phoenix (HBase) usage. Requires -Dxrebel.module.jdbc to be true.</li></ul></li></ul><h4 id="五、参考资料"><a href="#五、参考资料" class="headerlink" title="五、参考资料"></a>五、参考资料</h4><p>本篇文章基于个人在近期实践XRebel过程中,所用到的知识归纳成文。<br>XRebel实用性极高,对自己在日常的开发中,方便了发现代码缺陷以及存在的性能隐患,所以作此一文推荐一番。</p><p>具体官方资料可查阅:<a href="http://manuals.zeroturnaround.com/xrebel/index.html" target="_blank" rel="noopener">http://manuals.zeroturnaround.com/xrebel/index.html</a></p>]]></content>
<summary type="html">
XRebel入门、安装、实践
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="xrebel" scheme="https://jinlongchen.com/tags/xrebel/"/>
<category term="performance monitor" scheme="https://jinlongchen.com/tags/performance-monitor/"/>
</entry>
<entry>
<title>如何快速选择开源软件协议</title>
<link href="https://jinlongchen.com/posts/2016/07/22/easy-chosing-opensource-license/"/>
<id>https://jinlongchen.com/posts/2016/07/22/easy-chosing-opensource-license/</id>
<published>2016-07-22T02:00:00.000Z</published>
<updated>2020-07-02T18:56:45.504Z</updated>
<content type="html"><![CDATA[<p><img src="/images/2016-07-22-easy-chosing-opensource-license/14691964599348.jpg" alt=""></p><h3 id="一、背景介绍"><a href="#一、背景介绍" class="headerlink" title="一、背景介绍"></a>一、背景介绍</h3><p>目前开源协议有很多,同一款协议也衍生出了许多变种。开源世界中,我们可以使用合适的开源协议,保证自己作品的版权。<br>我们这里不做过多的介绍,只介绍一些自己常用的三个选择及其理由,以供参考。</p><h3 id="二、详细描述"><a href="#二、详细描述" class="headerlink" title="二、详细描述"></a>二、详细描述</h3><h4 id="2-1-“我需要一个简单宽松的协议”"><a href="#2-1-“我需要一个简单宽松的协议”" class="headerlink" title="2.1 “我需要一个简单宽松的协议”"></a>2.1 “我需要一个简单宽松的协议”</h4><p>MIT License是一个简短、宽松、自由的协议。该协议允许人们使用你的代码,但必须要保留你的版权信息。与此同时,并不会给你带来任何责任和风险。<code>jQuery</code>,<code>.NET Core</code>和<code>Rails</code>使用的均是MIT License。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">The MIT License (MIT)</span><br><span class="line">Copyright (c) <year> <copyright holders></span><br><span class="line"></span><br><span class="line">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</span><br><span class="line"></span><br><span class="line">The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</span><br><span class="line"></span><br><span class="line">THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</span><br></pre></td></tr></table></figure><h4 id="2-2-“我更加关心自己的专利”"><a href="#2-2-“我更加关心自己的专利”" class="headerlink" title="2.2 “我更加关心自己的专利”"></a>2.2 “我更加关心自己的专利”</h4><p>Apache License 2.0是一项和MIT License相似的协议,但自己希望自己的专利能在开源免费使用的同时,保留自己在开源产品中的专利权益。同样,该协议要求使用者必须保留你的版权信息。<br><code>Android</code>, <code>Apache</code>和<code>Swift</code>使用的均是Apache License 2.0协议。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">Apache License Version 2.0</span><br><span class="line">Copyright [yyyy] [name of copyright owner]</span><br><span class="line"></span><br><span class="line">Licensed under the Apache License, Version 2.0 (the "License");</span><br><span class="line">you may not use this file except in compliance with the License.</span><br><span class="line">You may obtain a copy of the License at</span><br><span class="line"></span><br><span class="line"> http://www.apache.org/licenses/LICENSE-2.0</span><br><span class="line"></span><br><span class="line">Unless required by applicable law or agreed to in writing, software</span><br><span class="line">distributed under the License is distributed on an "AS IS" BASIS,</span><br><span class="line">WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span><br><span class="line">See the License for the specific language governing permissions and</span><br><span class="line">limitations under the License.</span><br></pre></td></tr></table></figure><h4 id="2-3-“我关心代码的分享以及促进”"><a href="#2-3-“我关心代码的分享以及促进”" class="headerlink" title="2.3 “我关心代码的分享以及促进”"></a>2.3 “我关心代码的分享以及促进”</h4><p>如果你希望别人在分享的自己的作品之后,也必须遵循相同的协议,也必须是开源和免费。那么GPLv3是你更好的选择。该协议当中也明确地包含了贡献人的专利权益方面的款项。原作品的版权条款也必须延续保留。GPL协议存在非常强的“传染性”<br><code>Bash</code>, <code>GIMP</code>和<code>Privacy Badger</code>使用的均是GPLv3协议。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">GNU GENERAL PUBLIC LICENSE Version 3</span><br><span class="line">Copyright (C) {year} {name of author}</span><br><span class="line"></span><br><span class="line">This program is free software: you can redistribute it and/or modify</span><br><span class="line">it under the terms of the GNU General Public License as published by</span><br><span class="line">the Free Software Foundation, either version 3 of the License, or</span><br><span class="line">(at your option) any later version.</span><br><span class="line"></span><br><span class="line">This program is distributed in the hope that it will be useful,</span><br><span class="line">but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span class="line">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span><br><span class="line">GNU General Public License for more details.</span><br><span class="line"></span><br><span class="line">You should have received a copy of the GNU General Public License</span><br><span class="line">along with this program. If not, see <http://www.gnu.org/licenses/>.</span><br></pre></td></tr></table></figure><p>如果你的产品是基于终端的,你还可以加上如下一段,使得使用者知晓如何可以联系到你。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{project} Copyright (C) {year} {fullname}</span><br><span class="line">This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.</span><br><span class="line">This is free software, and you are welcome to redistribute it</span><br><span class="line">under certain conditions; type `show c' for details.</span><br></pre></td></tr></table></figure><h3 id="三、参考资料"><a href="#三、参考资料" class="headerlink" title="三、参考资料"></a>三、参考资料</h3><ul><li>Opensource License:<a href="http://choosealicense.com/licenses/" target="_blank" rel="noopener">http://choosealicense.com/licenses/</a></li><li>各种开源协议介绍:<a href="http://www.open-open.com/solution/view/1319816219625" target="_blank" rel="noopener">http://www.open-open.com/solution/view/1319816219625</a></li><li>开源那么重要,开源协议你认识吗?<a href="http://yeyuan.baijia.baidu.com/article/43303" target="_blank" rel="noopener">http://yeyuan.baijia.baidu.com/article/43303</a></li></ul>]]></content>
<summary type="html">
如何快速选择开源软件协议,How to easy chose and opensource license for your project
</summary>
<category term="架构设计" scheme="https://jinlongchen.com/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
<category term="opensource" scheme="https://jinlongchen.com/tags/opensource/"/>
<category term="license" scheme="https://jinlongchen.com/tags/license/"/>
</entry>
<entry>
<title>解决fastjson反序列化日期0000-00-00失败的方案</title>
<link href="https://jinlongchen.com/posts/2016/07/22/resolve-the-fastjson-problems-about-date/"/>
<id>https://jinlongchen.com/posts/2016/07/22/resolve-the-fastjson-problems-about-date/</id>
<published>2016-07-22T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.504Z</updated>
<content type="html"><![CDATA[<h4 id="一、案例场景复原"><a href="#一、案例场景复原" class="headerlink" title="一、案例场景复原"></a>一、案例场景复原</h4><p>示例场景里涉及两个class:<code>TestDemo.java</code>, <code>DateBeanDemo.java</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DateBeanDemo.java</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DateBeanDemo</span> </span>{</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * dateStr field with Date.class</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> Date dateStr;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Get dateStr <br></span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> Returns the dateStr. <br></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Date <span class="title">getDateStr</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> dateStr;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Set dateStr <br></span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dateStr The dateStr to set. <br></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setDateStr</span><span class="params">(Date dateStr)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.dateStr = dateStr;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 示例执行例子</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestDemo</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String jsonStr = <span class="string">"{\"dateStr\":\"0000-00-00\"}"</span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> DateBeanDemo resultObject = JSON.parseObject(TestDemo.jsonStr, DateBeanDemo<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行以上的main方法之后,并没有获取预期的结果,而是在fastjson的序列化解析中便发生了异常,如下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Exception in thread "main" com.alibaba.fastjson.JSONException: For input string: "0000-00-00"</span><br><span class="line">at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:555)</span><br><span class="line">at com.alibaba.fastjson.JSON.parseObject(JSON.java:251)</span><br><span class="line">at com.alibaba.fastjson.JSON.parseObject(JSON.java:227)</span><br><span class="line">at com.alibaba.fastjson.JSON.parseObject(JSON.java:186)</span><br><span class="line">at excel.TestDemo.main(TestDemo.java:23)</span><br><span class="line">Caused by: java.lang.NumberFormatException: For input string: "0000-00-00"</span><br><span class="line">at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)</span><br><span class="line">at java.lang.Long.parseLong(Long.java:419)</span><br><span class="line">at java.lang.Long.parseLong(Long.java:468)</span><br><span class="line">at com.alibaba.fastjson.parser.deserializer.DateDeserializer.cast(DateDeserializer.java:56)</span><br><span class="line">at com.alibaba.fastjson.parser.deserializer.AbstractDateDeserializer.deserialze(AbstractDateDeserializer.java:98)</span><br><span class="line">at Fastjson_ASM_DateBeanDemo_1.deserialze(Unknown Source)</span><br><span class="line">at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:551)</span><br><span class="line">... 4 more</span><br></pre></td></tr></table></figure><p>通过百度查阅了部分网上撰写的方案,可以使用fastjson中的注解<code>@JSONField(format="")</code>来重新定义<code>DateBeanDemo.dateStr</code>,如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@JSONField</span>(format = <span class="string">"yyyy-MM-dd"</span>,parseFeatures={Feature.AllowISO8601DateFormat})</span><br><span class="line"><span class="keyword">private</span> Date dateStr;</span><br></pre></td></tr></table></figure><p>然而,并没有解决这个exception的问题。同时还怀疑了注解是否在反序列化之时没有被使用到,经过查阅源码,fastjson已将其反序列化的开关定义了<code>true</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Target</span>({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> JSONField {</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">ordinal</span><span class="params">()</span> <span class="keyword">default</span> 0</span>;</span><br><span class="line"> <span class="function">String <span class="title">name</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line"> <span class="function">String <span class="title">format</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">serialize</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">true</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">deserialize</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">true</span></span>; <span class="comment">// 默认定义了true,反序列化之时也使用该注解</span></span><br><span class="line"> SerializerFeature[] serialzeFeatures() <span class="keyword">default</span> {};</span><br><span class="line"> Feature[] parseFeatures() <span class="keyword">default</span> {};</span><br><span class="line"> <span class="function">String <span class="title">label</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="二、原因剖析"><a href="#二、原因剖析" class="headerlink" title="二、原因剖析"></a>二、原因剖析</h4><p>经查找fastjson中相关代码发现,当代码执行到<code>com.alibaba.fastjson.parser.deserializer.DateDeserializer.class</code>的执行方法<code>cast()</code>中之时,所使用的JSONParser中所含带的dateFormat依旧是默认的<code>yyyy-MM-dd HH:mm:ss</code>,而并非注解@JSONField中所定义的<code>yyyy-MM-dd</code>。<br>所以发生了转换字段失败。</p><p><img src="/images/2016-07-22-resolve-the-fastjson-problems-about-date/14691655278897.jpg" alt=""></p><p>再深究一层,为什么不是@JSONField中所定义的<code>yyyy-MM-dd</code>作为JSONParser中的dateFormat呢?其实仔细阅读一遍<code>cast()</code>代码逻辑就会发现,并不是fastjson丢弃了JSONField的扫描,而是在方法中有这么一段:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 检查格式是否符合ISO8601的DateFormat规范</span></span><br><span class="line"><span class="keyword">if</span>(!dateLexer.scanISO8601DateIfMatch(<span class="keyword">false</span>)) {</span><br><span class="line"> <span class="keyword">break</span> label122;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当执行到以上代码段之时,由于字符串<code>0000-00-00</code>并不是ISO8601的DateFormat规范之内,故而代码便会<code>break label122</code>执行跳出逻辑。紧接着执行的就是<code>DateFormat dateFormat1 = parser.getDateFormat();</code>,此时,parser依然是global定义的parser,DateFormat并没有使用<code>@JSONField</code>中所定义的。</p><h4 id="三、解决方案:新增date反序列化解析器"><a href="#三、解决方案:新增date反序列化解析器" class="headerlink" title="三、解决方案:新增date反序列化解析器"></a>三、解决方案:新增date反序列化解析器</h4><p>解决方案并非只有一种,在众多解决方案中自己选择了”新增date反序列化解析器”的办法。除此之外还有诸如设置<code>JSON.DEFFAULT_DATE_FORMAT</code>属性的办法,也同样可以解决这一问题。下面作两种办法的对比和阐述。</p><ul><li>方案1:设置属性<code>JSON.DEFFAULT_DATE_FORMAT</code> </li></ul><p>这一方案只涉及修改<code>main()</code>方法代码即可实现,</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 示例执行例子</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestDemo</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String jsonStr = <span class="string">"{\"dateStr\":\"0000-00-00\"}"</span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JSON.DEFFAULT_DATE_FORMAT = <span class="string">"yyyy-MM-dd"</span>;</span><br><span class="line"> DateBeanDemo resultObject = JSON.parseObject(TestDemo.jsonStr, DateBeanDemo<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是翻阅fastjson中对于<code>JSON.DEFFAULT_DATE_FORMAT</code>可知,该属性属于静态属性,一旦设置影响全局。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// com.alibaba.fastjson.JSON.class</span><br><span class="line">public abstract class JSON implements JSONStreamAware, JSONAware {</span><br><span class="line"> public static String DEFFAULT_DATE_FORMAT;</span><br><span class="line"> // ...</span><br></pre></td></tr></table></figure><ul><li>方案2:新增date反序列化解析器</li></ul><p>主要思路是以fastjson原生的<code>DateDeserializer.class</code>为基础,定制化一个可以解析<code>0000-00-00</code>的日期反序列化解析器。<br>该方式是fastjson函数<code>JSON.parseObject()</code>的一个应用场景,通过定制化<code>ParserConfig</code>参数,达到局部改变JSON解析逻辑的目的。<br>如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> jeromechan.fixbug.fastjson;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.parser.DefaultJSONParser;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.parser.deserializer.DateDeserializer;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Type;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Copyright © 2016 Jerome Chan. All rights reserved.</span></span><br><span class="line"><span class="comment"> * An extended DateDeseializer for parsing '0000-00-00'.</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> chenjinlong</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@CreateDate</span> 7/20/16 5:55 PM</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JCDateDeserializer</span> <span class="keyword">extends</span> <span class="title">DateDeserializer</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> JCDateDeserializer instance = <span class="keyword">new</span> JCDateDeserializer();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">JCDateDeserializer</span><span class="params">()</span> </span>{</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span> <T> <span class="function">T <span class="title">cast</span><span class="params">(DefaultJSONParser parser, Type clazz, Object fieldName, Object val)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (val == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (val <span class="keyword">instanceof</span> String) {</span><br><span class="line"> String strVal = (String) val;</span><br><span class="line"> <span class="keyword">if</span> (strVal.length() == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (strVal.equals(<span class="string">"0000-00-00"</span>)) {</span><br><span class="line"> parser.setDateFormat(<span class="string">"yyyy-MM-dd"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.cast(parser, clazz, fieldName, val);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 示例执行例子</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestDemo</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String jsonStr = <span class="string">"{\"dateStr\":\"0000-00-00\"}"</span>;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{ </span><br><span class="line"> ParserConfig jcParserConfig = <span class="keyword">new</span> ParserConfig();</span><br><span class="line"> jcParserConfig.putDeserializer(Date<span class="class">.<span class="keyword">class</span>, <span class="title">JCDateDeserializer</span>.<span class="title">instance</span>)</span>;</span><br><span class="line"> DateBeanDemo resultObject = JSON.parseObject(TestDemo.jsonStr, DateBeanDemo<span class="class">.<span class="keyword">class</span>, <span class="title">jcParserConfig</span>, <span class="title">JSON</span>.<span class="title">DEFAULT_PARSER_FEATURE</span>)</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>假设觉得这种解析办法可以作为整个项目内的全局特性,感兴趣的话可以将定制好的<code>JCDateDeserializer</code>利用spring框架注入到项目容器中。这同样是对于方案2很不错的延伸。</p><h4 id="四、参考资料"><a href="#四、参考资料" class="headerlink" title="四、参考资料"></a>四、参考资料</h4><ul><li>fastjson在github上的issues:<a href="https://github.com/alibaba/fastjson/issues/414" target="_blank" rel="noopener">https://github.com/alibaba/fastjson/issues/414</a></li></ul>]]></content>
<summary type="html">
解决fastjson反序列化日期0000-00-00失败的方案
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="fastjson" scheme="https://jinlongchen.com/tags/fastjson/"/>
</entry>
<entry>
<title>什么是架构</title>
<link href="https://jinlongchen.com/posts/2016/07/06/what-is-architecture/"/>
<id>https://jinlongchen.com/posts/2016/07/06/what-is-architecture/</id>
<published>2016-07-06T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.504Z</updated>
<content type="html"><![CDATA[<p><img src="/images/2016-07-06-what-is-architecture/14678247250239.jpg" alt=""></p><p>先来看看老外们对于架构是如何诠释的。</p><blockquote><p><strong>What is Architecture?</strong><br>Architecture is the <strong>art</strong>,science, and profession of planning, <strong>designing</strong>, and supervising the construction of new <strong>buildings, landscapes, communities</strong>, and <strong>furnishings</strong> in their totality, examining their environment in accordance with the <strong>principles</strong> of utility, strength, and <strong>aesthetics</strong>.</p></blockquote><p>以上这段话虽然更适合解释建筑架构的含义,但是我们通过对比计算机工程类的架构的理念和要求,以上所提到的要点皆包含在内,不外乎生态不同罢了,其两者核心理念是相同的。</p><p>最近日子在工作中遇到的关于“架构”的探讨上,今天来谈谈自己理解的所谓架构,想以此篇文字来端正一下自己。</p><p>架构这词也是神圣不可侵犯的,如果你把一些b格很low的设计细节的东西当作是架构,那么毫无疑问,你正在掩耳盗铃。<br>设计和架构有着鸿沟一般的区别。而跨越这条鸿沟,需要不断学习,不停积攒天赋,同时伴随着需 要量的积累。 设计,首先它是一个架构范畴中的一个很小的子集。处于一个很单元的层次,但它也同时是不可逾 越不可或缺的阶段。 </p><p>而设计绝不等于架构。</p><p>架构,不仅考量技术实现,还需要诸如:</p><ul><li>客户关键业务点的理解</li><li>如何将概念具体化</li><li>项目目标和周期的估算</li><li>如何多元化团队沟通协作</li></ul><p>层次高的还有:</p><ul><li>关乎项目战略目标的敲定</li><li>如何在企业执行中追求更高roi</li></ul><p>而技术实现方面也不仅仅是说项目如何搭建如何设计db,如何实现分包分模块,还不可或缺的需要包括:</p><ul><li>如何将项目平台化</li><li>如何作冗余和可用架构而同时避免缠绕和蔓延</li><li>如何用专业手段作架构沟通和透视</li></ul><p>对于稳定系统的方面,如何去执行可持续治理也是所谓架构的关键点。 </p><p>所以,只是挂在嘴边念叨“架构架构”而从不事先勘探落地,这种“理论架构”毫无用处,能体现项目 生产力、技术价值、团队骄傲的,才是我们所需要的真正架构。 </p><blockquote><p><strong>真正懂架构的人,并不会把架构挂在嘴边</strong><br><strong>真正牛逼的架构师,从来都是拿实力和实例说话</strong></p></blockquote>]]></content>
<summary type="html">
什么是架构?架构的真正定义和导向
</summary>
<category term="架构设计" scheme="https://jinlongchen.com/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/"/>
<category term="architecture" scheme="https://jinlongchen.com/tags/architecture/"/>
</entry>
<entry>
<title>说说使用middleman的那些事情</title>
<link href="https://jinlongchen.com/posts/2016/07/02/talk-about-middleman/"/>
<id>https://jinlongchen.com/posts/2016/07/02/talk-about-middleman/</id>
<published>2016-07-02T01:00:00.000Z</published>
<updated>2020-07-02T18:56:45.503Z</updated>
<content type="html"><![CDATA[<p><img src="/images/2016-07-02-talk-about-middleman/DraggedImage.png" alt=""></p><h4 id="一、什么是静态网站"><a href="#一、什么是静态网站" class="headerlink" title="一、什么是静态网站"></a>一、什么是静态网站</h4><p>随着互联网发展的脚步,现在的个人博客网站已经不再局限于国内几大门户网站所提供的博客撰写服务。<br>记得上大学那会儿,为了阅读一些名人诸如国民岳父韩寒、李承鹏、徐静蕾等博主的文章和思想,每天必定会上博客上转上两遍,翻阅近十篇进行阅读。那个时候的博客页面加载速度,在寝室512k的带宽状况下,简直令人没有了脾气。</p><p>那个时候并没有太多纯静态网站的做法,有的是一些根据脚本生成指定的某些html页面,用于解决一些高流量访问的问题,例如门户站点的首页,其中只需要保持少量的动态加载元素,对付高流量高并发并不是困难的。</p><p>而个人博客、企业宣传页面不同于上文的需求场景,其更新频率定期,没有过多的注册用户信息的管理,不涉及复杂的交互实现,只需要满足查看和评论的功能即可。</p><blockquote><p>评论功能自然是需要实现r+w操作的,但是现在已经存在了很多第三方协助托管评论信息的平台,例如国内的多说,国外的disqus。只需要开发者简单地嵌入少量html+js代码即可完成评论功能的实现。</p></blockquote><p>故而,纯静态网站应运而生,各类纯静态网站生成工具也出现了较大的发展前景。下面说道的middleman就是一个静态网站的generator工具。</p><h4 id="二、放弃jekyll转投middleman"><a href="#二、放弃jekyll转投middleman" class="headerlink" title="二、放弃jekyll转投middleman"></a>二、放弃jekyll转投middleman</h4><p><img src="/images/2016-07-02-talk-about-middleman/DraggedImage-1.png" alt=""></p><p>这部分并不是说jekyll技术已经落后,而是从自身角度来看为什么自己会转投middleman。<br>自己是从2014年开始使用jekyll的,源于阅读了较多的大神yuguo对于前端栈的技术篇章,也就两年的光景,但是自己经历了从2.X版本变迁到3.X版本的过程。<br>对于为什么自己放弃jekyll转投middleman,下面分2点作出阐述。</p><p>第一点,我们先来说说2.X到3.X版本的更迭细节。<br><img src="/images/2016-07-02-talk-about-middleman/DraggedImage-2.png" alt=""><br>以上变迁的情况说明,大家都知道,jekyll 2.X里包含了ruby和python两门技术,而以上的更迭看来,一方面减少了非必需包的依赖,留出了定制空间,移除了python的依赖。或许jekyll作者更喜欢ruby only吧。</p><p>这些变更并不是向前兼容的,如果你的jekyll是2.X版本,请留意升级后带来的源代码修改的成本。虽然它不是向前兼容,但是升级过程并不会引入更多的包依赖,而恰恰是因为这点,_config.yml需要作出的变更会较多,例如coderay的highlighter引擎建议要更换为rouge;需要分页插件,则需要增加gems:[jekyll-paginate]作出依赖声明,等等。</p><p>而这里包含了一点自己无法接受的变更,看官方声明:</p><blockquote><p><strong>!! Don’t set a permalink</strong><br>Setting a permalink in the front matter of your blog page will cause pagination to break. Just omit the permalink.</p></blockquote><p>以上意思是说,如果你使用了pagination,那么permalink的使用会阻断pagination的执行。也就是说,如果你有一个分页列表的页面,因为你在该页面中使用了permalink改写路径,那么页面中的paginator.posts将会失效,页面内容也将加载失败。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">{% raw %}</span><br><span class="line"><span class="comment"><!-- This loops through the paginated posts,if you had used permalink settings, the paginator parsing job will fail. --></span></span><br><span class="line">{% for post in paginator.posts %}</span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span><span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"{{ post.url }}"</span>></span>{{ post.title }}<span class="tag"></<span class="name">a</span>></span><span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"author"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"date"</span>></span>{{ post.date }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"content"</span>></span></span><br><span class="line"> {{ post.content }}</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line">{% endfor %}</span><br><span class="line">{% endraw %}</span><br></pre></td></tr></table></figure><p>第二点,就要说到liquid的语法。<br>我们来假设这么个场景,在一个列表加载的页面中,我们需要在循环内判断到达指定加载条目预设上限之时,循环执行break跳出。<br>这个场景使用liquid引擎是这么写的:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">{% raw %}</span><br><span class="line">{% assign var = 0 %}</span><br><span class="line">{% for post in site.posts %}</span><br><span class="line"> {% assign var = var | plus: 1 %}</span><br><span class="line"> {% if var > 5 %}</span><br><span class="line"> {% break %}</span><br><span class="line"> {% endif %}</span><br><span class="line">{% endfor %}</span><br><span class="line">{% endraw %}</span><br></pre></td></tr></table></figure><p>也可以这么写:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">{% raw %}</span><br><span class="line">{% assign var = 0 %} // 这句话可有可无,因为increment会将var初始化为0</span><br><span class="line">{% for post in site.posts %}</span><br><span class="line"> {% increment var %} // 这句话会自动执行一个print语句,将var打印出来</span><br><span class="line"> {% if var > 5 %}</span><br><span class="line"> {% break %}</span><br><span class="line"> {% endif %}</span><br><span class="line">{% endfor %}</span><br><span class="line">{% endraw %}</span><br></pre></td></tr></table></figure><p>前者的写法让人觉得做一个简单的运算都需要去背记liquid的各种filters,让人觉得麻烦不已。<br>后者的写法会凭空多出一个默认的print效果,这个也是UI界面编写之时无法接受的。</p><h4 id="三、为什么不选用WordPress等LAMP架构的博客框架"><a href="#三、为什么不选用WordPress等LAMP架构的博客框架" class="headerlink" title="三、为什么不选用WordPress等LAMP架构的博客框架"></a>三、为什么不选用WordPress等LAMP架构的博客框架</h4><p><img src="/images/2016-07-02-talk-about-middleman/DraggedImage-3.png" alt=""></p><p>作为一名php开发者,wordpress是必须鼓捣的框架之一,但是为什么最终放弃了选用wp作为个人博客首选呢?<br>原因其实不复杂,主要有以下五个方面: </p><ol><li>涉及数据库技术,数据的灾备流程过于复杂,维护时间成本高; </li><li>wp框架体量重,适合开发较大型的cms内容管理站点,对于个人博客有点大材小用,如同杀鸡用了牛刀; </li><li>扩展插件多是其亮点,同时也是拖慢wp站点的一大坑点,php的通用单线程处理机制决定了其瓶颈,就算升级了鸟哥的php7估计也根治不了多扩展插件wp站点的加载时长的问题; </li><li>需要关注的安全问题较多,可定制化有限,如果不合适的定制会引发无法升级的问题; </li><li>需要一台VPS或者Web Host主机,也就是将多出一份硬件方面的开销,如果要求访问稳定、连接速度可接受的主机,购买价格也会逐级上升。 </li></ol><h4 id="四、简明middleman实践之路"><a href="#四、简明middleman实践之路" class="headerlink" title="四、简明middleman实践之路"></a>四、简明middleman实践之路</h4><h5 id="4-1-由于middleman是ruby语言的作品,先了解以下关于ruby语言的关键概念。"><a href="#4-1-由于middleman是ruby语言的作品,先了解以下关于ruby语言的关键概念。" class="headerlink" title="4.1 由于middleman是ruby语言的作品,先了解以下关于ruby语言的关键概念。"></a>4.1 由于middleman是ruby语言的作品,先了解以下关于ruby语言的关键概念。</h5><p><img src="/images/2016-07-02-talk-about-middleman/DraggedImage-4.png" alt=""></p><ol><li>ruby:顾名思义,指的就是ruby语言。 </li><li>rvm:全名ruby version manager,注意它和jvm不是一个含义哦。rvm是管理ruby本身,也包含了ruby插件的管理。 </li><li>rails:指的是ruby著名开发框架,认识ruby的人都认识它。 </li><li>rubygems:ruby程序包管理器,可以将ruby程序打包成gem,作为一个独立安装单元安装到计算机中。 </li><li>gem:指的是封装起来的ruby应用程序,或者代码库。终端中使用的gem命令,是指使用rubygems安装程序应用。 </li><li>gemfile:配置文件,用于定义指定应用所依赖的包,然后可以提供给bundle命令执行,类似于shell命令中的.sh脚本。 </li><li>rake:该程序包是ruby所需要安装的包中最为关键的一个。rake是一个ruby的构建工具,类似于Make、ant、maven、gradle不等。rakefile便是其执行的构建任务配置文件,其中涉及特定的DSL撰写方式,类似于groovy在gradle中的应用一样。 </li><li>rakefile:rake构建任务中所涉及的任务配置文件。 </li><li>bundle:等同于批量执行gem命令,配置好gemfile之后,使用bundle install可以实现包,及其依赖包的自动下载与安装。 </li></ol><h5 id="4-2-middleman的安装疑问"><a href="#4-2-middleman的安装疑问" class="headerlink" title="4.2 middleman的安装疑问"></a>4.2 middleman的安装疑问</h5><p>middleman依赖于rubygems、bundle、gemfile、rake等工具或框架。在安装一系列ruby依赖库的过程中,会遇到connection fail,error loading,can’t be found等问题,这些问题的原因是因为<a href="https://www.rubygems.org" target="_blank" rel="noopener">rubygems.org</a>在国内访问不稳定的缘故。 </p><p>正所谓兵来将挡,水来土掩,方法总是比困难要多得多。<br>国内的大淘宝,还有ruby-china官方,都给大家做了一个同步频率为15mins的ruby gems的镜像,我们可以将自己本地的ruby gems、bundle源配置切换到国内。<br>参见这里: </p><ul><li>ruby-china官方源:<a href="http://ruby-china.org" target="_blank" rel="noopener">http://ruby-china.org</a> </li><li>大淘宝的镜像源:<a href="http://ruby.taobao.org" target="_blank" rel="noopener">http://ruby.taobao.org</a></li></ul><p>在安装各项依赖之时,建议先配置好gemfile,然后使用命令bundle install自动实现下载和安装,而不是使用middleman server逐项发现缺失库,因为从操作效率上是得不偿失的。</p><h5 id="4-3-slim-VS-liquid"><a href="#4-3-slim-VS-liquid" class="headerlink" title="4.3 slim VS liquid"></a>4.3 slim VS liquid</h5><p><img src="/images/2016-07-02-talk-about-middleman/14674858884718.png" alt=""></p><p>关于liquid的一些用法和自己不感冒之处,前面已经提及了部分,这里不再大施笔墨赘述。<br>这里说说slim的情况。</p><p>liquid是jekyll默认配置的官方模板引擎,而slim也是middleman首推的前端引擎。<br>liquid有很多晦涩不易入门的语法结构,而slim相对而言就显得优雅了许多。</p><p>slim所倡导的是,一切页面元素、样式、js均可结构化。使用slim可以大大简化前端页面渲染的逻辑实现结构,没有了liquid般的html+js+liquid+css混合搭配在.html文件里使用的尴尬,映入眼帘的均是层次分明,结构清晰的模板语法。</p><p>下面作一下两者的对比,孰好各位看官心中自有结论。</p><ul><li>html+js+css+liquid混合应用</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">{% raw %}</span><br><span class="line"><span class="comment"><!-- html+js+css+liquid混合应用 --></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"theme-color"</span> <span class="attr">content</span>=<span class="string">"{{ page.color }}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"{{ "</span>/<span class="attr">assets</span>/<span class="attr">css</span>/<span class="attr">main.css</span>" | <span class="attr">prepend:site.baseurl</span> }}"></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"canonical"</span> <span class="attr">href</span>=<span class="string">"{{ page.url | replace:'index.html','' | prepend:site.baseurl | prepend: site.url }}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"alternate"</span> <span class="attr">type</span>=<span class="string">"application/rss+xml"</span> <span class="attr">title</span>=<span class="string">"{{ site.title }}"</span> <span class="attr">href</span>=<span class="string">"{{ "</span>/<span class="attr">feed.xml</span>" | <span class="attr">prepend:</span> <span class="attr">site.baseurl</span> | <span class="attr">prepend:</span> <span class="attr">site.url</span> }}" /></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line">{% endraw %}</span><br></pre></td></tr></table></figure><ul><li>slim的前端结构</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><!-- html+js+css+slim混合应用 --></span><br><span class="line">doctype html</span><br><span class="line">html lang="ja"</span><br><span class="line"> head</span><br><span class="line"> meta charset="utf-8"</span><br><span class="line"> title The Elevatorpitch</span><br><span class="line"> meta name="viewport" content="width=device-width, initial-scale=1.0"</span><br><span class="line"> css:</span><br><span class="line"> body {</span><br><span class="line"> padding-top: 20px;</span><br><span class="line"> padding-bottom: 40px;</span><br><span class="line"> }</span><br><span class="line"> body</span><br><span class="line"> div class="container-narrow"</span><br><span class="line"> div class="masthead visible-desktop"</span><br><span class="line">script src="/js/theelevatorpitch.js"</span><br></pre></td></tr></table></figure><p>更多slim应用可以关注我的gist:<a href="https://gist.github.com/jeromechan/0432a89f3e9810e9ba26052606e56f38" target="_blank" rel="noopener">Use slim engine for building html tags</a></p><h4 id="五、Try-the-way-you-like"><a href="#五、Try-the-way-you-like" class="headerlink" title="五、Try the way you like"></a>五、Try the way you like</h4><p>前端技术的不断发展,纯静态网站的性能问题也逐步被放大,在我们使用静态网站生成工具的同时,性能问题也一定需要着重考虑。<br>例如,gzip压缩、js+css压缩、单次加载批量样式文件和js文件、html页面布局层次简化、js依赖关系处理、css继承关系的整理、前端cache/data等缓冲区域技术的应用、异步ajax请求时长的监控、sass/less的样式结构简化和预处理,等等方面。</p><p>只要始终坚持用户至上,读者至上的原则,一定能够找到最适合自己预想的方案。</p><p>前面所涉及的参考资料有如下,感兴趣可以自己翻翻看看: </p><ul><li><a href="https://shopify.github.io/liquid/" target="_blank" rel="noopener">Liquid官方文档</a> </li><li><a href="http://jekyllrb.com/docs/pagination/" target="_blank" rel="noopener">Jekyll关于pagination说明</a> </li><li><a href="https://middlemanapp.com/basics/install/" target="_blank" rel="noopener">Middleman官方文档</a> </li><li><a href="https://www.reddit.com/r/Jekyll/comments/3uhk2j/cant_get_pagination_working_with_jekyll_301/" target="_blank" rel="noopener">Jekyll中paginate与permalink的冲突</a> </li><li><a href="http://blog.virtuacreative.com.br/upgrade-jekyll-2-to-3-gh-pages.html" target="_blank" rel="noopener">Upgrading Jekyll 2 to 3 on GitHub Pages</a> </li><li><a href="https://gist.github.com/jeromechan/0432a89f3e9810e9ba26052606e56f38" target="_blank" rel="noopener">How to use slim engine for building html tags?</a> </li></ul>]]></content>
<summary type="html">
“middleman实践分析及其简明教程”
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="middleman" scheme="https://jinlongchen.com/tags/middleman/"/>
<category term="jekyll" scheme="https://jinlongchen.com/tags/jekyll/"/>
</entry>
<entry>
<title>学习笔记:项目管理之规划项目</title>
<link href="https://jinlongchen.com/posts/2016/07/01/how-to-make-a-project-plan/"/>
<id>https://jinlongchen.com/posts/2016/07/01/how-to-make-a-project-plan/</id>
<published>2016-06-29T16:00:00.000Z</published>
<updated>2020-07-02T18:56:45.503Z</updated>
<content type="html"><![CDATA[<h3 id="一、回顾基础知识"><a href="#一、回顾基础知识" class="headerlink" title="一、回顾基础知识"></a>一、回顾基础知识</h3><ul><li>项目经理:把控“完成”的含义,并带领团队完成项目的人;</li><li>项目的关键驱动因素、约束和浮动因素;</li><li>传统铁三角:成本 + 时间 + 质量/范围;</li><li>通过沟通发掘决定项目的关键驱动因素;</li><li>少用“为什么”,避免“怎么做”,项目如何才算成功;</li><li>探讨制定项目章程,有助于凝聚团队;</li><li>从“摩尔鸿沟”表象,看项目、团队发展历程。</li></ul><h3 id="二、引导语"><a href="#二、引导语" class="headerlink" title="二、引导语"></a>二、引导语</h3><blockquote><p>“项目规划是在不断进行的,这只是开始。”</p></blockquote><h3 id="三、纲要"><a href="#三、纲要" class="headerlink" title="三、纲要"></a>三、纲要</h3><ul><li>如何踏上项目征程</li><li>讲解开发项目规划模版条目</li><li>项目如何才适合发布上线</li><li>发布条件的管理</li><li>最后的几句总结</li></ul><h3 id="四、辨析两句短语"><a href="#四、辨析两句短语" class="headerlink" title="四、辨析两句短语"></a>四、辨析两句短语</h3><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673084562094.jpg" alt=""></p><h3 id="五、使项目踏上征程"><a href="#五、使项目踏上征程" class="headerlink" title="五、使项目踏上征程"></a>五、使项目踏上征程</h3><ul><li>规划不必完美无缺,实际上这也是做不到的;</li><li>采用时间盒子的方式启动项目的规划;</li><li>要根据经验而不是预言来规划项目。</li></ul><blockquote><p>“规划毫无用处,但是制定规划必不可少。”<br>——艾森 · 豪威尔<br><img src="/images/2016-06-30-how-to-make-a-project-plan/14673086699212.jpg" alt=""></p></blockquote><h3 id="六、开发项目规划模版"><a href="#六、开发项目规划模版" class="headerlink" title="六、开发项目规划模版"></a>六、开发项目规划模版</h3><p><em>下面介绍一些常用的模版条目 …</em></p><ol><li><p>产品意图</p></li><li><p>发布历史记录</p></li><li><p>发布条件</p></li><li><p>项目目标、时间盒子目标</p></li><li><p>团队职责、项目运作的组织</p></li><li><p>项目日程总览</p><ul><li><p>样式1</p><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673089688628.jpg" alt=""></p></li><li><p>样式2</p><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673090092803.jpg" alt=""></p></li><li><p>样式3</p><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673090209493.jpg" alt=""></p></li></ul></li><li><p>人员配备</p></li><li><p>建议日程</p><ul><li><p>样式1</p><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673090942241.jpg" alt=""></p></li><li><p>样式2</p><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673091056258.jpg" alt=""></p></li></ul></li></ol><blockquote><p>温馨提示:小心过早细化日程<br><img src="/images/2016-06-30-how-to-make-a-project-plan/14673091416870.jpg" alt=""></p></blockquote><p>9.制定项目风险列表<br><img src="/images/2016-06-30-how-to-make-a-project-plan/14673092036240.jpg" alt=""></p><h3 id="七、制定发布条件"><a href="#七、制定发布条件" class="headerlink" title="七、制定发布条件"></a>七、制定发布条件</h3><blockquote><p>发布条件可以告诉我们项目“完成”的含义。</p></blockquote><h3 id="八、拟定步骤"><a href="#八、拟定步骤" class="headerlink" title="八、拟定步骤"></a>八、拟定步骤</h3><ol><li>确定当前发布的最重要的因素(包含关键驱动因素)</li><li>草拟发布条件</li><li>修订发布条件,使其符合SMART原则</li><li>达成多方共识</li></ol><h3 id="九、SMART原则"><a href="#九、SMART原则" class="headerlink" title="九、SMART原则"></a>九、SMART原则</h3><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673093012558.jpg" alt=""></p><h3 id="十、使用发布条件"><a href="#十、使用发布条件" class="headerlink" title="十、使用发布条件"></a>十、使用发布条件</h3><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673093575826.jpg" alt=""></p><h3 id="十一、关键知识点"><a href="#十一、关键知识点" class="headerlink" title="十一、关键知识点"></a>十一、关键知识点</h3><ul><li>项目规划实在不断进行的,这只是开始;</li><li>为项目团队、出资人和项目经理自己制定发布条件,以明确定义“完成”的含义;</li><li>项目规划不必完美无瑕,但是它必须存在。</li></ul><h3 id="十二、结束语"><a href="#十二、结束语" class="headerlink" title="十二、结束语"></a>十二、结束语</h3><p><img src="/images/2016-06-30-how-to-make-a-project-plan/14673094267010.jpg" alt=""></p>]]></content>
<summary type="html">
<h3 id="一、回顾基础知识"><a href="#一、回顾基础知识" class="headerlink" title="一、回顾基础知识"></a>一、回顾基础知识</h3><ul>
<li>项目经理:把控“完成”的含义,并带领团队完成项目的人;</li>
<li>项目的
</summary>
<category term="管理哲学" scheme="https://jinlongchen.com/categories/%E7%AE%A1%E7%90%86%E5%93%B2%E5%AD%A6/"/>
<category term="项目管理" scheme="https://jinlongchen.com/tags/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
<category term="project manage" scheme="https://jinlongchen.com/tags/project-manage/"/>
<category term="规划项目日程" scheme="https://jinlongchen.com/tags/%E8%A7%84%E5%88%92%E9%A1%B9%E7%9B%AE%E6%97%A5%E7%A8%8B/"/>
</entry>
<entry>
<title>Git软件开发过程</title>
<link href="https://jinlongchen.com/posts/2015/11/16/work-in-git/"/>
<id>https://jinlongchen.com/posts/2015/11/16/work-in-git/</id>
<published>2015-11-16T01:51:05.000Z</published>
<updated>2020-07-02T18:56:45.503Z</updated>
<content type="html"><![CDATA[<h4 id="一、关于Git与Subversion的区别"><a href="#一、关于Git与Subversion的区别" class="headerlink" title="一、关于Git与Subversion的区别"></a>一、关于Git与Subversion的区别</h4><p><a href="/images/2015-11-16-work-in-git/DraggedImage1.png"><img src="/images/2015-11-16-work-in-git/DraggedImage1-1024x1013.png" alt="DraggedImage"></a></p><h4 id="二、目前我们用Subversion是怎么执行软件过程的"><a href="#二、目前我们用Subversion是怎么执行软件过程的" class="headerlink" title="二、目前我们用Subversion是怎么执行软件过程的"></a>二、目前我们用Subversion是怎么执行软件过程的</h4><p><a href="/images/2015-11-16-work-in-git/DraggedImage-1.png"><img src="/images/2015-11-16-work-in-git/DraggedImage-1-1024x437.png" alt="DraggedImage-1"></a></p><h4 id="三、优势与缺点"><a href="#三、优势与缺点" class="headerlink" title="三、优势与缺点"></a>三、优势与缺点</h4><ol><li>架构</li></ol><pre><code>* Git:分布式,所有的teammates本地可以clone一份独立完整的仓库,而不仅仅是某一个版本的镜像拷贝;开发者可以在本地clone仓库中完成所有vcs的操作,只有当需要协同工作提交代码到远程仓库的时候,才需要联上网络。* Subversion:中央集中式,所有的teammates都面向同样一个远程仓库工作;checkout出来的本地工作区代码只是远程仓库某一版本的一份镜像拷贝。</code></pre><ol start="2"><li>仓库结构与URL</li></ol><pre><code>* Git:对于Git而言,仓库会独立于开发者的本地磁盘中,在仓库的根目录中只包含了一个”.git”文件夹,所有的branches、trunk(PS:git中名称为master)、tags均是通过命令操作而生成的,并非通过URL路径。</code></pre><p>在Git中,URL类似于ssh://git@example.com/path/to/git-repo.git,仅仅是指向了仓库的一个标识。</p><pre><code>* Subversion:分支的url类似于svn+ssh://svn@example.com/svn/trunk,每一个分支独占一个唯一URL,每一个URL都会直接定位到每一个分支在远程仓库中的路径位置。</code></pre><p>对于Subversion,会有一个trunk分支作为开发主线分支,会有很多branches分支作为并行分支,tags则是mark上某一特定的发布版本。</p><ol start="3"><li>分支管理</li></ol><pre><code>* Git:Git分支相对于其他的vcs是非常不一样的设计理念,一个Git分支仅仅指的是指向某一确定版本的简单指针,因此,Git的分支是无拷贝、无新建目录、几乎无开销的。* Subversion:正如我们所知道的,SVN中的分支仅仅是项目的一份拷贝,是一个具有特殊含义的普通文件夹;多分支则是多文件夹的形式。</code></pre><ol start="4"><li>提交操作</li></ol><pre><code>* Git:若你使用的是Git,你的提交操作不受网络的影响,你的提交仅仅影响了本地仓库,仅当你需要于远端仓库同步内容之时,才需要使用到网络;</code></pre><p>另外的,在你本地仓库还存在一个so-called Staging Area,并非你的所有文件需要在一次提交中全部commit,你可以选择指定的变更放入staging area中,从而在本次提交中仅仅包含你所选定的变更条目;<br>关于Git的版本号,大家都知道,Git是分布式的vcs,要想和svn、cvs一般生成revision#5,revision#6类似的递增数值作为唯一版本号是不可取的,但是我们也同样需要一个唯一的标识来辨别每一次提交,而Git的做法是使用了”commit hashes”。</p><pre><code>* Subversion:当你使用的是SVN,假设你要提交代码,以下是你的提交过程: * 首先设备必须是联网的,可以与远端中央仓库建立连接; * 将提交的内容立即传输到远端中央仓库; * 远端中央仓库生成递增的版本号,并赋予本地分支。</code></pre><ol start="5"><li><p>协同工作</p><ul><li>Git:若你使用的是Git,你需要决定何时将你的本地仓库的内容同步上传到远端仓库分享出来,而Git不会为你作任何的自动上传的操作;这样子的分享过程相对于其他的中央仓库式的vcs系统来说是更加安全的,所发生的冲突也只会发生于你的本地(仓库)而非远端服务器的仓库,这将更能帮助你规避打乱teammates工作内容冲突的风险。</li><li>Subversion:当你将本地分支内容作commit操作之时,你的内容便会分享到远端中央仓库中,其他teammates也都能同步到你所提交的内容。</li></ul></li></ol><h4 id="四、集成工具简介"><a href="#四、集成工具简介" class="headerlink" title="四、集成工具简介"></a>四、集成工具简介</h4><ol><li>SourceTree:开源的Git源代码管理工具</li></ol><ol start="2"><li>TortoiseGit:开源的Git源代码管理工具</li></ol><ol start="3"><li>EGit:Eclipse插件,最新Mars版本已经自带</li></ol><ol start="4"><li>Gitflow Nightly:Eclipse插件,支持Git-Flow</li></ol><h4 id="五、常用Git基础知识"><a href="#五、常用Git基础知识" class="headerlink" title="五、常用Git基础知识"></a>五、常用Git基础知识</h4><blockquote>认识Git的几个关键目录</blockquote><p><a href="/images/2015-11-16-work-in-git/DraggedImage-2.png"><img src="/images/2015-11-16-work-in-git/DraggedImage-2.png" alt="DraggedImage-2"></a></p><ul><li>Working directory:工作区</li></ul><ul><li>Index directory:暂存区</li></ul><ul><li>Local repository:本地仓库</li></ul><ul><li>Remote repository:远端仓库</li></ul><blockquote>常用的Git操作</blockquote><p><a href="/images/2015-11-16-work-in-git/134623_LGJb_1469576.png"><img src="/images/2015-11-16-work-in-git/134623_LGJb_1469576.png" alt="134623_LGJb_1469576"></a></p><ol><li>clone:克隆项目到本地工作区,类似svn checkout</li></ol><ol start="2"><li>checkout:创建/切换本地仓库的指定分支到工作区中</li></ol><ol start="3"><li>commit:将本地工作区代码提交到本地仓库</li></ol><ol start="4"><li>push:将本地仓库代码同步到远端仓库</li></ol><ol start="5"><li>pull / fetch:将远端仓库的代码同步到本地仓库/工作区</li></ol><pre><code>* pull:fetch + merge,该操作会影响工作区* fetch:从远端仓库获取并更新到本地仓库中,不影响工作区</code></pre><ol start="6"><li>merge / rebase:从指定分支(<em>PS:分支名称常跟在命令之后</em>)中获取更新并合并到当前分支</li></ol><pre><code>* merge:* rebase:</code></pre><ol start="7"><li>stash:备份/唤出当前的现场状态(包含工作区和暂存区)</li></ol><pre><code>* git stash [save -a “msg”] 备份当前的现场状态* git stash list 显示已保存的现场状态列表* git stash pop/apply [--index][<stash>] 恢复工作状态,若不含带参数,则从状态栈中获取最新的。pop在获取完成后,从栈中移除该状态,apply则不会从栈中移除* git stash clear 清空状态栈中的所有内容* git drop 删除状态栈中的指定状态</code></pre><h4 id="六、可供参考的高阶应用方案"><a href="#六、可供参考的高阶应用方案" class="headerlink" title="六、可供参考的高阶应用方案"></a>六、可供参考的高阶应用方案</h4><blockquote>什么是Git-SVN的扩展开发模式,即本地开发应用Git的强大分支特性,当最终push操作的时候,目标仓库设定为SVN远端仓库。这里点到为止,只提及一下,以便有既想使用Git又纠结无法脱离Subversion的开发者去使用,这确实是一种很赞的“曲线救国”方案。<p>什么是Git Stash的开发模式,即一个工程师可以并行开发多项内容,要求用到切换分支的操作,而在没有提交到本地仓库之前,可以使用git stash命令将当前分支的工作区和暂存区的状态镜像下来。当回过头来需要继续开发的时候,使用git stash pop将指定的状态唤出后,可以继续未完成的内容。</blockquote></p><h4 id="七、Git-Flow介绍"><a href="#七、Git-Flow介绍" class="headerlink" title="七、Git-Flow介绍"></a>七、Git-Flow介绍</h4><blockquote>一图胜过千言万语</blockquote><p><a href="/images/2015-11-16-work-in-git/DraggedImage-3.png"><img src="/images/2015-11-16-work-in-git/DraggedImage-3.png" alt="DraggedImage-3"></a></p><blockquote>关键几个分支的概念全解</blockquote><ul><li>主分支</li></ul><pre><code>* branch:保存当前开发成果的分支* master:保存当前可供生产部署的代码,在每次发布之时推荐为每次新增发布的代码都打上一个TAG,供后续代码维护使用</code></pre><ul><li>辅助分支</li></ul><pre><code>* Feature:开发完整功能、新特性,从develop分支发起的分支* Release:用于发布新的产品版本而设计的,支持从develop分支派生* Hotfix:属于计划外创建的可供生产部署的代码分支,普遍场景是软件遇到了异常情况或发生了严重必须要立即修复的缺陷之时。支持从master分支(或者其中的某一个TAG版本)中派生出来</code></pre><ul><li>分支命名惯例</li></ul><pre><code>* Feature分支:feature-** Release分支:release-** Hotfix分支:hotfix-*</code></pre><h4 id="八、GitHub-Flow"><a href="#八、GitHub-Flow" class="headerlink" title="八、GitHub Flow"></a>八、GitHub Flow</h4><p><a href="/images/2015-11-16-work-in-git/DraggedImage-4.png"><img src="/images/2015-11-16-work-in-git/DraggedImage-4.png" alt="DraggedImage-4"></a></p><h4 id="九、Mike-Flow-Base-on-《Git-in-Practice》"><a href="#九、Mike-Flow-Base-on-《Git-in-Practice》" class="headerlink" title="九、Mike Flow (Base on 《Git in Practice》)"></a>九、Mike Flow (Base on 《Git in Practice》)</h4><blockquote>Single Pattern</blockquote><p><a href="/images/2015-11-16-work-in-git/DraggedImage-5.png"><img src="/images/2015-11-16-work-in-git/DraggedImage-5.png" alt="DraggedImage-5"></a></p><blockquote>Multiple Pattern</blockquote><p><a href="/images/2015-11-16-work-in-git/DraggedImage-6.png"><img src="/images/2015-11-16-work-in-git/DraggedImage-6.png" alt="DraggedImage-6"></a></p><h4 id="十、Jerome-Flow-I-call-it-this-name"><a href="#十、Jerome-Flow-I-call-it-this-name" class="headerlink" title="十、Jerome Flow (I call it this name^_^)"></a>十、Jerome Flow (I call it this name^_^)</h4><ul><li>Version 1: 该版本适合于团队成员较小,各个泳道分支规范制定严格的项目。</li></ul><p><img src="/images/2015-11-16-work-in-git/jerome-git-flow1.jpg" alt="jerome-git-flow-1"></p><ul><li>Version 2:适合于多环境分支、多团队协作的项目,没有太多的规范约束,将最大的自由度释放给开发者们。</li></ul><p><img src="/images/2015-11-16-work-in-git/14781398459686.jpg" alt="jerome-git-flow-2"></p><h4 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a><em>参考资料</em></h4><ul><li><a href="http://nvie.com/posts/a-successful-git-branching-model/" target="_blank" rel="noopener">A successful Git branching model - Vincent Driessen</a></li></ul><ul><li><a href="http://www.ituring.com.cn/article/56870" target="_blank" rel="noopener">基于git的源代码管理模型-Git-Flow</a></li></ul><ul><li><a href="http://boxysystems.com/index.php/5-fundamental-differences-between-git-svn/" target="_blank" rel="noopener">5 Fundamental differences between GIT & SVN</a></li></ul><ul><li><a href="http://stackoverflow.com/questions/16666089/whats-the-difference-between-git-merge-and-git-rebase" target="_blank" rel="noopener">What’s the difference between ‘git merge’ and ‘git rebase’?</a></li></ul><ul><li><a href="http://blog.jobbole.com/84664/" target="_blank" rel="noopener">Git的merge和rebase </a></li></ul><ul><li><a href="http://blog.csdn.net/rryqsh/article/details/8230560" target="_blank" rel="noopener">Git中分支merge和rebase的适用场景及区别</a></li></ul><ul><li><a href="http://www.infoq.com/cn/git-adventures" target="_blank" rel="noopener">InfoQ - Git历险记</a></li></ul><ul><li><a href="http://www.oschina.net/translate/10-tips-git-next-level?cmp" target="_blank" rel="noopener">10 Tips to Push Your Git Skills to the Next Level</a></li></ul><ul><li><a href="http://book.douban.com/subject/26107548/" target="_blank" rel="noopener">《Git in Practice》-Manning Press - Mike McQuaid</a></li></ul><ul><li><a href="http://www.infoq.com/articles/interview-Mike-McQuaid-git-practice" target="_blank" rel="noopener">Interview with Mike McQuaid about Git in Practice</a></li></ul><ul><li><a href="http://www.oschina.net/translate/git-conventions?cmp" target="_blank" rel="noopener">GIT Conventions</a></li></ul><ul><li><a href="http://www.oschina.net/question/565065_68194" target="_blank" rel="noopener">git使用简介</a></li></ul><ul><li><a href="http://www.git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging" target="_blank" rel="noopener">Git-Branching-Basic-Branching-and-Merging</a></li></ul><ul><li><a href="http://www.git-scm.com/book/en/v2/Git-Branching-Rebasing" target="_blank" rel="noopener">Git-Branching-Rebasing</a></li></ul><ul><li><a href="http://www.git-tower.com/learn/git/ebook/mac/appendix/from-subversion-to-git" target="_blank" rel="noopener">Switching from Subversion to Git</a></li></ul>]]></content>
<summary type="html">
<h4 id="一、关于Git与Subversion的区别"><a href="#一、关于Git与Subversion的区别" class="headerlink" title="一、关于Git与Subversion的区别"></a>一、关于Git与Subversion的区别</
</summary>
<category term="工具环境" scheme="https://jinlongchen.com/categories/%E5%B7%A5%E5%85%B7%E7%8E%AF%E5%A2%83/"/>
<category term="Git" scheme="https://jinlongchen.com/tags/Git/"/>
<category term="git flow" scheme="https://jinlongchen.com/tags/git-flow/"/>
<category term="github flow" scheme="https://jinlongchen.com/tags/github-flow/"/>
<category term="git流程" scheme="https://jinlongchen.com/tags/git%E6%B5%81%E7%A8%8B/"/>
<category term="git软件开发过程" scheme="https://jinlongchen.com/tags/git%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B/"/>
<category term="mike flow" scheme="https://jinlongchen.com/tags/mike-flow/"/>
</entry>
<entry>
<title>研发人看互联网营销</title>
<link href="https://jinlongchen.com/posts/2015/06/18/internet-marketing-views-of-developers/"/>
<id>https://jinlongchen.com/posts/2015/06/18/internet-marketing-views-of-developers/</id>
<published>2015-06-17T08:24:40.000Z</published>
<updated>2020-07-02T18:56:45.503Z</updated>
<content type="html"><![CDATA[<p>很荣幸地,昨天参加了公司市场VP(以下简称FW)面向全公司作的关于互联网营销的公开演讲。这些年来,公司的市场营销方面从一个局外人的角度上看来确实发生了非常大的变化、进步和飞跃,加以是FW在公司层面公开分享的为数不多的干货讲座之一,满怀期待,同时感触良多,希望写下来以作为后面需要时作参考。</p><p>文章内容准备分为两部分进行记录,第一部分阐述一下的讲座的具体内容,从中挑选出一些自己觉得非常干货的关键点详细撰写一下。第二部分将从讲座中的关键点出发,映射到研发的日常工作上来,分析如何将其中的策略应用到研发工作上来,帮助提高程序员以致于团队层面上的工作效率(注:后续将补发第二部分的博文作扩展分析,请期待~)。</p><h4 id="一、学习能力"><a href="#一、学习能力" class="headerlink" title="一、学习能力"></a>一、学习能力</h4><p>作为从原研发负责人的角色变为市场部门负责人的角色,FW是非常成功的一个案例,当中FW提到了自己再三强调的一点——学习能力。一个人拥有强有力的学习能力,他将成为或者正走在成为胜利者的路上。有的人虽然在十几年的象牙塔学习生活中并不是最优秀的,但是有一点如果做到了,那么你就是值得被社会所肯定的,那就是“笨鸟先飞”“勤能补拙”“早起的鸟儿有虫吃”。</p><h4 id="二、拥抱变化"><a href="#二、拥抱变化" class="headerlink" title="二、拥抱变化"></a>二、拥抱变化</h4><p>可能是因为这四个字作为公司里经常秉承的工作态度吧,季节在变化,时代在变化,客户群在变化,消费态度在变化,总而言之,人们是贪婪的,心中的需求将会永无止境地在发生着变化。虽然中学时候老师们都教育我们要学会对生活满足,但是对于有更为远大理想的人们,这句话可以也应该被改变。</p><p>年轻人,抱怨多了总是不好的,假设生活给了你不能承受之轻,不要悲伤,不要心急,要坦然地去面对和处理好,这也将毫无疑问地将帮助你更快地踏上成功之路。看到这里,是不是觉得很鸡汤,不过虽然鸡汤,但是道理却还是道理。</p><h4 id="三、抓住大面,傍大款"><a href="#三、抓住大面,傍大款" class="headerlink" title="三、抓住大面,傍大款"></a>三、抓住大面,傍大款</h4><p>看着这字眼挺晕的对吧,其实当时作者我也晕。不过听完之后就豁然开朗了。</p><p>什么是抓住大面?大面,指的是关键点,主要方面,或者说是痛点。任何一件事情,总有这么一个或者几个举足轻重的关键点,就像武侠小说中提及的命门、致命穴道。只要将这一点抓住了,就等于抓住了所有。其中当然,包含了一定方面的避轻就重的处事思路。</p><p>那么什么是傍大款?傍大款 == 抱大腿?是的。不过关键在于,你要找到合适的大腿。与携程的合作是傍大款,与京东的合作是傍大款,请林志颖Kimi代言是傍大款,冠名江苏卫视非诚勿扰节目是傍大款…</p><p>还有许许多多的类似案例,至于如果你需要确切的文字上的含义来解释傍大腿是什么,那么建议你去百度吧,这篇文章不会去解释这一点。</p><h4 id="四、单点营销推广,打穿打透,增加压强"><a href="#四、单点营销推广,打穿打透,增加压强" class="headerlink" title="四、单点营销推广,打穿打透,增加压强"></a>四、单点营销推广,打穿打透,增加压强</h4><p>无论是在何种渠道上投放广告或者作品牌宣传,都要打透吃透。什么是打透吃透?无论在推广时间上、广告类型上、还是用户群的覆盖面上,都要做到预期的层面,抓住关键点,甚至更好的是将这一关键点做到极致。</p><h4 id="五、线上线下整合"><a href="#五、线上线下整合" class="headerlink" title="五、线上线下整合"></a>五、线上线下整合</h4><p>对于当下互联网环境因素决定,在互联网企业中,不能仅仅满足于在互联网的传播上做到脍炙人口,这一点是不够的,还需要在线下的日常生活中继续延伸,使得家喻户晓,人人皆知。只有线上线下整体综合起来,才能更好更为全面地覆盖客户群体,缺了任一方面都是不完整的推广方案。</p><h4 id="六、价格战"><a href="#六、价格战" class="headerlink" title="六、价格战"></a>六、价格战</h4><p>这里要说的不是推荐大家一味地在和竞争对手拼低廉价格竞品,而是说,我们不仅要关注产品的性价比高,便宜,更要让客户知道我们是真的便宜,并不是噱头。</p><h4 id="七、找合适的渠道或者实物作为宣传载体,把推广的理念-概念投射出去"><a href="#七、找合适的渠道或者实物作为宣传载体,把推广的理念-概念投射出去" class="headerlink" title="七、找合适的渠道或者实物作为宣传载体,把推广的理念/概念投射出去"></a>七、找合适的渠道或者实物作为宣传载体,把推广的理念/概念投射出去</h4><p>江苏卫视非诚勿扰节目是渠道,林志颖Kimi代言是载体,一个覆盖了客厅互联网用户群体,一个覆盖了亲子旅游的概念,两者都是精挑细选出来最为合适,性价比最好的推广载体/渠道。寻找这一类载体之前,要从各个方面收集关联信息作对比,或者请专业调研机构帮助统计与分析,不建议决策者个人的喜好来决定推广选型。</p><p>为什么是林志颖Kimi?其中缘由可以概括为俩字:“够火”。</p><p>为什么是江苏卫视非诚勿扰?其中缘由可以概括为仨字:“够普及”。</p><p>另外一点需要提示的是,选定的载体或者渠道,一定要与本身产品、企业文化、宣传理念相符合,这一点是决然不能摒弃的。</p><h4 id="八、口号一定要喊,喊多了自然就是真的"><a href="#八、口号一定要喊,喊多了自然就是真的" class="headerlink" title="八、口号一定要喊,喊多了自然就是真的"></a>八、口号一定要喊,喊多了自然就是真的</h4><p>这一点觉得没必要详细阐述,懂行的人,一看就懂了。可能办法瞅着比较土,但是应用起来还是实在的。什么“引导者”“领航品牌”神马的等等,都是需要喊的,当然,这仍然是要具备基本的统计数据支撑,不能瞎喊,要找准了痛点喊。</p><h4 id="九、简单粗暴可能是某些情况下最为合适的"><a href="#九、简单粗暴可能是某些情况下最为合适的" class="headerlink" title="九、简单粗暴可能是某些情况下最为合适的"></a>九、简单粗暴可能是某些情况下最为合适的</h4><p>推广方式不一定要多么地复杂,要求每次都具备多么地技术含量。这里并不是推崇没有技术含量的手段,这里想要说明的是,除了技术含量外,我们更要着眼于要点,击中营销推广的“要害”,简单粗暴,不择手段,达到目的的办法,就是当下最好、最有效的方法。如果一味地追求高技术含量,但是却低产能的办法,那么这些办法舍弃也罢。</p><h4 id="十、产品即营销,产品的质量仍然是最重要的,没有之一"><a href="#十、产品即营销,产品的质量仍然是最重要的,没有之一" class="headerlink" title="十、产品即营销,产品的质量仍然是最重要的,没有之一"></a>十、产品即营销,产品的质量仍然是最重要的,没有之一</h4><p>无论是何种办法,无论得到什么样子的推广效果,要想脍炙人口,人人信赖,忠诚于你的产品,产品的高质量保证是务必不可松懈的。只有好的产品才值得被推广,只有高质量的产品才能更好地被推广,被消费者所接受。</p><h4 id="十一、口碑品牌在OTA行业尤其重要"><a href="#十一、口碑品牌在OTA行业尤其重要" class="headerlink" title="十一、口碑品牌在OTA行业尤其重要"></a>十一、口碑品牌在OTA行业尤其重要</h4><p>旅游产品不同于一般的实体产品,不像iPhone,不像小米手机,不像乐视电视。旅游产品是虚拟存在的,在出游之前客人都将看不见摸不着,只收到一条出游通知的短信,只能客人自己YY想象。如果产品服务口碑不好,很难让消费者掏腰包下单消费;如果企业口碑不好,很难得到消费者的光临和信赖。旅游产品是体验式产品,口口相传的口碑式传播是非常重要的。</p><h4 id="十二、营销社会化"><a href="#十二、营销社会化" class="headerlink" title="十二、营销社会化"></a>十二、营销社会化</h4><p>无论是正面积极的营销手段,还是有意无意中收获的在可控范围内的消极影响,从营销推广上看来,都是具有一定积极意义的。营销的PR老大就好比战场上渴望着打仗的将军,如果没有战役,那么这位将军将是孤独的和寂寞的。其中提及的2015年发生的17家供应商联名抵制的案例便是这一内容的参考案例。</p><h4 id="十三、擦亮品牌,全员营销"><a href="#十三、擦亮品牌,全员营销" class="headerlink" title="十三、擦亮品牌,全员营销"></a>十三、擦亮品牌,全员营销</h4><p>一个人的力量是有限的,但是假设是几个人、一群人、一大伙人、甚至于所有人都在为公司的产品品牌作有意无意地、潜移默化地在亲朋好友间宣传,那么力量可以相信是不可估量的。</p><h4 id="十四、追求不会停止"><a href="#十四、追求不会停止" class="headerlink" title="十四、追求不会停止"></a>十四、追求不会停止</h4><p>途牛会继续在品牌塑造的道路上继续迈步前进,在2015年的夏秋交际,依然会有重磅举措,鉴于机密考虑,各位请继续关注我们,你会发现我们会让你们继续惊讶惊叫着。</p><p>最后,6.16是FW的生日,这里也同时给他送上一份衷心的祝福,Happy birthday to FW ALM!</p>]]></content>
<summary type="html">
<p>很荣幸地,昨天参加了公司市场VP(以下简称FW)面向全公司作的关于互联网营销的公开演讲。这些年来,公司的市场营销方面从一个局外人的角度上看来确实发生了非常大的变化、进步和飞跃,加以是FW在公司层面公开分享的为数不多的干货讲座之一,满怀期待,同时感触良多,希望写下来以作为后面
</summary>
<category term="管理哲学" scheme="https://jinlongchen.com/categories/%E7%AE%A1%E7%90%86%E5%93%B2%E5%AD%A6/"/>
<category term="互联网市场" scheme="https://jinlongchen.com/tags/%E4%BA%92%E8%81%94%E7%BD%91%E5%B8%82%E5%9C%BA/"/>
<category term="互联网营销" scheme="https://jinlongchen.com/tags/%E4%BA%92%E8%81%94%E7%BD%91%E8%90%A5%E9%94%80/"/>
<category term="推广" scheme="https://jinlongchen.com/tags/%E6%8E%A8%E5%B9%BF/"/>
<category term="研发人观点" scheme="https://jinlongchen.com/tags/%E7%A0%94%E5%8F%91%E4%BA%BA%E8%A7%82%E7%82%B9/"/>
</entry>
<entry>
<title>走进社会生活,在实践中成长</title>
<link href="https://jinlongchen.com/posts/2015/06/16/practice-in-senior-school-period/"/>
<id>https://jinlongchen.com/posts/2015/06/16/practice-in-senior-school-period/</id>
<published>2015-06-16T04:11:24.000Z</published>
<updated>2020-07-02T18:56:45.502Z</updated>
<content type="html"><![CDATA[<blockquote>接下去的文字是自己高中时期跟随社会实践导师们和一个team的同学们一起完成的旱情调查报告原文。由于时间已经过去多年了,在互联网上已经几乎找不到了。这里发出来分享给大家,顺带也怀念/纪念一下已经逝去的中学年华。</blockquote><h4 id="――“儋州市旱情调查”研究性学习活动工作小结"><a href="#――“儋州市旱情调查”研究性学习活动工作小结" class="headerlink" title="――“儋州市旱情调查”研究性学习活动工作小结"></a>――“儋州市旱情调查”研究性学习活动工作小结</h4><p>海南省儋州市那大中学“儋州市旱情调查”小组</p><p>指导教师: 曾良 郑培生 陈美娟</p><p>2004年以来,海南省遭受到50年来少有的干旱,儋州市作为海南省人口最多的市县和全国农业百强县,农业生产和人民生活均不同程度地受到旱情的影响。为了解儋州市旱情,分析干旱产生的原因及影响,为学校、社会抗旱避灾提出相关建议,培养创新精神和实践能力,提高学生综合素质,2005年3月4日至30日,活动小组72名同学在3名指导教师指导下走进社会生活,到儋州市全部17个乡镇、44个村庄和海南省松涛水利工程管理局、儋州市水务局、儋州市农业局、儋州市气象局等部门进行调查,在实践中积累了大量的材料,撰写了13篇《小组调查报告》和50多篇《个人感想》,分别向学校、政府相关部门提出5点建议和6点建议,向社会发出1000份《节约用水,共抗旱灾》倡议书。这次活动的部分成果在校内外展出后,在社会上引起极大的反响,《海南日报》和儋州市电视台先后对此次活动进行报道。</p><p>一、活动的选题目的和活动特点</p><p>1、这次活动具有较强的针对性。2004年以来,海南省遭受到50年来最严重的旱灾。作为海南省人口最多,面积最大的市(县)和全国农业百强市(县),儋州市的旱情牵动着全市人民的心。海南西部和北部工农业、居民生活用水的源头,也是海南最大的水库――松涛水库就坐落在儋州市和白沙县、琼中县交界处,海南松涛水利工程管理局就设在儋州市政府所在地――那大镇,松涛水库管理区设在儋州市南丰镇。</p><p>2、这次活动具有较强的科学性。在这次活动的过程中,学生在教师的指导下注意学习和运用科学研究的方法进行调查,大多数学生已经初步掌握问卷调查法、访谈调查法等研究方法,并在活动中运用上网查询、查阅文献、访谈当地的专家学者、实地考察等方式进行调查,对资料的整理、分析,形成调查报告等也基本符合科学研究的要求。各活动小组共撰写了13份小组调查报告。活动小组运用相关学科知识,从客观、人为两大角度,从地球环境、气候、社会经济发展、生活习惯等方面科学地分析干旱产生的原因。</p><p>3、这次活动参加学生多,调查面广。活动小组是在同学们自愿报名的基础上成立的,有72名学生参加。到儋州市全部17个乡镇、44个村庄和海南省松涛水利工程管理局、儋州市水务局、儋州市农业局、儋州市气象局等部门进行调查,调查面涉及儋州市农业、生活用水情况、松涛水库的蓄水情况、人们对干旱的认识、干旱产生的原因、政府社会抗旱救灾的措施等。</p><p>4、这次活动凸显教育性。在这次活动中,学生们对儋州市乃至海南的旱情表现出前所未有的关注,并反思生活上用水习惯,这一点他们所撰写的《小组调查报告》和《个人感想》中体现得尤为突出,他们还行动起来,共同向全校师生发生倡议,并走上街头向市民发放1000份《节约用水,抗击旱灾》倡议书,并积极在市政府所在地――那大镇上进行多种形式的宣传。在活动过程中,学生的团体精神和合作意识得到锻炼和培养。</p><p>5、这次活动社会影响大。活动小组的活动在全校的广大师生中引起了强烈的震撼,许多学生通过学校校园网等途径对节约用水、保护水资源进行呼吁。活动小组所到之处,得到各乡镇领导、村民的大力支持和帮助,在社会上引起广泛的关注。活动小组的部分成果在校内外的展出及向全体同学向全市人民发出的倡议书在市民中极大的反响,《海南日报》、儋州电视台等媒体先后对这次活动进行报道。</p><p>二、活动的内容</p><p>1、科学调查</p><p>①调查儋州各乡镇旱情,特别是各乡镇农业受旱情况和居民用水情况</p><p>②调查这次旱情发生的原因</p><p>③调查儋州市农业生产、农民的生活受到旱情的影响情况</p><p>④调查儋州市90万市民的生活受干旱的影响情况</p><p>⑤调查当前儋州市抗旱形势</p><p>2、自我反思</p><p>①撰写活动感受</p><p>②小组讨论、交流活动的感受</p><p>③各小组撰写调查报告</p><p>3、宣传教育</p><p>①展示调查成果,进一步扩大活动的影响</p><p>②向社会发出倡议书,向学校、政府提出建议</p><p>三、活动的实施过程</p><p>(一)活动的准备阶段(3月4日-3月18日)</p><p>1、活动方案的制定。新学期开学后第一个周(3月4日),在广泛征求师生意见的基础上,儋州市那大中学“儋州市旱情调查”研究性学习活动小组成立,并制定活动方案。(《“儋州市旱情调查”研究性学习活动实施方案》见附件1)</p><p>2、确定小组成员、分组。在学生自愿报名的基础上,共确定了72名学生成为小组的成员,并划分为18个小组(按乡镇和部门划分,17个乡镇分成17个小组,松涛水利工程管理局和市政府相关部门为部门组),明确实地调查的主要任务是调查各乡镇的旱情,从各部门了解、调查旱情及相关原因、相关抗旱措施。</p><p>3、加强辅导,做好活动的各项准备工作和安全教育工作。</p><p>3月12日 召开活动小组全体成员会议,进行动员,并分别举行三场辅导讲座</p><p>曾良老师为全体成员作“如何开展调查研究”的辅导报告</p><p>郑培生老师为全体成员作“提高调查研究的科学性”辅导报告</p><p>陈美娟老师为全体成员作“从地理环境分析、研究旱情”的辅导报告</p><p>3月19日 召开全体活动小组成员第二次会议,做好调查所需材料的准备工作和安全教育工作</p><p>(二)活动的实施阶段(2005年3月20日―3月21日)</p><p>2005年3月20日 活动小组全体成员集中的方式到松涛水库管理区和新州镇、木棠镇等两个乡镇实地考察松涛水库目前的基本情况以及两个镇的旱情及当地的抗旱情况。全部72名学生中除一名因病中途退出外,其他71名学生全部参加。</p><p>2005年3月21日 71名学生以分组活动的方式分散到全市17个乡镇及儋州市农业局、水务局、气象局及海南松涛水利工程管理局进行调查。</p><p>(三)整理材料阶段(2005年3月22日-3月26日)</p><p>2005年3月22日 各小组分组整理材料,上网或到图书馆查阅相关资料</p><p>2005年3月23日-3月25日 各小组撰写小组调查报告,小组成员撰写活动个人感想</p><p>2005年3月26日 各小组把这次活动的各种材料(包括活动记录、所拍摄的相片、撰写活动的感受和各小组的调查报告等资料)上交指导教师</p><p>(四)活动的总结及成果展出阶段(2005年3月27-3月30日)</p><p>2005年3月27日 召开活动小组全体成员会议,各小组、各成员交流活动的心得体会,总结这次活动</p><p>2005年3月28日 活动小组把这次活动的部分成果在校内外进行展出,并向全校发出“节约用水,共抗旱灾”的倡议,并向学校提出“共抗旱灾,从我做起”的五点建议;活动小组向市政府及相关部门提出“全市动员,形成节约用水、共同抗击旱灾的共识”等六点建议。</p><p>3、2005年3月29日―3月30日 活动小组向全市市民发出“节约用水,共抗旱灾”的倡议书,并走上街头发放1000份倡议书。</p><p>四、活动的成果</p><p>(一)较为全面地了解儋州市旱情、水情及抗旱形势</p><p>1、儋州市目前农业生产受到旱情的严重影响。从调查到的材料和数字看,除了两个镇没有调查到相关数字外,其他15个镇都存在不同程度的旱情,受旱面积超过50%的镇有4个,受旱面积超过20%的镇有10个。全市受旱农田面积目前已达64.25万亩,如果干旱持续下去,受旱面积将会继续扩大。(具体见表一)</p><p>2、我市目前的生活用水基本还能保持正常,但部分农村的生活用水面临严峻的形势。目前,全市因旱有10个镇40个自然村5万人出现饮水困难,占全市人口的5.56%,比重虽小,但绝对数并不小。而且,如果干旱再持续两个月以上,松涛水库将无水可放,全市居民的生活用水将面临严重威胁。(具体见材料一)</p><p>3、广大农村,特别是受灾农村目前生活秩序基本正常,但部分受灾严重农村的农民生活受到干旱较大的影响。</p><p>4、大多数村民已感受到旱灾对他们生活的影响,但在市政府所在地――那大镇,居民认为旱灾对他们生活有影响的仅为41.6%,远低于农村的77%,这说明市政府和松涛管理局以人民生活为重的决策已经发挥作用。(具体见附件二《问卷调查统计结果》)</p><p>5、大多数村民及市民已经认识到节水的重要性,但落实到行动上明确不够,如被调查市民中有93.19%的人认为应该节约用水,但只有52.69%的人认为身边的人在节约用水。(具体见附件二《问卷调查统计结果》)</p><p>6、儋州市各相关政府部门、各乡镇,非常重视领导农民抗击旱灾,引导广大干部群众采取封江堵溪、机械抽水、打田头井、挖深水井等方式解决水源,引导农民改种抗旱作物等,但由于资金、设备等方面的原因,没法解决所有受旱土地的用水问题,相当部分的受旱土地没法耕种。</p><p>7、受旱灾的影响,儋州市农业生产和农民的生活将会面临近50年来从未有过的严峻局面,预计今年我市农业生产和农民的收入将会受到严重的影响。</p><p>8、儋州市政府所在地――那大镇的广大市民关注旱情,也有较强的社会责任感,如有81.17%的市民了解儋州的旱情,73.73%的市民会为受旱群众伸出援助之手。(具体见附件二《问卷调查统计结果》)</p><p>表一:各乡镇农业生产的情况</p><p>注:表中数字为各小组调查的结果</p><p>表二:松涛水库情况</p><p>表三:松涛水库近4年水位</p><p>材料一:儋州市当前水情旱情及抗旱动态(资料来源:《儋州三防》[第三期]2005年3月11日)</p><p>1、儋州旱情:</p><p>全市受旱面积:目前受旱面积64.25万亩。</p><p>农作物受旱面积:其中甘蔗22万亩;水稻:0.45万亩;瓜菜:2.5万亩;花生:4.5万亩;番薯:3.5万亩;水果:5万亩;橡胶:26万亩;其他:0.3 万亩;水田无水插秧面积6万亩。</p><p>饮水情况:全市因旱有10个镇40个自然村5万人和1.0万头大牲畜出现饮水困难。</p><p>2、儋州水情:</p><p>至2005年3月11日止,全市水库(不含松涛)总蓄水量为7539万立方米,有效供水量6333万立方米,比2004年同期减少了1652万立方米。</p><p>松涛水库今年早造最多再给我市供水8980万立方米,比2004年同期减少5850万立方米。</p><p>目前全市有效供水量为15312万立方米,比去年同期减少7502万立方米。</p><p>3、儋州抗旱动态:</p><p>2004年入秋以来,市农机中心共组织各类抗旱机具1.5万台套,投入抗旱人力11.8万人次,投入作业消耗柴油近200吨,浇灌面积达50多万亩次。2005年3月1日至3月11日,全市投入抗旱人数3.5万人,抗旱泵站4处,抽水机2000台,运水车辆30辆次,抗旱用电1.2万度,抗旱用油17吨,浇灌面积5万亩,临时解决1.632万人和0.52万头牲畜的饮水困难,全市共投入抗旱资金79万元.</p><p>(二)旱情原因分析</p><p>1、客观原因</p><p>(1)全省降水偏少是导致这次旱灾发生的直接原因</p><p>海南岛属热带海洋性季风气候,旱季、雨季分明。每年冬春时节即当年11月至次年4月,为干旱期,降水少。而每年的夏秋时节即5月至10月,则是琼岛的多雨时节,降水多,降雨量约占全年总量的80%,这是琼岛气候的一大特点。但就去年而言,在传统的干旱期过后,进入雨季的琼岛降水仍然偏少,并出现了阶段性夏旱和持续性秋旱。2004年以来,儋州市各地高温少雨,1―10月份,全市平均降雨量仅为1077.7毫米,比正常年份平均少418.4毫米, 2004年11月―2005年4月,降雨偏少130-190毫米。2004年是1957年以来年降雨量最少的一年。它直接导致水库蓄水量不会有较大补充,这是导致这次旱灾发生的直接原因。</p><p>(2)厄尔尼诺现象</p><p>根据以往50年的数据统计,出现厄尔尼诺年时,气温一般偏高,冬天比较暖和。据介绍,2004年也是我国自1961年以来第4个气温偏高年。海南省大部分地区偏高0.7摄氏度,部分地区偏高1摄氏以上。儋州市2004年11月―2005年4月,天气偏暖,气温偏高。气温偏高加快水分蒸发,对加剧琼岛旱情尤如“火上浇油”,这是导致这次旱灾发生、持续的又一原因。</p><p>(3)55年一遇“空台年”</p><p>2004年海南岛无热带气旋登陆或影响,海南省遭遇55年来的第一个“空台年”。据了解,西北太平洋和南海地区平均每年大约有二十七八个台风生成,平均每年有6.9个台风在我国登陆。去年,热带风暴、台风生成总数和沿海登陆个数均较常年偏多。但在南海仅有3个风暴生成,且热带对流活动的区域相对偏北和偏东, 使大部分生成的台风和热带风暴相对偏北和偏东,2004年台风登陆地点集中于广东珠江口以东到浙江杭州湾一带,而没有进入海南岛。由于没有热带气旋影响,海南岛大范围的强降水过程明显偏少,绝大部分地区年总降水量不同程度偏少,各地降雨量的时空分布不均匀更为明显,这是导致干旱发生的又一原因。</p><p>2、人为的原因</p><p>(1)天然林破坏</p><p>天然林有落叶等长期堆积形成的凋落层,它们覆盖在地表,在降水时可以减缓水流,加大地表水渗透量。同时,天然林里每株植物体内都能蓄积大量水分,在干旱时释放出来,缓减旱情。但近十年,海南一些地区,天然林破坏严重,导致森林对水源的涵养功能降低,从而使得河流、水库、地下水等水位下降,加重了旱情。</p><p>(2)恶性开发</p><p>海南是农业大省,冬季瓜菜、果树种植等具有得天独厚的自然条件,水库库区由于水源充足,不少果农、开发商都盯上了海南的水库,他们毁掉库区周边的天然林种植芒果、龙眼、香蕉等经济作物,活动小组在松涛水库周边考察时发现大量存在此类现象。</p><p>(3)水利基础设施先天不足</p><p>儋州市由于松涛水库的灌溉,大部分灌区的农业用水基本得到保证。但儋州市的地形较高,不易储水,而且灌区渠道老化失修,渠道过水能力低,灌溉效益逐年下降。三都、峨蔓、松林等乡镇大量耕地因缺水而丢荒。一些中小水库由于资金的原因年久失修,蓄水能力下降,导致在干旱到来时没能发挥其作用。</p><p>(4)不合理的用水习惯</p><p>在儋州的许多农村,还存在“漫灌”的习惯,就算在干旱严重的今天,在水库库区上游,农田灌溉现象还存在。在生活用水方面,由于大多数的市民缺乏节约用水的意识,浪费水、污染水的现象十分严重,这些因素都人为地加剧了旱情。</p><p>(三)活动小组为学校、市政府相关部门抗旱避灾分别提出5条建议和6条建议,向全社会发出1000份《节约用水,共抗旱灾》的倡议书。</p><p>1、给学校的建议</p><p>(1)学校要利用各种手段加强对学生的宣传,提高学生的节水意识。</p><p>(2)同学们应立即行为起来,从我做起,从现在做起,节约身边的每一滴水。</p><p>(3)学生干部、共青团员和广大青少年学生应发挥自身的优势,向自己的亲戚好友、向社会宣传节水意识。</p><p>(4)把旱情当成重要的课程资源来利用,组织学生深入进行研究。</p><p>(5)组织各种形式的报告会、研讨会,请相关专家、学者到会为广大师生科学的分析旱情,提高学生的科学意识和环境保护意识。</p><p>2、给市政府的建议</p><p>(1)全市动员,形成节约用水、共同抗击旱灾的共识。</p><p>第一、儋州市的主要媒体要向全社会通报当前旱情,每周向全市市民公布本市的旱情、水情及抗旱动态。</p><p>第二、政府机关及各部门要动员起来,把抗旱救灾当成是一项关系到人民群众切身利益的大事来抓。</p><p>(2)做好干旱持续下去的防范准备工作。</p><p>当前,政府相关部门应未雨绸缪,建立防旱预案,做好旱灾持续下去的各种准备工作,形成抗旱救灾的常规机制。</p><p>(3)要从“地上、地下、天上”做好利用水资源的各项工作。</p><p>政府相关部门要引导广大干部群众采取的封江堵溪、机械抽水、打田头井、挖深水井等途径解决受灾地区农业生产和居民用水问题,并在人力、财力、物力等方面予以支持,要积极采取人工增雨作业等方式缓解旱情,从“地上、地下、天上”多方利用有限的水资源。</p><p>(4)各相关部门要协调处理好各镇、各村之间的用水矛盾,防患于未然。</p><p>相关部门要制定并科学实施水资源利用工作方案,防止抢水、争水及各种因水资源而引发的各种矛盾的产生,有关部门要协调处理好各镇、各村之间的用水矛盾,防患于未然。</p><p>(5)社会、政府要帮助受灾地区农民恢复生产,抗灾自救。</p><p>第一、政府在财政上要努力为受灾严重的灾区提供更多的抗灾资金和设备。</p><p>第二、社会各界要发起一场为灾区人民捐款的筹款活动,多方筹集抗灾资金。</p><p>第三、政府、社会要帮助灾区人民解决生产和生活问题,一方面要千方百计地帮助受灾群众恢复生产,解决他们的基本生活问题;另一方面,要通过劳务输出等方式解决受旱农民工的生活来源问题。</p><p>(6)要从长远上保护好我们的水资源,做好松涛水库等水利设施周边环境的整治工作和长远规划。</p><p>3、给社会的倡议</p><p>活动小组的所有成员向社会各界群众发出“立即行动起来,从我做起,从现在做起,众志成城,万众一心,夺取抗旱斗争的伟大胜利”的倡议:(倡议书见附件五)</p><p>(1)增强节水意识,请勿浪费每一滴水。</p><p>(2)积极行动起来,改变不良的用水习惯。</p><p>(3)当好节水宣传员,做好节水宣传工作。</p><p>(4)伸出你的手,为灾区人民献上一份爱心。</p><p>五、体会和感受</p><p>参加活动的学生,除一名因病中途退出外,其余71名学生按计划完成全部活动。18个调查小组共撰写了13份《小组调查报告》,71名学生共撰写了50多份《个人感想》。从学生撰写的调查报告和活动感受中,可以深切的感受到学生所受到的教育,思想所发生的变化,关注社会、关注人的思维品质得到培养,环境保护意识得到提高,科学研究的精神得到培养。</p><p>(一)部分《调查报告》节选</p><p>兰洋镇农业受旱情况如下:全镇共有水田7800亩,其中因旱无法耕种的农田有5000亩。旱情最严重的有羊龙村、新辛村、头地村、新福村、上村二队五个自然村。</p><p>抽样调查兰洋镇南罗村委会:</p><p>(1)南罗村委会五个自然村中,有860亩地因旱无法耕种。</p><p>(2)仅有360亩田地可插秧(已插秧);无法插秧的田地改种玉米、番薯等耐旱作物。</p><p>(3)五个自然村中大部分水井已干枯无水;农业用水和生活用水主要来源于山沟水。</p><p>(4)五个自然村中羊龙村的旱情最严重,村民们几乎无水可用,只能从几公里外的山泉挑水解决生活用水问题。</p><p>―― 兰洋镇调查小组</p><p>此次旱情对南丰镇各村造成的影响有:</p><p>⑴可以正常耕作的土地面积大大减少,为今年的收成带来了困难。</p><p>⑵基本口粮已难以维持农民的需求;农民的饮水困难问题越来越严重,难度越来越大。</p><p>⑶今年各村农业生产面临严峻的局面,村民的生活面临严峻的局面。</p><p>――南丰镇调查小组</p><p>镇政府全力组织抗旱,措施得力,如向农民提供一些免费的耐旱种子;合理的分配现有水资源;组织劳动力输出等。</p><p>――雅星镇调查小组</p><p>几条建议:</p><p>(1)镇政府人员应定时向各村进行调查,了解各村的受灾情况,并指导村民抗灾自救。</p><p>(2)各村委会要积极号召村民抗旱,并做好组织工作。</p><p>(3)村民要理智,要有共抗旱灾的决心,积极投入到生产自救中去。</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><p>――吴特丽</p><p>附件1:《儋州市旱情调查研究性学习活动实施方案》</p><p>附件2:《调 查 问 卷(一)》</p><p>附件3:《调 查 问 卷(二)》</p><p>附件4:《问卷调查统计结果》</p><p>附件5:《倡议书》</p><p>附件6:《部分小组撰写的调查报告》</p><p>二00五年三月三十一日</p><p>附件1:</p><h3 id="“儋州市旱情调查”研究性学习活动方案"><a href="#“儋州市旱情调查”研究性学习活动方案" class="headerlink" title="“儋州市旱情调查”研究性学习活动方案"></a>“儋州市旱情调查”研究性学习活动方案</h3><p>一、课题背景</p><p>2004年以来,海南省遭受到50年来最严重的旱灾。作为海南省人口最多,面积最大的市(县)和全国农业百强市(县),儋州市的旱情牵动着全市人民的心。海南西部和北部工农业、居民生活用水的源头,也是海南最大的水库――松涛水库就坐落在儋州市和白沙县、琼中县交界处,海南松涛水利工程管理局就设在儋州市政府所在地――那大镇,松涛水库管理区设在儋州市南丰镇。</p><p>旱情的背后是各种深刻的科学道理,现实的背后是理性,是什么原因造成这次儋州市乃至整个海南大旱,我们希望通过调查能弄清背后的原因。同时,通过这项活动让我们深入社会,在活动中提高我们的综合素质。</p><p>二、活动的目的</p><p>了解儋州市的旱情,分析干旱产生的原因及影响,为学校、社会抗旱避灾提出相关建议。推动研究性学习活动的蓬勃发展,培养青少年的创新精神和实践能力,提高青少年的综合素质,鼓励优秀人才的涌现。</p><p>三、活动的时间、地点和人员安排及准备的资料</p><p>1、活动时间:3月4日――3月30日</p><p>2、活动地点:儋州市各乡镇、市政府相关部门和松涛水利工程管理局及松涛水库库区</p><p>3、活动人员安排</p><p>(1)儋州市那大中学旱情调查研究性学习活动小组指导教师分工</p><p>曾良(学校教研室主任,中学政治一级教师):统筹指导本次活动,并负责指导学生撰写调查报告、活动感受</p><p>郑培生(中学物理一级教师,有多年指导青少年科技创新大赛经验):负责指导科学研究方法的指导</p><p>陈美娟(中学地理一级教师):负责指导学生分析干旱产生的原因及提出科学性的设想、建议</p><p>(2)活动参与学生:原则上划分为18个活动小组,按17个乡镇和各相关部门(包括海南松涛水利工程管理局和儋州市各相关政府部门)划分,每组学生4人,在学生自愿报名的基础上产生。</p><p>(3)活动开展所需材料</p><p>1、调查材料:普通相机、数码相机、录音机、调查问卷、摄像机、学校证明</p><p>2、新闻媒体:在调查过程中,希望借助儋州电视台和儋州通讯等新闻媒体进行宣传,扩大调查影响</p><p>四、活动的主要内容和具体实施步骤</p><p>(一)活动的主要内容</p><p>1、科学调查</p><p>①调查儋州各乡镇旱情,特别是农业受旱情况和居民用水情况是这次调查的重点。</p><p>②调查这次旱情发生的原因</p><p>③调查儋州市农业生产、农民的生活受到旱情的影响情况</p><p>④调查儋州市90万市民的生活受干旱的影响情况</p><p>⑤调查当前儋州市抗旱形势</p><p>2、自我反思</p><p>①撰写活动感受</p><p>②小组讨论与交流活动的感受</p><p>③各小组撰写调查报告</p><p>3、宣传教育</p><p>①展示调查成果,进一步扩大活动的影响</p><p>②向社会发出倡议书、</p><p>(二)活动的具体实施步骤</p><p>1、3月4日 制定的活动方案。</p><p>2、3月4日―11日 进行动员,了解学生的情况,征求师生的意见;确定参加“儋州市旱情调查”研究性学习活动小组成员,并进行分组。</p><p>3、3月12日-18日 召开动员大会,分别举行社会调查、科学性实践活动、科学(理性)地看待干旱等三场辅导讲座,做好调查所需材料的准备工作和安全教育工作。如:调查所需的问卷、相机等;召开临行前学生动员大会,落实各项准备工作。</p><p>6、3月20日 集中活动阶段</p><p>指导教师带领学生深入到松涛水库库区、新州镇和木棠镇调查,请当地的相关领导介绍当时的旱情及当地面对旱情所采取的措施。</p><p>7、3月21日 分组活动阶段</p><p>学生以小组为单位深入到儋州市各乡镇、农村及松涛水利工程管理局、市政府相关部门,请当地(相关部门)的相关领导介绍当时的旱情及当地对旱情所采取的措施。</p><p>8、3月22日―26日整理统计调查数据,组织材料,教师指导学生撰写调查报告和活动个人感想。</p><p>9、3月27日-3月29日 展示调查成果,进一步扩大活动的影响</p><p>10、3月30日 向社会提出倡议、向学校、政府提出建议</p><p>“儋州市旱情调查”</p><p>研究性学习活动小组</p><p>二00五年三月四日</p><p>附件2:</p><h3 id="调-查-问-卷(一)"><a href="#调-查-问-卷(一)" class="headerlink" title="调 查 问 卷(一)"></a>调 查 问 卷(一)</h3><p>您好,谢谢你在百忙中能接受我们的调查,感谢你对我们工作的支持。</p><p>下面有9道问题,请你据实回答。</p><p>1、你认为旱情对你的生活和生产有影响吗?</p><p>A、影响很大 B、没有 C、有一点 D、说不清</p><p>2、在日常生活中,你感到用水有困难吗?</p><p>A、困难 B、不困难 C、 说不清</p><p>3、你认为应该节约生活用水吗?</p><p>A、应该 B、 不应该</p><p>4、你身边的人是否节约用水?</p><p>A、是 B、否 C、说不清</p><p>5、你认为本地农业生产受旱情的影响吗?</p><p>A、有影响 B、没有影响 C、说不清</p><p>6、你的日常生活受这次旱情的影响吗?</p><p>A、有影响 B、没有影响 C、说不清</p><p>7、面对旱情,你是否改种其他耐旱作物?</p><p>A、是 B、否</p><p>8、政府有否帮助你们克服旱灾保持生产?</p><p>A、有 B、没有</p><p>9、你是否采取措施来解决干旱带来的问题?</p><p>A、有 B、没有</p><p>再次感谢您对我们工作的支持。</p><p>附件3:</p><h3 id="调-查-问-卷(二)"><a href="#调-查-问-卷(二)" class="headerlink" title="调 查 问 卷(二)"></a>调 查 问 卷(二)</h3><p>您好,谢谢你在百忙中能接受我们的调查,感谢您对我们工作的支持。</p><p>下面有7道问题,请您据实回答。</p><p>1、你了解儋州市目前的旱情吗?</p><p>A、很了解 B、知道一些 C、不清楚</p><p>2、你的日常生活受这次旱情的影响吗?</p><p>A、有影响 B、没有影响 C、说不清</p><p>3、在日常生活中,你感到用水有困难吗?</p><p>A、很困难 B、有一点困难 C、 说不清</p><p>4、你认为应该节约生活用水吗?</p><p>A、应该 B、 不应该</p><p>5、你身边的人是否节约用水?</p><p>A、是 B、否 C、不清楚</p><p>6、你认为本地农业生产受到旱情的影响吗?</p><p>A、有影响 B、没有影响 C、说不清</p><p>7、你会为受旱群众伸出援助之手吗?</p><p>A、会 B、不会 C、说不清</p><p>再次感谢您对我们工作的支持!</p><p>附件4:</p><h3 id="问卷调查统计结果"><a href="#问卷调查统计结果" class="headerlink" title="问卷调查统计结果"></a>问卷调查统计结果</h3><p>一、调查小组共向所到乡镇居民(村民)发放调查问卷(一)共1700份,回收1460份,结果统计如下:</p><p>1、 你认为旱情对你的生活和生产有影响吗?</p><p>93%认为有影响;2%认为没有影响;5%说不清</p><p>2、 日常生活中,你感到用水有困难吗?</p><p>11%认为不困难;22%认为有一点困难;65%认为很困难</p><p>3、 你认为应该节约生活用水吗?</p><p>90%认为应该;6%认为不应该</p><p>4、 你身边的人是否节约用水?</p><p>70%认为身边的人在节约用水;有10%认为身边的人没有节约用水;16%说不清</p><p>5、 你认为本地农业生产受旱情的影响吗?</p><p>88%认为有影响;2%认为没有影响;5%说不清</p><p>6、 你的日常生活受这次旱情的影响吗?</p><p>77%认为有影响;7%认为没有影响;11%说不清</p><p>7、 面对旱情,你是否改种耐旱作物?</p><p>48%改种;47%没有改种</p><p>8、政府有否帮助你们克服旱灾保持生产?</p><p>25%认为政府帮助他们;62%认为政府没有帮助他们</p><p>9、你是否采取措施来解决干旱带来的问题?</p><p>38%已采取措施;56%没有采取措施</p><p>二、调查小组共向所到市政府所在地那大镇市民发放调查问卷(二)共800份,回收632份,结果如下:</p><p>1、 你了解儋州市目前的旱情吗?</p><p>23.42%很了解;57.75%知道一些;17.25%不清楚</p><p>2、 你的日常生活受这次旱情的影响吗?</p><p>41.6%有影响;39.56%;没有影响;15.19%说不清</p><p>3、 在日常生活中,你感到用水有困难吗?</p><p>23.58%有困难;60.44%一般;15.03% 说不清</p><p>4、 你认为应该节约生活用水吗?</p><p>93.19%应该;3.6%不应该</p><p>5、 你身边的人是否节约用水?</p><p>52.69%是;19.30否;20.09%不清楚</p><p>6、 你认为本地农业生产受到旱情的影响吗?</p><p>73.89%有影响;5.5%没有影响;18.67%说不清</p><p>7、 你会为受旱群众伸出援助之手吗?</p><p>73.73%会;4.74%不会;13.29%说不清</p><p>附件5:</p><h3 id="倡-议-书"><a href="#倡-议-书" class="headerlink" title="倡 议 书"></a>倡 议 书</h3><p>尊敬的儋州市民:</p><p>你知道海南的旱情吗?你了解儋州的旱情吗?明天我们会无水可用吗?</p><p>目前,松涛水库只有3.3亿立方米的水能放出,水库水位每天在下降7―9厘米,如果旱情再持续两个月,松涛水库就到了死水位,到那时,松涛水库就再也放不出水!如果两个月后雨季还没出现,我们的生活将会怎样?</p><p>目前,我市受旱面积为64.25万亩,全市因干旱有10个镇40个自然村和1.0万头大牲畜出现饮水困难,如果旱情再持续下去,广大农民群众将会面临何种状况呢?</p><p>目前,我市广大干部群众正采取有效措施,奋起抗旱。但是,在我们身边,还存在种种随意浪费水的现象,宝贵的水资源被人为地污染的事仍不时发生,不少人对于缺水的危险熟视无睹。</p><p>为了我们的生活,为了广大的农民群众,为了夺取这场抗旱斗争的胜利。我们向全市所有市民提出如下倡议:</p><p>1、增强节水意识,请勿浪费每一滴水。</p><p>2、积极行动起来,改变不良的用水习惯。</p><p>3、当好节水宣传员,做好节水宣传工作。</p><p>4、伸出你的手,为灾区人民献上一份爱心。</p><p>请让我们立即行动起来,从我做起,从现在做起,众志成城,万众一心,夺取抗旱斗争的伟大胜利。</p><p>儋州市那大中学</p><p>“儋州市旱情调查”研究性学习活动小组</p><p>二0 0五年三月二十九日</p><p>附件6:《部分小组撰写的调查报告》</p><p>儋州市南丰镇旱情调查报告</p><h3 id="儋州市那大中学儋州市旱情调查课题组:吴特丽、吴锦正、吴秋燕"><a href="#儋州市那大中学儋州市旱情调查课题组:吴特丽、吴锦正、吴秋燕" class="headerlink" title="儋州市那大中学儋州市旱情调查课题组:吴特丽、吴锦正、吴秋燕"></a>儋州市那大中学儋州市旱情调查课题组:吴特丽、吴锦正、吴秋燕</h3><p>指导教师:曾良 郑培生 陈美娟</p><p>一、课题背景</p><p>2004年以来,海南省遭受到50年来最严重的旱灾。作为海南省人口最多,面积最大的市(县)和全国农业百强市(县),儋州市的旱情牵动着全市人民的心。海南西部和北部工农业、居民生活用水的源头,也是海南最大的水库――松涛水库就坐落在儋州市和白沙县、琼中县交界处,海南松涛水利工程管理局就设在儋州市政府所在地――那大镇,松涛水库管理区设在儋州市南丰镇。</p><p>旱情的背后是各种深刻的科学道理,现实的背后是理性,是什么原因造成这次儋州市乃至整个海南大旱,我们希望通过调查能弄清背后的原因。同时,通过这项活动让我们深入社会,在活动中提高我们的综合素质。</p><p>二、研究目的</p><p>1、了解儋州市旱情发生的原因,为抗灾救灾提供建议。</p><p>2、提高综合活动能力</p><p>3、培养科学精神和创新能力</p><p>三、研究过程</p><p>1、准备阶段:准备调查问卷、查阅资料、做好外出活动的各种集备。</p><p>2、活动阶段: 深入到儋州市各乡镇、农村及松涛水利工程管理局、市政府相关部门,请当地(相关部门)的相关领导介绍当时的旱情及当地对旱情所采取的措施。</p><p>3、整理资料阶段:整理统计调查数据,组织材料,教师指导学生撰写调查报告。</p><p>4、展示阶段:展示活动的结果,向社会提出倡议、向学校、政府提出建议。</p><p>四、研究方法</p><p>1、网上搜集资料</p><p>根据所选课题,我们有目的地在网上搜索资料,寻找与研究有关的信息,并把网上和网情有关的资料进行整理。</p><p>2、实地调查研究</p><p>我们深入到松涛水库、新州镇、木棠镇等地的水库、农村、田地进行实地考察,掌握第一手资料,并拍摄了近50张的像片和资料。</p><p>3、查阅图书资料</p><p>我们在市图书馆和学校图书馆查阅有关儋州市气象资料,了解相关地理、社会科学等的资料,进一步丰富我们的主题内容。</p><p>4、问卷调查</p><p>我们小组到南丰镇向居民和村民发放调查问卷150份,收回142份,并进行统计。</p><p>5、访谈调查法</p><p>在活动的过程中,我们到松涛库区和南丰镇采访相关领导及相关技术人员,了解松涛水库及南丰镇的受旱情况及相关原因。</p><p>五、调查结果</p><p>1、南丰镇耕地受旱情况</p><p>因无水播种的面积有8667亩。其中水道为4785亩,瓜菜为815亩,花生为352亩,甘蔗为2100亩,橡胶为385亩。</p><p>重旱的土地面积为6982亩。其中瓜菜为330亩,花生为22亩,甘蔗为3120亩,水果为3120亩,番薯为75亩,橡胶为315亩。因旱情严重无法播种。</p><p>旱情较轻的土地面积为5449亩。其中瓜菜450亩,花生为46亩,甘蔗为115亩,水果为4071亩,番薯为352亩,橡胶为415亩。</p><p>2、旱情因干旱人畜饮水困难的村委会有:⑴尖岭;⑵头佑;⑶陶江;⑷六门;⑸武教;⑹油麻;⑺南丰;⑻居委;⑼苗族;⑽油文;⑾新村;⑿农场。其中:</p><p>⑴尖岭有3个自然村,210户人口,共1300人,45头性畜因干旱饮水困难;</p><p>⑵陶口有14个自然村,183户人口,共1100人,65头性畜因干旱饮水困难;</p><p>⑶武教有11个自然村,78户人口,共467人,56头性畜因干旱饮水困难;</p><p>⑷油麻有7个自然村,52户人口,共316人,63头性畜因干旱饮水困难;</p><p>⑸居委有5个自然村,350户人口,共2101人,15头性畜因干旱饮水困难;</p><p>⑹苗族有7个自然村,71户人口,共427人,47头性畜因干旱饮水困难;</p><p>⑺油文有4个自然村,16户人口,65人,35头性畜因干旱饮水困难;</p><p>⑻新村有4个自然村,29户人口,123人,31头性畜因干旱饮水困难。</p><p>3、南丰镇地区内水库,山塘降至死水位的主要有:⑴尖岭山塘;⑵南荣山塘;⑶白马山塘;⑷武教山塘;⑸马荷山塘;⑹后岭山塘。</p><p>4、旱情对各个村造成的影响有:⑴可以耕作的土地面积大大减少,为今年的收成带来了困难,⑵基本口粮已难以维持农民的需求;农民的饮水问题越来越严重,难度越来越大;⑶使农民的农作物的损失增大。</p><p>5、地方政府,村委会及农民自己一起采取的抗旱措施有;⑴投入抗旱人数为4872人;⑵机动抗旱设备161台抽水机;⑶车辆运水15辆;镇府拨款1。6万元;⑷群众自筹3万元;⑸抗旱用油3。7吨;⑹抗旱灌溉面积1576亩;⑺临时解决饮水困难2000人,性畜的饮水困难有162头;⑻抗旱井5口。</p><p>6、南丰镇居民解决用水问题的主要途径有:⑴打井,修水利;⑵到水库去拉水;⑶到镇里买水(其中有5元一小车,15元一大车)。例如:我们到过的红苓村,打水的人们排着长队从早上一直打到晚上,甚至用15元买一车水(拖拉机)。</p><p>六、南丰镇周边的各村发生旱灾的原因探讨</p><p>1、客观原因</p><p>(1)全省降水偏少是导致这次旱灾发生的直接原因</p><p>海南岛属热带海洋性季风气候,旱季、雨季分明。每年冬春时节即当年11月至次年4月,为干旱期,降水少。而每年的夏秋时节即5月至10月,则是琼岛的多雨时节,降水多,降雨量约占全年总量的80%,这是琼岛气候的一大特点。但就去年而言,在传统的干旱期过后,进入雨季的琼岛降水仍然偏少,并出现了阶段性夏旱和持续性秋旱。2004年以来,儋州市各地高温少雨,1―10月份,全市平均降雨量仅为1077.7毫米,比正常年份平均少418.4毫米, 2004年11月―2005年4月,降雨偏少130-190毫米。2004年是1957年以来年降雨量最少的一年。</p><p>(2)厄尔尼诺现象</p><p>根据以往50年的数据统计,出现厄尔尼诺年时,气温一般偏高,冬天比较暖和。据介绍,2004年也是我国自1961年以来第4个气温偏高年。海南省大部分地区偏高0.7摄氏度,部分地区偏高1摄氏以上。儋州市2004年11月―2005年4月,天气偏暖,气温偏高。气温偏高加快水分蒸发,对加剧琼岛旱情尤如“火上浇油”,这是导致这次旱灾发生、持续的又一原因。</p><p>(3)55年一遇“空台年”</p><p>2004年海南岛无热带气旋登陆或影响,海南省遭遇55年来的第一个“空台年”。 2004年台风登陆地点集中于广东珠江口以东到浙江杭州湾一带,而没有进入海南岛。由于没有热带气旋影响,海南岛大范围的强降水过程明显偏少,绝大部分地区年总降水量不同程度偏少,各地降雨量的时空分布不均匀更为明显,这是导致干旱发生的又一原因。</p><p>(1)恶性开发</p><p>海南是农业大省,冬季瓜菜、果树种植等具有得天独厚的自然条件,南丰镇内的松涛水库库区由于水源充足,不少果农、开发商都盯上了海南的水库,他们毁掉库区周边的天然林种植芒果、龙眼、香蕉等经济作物,活动小组在松涛水库周边考察时发现大量存在此类现象。</p><p>(2)不合理的用水习惯</p><p>在儋州的许多农村,还存在“漫灌”的习惯,就算在干旱严重的今天,在水库库区上游,农田灌溉现象还存在。在生活用水方面,由于大多数的市民缺乏节约用水的意识,浪费水、污染水的现象十分严重,这些因素都人为地加剧了旱情。</p><p>(3)政府对于干旱不够重视,抗旱资金不足,许多措施没有真正实施,水利局供水不足。</p><p>七、几条建议:</p><p>1、学校要利用各种手段加强对学生的宣传,提高学生的节水意识。同学们应立即行为起来,从我做起,从现在做起,节约身边的每一滴水。</p><p>2、政府相关部门应未雨绸缪,建立防旱预案,做好旱灾持续下去的各种准备工作,形成抗旱救灾的常规机制。</p><p>3、社会、政府要帮助受灾地区农民恢复生产,抗灾自救。</p><p>第一、政府在财政上要努力为受灾严重的灾区提供更多的抗灾资金和设备。</p><p>第二、社会各界要发起一场为灾区人民捐款的筹款活动,多方筹集抗灾资金。</p><p>第三、政府、社会要帮助灾区人民解决生产和生活问题,一方面要千方百计地帮助受灾群众恢复生产,解决他们的基本生活问题;另一方面,要通过劳务输出等方式解决受旱农民工的生活来源问题。</p><p>4、要从长远上保护好我们的水资源,做好松涛水库等水利设施周边环境的整治工作和长远规划。</p><p>“旱情调查小组”学生分组名单</p><p>那大组:吕珊燕 黄 颖 余雅静 陈 樱 潘 辉 陈金龙</p><p>排浦组:刘 清 符启俊 黄万妍 王永慧</p><p>长坡组:陈美嵩 李金利 李 玮 汪秀秀 韦海娟</p><p>中和组:林颖全 何烈方 鲍系承 万明策 李振扬</p><p>木棠组:李汉东 刘钟锴 曾世盛 李巨辉</p><p>马井组:曾凡鳞 江 玲 羊艳梅 韩壮恒</p><p>东成组:吴红丽 郑文浩 符光凯 曾允茹</p><p>光村组:陈红山 刘井女 陈卓东 张再经 陈桂玫 徐伟姣</p><p>南丰组:吴特丽 吴秋燕(4) 吴锦正</p><p>新州组:林宇兴 肖定和 王广川 吴雄伟</p><p>大成组:吴万妃 黎贵福(5) 张丽君</p><p>峨蔓组:杜雪兰(16) 王小支(15) 符春红(15) 陈初玲(8)</p><p>三都组:张鹏海 羊家聪 张政达 刘明芬 陈玉女</p><p>海头组:吴广兵 符凤兰 梁彩梅 吕瑞婷</p><p>王五组:吴永宁 麦亮文 吴剑锋</p><p>雅星组:符雅丽(6) 吴子良(6) 郭俊鼎(6) 张丽婷(6)</p><p>兰洋组:林 莹(8) 谢春榴(8) 张明嘉 刘壁如</p><p>注:共分17个小组,总计72人。括号内为除1、2班外的学生的班别,姓名下划线者为各组组长。</p>]]></content>
<summary type="html">
<blockquote>接下去的文字是自己高中时期跟随社会实践导师们和一个team的同学们一起完成的旱情调查报告原文。由于时间已经过去多年了,在互联网上已经几乎找不到了。这里发出来分享给大家,顺带也怀念/纪念一下已经逝去的中学年华。</blockquote>
<h4 id
</summary>
<category term="生活感悟" scheme="https://jinlongchen.com/categories/%E7%94%9F%E6%B4%BB%E6%84%9F%E6%82%9F/"/>
<category term="2004儋州旱情" scheme="https://jinlongchen.com/tags/2004%E5%84%8B%E5%B7%9E%E6%97%B1%E6%83%85/"/>
<category term="中学社会实践" scheme="https://jinlongchen.com/tags/%E4%B8%AD%E5%AD%A6%E7%A4%BE%E4%BC%9A%E5%AE%9E%E8%B7%B5/"/>
<category term="社会实情调查" scheme="https://jinlongchen.com/tags/%E7%A4%BE%E4%BC%9A%E5%AE%9E%E6%83%85%E8%B0%83%E6%9F%A5/"/>
</entry>
<entry>
<title>PHP正则表达式</title>
<link href="https://jinlongchen.com/posts/2015/06/11/php-regular-expression/"/>
<id>https://jinlongchen.com/posts/2015/06/11/php-regular-expression/</id>
<published>2015-06-11T01:32:00.000Z</published>
<updated>2020-07-02T18:56:45.502Z</updated>
<content type="html"><![CDATA[<p><strong>一、介绍</strong></p><p>正则表达式,大家在开发中应该是经常用到,现在很多开发语言都有正则表达式的应用,比如javascript,java,.net,php等等,我今天就把我对正则表达式的理解跟大家唠唠,不当之处,请多多指教!</p><p>需要知道的术语——下面的术语你知道多少?</p><p>Δ 定界符<br>Δ 字符域<br>Δ 修饰符<br>Δ 限定符<br>Δ 脱字符<br>Δ 通配符(正向预查,反向预查)<br>Δ 反向引用<br>Δ 惰性匹配<br>Δ 注释<br>Δ 零字符宽 </p><ul><li><p>定位<br>我们什么时候使用正则表达式呢?不是所有的字符操作都用正则就好了,php在某些方面用正则反而影响效率。当我们遇到复杂文本数据的解析时候,用正则是比较好的选择。</p></li><li><p>优点<br>正则表达式在处理复杂字符操作的时候,可以提高工作效率,也在一定程度节省你的代码量。</p></li><li><p>缺点<br>我们在使用正则表达式的时候,复杂的正则表达式会加大代码的复杂度,让人很难理解。所以我们有的时候需要在正则表达式内部添加注释。</p></li></ul><p><strong>二、通用模式</strong></p><p>¤ 定界符<br>通常使用 “/“做为定界符开始和结束,也可以使用”#”。<br>什么时候使用”#”呢?一般是在你的字符串中有很多”/“字符的时候,因为正则的时候这种字符需要转义,比如uri。 </p><p>使用”/“定界符的代码如下.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/^http:\/\/([\w.]+)\/([\w]+)\/([\w]+)\.html$/i';</span><br><span class="line">$str = 'http://www.youku.com/show_page/id_ABCDEFG.html';</span><br><span class="line">$matches = array();</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><p>preg_match中的$matches[0]将包含与整个模式匹配的字符串。<br>使用”#”定界符的代码如下.这个时候对”/“就不转义!</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$regex = '#^http://([\w.]+)/([\w]+)/([\w]+)\.html$#i';</span><br><span class="line">$str = 'http://www.youku.com/show_page/id_ABCDEFG.html';</span><br><span class="line">$matches = array();</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><p>¤ 修饰符:用于改变正则表达式的行为。</p><p>我们看到的<code>('/^http:\/\/([\w.]+)\/([\w]+)\/([\w]+)\.html/**i**')</code> 中的最后一个”i”就是修饰符,表示忽略大小写,还有一个我们经常用到的是”x”表示忽略空格。<br>贡献代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/HELLO/';</span><br><span class="line">$str = 'hello word';</span><br><span class="line">$matches = array();</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">echo 'No i:Valid Successful!',"\n";</span><br><span class="line">}</span><br><span class="line">if(preg_match($regex.'i', $str, $matches)){</span><br><span class="line">echo 'YES i:Valid Successful!',"\n";</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>¤ 字符域:[\w]用方括号扩起来的部分就是字符域。<br>¤ 限定符:如[\w]{3,5}或者[\w]*或者[\w]+这些[\w]后面的符号都表示限定符。现介绍具体意义。 </p><p>{3,5}表示3到5个字符。{3,}超过3个字符,{,5}最多5个,{3}三个字符。 </p><ul><li>表示0到多个 </li></ul><ul><li>表示1到多个<br>¤ 脱字符号(^:)<br>(1)放在字符域(如:[^\w])中表示否定(不包括的意思)——“反向选择”<br>(2)放在表达式之前,表示以当前这个字符开始。(/^n/i,表示以n开头)<br>注意,我们经常管”"叫”跳脱字符”。用于转义一些特殊符号,如”.”,”/“</li></ul><p><strong>三、通配符(lookarounds)</strong></p><p>断言某些字符串中某些字符的存在与否!<br>格式:<br>正向预查:(?=) 相对应的 (?!)表示否定意思<br>反向预查:(?<=) 相对应的 (?前后紧跟字符 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/(?<=c)d(?=e)/'; /* d 前面紧跟c, d 后面紧跟e*/</span><br><span class="line">$str = 'abcdefgk';</span><br><span class="line">$matches = array();</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><p>否定意义:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/(?</span><br><span class="line">$str = 'abcdefgk';</span><br><span class="line">$matches = array();</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><p>字符宽度:零<br>验证零字符代码 </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/HE(?=L)LO/i';</span><br><span class="line">$str = 'HELLO';</span><br><span class="line">$matches = array();</span><br><span class="line"></span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br><span class="line">打印不出结果!</span><br><span class="line"></span><br><span class="line">$regex = '/HE(?=L)LLO/i';</span><br><span class="line">$str = 'HELLO';</span><br><span class="line">$matches = array();</span><br><span class="line"></span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><p>能打印出结果!</p><p>说明:(?=L)意思是HE后面紧跟一个L字符。但是(?=L)本身不占字符,要与(L)区分,(L)本身占一个字符。</p><p>*<em>四、捕获数据<br>*</em></p><p>没有指明类型而进行的分组,将会被获取,供以后使用。</p><blockquote><p>指明类型指的是通配符。所以只有圆括号起始位置没有问号的才能被捕捉。<br>在同一个表达式内的引用叫做反向引用。<br>调用格式: \编号(如\1)。</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/^(Chuanshanjia)[\w\s!]+\1$/';</span><br><span class="line">$str = 'Chuanshanjia thank Chuanshanjia';</span><br><span class="line">$matches = array();</span><br><span class="line"></span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><blockquote><p>避免捕获数据 </p></blockquote><p>格式:(?:pattern) </p><p>优点:将使有效反向引用数量保持在最小,代码更加、清楚。</p><blockquote><p>命名捕获组</p></blockquote><p>格式:(?P<组名>) 调用方式 (?P=组名)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">arrray(3) {</span><br><span class="line"></span><br><span class="line">[0] =></span><br><span class="line">string(28) “chuanshanjia Is chuanshanjia”</span><br><span class="line">[“author”] =></span><br><span class="line">string(12) “chuanshanjia”</span><br><span class="line">[1] =></span><br><span class="line">string(12) “chuanshanjia"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>五、惰性匹配</strong></p><p>(记住:会进行两部操作,请看下面的原理部分)<br>格式:限定符?<br>原理:”?”:如果前面有限定符,会使用最小的数据。如“*”会取0个,而“+”会取1个,如过是{3,5}会取3个。<br>先看下面的两个代码:<br>代码1</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/heL*/i';</span><br><span class="line">$str = 'heLLLLLLLLLLLLLLLL';</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">echo "\n";</span><br><span class="line">结果1:</span><br><span class="line">array(1) {</span><br><span class="line">[0] =></span><br><span class="line">string(8) "heLLLLLLLLLLLLLLLL"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>代码2</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/heL*?/i';</span><br><span class="line">$str = 'heLLLLLLLLLLLLLLLL';</span><br><span class="line"></span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br><span class="line">结果2:</span><br><span class="line">array(1) {</span><br><span class="line">[0] =></span><br><span class="line">string(2) "he"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>代码3,使用“+”</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/heL+?/i';</span><br><span class="line"></span><br><span class="line">$str = 'heLLLLLLLLLLLLLLLL';</span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line">echo "\n";</span><br><span class="line"></span><br><span class="line">结果3</span><br><span class="line">array(1) {</span><br><span class="line"></span><br><span class="line">[0] =</span><br><span class="line">string(3) "heL"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>代码4,使用{3,5}</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/heL{3,10}?/i';</span><br><span class="line">$str = 'heLLLLLLLLLLLLLLLL';</span><br><span class="line"></span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">echo "\n";</span><br><span class="line">结果4</span><br><span class="line">array(1) {</span><br><span class="line">[0] =></span><br><span class="line">string(5) "heLLL"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>六、正则表达式的注释</strong></p><p>格式:(?# 注释内容)<br>用途:主要用于复杂的注释</p><p>贡献代码:是一个用于连接MYSQL数据库的正则表达式</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$regex = '/</span><br><span class="line">^host=(?<!\.)([\d.]+)(?!\.) (?#主机地址)</span><br><span class="line">\|</span><br><span class="line">([\w!@#$%^&*()_+\-]+) (?#用户名)</span><br><span class="line">\|</span><br><span class="line">([\w!@#$%^&*()_+\-]+) (?#密码)</span><br><span class="line">(?!\|)$/ix';</span><br><span class="line"></span><br><span class="line">$str = 'host=192.168.10.221|root|123456';</span><br><span class="line">$matches = array();</span><br><span class="line"></span><br><span class="line">if(preg_match($regex, $str, $matches)){</span><br><span class="line">var_dump($matches);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">echo "\n";</span><br></pre></td></tr></table></figure><p><strong>七、特殊字符</strong></p><p>|——-+——-|<br>|特殊字符 | 解释|<br>|——-|:——-|<br>|* | 0到多次|<br>|——-|:——-|<br>|+ | 1到多次还可以写成{1,}|<br>|——-|:——-|<br>|? | 0或1次|<br>|——-|:——-|<br>|. | 匹配除换行符外的所有单个的字符|<br>|——-|:——-|<br>|\w | [a-zA-Z0-9_]|<br>|——-|:——-|<br>|\s | 空白字符(空格,换行符,回车符)[\t\n\r]|<br>|——-|:——-|<br>|\d | [0-9]|<br>|——-+——-|</p><blockquote><p>本篇文章为转载文章,因为觉得写得的确是好,二次分享给大家,版权归原作者“川山甲”所有。<br>原文链接:<a href="http://www.cnblogs.com/baochuan/archive/2012/03/12/2391135.html" target="_blank" rel="noopener">http://www.cnblogs.com/baochuan/archive/2012/03/12/2391135.html</a></p></blockquote>]]></content>
<summary type="html">
<p><strong>一、介绍</strong></p>
<p>正则表达式,大家在开发中应该是经常用到,现在很多开发语言都有正则表达式的应用,比如javascript,java,.net,php等等,我今天就把我对正则表达式的理解跟大家唠唠,不当之处,请多多指教!</p>
<p>
</summary>
<category term="编程语言" scheme="https://jinlongchen.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
<category term="正则表达式" scheme="https://jinlongchen.com/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
</entry>
</feed>