-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
798 lines (767 loc) · 77.5 KB
/
atom.xml
File metadata and controls
798 lines (767 loc) · 77.5 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
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[VectorHo]]></title>
<subtitle><![CDATA[fsd]]></subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2015-10-26T15:48:52.000Z</updated>
<id>http://yoursite.com/</id>
<author>
<name><![CDATA[Vector Ho]]></name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title><![CDATA[Tips For Front-End —— LiveReload]]></title>
<link href="http://yoursite.com/2015/10/26/web-hot-load/"/>
<id>http://yoursite.com/2015/10/26/web-hot-load/</id>
<published>2015-10-25T16:01:15.000Z</published>
<updated>2015-10-26T15:48:52.000Z</updated>
<content type="html"><![CDATA[<p><strong>ps</strong>:下文我将打算用<code>fe</code>代替<code>Front-End</code>一词。</p>
<p>时间过得真快,眨眼一周又过去了。今天我们来侃侃liveReload。聊之前先来扯扯开发家常。
<br>通常来说,Web页面开发的流程大致是这样的:设计师提供设计稿,通常是psd格式。然后fe开发人员来手工的将图片转换为对应的HTML+CSS,常常还需要在各个浏览器中调试等。
<br>一般,设计师还会提供色卡,或至少前景色、背景色、高亮色的值给开发人员。如果没有的话,开发人员会用到一些工具如colorpicker,ruler之类来确保最终的效果和设计稿是一致的。如果你观察过此开发的工作流程的话,你会发现基本的上是这样的:编写HTML,CSS,保存修改内容,切换到浏览器窗口,按F5或者Ctrl-R刷新,然后对比设计稿和实现,如果发现不一致的地方,再切换到编辑器修改代码,反复操作。开发时间长了,效率必定受到影响。</p>
<h2 id="LiveReload">LiveReload</h2>
<p>liveReload应该是fe比较关心技术了,有了这个liveReload技术,将大大提高fe效率。别急,我知道还没说liveReload是什么! T_T
<br>何谓liveReload,其实说白了就是监听资源文件变动让浏览器实时自动刷新,理想状态下reload应该是replace —— 实时替换,用资源局部变化方案代替全部重载。也是热门的<code>hot load</code>技术,比如下文提到的webpack就提供了一套成熟<code>hot load</code>的解决方案。</p>
<h2 id="Workflow">Workflow</h2>
<p>有了LiveReload技术后,建立高效的开发流程简直轻而易举:当你修改保存某个文件,浏览器都会自动刷新或替换变化。这样的快速反馈可以告诉我们下一步如何修改:将背景色调整的再淡一点,还是把会h2的字体变得更大,或者图片和文字的上边缘没有对齐 etc.
<br><code>实时反馈,点滴前进</code>。如果你有双显示器效果就更佳了😄,一台显示PSD设计稿,一台显示编辑器和浏览器,这样调试无非是每个前端的天堂模式。
<br>ps:为了加强理解提供效果图,btw效果图来自互联网!
<br><img src="/2015/10/26/web-hot-load/workflow-resized.png" alt="[workflow]" title="[workflow]">
<br><img src="/2015/10/26/web-hot-load/two-displays-resized.png" alt="[two-displays]" title="[two-displays]"></p>
<h2 id="实现方案">实现方案</h2>
<p>下面,我将给大家介绍我熟悉的三种方案。</p>
<h3 id="LiveReload插件_+_Guard">LiveReload插件 + Guard</h3>
<p>上文提到的频繁的F5刷新,可以通过<a href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei" target="_blank" rel="external">LiveReload插件</a> + <a href="https://github.com/guard/guard" target="_blank" rel="external">Guard</a>两个工具的组合来解决。<a href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei" target="_blank" rel="external">LiveReload插件</a>是一个浏览器的插件,通过协议与后台的服务器进行通信。当后台文件发生变化时,
<a href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei" target="_blank" rel="external">LiveReload插件</a>会自动刷新页面。<code>Guard</code>会使用操作系统的API来感知本地文件的变化,当文件变化后,它可以通知<code>LiveReload</code>进行刷新,当然<code>Guard</code>可以做其他一些事情,比如监听less文件,当发生变化时,自动编译css等。
<br>由于此方案不是最佳方案,就不再赘述,有兴趣的童鞋可以自行google,查阅相关资料。缺点:这种方式需要先给浏览器安装<a href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei" target="_blank" rel="external">LiveReload插件</a>。</p>
<h3 id="Browsersync_+_gulp">Browsersync + gulp</h3>
<p>假如你恰好使用的是gulp作为fe工作流开发,假如你恰好为开发效率烦恼着。哈哈,不用担心,我觉得这套liveReload技术就是为你装备的。这里假设的前提是大家熟悉gulp基本使用。
<br>首先确立一个大致思路:</p>
<ul>
<li>
<p>gulp监听静态资源,根据自己的业务情况实现监听,当有文件变化即调用Browsersync提供的API;</p>
<figure class="highlight coffee">
<table>
<tr>
<td class="code"><pre><span class="line">...</span><br><span class="line"><span class="built_in">module</span>.exports = gulp = <span class="built_in">require</span> <span class="string">'gulp'</span></span><br><span class="line">browserSync = <span class="built_in">require</span> <span class="string">'browser-sync'</span></span><br><span class="line"><span class="comment"># 声明任务</span></span><br><span class="line">gulp.task <span class="string">'hot'</span>, <span class="function">-></span></span><br><span class="line"> <span class="comment"># 初始化browserSync工具</span></span><br><span class="line"> browserSync.init</span><br><span class="line"> <span class="attribute">ui</span>: <span class="comment"># browserSync管理界面 http://localhost:8082</span></span><br><span class="line"> <span class="attribute">port</span>: <span class="number">8082</span></span><br><span class="line"> <span class="attribute">weinre</span>: <span class="attribute">port</span>: <span class="number">9092</span></span><br><span class="line"> <span class="attribute">proxy</span>: <span class="comment"># 转发server</span></span><br><span class="line"> <span class="comment"># middleware: (req, res, next) -></span></span><br><span class="line"> <span class="attribute">target</span>: <span class="string">"localhost:3100"</span></span><br><span class="line"> <span class="attribute">startPath</span>: <span class="string">'/index.html?token=jytpsm1433227719&site_id=10001'</span> <span class="comment"># 弹出首页</span></span><br><span class="line"> <span class="attribute">ghostMode</span>: <span class="literal">false</span> <span class="comment"># 关闭自动模拟事件,这是一个坑</span></span><br><span class="line"> <span class="built_in">console</span>.log <span class="string">"开始监听资源文件..."</span></span><br><span class="line"> gulp.watch <span class="string">'less/**/*.less'</span>, <span class="function">-></span></span><br><span class="line"> <span class="built_in">console</span>.log <span class="string">"less文件重新加载..."</span></span><br><span class="line"> browserSync.reload()</span><br><span class="line"> gulp.watch <span class="string">'js/**/*.{js,html,mustache}'</span>, <span class="function">-></span></span><br><span class="line"> <span class="built_in">console</span>.log <span class="string">"组件文件重新加载..."</span></span><br><span class="line"> browserSync.reload()</span><br><span class="line"> gulp.watch <span class="string">'static/runtime/**/*.{js}'</span>, <span class="function">-></span></span><br><span class="line"> <span class="built_in">console</span>.log <span class="string">"runtime文件重新加载..."</span></span><br><span class="line"> browserSync.reload()</span><br><span class="line">...</span><br></pre></td>
</tr>
</table>
</figure>
</li>
<li>
<p>Browsersync负责调用API刷新浏览器或者替换变化,访问管理界面,还能进一步调试页面、多设备同步效果:</p>
<figure class="highlight coffee">
<table>
<tr>
<td class="code"><pre><span class="line">...</span><br><span class="line"><span class="comment"># 重新加载资源</span></span><br><span class="line">browserSync.reload()</span><br><span class="line">...</span><br></pre></td>
</tr>
</table>
</figure>
</li>
</ul>
<p>更多文档:<a href="http://www.browsersync.cn/docs" target="_blank" rel="external">http://www.browsersync.cn/docs</a></p>
<ul>
<li>开发模式下,主进程开启liveReload功能,只需要调用gulp提供的api即可:
<figure class="highlight coffee">
<table>
<tr>
<td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">if</span> process.env.NODE_ENV != <span class="string">'production'</span></span><br><span class="line"> gulp = <span class="built_in">require</span> <span class="string">'./gulpfile'</span></span><br><span class="line"> gulp.start <span class="string">'hot'</span></span><br><span class="line">...</span><br></pre></td>
</tr>
</table>
</figure>
</li>
</ul>
<p><code>ps:</code>此方案还能进一步优化,比如浏览器不必实时刷新,仅改变页面元素变化即可,当然Browsersync插件-<a href="http://www.browsersync.io/docs/options/#option-plugins" target="_blank" rel="external">bs-html-injector</a>已经实现了我们想要的,不必重复造轮子了。</p>
<h3 id="Webpack-Hot技术">Webpack-Hot技术</h3>
<p> 后续…</p>
<h2 id="技术原理">技术原理</h2>
<p>再来说说上文介绍的三种方案背后的原理,首先要明确这些技术的目标是什么,简单地说就是后端监听到资源被修改立刻通知浏览器。带着问题去试探技术背后的原理会比较容易理解。</p>
<h3 id="browserSync">browserSync</h3>
<p><code>1、browserSync 工作模式:</code>
<br>browserSync本身就内置一个静态文件服务器,此服务器不仅可以脱离业务逻辑直接管理静态资源,并且可以不处理业务逻辑代理到下游服务,前者是手动模式,后者是代理模式。代理模式是方便了对接已有业务的情况。
<br><code>2、browserSync 初始化:</code>
<br>browserSync一旦配置成功便会初始化一个server实例,以便browserSync服务与浏览器通信,通信协议用的是websocket协议实现长连接通信。
<br><code>3、proxy模式:</code>
<br> 选择代理模式,browserSync将完全接管后端入口,初始化完毕后会启动指定网页入口,页面渲染时将在
<body>里注入一段script脚本,即ws协议的client,用来与后端server通信。
<br> <img src="/2015/10/26/web-hot-load/browserSync-proxy-1.png" alt="[proxy模式-1]" title="[proxy模式-1]">
<br> <img src="/2015/10/26/web-hot-load/browserSync-proxy-2.png" alt="[proxy模式-2]" title="[proxy模式-2]">
<br><code>4、手动模式:</code>
<br> 选择手动模式,browserSync将会提示你手动插入script脚本,方便建立ws长连接。
<br> <img src="/2015/10/26/web-hot-load/browserSync-manual.png" alt="[Manual模式]" title="[Manual模式]">
<br><code>5、browserSync 实现通信方式:</code>
<br> 由图可以看到当html页面渲染时,browserSync注入script会将和后端服务建立websocket长连接服务。
<br> <img src="/2015/10/26/web-hot-load/browserSync-ws.png" alt="[websocket]" title="[websocket]">
<br><code>6、browserSync UI管理:</code>
<br> UI管理界面很好地给开发人员提供管理界面,其原理就是调用browserSync提供的api,然后通过ws协议让浏览器实时响应。 e.g. 比如向页面注入weinre服务 etc.
<br> <img src="/2015/10/26/web-hot-load/browserSync-ui-1.png" alt="[ui-1]" title="[ui-1]">
<br> <img src="/2015/10/26/web-hot-load/browserSync-ui-2.png" alt="[ui-2]" title="[ui-2]">
<br><code>7、FAQ</code>
<br> 这里说的是别人踩过的坑,记录的目的是省得再走弯路。
<br> <img src="/2015/10/26/web-hot-load/browserSync-bug-1.jpg" alt="[bug-1]" title="[bug-1]">
<br> <img src="/2015/10/26/web-hot-load/browserSync-bug-2.jpg" alt="[bug-2]" title="[bug-2]">
<br> <img src="/2015/10/26/web-hot-load/browserSync-bug-3.jpg" alt="[bug-3]" title="[bug-3]"></body></p>
<h3 id="webpack">webpack</h3>
<p>后续…</p>
<h2 id="总结">总结</h2>
<p>一个优秀技术人员应勤于思考,善于总结。绝不限定自己的思维和能力,通过不断总结完善自我知识体系;不断吸收先进技术培养出大局观是成为技术大牛必经之路!</p>]]></content>
<summary type="html">
<![CDATA[<p><strong>ps</strong>:下文我将打算用<code>fe</code>代替<code>Front-End</code>一词。</p>
<p>时间过得真快,眨眼一周又过去了。今天我们来侃侃liveReload。聊之前先来扯扯开发家常。
<br>通常来说,We]]>
</summary>
<category term="Front-End" scheme="http://yoursite.com/tags/Front-End/"/>
<category term="web技术" scheme="http://yoursite.com/categories/web%E6%8A%80%E6%9C%AF/"/>
</entry>
<entry>
<title><![CDATA[Hash简介]]></title>
<link href="http://yoursite.com/2015/10/17/algorithm-hash/"/>
<id>http://yoursite.com/2015/10/17/algorithm-hash/</id>
<published>2015-10-17T14:53:06.000Z</published>
<updated>2015-10-25T16:02:16.000Z</updated>
<content type="html"><![CDATA[<p>译文:<a href="http://codecapsule.com/2013/05/13/implementing-a-key-value-store-part-5-hash-table-implementations/" target="_blank" rel="external">http://codecapsule.com/2013/05/13/implementing-a-key-value-store-part-5-hash-table-implementations/</a></p>
<p>大家都知道每种编程语言里都提供Hash结构,不过对于Hash的知识,大家是不是觉得很抽象?
<br>Hash结构在内存到底是怎么存放的?Hash算法使用场景?带着这些问题来读读此文,可能将会有不一样的收获。
<br>到底哈希函数是CPU密集型的并且应该维持而优化。然而,大部分哈希表的内部机制只关心内存效率和I/O访问,本文仅仅是<a href="http://codecapsule.com/2013/05/13/implementing-a-key-value-store-part-5-hash-table-implementations/" target="_blank" rel="external">《Implementing a Key-Value Store》</a>读书笔记,
<br>来看看哈希表三种的实现,既有内存中的又有硬盘上的,并看看数据是怎么组织和访问的。</p>
<h2 id="哈希表">哈希表</h2>
<blockquote>
<p>哈希表可以认为是人类所知最为重要的数据结构。</p>
<p>— 斯蒂夫 耶奇</p>
</blockquote>
<h3 id="哈希表简介">哈希表简介</h3>
<p>哈希表可以高效的访问关联数据。每个entry都有一对对应的key和value,并且能仅通过key来快速的取回和赋值。
<br><code>为了达到这个目的,把key通过哈希函数进行哈希,以将key从原始形式转换为整数。
此整数之后作为索引来得到要访问的entry所在的bucket在bucket数组中的地址。很多key可以被哈希为相同的值,这表示这些key在bucket数组中回出现冲突。</code>有多种方法解决冲突,e.g. 使用链表的分离链表(separate chaining 亦称开链或单独链表)或自平衡二叉树或线性或者二次探测的开放寻址。
<br>正常情况下,Hash算法输出分布越均匀越好。</p>
<p>从现在开始,我默认你知道什么是哈希表。如果你认为自己需要温习一下知识,Wikipedia的“Hash table”词条<a href="#">[1]</a>(及其底部的扩展链接一节)和Cormen 等人写的Introduction to Algorithms一书中Hash table一章<a href="#">[2]</a>都是很好的参考文献。</p>
<h3 id="哈希函数">哈希函数</h3>
<blockquote>
<p>hash(a) -> xxxx 😄</p>
</blockquote>
<p>哈希函数的选择相当重要。一个好哈希函数的基本需求是输出的哈希值比较均匀。这样可以使碰撞的发生最小化,同时使得各个bucket中碰撞的条目比较平均。</p>
<p>哈希函数实现算法有很多,除非你确切的知道数据会变成什么样子,最安全的方法是找一个能够将随机数据分布均匀的哈希函数,如果可能的话符合雪崩效应<a href="#">[3]</a>。
<br>有少数人对哈希函数做过比较<a href="#">[4]</a> <a href="#">[5]</a> <a href="#">[6]</a> <a href="#">[7]</a>,
<br>而他们的结论是MurmurHash3 <a href="#">[8]</a>和CityHash <a href="#">[9]</a> 是在目前最好的哈希函数。</p>
<h2 id="实现算法">实现算法</h2>
<p>在下述三个哈希表库的描述中,我的通用示例entry是用城市名作为key其各自的GPS坐标作为value。
<br><img src="/2015/10/17/algorithm-hash/hash1.jpg" alt="[3种hash算法]" title="[3种hash算法]"></p>
<h3 id="TR1的_unordered_map">TR1的 unordered_map</h3>
<p>TR1的unordered_map提供了一个用链表(分离链)解决碰撞的哈希表。
<br>Bucket数组位于堆中,并且基于哈希表的负载系数自动调整因子大小。而bucket的链表则是用叫做_Hash_node的节点结构体创建。</p>
<p><code>ps:用hash(key)算出桶号,为了解决冲突,算出key冲突的全部在用相同桶号用一个链表结构存着;
利用key存取道理都是一样:hashcode定位了桶,key本身定位了链表结构中的某个entry。
java里的hashMap就是这样结构,负载因子是用来决定桶bucket大小的,默认是0.75 ...</code></p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="comment">/* from gcc-4.8.0/libstdc++-v3/include/tr1/hashtable_policy.h */</span></span><br><span class="line">template</span><br><span class="line"> struct _Hash_node<_Value, <span class="literal">false</span>> {</span><br><span class="line"> _Value _M_v;</span><br><span class="line"> _Hash_node* _M_next;</span><br><span class="line"> };</span><br></pre></td>
</tr>
</table>
</figure>
<p>如果键和值都是整型,其可以直接存储在_M_v结构体中。否则将会存储指针,同时需要额外的内存。
<br>Bucket数组是在堆中一次性分配的,但并不分配节点的空间,节点的空间是通过各自调用C++内存分配器来分配的。</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="comment">/* from gcc-4.8.0/libstdc++-v3/include/tr1/hashtable.h */</span></span><br><span class="line">Node* _M_allocate_node(<span class="keyword">const</span> value_type& __v)</span><br><span class="line"> {</span><br><span class="line"> _Node* __n = _M_node_allocator.allocate(<span class="number">1</span>);</span><br><span class="line"> __try</span><br><span class="line"> {</span><br><span class="line"> _M_get_Value_allocator().construct(&__n->_M_v, __v);</span><br><span class="line"> __n->_M_next = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> __n;</span><br><span class="line"> }</span><br><span class="line"> __catch(...)</span><br><span class="line"> {</span><br><span class="line"> _M_node_allocator.deallocate(__n, <span class="number">1</span>);</span><br><span class="line"> __throw_exception_again;</span><br></pre></td>
</tr>
</table>
</figure>
<p>因为这些节点是各自分配的,分配过程中可能浪费大量的内存。这取决于编译器和操作系统使用的内存分配过程。我甚至还没说每次分配中系统执行的调用。SGI哈希表的原始实现为这些节点做了一些资源预分配工作,但这个方法没有保留在TR1的unordered_map实现中。</p>
<p>下图展示了TR1中unordered_map的内存和访问模式。当我们访问key是“Johannesburg”相关的GPS坐标的时候会发生什么。这个键名被哈希并映射到了bucket #0。
<br>在那我们跳到了此bucket的链表的第一个节点(bucket#0左边的橙色箭头),我们可以访问堆内存中存储了键“Johannesburg”所属数据的内存区域(节点右侧的黑色箭头)。
<br>如果key所指向的第一个节点不可用,就必须遍历其他的节点来访问。</p>
<p>至于CPU性能,不能指望所有的数据都在处理器的同一个缓存行中。实际上,基于bucket数组的大小,初始bucket和初始节点不会在同一个缓存行中,而和节点相关的外部数据同样不太可能在同一个缓存行中。
<br>而随后的节点机器相关数据同样不会在同一个缓存行中并且需要从RAM中取回。如果你不熟悉CPU优化和缓存行,维基上的“CPU Cache”文章是一个很好的介绍<a href="#">20</a>。
<br><img src="/2015/10/17/algorithm-hash/hash2.jpg" alt="[unordered_map内存结构]" title="[unordered_map内存结构]"></p>
<h3 id="SparseHash的_dense_hash_map">SparseHash的 dense_hash_map</h3>
<p>SparseHash库提供了两个哈希表实现,sparse_hash_map和dense_hash_map。sparse_hash_map在低成本下提供了出色的内存占用,并使用一个特定的数据结构sparsetable来打到这个目的。在SparseHash的“Implementation notes”页面<a href="#">19</a>可以找到更多关于sparsetables 和sparse_hash_map的信息。在此我只讨论dense_hash_map。</p>
<p>dense_hash_map用二次内部探测处理碰撞。和unordered_map一样,bucket数组也是在堆中一次分配,并基于哈希表的负载因子调整大小。bucket数组的元素是std::pair的实例,其中Key和T分别是键名和键值的模版参数。在64位架构下储存字符串的时候,pair的实例大小是16字节。</p>
<p>下图是dense_hash_map内存和访问模式的展示。如果我们要寻找“Johannesburg”的坐标,我们一开始会进入bucket #0,其中有“Paris”(译注:图上实际应为“Dubai”)的数据(bucket #0右侧的黑色箭头)。因此必须探测然后跳转到bucket (i + 1) = (0 + 1) = 1(bucket #0左侧的橙色箭头),然后就能在bucket #1中找到“Johannesburg”的数据。这看上去和unordered_map中做的事情差不多,但其实完全不同。当然,和unordered_map一样,键名和键值都必须存储在分配于堆中的内存,这将导致对键名和键值的寻找会使缓存行无效化。但为碰撞的条目寻找一个bucket相对较快一些。实际上既然每个pair都是16字节而大多数处理器上的缓存行都是64字节,每次探测就像是在同一个缓存行上。这将急剧提高运算速度,与之相反的是unordered_map中的链表需要在RAM中跳转以寻找余下的节点。</p>
<p>二次内部探测提供的缓存行优化使得dense_hash_map成为所有内存哈希性能测试中的赢家(至少是在我目前读过的这些中)。你应该花点时间来看看Nick Welch的文章“Hash Table Benchmarks” <a href="#">10</a>。
<br><img src="/2015/10/17/algorithm-hash/hash3.jpg" alt="[dense_hash_map内存结构]" title="[dense_hash_map内存结构]"></p>
<h3 id="Kyoto_Cabinet的_HashDB">Kyoto Cabinet的 HashDB</h3>
<p>Kyoto Cabinet实现了很多数据结构,其中就有哈希表。这个哈希表HashDB虽然有一个选项可以用来把他用作代替std::map的内存哈希表,但其是设计用于在硬盘上持久化的。哈希表的元数据和用户数据一起用文件系统依次存储在硬盘上唯一的文件中。
<br>Kyoto Cabinet使用每个bucket中独立的二叉树处理碰撞。Bucket数组长度固定且不改变大小,无视负载因子的状态。这是Kyoto Cabinet的哈希表实现的主要缺陷。实际上,如果数据库创建的时候定义的bucket数组的长度低于实际需求,当条目开始碰撞的时候性能会急剧下降。</p>
<p>允许硬盘上的哈希表实现改变bucket数组大小是很难的。首先,其需要bucket数组和条目存储到两个不同的文件中,其大小会各自独立的增长。第二,因为调整bucket数组大小需要将键名重新哈希到新bucket数组的新位置,这需要从硬盘中读取所有条目的键名,这对于相当大的数据库来说代价太高以至于几乎不可能。避免这种重新哈希过程的一种方法是,存储哈希后键名的时候每个条目预留4或8个字节(取决于哈希是长度32还是64 bit)。因为这些麻烦事,固定长度的bucket数组更简单,而Kyoto Cabinet中采用了这个方法。</p>
<p>下图显示出文件中存储的一个HashDB的结构。我是从calc_meta()方法的代码,和kchashdb.h尾部HashDB类中属性的注释中得到的这个内部结构。此文件以如下几个部分组织:</p>
<ul>
<li>头部有数据库所有的元数据</li>
<li>包含数据区域中可用空间的空块池</li>
<li>bucket数组</li>
<li>记录(数据区域)</li>
</ul>
<p>一条记录包含一个条目(键值对),以及此独立链的二叉树节点。这里是Record结构体:
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="comment">/* from kyotocabinet-1.2.76/kchashdb.h */</span></span><br><span class="line"><span class="comment">/*** Record data.*/</span></span><br><span class="line"> struct Record {</span><br><span class="line"> int64_t off; </span><br><span class="line"><span class="comment">///< offset</span></span><br><span class="line"> size_t rsiz; </span><br><span class="line"><span class="comment">///< whole size</span></span><br><span class="line"> size_t psiz; </span><br><span class="line"><span class="comment">///< size of the padding</span></span><br><span class="line"> size_t ksiz; </span><br><span class="line"><span class="comment">///< size of the key</span></span><br><span class="line"> size_t vsiz; </span><br><span class="line"><span class="comment">///< size of the value</span></span><br><span class="line"> int64_t left; </span><br><span class="line"><span class="comment">///< address of the left child record</span></span><br><span class="line"> int64_t right; </span><br><span class="line"><span class="comment">///< address of the right child record</span></span><br><span class="line"> <span class="keyword">const</span> char* kbuf; </span><br><span class="line"><span class="comment">///< pointer to the key</span></span><br><span class="line"> <span class="keyword">const</span> char* vbuf; </span><br><span class="line"><span class="comment">///< pointer to the value</span></span><br><span class="line"> int64_t boff; </span><br><span class="line"><span class="comment">///< offset of the body</span></span><br><span class="line"> char* bbuf; </span><br><span class="line"><span class="comment">///< buffer of the body</span></span><br><span class="line"> };</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>下图可以看到记录在硬盘上的组织。我从kchashdb.h中的write_record()方法中得到组织方法。注意其和Record结构体不同:保存在硬盘上的目标是最小化硬盘占用,然而结构体的目标是使记录在编程的时候用起来比较方便。下图的所有变量都有固定长度,除了key、value、 和padding,其当然是取决于数据条目中数据的尺寸。变量left 和right是二叉树节点的一部分,储存文件中其他记录的offset。
<br><img src="/2015/10/17/algorithm-hash/hash4.jpg" alt="[HashDB结构]" title="[HashDB结构]">
<br><img src="/2015/10/17/algorithm-hash/hash5.jpg" alt="[record结构]" title="[record结构]"></p>
<p>上图如果我们要访问键名”Paris”的键值,一开始要获得相关bucket的初始记录,在本例中是bucket #0。然后跳转到此bucket二叉树的头节点(bucket #0右侧的橙色箭头),其保存键名为”Johannesburg”的数据。键名为”Paris”的数据需要通过当前节点的右侧节点来访问(”Johannesburg”记录右侧的黑色箭头)。二叉树需要一个可比较的类型来对节点分类。这里用的可比较类型是用fold_hash()方法将哈希过的键名缩减得到的。
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="comment">/* from kyotocabinet-1.2.76/kchashdb.h */</span></span><br><span class="line">uint32_t fold_hash(uint64_t hash) {</span><br><span class="line"> _assert_(<span class="literal">true</span>);</span><br><span class="line"> <span class="keyword">return</span> (((hash & <span class="number">0xffff000000000000</span>ULL) >> <span class="number">48</span>) | ((hash & <span class="number">0x0000ffff00000000</span>ULL) >> <span class="number">16</span>)) ^</span><br><span class="line"> (((hash & <span class="number">0x000000000000ffff</span>ULL) << <span class="number">16</span>) | ((hash & <span class="number">0x00000000ffff0000</span>ULL) >> <span class="number">16</span>));</span><br><span class="line">}</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>把数据条目和节点一起存储在单一记录中,乍一看像是设计失误,但其实是相当聪明的。为了存储一个条目的数据,总是需要保持三种不同的数据:bucket、碰撞和条目。既然bucket数组中的bucket必须顺序存储,其需要就这样存储并且没有任何该进的方法。假设我们保存的不是整型而是不能存储在bucket中的字符或可变长度字节数组,这使其必须访问此bucket数组区域之外的其他内存。这样当添加一个新条目的时候,需要即保存冲突数据结构的数据,又要保存该条目键名和键值的数据。</p>
<p>如果冲突和条目数据分开保存,其需要访问硬盘两次,再加上必须的对bucket的访问。如果要设置新值,其需要总计3次写入,并且写入的位置可能相差很远。这表示是在硬盘上的随机写入,这差不多是I/O的最糟糕的情况了。现在既然Kyoto Cabinet的HashDB中节点数据和条目数据存储在一起,其就可以只用一次写入写到硬盘中。当然,仍然必须访问bucket,但如果bucket数组足够小,就可以通过操作系统将其从硬盘中缓存到RAM中。如规范中”Effective Implementation of Hash
Database”一节[17]声明的,Kyoto Cabinet可能采用这种方式。</p>
<p>然而在硬盘上用二叉树存储条目需要注意的一点是,其会降低读取速度,至少当碰撞出现的时候会是这样。实际上,因为节点和条目存储在一起,处理一个bucket中的碰撞实际上是在一个二叉树中寻找要找的条目,这可能需要大量的对硬盘的随机读取。这可以让我们理解当条目的数量超过bucket数量时Kyoto Cabinet的性能急剧下降的原因。</p>
<p>最后,因为所有的东西都是存在文件中,Kyoto Cabinet是自己处理内存管理,而非像unordered_map 和dense_hash_map那样交给操作系统处理。FreeBlock结构体保存着和文件中空闲空间的信息,其基本上是offset和大小,如下:
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="comment">/* from kyotocabinet-1.2.76/kchashdb.h */</span></span><br><span class="line"><span class="comment">/*** Free block data.*/</span></span><br><span class="line"> struct FreeBlock {</span><br><span class="line"> int64_t off; </span><br><span class="line"><span class="comment">///< offset</span></span><br><span class="line"> size_t rsiz; </span><br><span class="line"><span class="comment">///< record size</span></span><br><span class="line"><span class="comment">/** comparing operator */</span></span><br><span class="line"> bool operator <(<span class="keyword">const</span> FreeBlock& obj) <span class="keyword">const</span> {</span><br><span class="line"> _assert_(<span class="literal">true</span>);</span><br><span class="line"> <span class="keyword">if</span> (rsiz < obj.rsiz) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">if</span> (rsiz == obj.rsiz && off > obj.off) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> };</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>所有的FreeBlock实例都加载在std::set中,其可以像fetch_free_block()方法中那样使用std::set的upper_bound()方法来释放内存块,从而实现“最佳拟合”的内存分配策略。当可用空间显得过分细碎或者FreeBlock池中没有空间了,文件将进行碎片整理。此碎片整理过程通过移动各条记录来减少数据库文件的整体大小。</p>
<h2 id="小结">小结</h2>
<p>本文中,展示了三个不同哈希表库的数据组织和内存访问模式。<code>TR1的 unordered_map和SparseHash的 dense_hash_map是在内存中的,而Kyoto Cabinet的 HashDB是在硬盘上的。</code>此三者用不同的访问处理hash碰撞,并对性能有不同的影响。将bucket数据、碰撞数据和条目数据各自分开将影响性能,这是unordered_map中出现的情况。如dense_hash_map及其二次内部探测那样,将碰撞数据和bucket存储在一起;或者像HashDB那样,将碰撞数据和条目数据存储在一起都可以大幅提高速度。此两者都可以提高写入速度,但将碰撞数据和bucket存储在一起可以使读取更快。</p>
<p>如果让我说从这些哈希表中学到的最重要的东西是什么的话,我会说在设计哈希表的数据组织的时候,首选的解决方案是将碰撞数据和bucket数据存储在一起。因为即便是在硬盘上,bucket数组和碰撞数据也会相当小,足够它们存储在RAM中,随机读取的花费将会比在硬盘上低得多。</p>
<h2 id="参考文献">参考文献</h2>
<p>[1]<a href="http://en.wikipedia.org/wiki/Hash_table" target="_blank" rel="external">http://en.wikipedia.org/wiki/Hash_table</a>
<br>[2]<a href="http://www.amazon.com/Introduction-Algorithms-Thomas-H-Cormen/dp/0262033844" target="_blank" rel="external">http://www.amazon.com/Introduction-Algorithms-Thomas-H-Cormen/dp/0262033844</a>
<br>[3]<a href="http://en.wikipedia.org/wiki/Avalanche_effect" target="_blank" rel="external">http://en.wikipedia.org/wiki/Avalanche_effect</a>
<br>[4]<a href="http://blog.reverberate.org/2012/01/state-of-hash-functions-2012.html" target="_blank" rel="external">http://blog.reverberate.org/2012/01/state-of-hash-functions-2012.html</a>
<br>[5]<a href="http://www.strchr.com/hash_functions" target="_blank" rel="external">http://www.strchr.com/hash_functions</a>
<br>[6]<a href="http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633" target="_blank" rel="external">http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633</a>
<br>[7]<a href="http://blog.aggregateknowledge.com/2012/02/02/choosing-a-good-hash-function-part-3" target="_blank" rel="external">http://blog.aggregateknowledge.com/2012/02/02/choosing-a-good-hash-function-part-3</a>
<br>[8]<a href="https://sites.google.com/site/murmurhash" target="_blank" rel="external">https://sites.google.com/site/murmurhash</a>
<br>[9]<a href="http://google-opensource.blogspot.fr/2011/04/introducing-cityhash.html" target="_blank" rel="external">http://google-opensource.blogspot.fr/2011/04/introducing-cityhash.html</a>
<br>[10]<a href="http://incise.org/hash-table-benchmarks.html" target="_blank" rel="external">http://incise.org/hash-table-benchmarks.html</a>
<br>[11]<a href="http://preshing.com/20110603/hash-table-performance-tests" target="_blank" rel="external">http://preshing.com/20110603/hash-table-performance-tests</a>
<br>[12]<a href="http://attractivechaos.wordpress.com/2008/08/28/comparison-of-hash-table-libraries" target="_blank" rel="external">http://attractivechaos.wordpress.com/2008/08/28/comparison-of-hash-table-libraries</a>
<br>[13]<a href="http://attractivechaos.wordpress.com/2008/10/07/another-look-at-my-old-benchmark" target="_blank" rel="external">http://attractivechaos.wordpress.com/2008/10/07/another-look-at-my-old-benchmark</a>
<br>[14]<a href="http://blog.aggregateknowledge.com/2011/11/27/big-memory-part-3-5-google-sparsehash" target="_blank" rel="external">http://blog.aggregateknowledge.com/2011/11/27/big-memory-part-3-5-google-sparsehash</a>
<br>[15]<a href="http://gcc.gnu.org" target="_blank" rel="external">http://gcc.gnu.org</a>
<br>[16]<a href="https://code.google.com/p/sparsehash" target="_blank" rel="external">https://code.google.com/p/sparsehash</a>
<br>[17]<a href="http://fallabs.com/kyotocabinet/spex.html" target="_blank" rel="external">http://fallabs.com/kyotocabinet/spex.html</a>
<br>[18]<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1456.html" target="_blank" rel="external">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1456.html</a>
<br>[19]<a href="http://sparsehash.googlecode.com/svn/trunk/doc/implementation.html" target="_blank" rel="external">http://sparsehash.googlecode.com/svn/trunk/doc/implementation.html</a>
<br>[20]<a href="http://en.wikipedia.org/wiki/CPU_cache" target="_blank" rel="external">http://en.wikipedia.org/wiki/CPU_cache</a></p>]]></content>
<summary type="html">
<![CDATA[<p>译文:<a href="http://codecapsule.com/2013/05/13/implementing-a-key-value-store-part-5-hash-table-implementations/" target="_blank" rel="ext]]>
</summary>
<category term="Hash" scheme="http://yoursite.com/tags/Hash/"/>
<category term="Algorithms" scheme="http://yoursite.com/categories/Algorithms/"/>
</entry>
<entry>
<title><![CDATA[Node.js性能调优]]></title>
<link href="http://yoursite.com/2015/10/11/web-nodejs-performance/"/>
<id>http://yoursite.com/2015/10/11/web-nodejs-performance/</id>
<published>2015-10-11T06:44:12.000Z</published>
<updated>2015-10-18T07:29:47.000Z</updated>
<content type="html"><![CDATA[<p><a href="/2015/10/11/web-nodejs-debug/">上篇文章:《Node.js断言方案》</a>介绍了Node.js断言技术,现在我们再来说说有关性能调优相关技术。</p>
<h2 id="性能调优">性能调优</h2>
<p>说完了<code>Node.js</code>调试方面需要掌握的基本技能,下面说一下性能调优相关的技巧。
<br>Node.js相比Java、PHP这些老牌语言,基础设施还是有所欠缺的:比如性能分析和监控工具等,加上它的单线程运行特性,在大型应用中,很容易让系统的CPU、内存或者事件队列太满达到瓶颈,从而导致程序崩溃。一旦发现程序警报CPU负载过高,或者内存飙高时,我们该如何排查Node.js代码存在的问题呢?首先先分析一下问题。</p>
<p>内存持高存在的因素:</p>
<ul>
<li>缓存,很多人在code的时候把内存当缓存用,e.g. 使用js对象储存用户的session信息</li>
<li>闭包,作用域长时间不能被释放掉</li>
<li>生产者和消费者存在速度延迟,e.g. 数据库忙不过来,query队列堆积</li>
</ul>
<p>CPU负载过高可能因素:</p>
<ul>
<li>垃圾回收频率过高、量太大,这一般是因为内存或者缓存暴涨导致的</li>
<li>密集型的长循环计算,e.g. 大量遍历文件夹、大量计算等</li>
<li>客户端并发数太大,e.g. 利用node作为socket服务端时,客户端连接数太大时,心跳处理也属于一次网络io,最终libuv转换成事件,严重影响业务消息发送</li>
</ul>
<p>这些问题都是让人头疼的,一个项目几十上百个文件,收到这些警报如果没有经验,根本无从下手排查。</p>
<p>最直接的手段就是分析 GC 日志,因为程序的一举一动都会反馈到 GC 上,而上述问题也会一一指向 GC,如:
<br>内存暴涨,尤其是 Old Space 内存的暴涨,会直接导致 GC 的次数和时间增长
<br>缓存增加,导致 GC 的时间增加,无用遍历过多
<br>密集型计算,导致 GC Now Space次数增加</p>
<p><code>ps:</code>阿里出了一套调优工具<a href="http://alinode.aliyun.com/" target="_blank" rel="external">alinode</a>,看起来比较不错,感兴趣的童鞋可以试试,顺便反馈一下情况。😄</p>
<h3 id="内存管理">内存管理</h3>
<p>有关<code>Node.js</code>内存管理和垃圾回收机制,这里简单介绍一下,用心的同学可以去搜索一下这方面的资料。
<br><code>V8</code>把内存分为 New Space 和 Old Space,New Space 的大小默认为 8M,Old Space 的大小默认为 0.7G,64位系统这两个数值翻倍。
<br>对象的生命周期是:首先进入 New Space,然而 New Space 被平均分为两份,当一次GC运行后都会将其中一份的活着的对象复制到另一份,所以它的空间使用率是<code>50%</code>,这个算法叫做 Cheney 算法,这个操作叫做 Scavenge。再过一段时间,如果 New Space 中的对象还活着,这些对象会被挪到 Old Space 中去,GC 会每隔一段时间遍历 Old Space 中死掉的对象,然后整理碎片(这里有两种模式 mark-sweep 和 mark-compact,不赘述)。上面提到的<code>死掉的对象</code>指的是对象已经没有被引用了,或者说被引用的次数为零了。</p>
<h3 id="分析GC日志">分析GC日志</h3>
<p>上面说完Node.js内存管理后,下面我们开始分析具体问题了,e.g. 假设缓存增加(比如使用对象缓存了很多用户信息),GC 是不知道这些缓存死了还是活着的,他们会不停地查看这个对象,以及这个对象中的子对象是否还存活,如果这个对象数特别大,那么GC遍历的时间也会特别长。
<br>当我们进行密集型计算的时候,会产生很多中间变量,这些变量往往在New Space中就死掉了,那么GC也会在这里多次地进行New Space区域的垃圾回收。
<br>如何分析?
<br>Node在启动程序的时候添加<code>--trace_gc</code>参数,当V8在进行垃圾回收的时候,会将垃圾回收的信息打印出来:
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node --trace_gc test.js</span><br><span class="line">...</span><br><span class="line">[<span class="number">94036</span>] <span class="number">68</span> ms: Scavenge <span class="number">8.4</span> (<span class="number">42.5</span>) -> <span class="number">8.2</span> (<span class="number">43.5</span>) MB, <span class="number">2.4</span> ms [allocation failure].</span><br><span class="line">[<span class="number">94036</span>] <span class="number">74</span> ms: Scavenge <span class="number">8.9</span> (<span class="number">43.5</span>) -> <span class="number">8.9</span> (<span class="number">46.5</span>) MB, <span class="number">5.1</span> ms [allocation failure].</span><br><span class="line">[<span class="number">94036</span>] Increasing marking speed to <span class="number">3</span> due to high promotion rate</span><br><span class="line">[<span class="number">94036</span>] <span class="number">85</span> ms: Scavenge <span class="number">16.1</span> (<span class="number">46.5</span>) -> <span class="number">15.7</span> (<span class="number">47.5</span>) MB, <span class="number">3.8</span> ms (+ <span class="number">5.0</span> ms <span class="keyword">in</span> <span class="number">106</span> steps since last GC) [allocation failure].</span><br><span class="line">[<span class="number">94036</span>] <span class="number">95</span> ms: Scavenge <span class="number">16.7</span> (<span class="number">47.5</span>) -> <span class="number">16.6</span> (<span class="number">54.5</span>) MB, <span class="number">7.2</span> ms (+ <span class="number">1.3</span> ms <span class="keyword">in</span> <span class="number">14</span> steps since last GC) [allocation failure].</span><br><span class="line">[<span class="number">94036</span>] <span class="number">111</span> ms: Mark-sweep <span class="number">23.6</span> (<span class="number">54.5</span>) -> <span class="number">23.2</span> (<span class="number">54.5</span>) MB, <span class="number">6.2</span> ms (+ <span class="number">15.3</span> ms <span class="keyword">in</span> <span class="number">222</span> steps since start <span class="keyword">of</span> marking, biggest step <span class="number">0.3</span> ms) [GC interrupt] [GC <span class="keyword">in</span> old space requested].</span><br><span class="line">...</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>V8也提供了很多程序启动选项:</p>
<table>
<thead>
<tr>
<th>选项</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>–max-stack-size</td>
<td>设置栈大小</td>
</tr>
<tr>
<td>–v8-options</td>
<td>打印V8相关命令</td>
</tr>
<tr>
<td>–trace-bailout</td>
<td>查找不能被优化的函数,重写</td>
</tr>
<tr>
<td>–trace-deopt</td>
<td>查找不能优化的函数</td>
</tr>
</tbody>
</table>
<p>这些选项可以让我们查看V8在执行时的各种log日志,对于排查隐晦问题比较有用。</p>
<p>然而这堆日志看起来很糟糕,可以先将日志输出来之后交给专业的工具帮我们分析,相信大部分人都用Chrome DevTools的JavaScript CPU Profile:
<br>
</p>
<p>通过Profile工具可以找到具体函数在整个程序中的执行时间和执行时间占比,从而分析到具体的代码问题,V8 也提供了Profile日志导出:
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">node --prof test.js</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>执行命令之后,会在该目录下产生一个 *-v8.log 的日志文件,我们可以安装一个日志分析工具 tick:</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ sudo npm install tick -g</span><br><span class="line">➜ $ node-tick-processor *-v8.log</span><br><span class="line">[Top down (heavy) profile]:</span><br><span class="line"> Note: callees occupying less than <span class="number">0.1</span>% are not shown.</span><br><span class="line"> inclusive self name</span><br><span class="line"> ticks total ticks total</span><br><span class="line"> <span class="number">426</span> <span class="number">36.7</span>% <span class="number">0</span> <span class="number">0.0</span>% <span class="built_in">Function</span>: ~<span class="xml"><span class="tag"><<span class="title">anonymous</span>></span> node.js:27:10</span><br><span class="line"> 426 36.7% 0 0.0% LazyCompile: ~startup node.js:30:19</span><br><span class="line"> 410 35.3% 0 0.0% LazyCompile: ~Module.runMain module.js:499:26</span><br><span class="line"> 409 35.2% 0 0.0% LazyCompile: Module._load module.js:273:24</span><br><span class="line"> 407 35.1% 0 0.0% LazyCompile: ~Module.load module.js:345:33</span><br><span class="line"> 406 35.0% 0 0.0% LazyCompile: ~Module._extensions..js module.js:476:37</span><br><span class="line"> 405 34.9% 0 0.0% LazyCompile: ~Module._compile module.js:378:37</span><br><span class="line">...</span></span><br></pre></td>
</tr>
</table>
</figure>
<p>也可以使用headdump之类的工具将日志导出,然后放到Chrome的Profile中去分析,如果看过深入浅出node.js的童鞋可以参考内存调优的章节。</p>
<h2 id="总结">总结</h2>
<p>对于任何一门程序而已,内存调优都是比较难的,这方面是需要智慧和经验,然而并不要被困难吓到,不断去折腾,相信每一次都会有新的收获。</p>
<p>参考:
<br><a href="https://nodejs.org" target="_blank" rel="external">https://nodejs.org</a>
<br><a href="https://iojs.org" target="_blank" rel="external">https://iojs.org</a>
<br><a href="http://www.barretlee.com/blog/2015/10/07/debug-nodejs-in-command-line/" target="_blank" rel="external">http://www.barretlee.com/blog/2015/10/07/debug-nodejs-in-command-line/</a></p>
<hr>
<p>博客:<a href="https://vectorho.github.io" target="_blank" rel="external">https://vectorho.github.io</a>
<br>作者:<a href="https://vectorho.github.io/about/" target="_blank" rel="external">Vector Ho</a></p>]]></content>
<summary type="html">
<![CDATA[<p><a href="/2015/10/11/web-nodejs-debug/">上篇文章:《Node.js断言方案》</a>介绍了Node.js断言技术,现在我们再来说说有关性能调优相关技术。</p>
<h2 id="性能调优">性能调优</h2>
<p>说完了<code>]]>
</summary>
<category term="Node.js" scheme="http://yoursite.com/tags/Node-js/"/>
<category term="web技术" scheme="http://yoursite.com/categories/web%E6%8A%80%E6%9C%AF/"/>
</entry>
<entry>
<title><![CDATA[Node.js断言方案]]></title>
<link href="http://yoursite.com/2015/10/11/web-nodejs-debug/"/>
<id>http://yoursite.com/2015/10/11/web-nodejs-debug/</id>
<published>2015-10-11T06:44:12.000Z</published>
<updated>2015-10-12T07:50:13.000Z</updated>
<content type="html"><![CDATA[<p><a href="https://nodejs.org" target="_blank" rel="external">Node.js</a>是什么?Node.js适合什么场景?多少公司已用于生产环境?Node.js靠不靠谱?这个话题应该无可厚非了,如果今天还在质疑Node.js的能力,相信它一定不是一家面向互联网公司。
<br>自 2009 年<code>Ryan Dahl</code>发布后,到现在已经六个年头了。人红是非多,先后经历了ruby、php web擂台之战,后因某些原因派生出了<a href="https://iojs.org" target="_blank" rel="external">iojs</a>这个兄弟;分久必合,大势所趋。最近两兄弟又合体了,衍生出了Node.js V4.0版本,官方又解释这个版本是Node.js V1.0 新版本,原班人马重新回归。关于1.0新时代,有兴趣的童鞋请参考详情:<a href="http://mp.weixin.qq.com/s?__biz=MzAxMTU0NTc4Nw==&mid=239799758&idx=1&sn=9762fe70f14829e7bd42fb1f35a6362e&scene=23&srcid=1011W2vcX80qGPXQEhjJAeB5#rd" target="_blank" rel="external">http://mp.weixin.qq.com/s?__biz=MzAxMTU0NTc4Nw==&mid=239799758&idx=1&sn=9762fe70f14829e7bd42fb1f35a6362e&scene=23&srcid=1011W2vcX80qGPXQEhjJAeB5#rd</a>。
<br>btw,po一个js全栈图:
<br><img src="/2015/10/11/web-nodejs-debug/nodejs.png" alt="[js全栈示例]" title="[js全栈示例]"></p>
<h2 id="断言调试">断言调试</h2>
<p>我们日常使用<code>IDE</code>工具,实际上很多<code>IDE</code>工具已经集成了Node.js的调试工具;
<br>比如Eclipse、webStorm等等,他们断言的原理是利用Node.js的<code>Debugger</code>内建模块,然后在这个基础上进行了封装。</p>
<h3 id="Debugger内建模块">Debugger内建模块</h3>
<p>Node.js提供的内建Debugger模块十分强大,它告诉V8,在执行代码的时候中断程度,等待开发者操控代码的执行进度。</p>
<ul>
<li>
<p><a href="#">立即调试模式</a>:
<br>一般用于程序主动执行方式,比如一个程序计算某种结果,并非要监听某个端口来等待请求。</p>
<p><code>node debug xx.js</code> // 利用命令行来向5858端口传送调试指令</p>
</li>
<li>
<p><a href="#">等待调试模式</a>:
<br>正好与立即模式相反,常用于web程序 e.g. express要调试,必须使用这种方式启动,然后当请求来了才能进行调试,并非程序马上执行的类型。</p>
<p><code>node --debug xx.js</code> // 监听5858默认端口接收调试命令
<br><code>node --debug-brk=8080 xx.js</code> // 监听8080端口来接收调试指令</p>
</li>
</ul>
<h4 id="立即调试模式">立即调试模式</h4>
<p><code>see</code> test.js
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="keyword">var</span> hello = <span class="string">'hello'</span>;</span><br><span class="line"><span class="keyword">var</span> world = <span class="string">'nodejs'</span>;</span><br><span class="line"><span class="keyword">debugger</span>;</span><br><span class="line"><span class="keyword">var</span> hello_world = hello + <span class="string">' '</span> + world;</span><br><span class="line"><span class="built_in">console</span>.log(hello_world);</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>执行命令:<code>node debug test.js</code> 就可以进入调试模式。
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node debug test.js</span><br><span class="line">< <span class="keyword">debugger</span> listening on port <span class="number">5858</span></span><br><span class="line">connecting... ok</span><br><span class="line"><span class="keyword">break</span> <span class="keyword">in</span> helloword-debug.js:<span class="number">1</span></span><br><span class="line"> <span class="number">1</span> <span class="keyword">var</span> hello = <span class="string">'hello'</span>;</span><br><span class="line"> <span class="number">2</span> <span class="keyword">var</span> world = <span class="string">'nodejs'</span>;</span><br><span class="line"> <span class="number">3</span></span><br><span class="line">debug> help</span><br><span class="line">Commands: run (r), cont (c), next (n), step (s), out (o), backtrace (bt), setBreakpoint (sb), clearBreakpoint (cb),</span><br><span class="line">watch, unwatch, watchers, repl, restart, kill, list, scripts, breakOnException, breakpoints, version</span><br><span class="line">debug></span><br><span class="line">debug> n</span><br><span class="line"><span class="keyword">break</span> <span class="keyword">in</span> helloword-debug.js:<span class="number">2</span></span><br><span class="line"> <span class="number">1</span> <span class="keyword">var</span> hello = <span class="string">'hello'</span>;</span><br><span class="line"> <span class="number">2</span> <span class="keyword">var</span> world = <span class="string">'nodejs'</span>;</span><br><span class="line"> <span class="number">3</span></span><br><span class="line"> <span class="number">4</span> <span class="keyword">debugger</span>;</span><br><span class="line">debug> repl</span><br><span class="line">Press Ctrl + C to leave debug repl</span><br><span class="line">> hello</span><br><span class="line"><span class="string">'hello'</span></span><br></pre></td>
</tr>
</table>
</figure>
</p>
<h4 id="等待调试模式">等待调试模式</h4>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node --debug test.js <span class="comment">// 告知V8,test.js在默认端口5858上接收指令</span></span><br></pre></td>
</tr>
</table>
</figure>
<p>当我们使用<code>debug</code>参数打开一个node文件时,会输出这样一行文字:
<br><img src="/2015/10/11/web-nodejs-debug/node-debug.png" alt="[--debug]" title="[--debug]">
<br>打开浏览器访问 <a href="http://localhost:5858" target="_blank" rel="external">http://localhost:5858</a>:
<br><img src="/2015/10/11/web-nodejs-debug/debug.png" alt="[node --debug test.js]" title="[node --debug test.js]"></p>
<p><code>--debug</code>其实是告诉用户<code>Node</code>在运行脚本时启动了内建debugger功能,并监听5858端口来处理传输的的调试命令。
<br>这里强调一下,如果要进行远程调试,还要结合node debug来实现:</p>
<ul>
<li>
<p>服务端(程序):</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node --debug server.js <span class="comment">// 通知V8开启调试功能,等待接收调试指令</span></span><br></pre></td>
</tr>
</table>
</figure>
</li>
<li>
<p>客户端(终端): </p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">node debug <URI>, 通过 URI 连接调试,e.g. node debug <服务器IP>:<span class="xml"><span class="tag"><<span class="title">调试端口,默认5858</span>></span></span><br><span class="line">node debug -p <span class="tag"><<span class="title">pid</span>></span> 通过 PID 链接调试</span><br><span class="line">node debug localhost:5858</span><br><span class="line">➜ $ node debug localhost:5858 // 客户端可以远程调试--debug启动server.js</span></span><br></pre></td>
</tr>
</table>
</figure>
</li>
</ul>
<p>当然如果要修改默认端口,可以用选项<code>--debug-brk=8080</code>这样的方式:
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node --debug-brk=<span class="number">8080</span> xx.js <span class="comment">// 通知V8开启调试功能,等待接收调试指令</span></span><br></pre></td>
</tr>
</table>
</figure>
</p>
<h3 id="推荐node-inspector神器">推荐node-inspector神器</h3>
<p>Node.js断言技术有很多种方式,本质都是基于Node.js本身提供的debugger原理。
<br>推荐用户使用<a href="https://www.npmjs.com/package/node-inspector" target="_blank" rel="external">node-inspector</a>原因是它不依赖某个IDE,接近原生且更加灵活,并且可以断言coffee预处理js。
<br>所以下面来讲讲Node.js神器<a href="https://www.npmjs.com/package/node-inspector" target="_blank" rel="external">node-inspector</a>。
<br><code>node-inspector</code>就是用了上述<code>等待调试模式</code>原理,另外加上websocket技术传输信息,并且提供GUI方便用户调试。</p>
<h4 id="原理">原理</h4>
<p>node-inspector提供一个web server作为GUI接收用户的指令,然后内部用websocket协议与需要被调试的程序进行实时交互。
<br>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">用户指令 ---GUI--<span class="number">8080</span>---> node-inspector ---websocket--<span class="number">5858</span>---> node\<span class="string">'s debugger</span></span><br></pre></td>
</tr>
</table>
</figure>
</p>
<h4 id="实践">实践</h4>
<ul>
<li>
<p>安装node-inspector:</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ npm install -g node-inspector <span class="comment">// 安装node-inspector</span></span><br></pre></td>
</tr>
</table>
</figure>
</li>
<li>
<p>启动node-inspector:
<br>开启一个终端窗口,执行如下</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node-inspector <span class="comment">// 直接监听默认端口8080</span></span><br><span class="line">➜ $ node-inspector --web-port <span class="number">8080</span> --debug-port <span class="number">5858</span></span><br></pre></td>
</tr>
</table>
</figure>
<p><code>--web-port</code>是<code>Chrome Devtools</code>的调试页面端口,<code>--debug-port</code>为Node.js启动的内建debug端口,即需被调试的程序xx.js <code>node --debug-brk=5858 xx.js</code> 等待接收调试指令的端口! 😄</p>
</li>
<li>
<p>启动被调试程序:
<br>再开启一个终端窗口,执行如下</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ node --debug-brk=<span class="number">5858</span> test.js || node --debug test.js</span><br><span class="line"> 或者</span><br><span class="line">➜ $ node-debug test.js</span><br></pre></td>
</tr>
</table>
</figure>
</li>
</ul>
<p>可以在 <a href="http://localhost:8080/debug?port=5858" target="_blank" rel="external">http://localhost:8080/debug?port=5858</a> 打开node-inspector页面,调试使用<code>--debug(-brk)</code>或<code>node-debug</code>参数启动的程序。
<br><img src="/2015/10/11/web-nodejs-debug/inspector-ui.png" alt="[inspector GUI]" title="[inspector GUI]">
<br>更多设置可以查阅<a href="https://www.npmjs.com/package/node-inspector" target="_blank" rel="external">官方文档</a>。</p>
<p><code>tips:</code>这里提示一下利用node-inspector调试coffeescript程序时的设置,开启node-inspector方式一样,但启动需要被调试的程序有点不一样,如下。
<br>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ coffee --nodejs --debug xx.coffee</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<h3 id="FAQ">FAQ</h3>
<p>注意,如果出现
<br>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ Failed to open socket on port <span class="number">5858</span>, waiting <span class="number">1000</span> ms before retrying</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>请结束掉所有debug进程
<br>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">➜ $ ps -ef|grep debug-brk|awk <span class="string">'{print $2}'</span>|xargs <span class="built_in">kill</span> -<span class="number">9</span></span><br></pre></td>
</tr>
</table>
</figure>
</p>
<h2 id="总结">总结</h2>
<p>只有真正理解了一门语言提供的debug技术的原理,才能更加熟练地使用它来调试程序。</p>
<p>参考:
<br><a href="https://nodejs.org" target="_blank" rel="external">https://nodejs.org</a>
<br><a href="https://iojs.org" target="_blank" rel="external">https://iojs.org</a></p>
<hr>
<p>博客:<a href="https://vectorho.github.io" target="_blank" rel="external">https://vectorho.github.io</a>
<br>作者:<a href="https://vectorho.github.io/about/" target="_blank" rel="external">Vector Ho</a></p>]]></content>
<summary type="html">
<![CDATA[<p><a href="https://nodejs.org" target="_blank" rel="external">Node.js</a>是什么?Node.js适合什么场景?多少公司已用于生产环境?Node.js靠不靠谱?这个话题应该无可厚非了,如果今天还在质疑Node]]>
</summary>
<category term="Node.js" scheme="http://yoursite.com/tags/Node-js/"/>
<category term="web技术" scheme="http://yoursite.com/categories/web%E6%8A%80%E6%9C%AF/"/>
</entry>
<entry>
<title><![CDATA[OS X El Capitan 快速更新]]></title>
<link href="http://yoursite.com/2015/10/02/mac-osx-capitan/"/>
<id>http://yoursite.com/2015/10/02/mac-osx-capitan/</id>
<published>2015-10-01T23:26:19.000Z</published>
<updated>2015-10-02T01:02:00.000Z</updated>
<content type="html"><![CDATA[<h2 id="前奏">前奏</h2>
<p>总所周知,<a href="#">app store</a> 国内下载速度实在不敢恭维,然而系统升级一般没个一天是搞不定的(心中有一万只草泥马在奔腾ಥ_ಥ)。
<br>但是,你们天真地以为这就能难倒技术控了么?23333…</p>
<p>今天小弟就给看官们带来一次全新体验。</p>
<h2 id="原理">原理</h2>
<p>抓取苹果官方下载链接,离线到第三方服务器分流。再使用第三方下载工具下载系统<code>.pkg</code>文件。搭建本地静态文件<code>server</code>,
<br>将苹果官方的下载地址解析到本地的服务器,从而实现快速下载安装。</p>
<p>抓取工具首推charles(本人和charles不存在隶属关系^.^)。</p>
<p><code>ps</code>:以上适用于 El Capitan 更新慢的用户,有百兆光纤的用户可忽略。</p>
<a id="more"></a>
<h2 id="实践">实践</h2>
<p>相信实践出真理。</p>
<h3 id="抓取官方下载链接">抓取官方下载链接</h3>
<p>由于前段时间的 Xcode Ghost 影响,不能再信任第三方的网盘, 通过 Charles 抓取到的链接如下:
<br><a href="http://osxapps.itunes.apple.com/apple-assets-us-std-000001/Purple3/v4/74/d2/82/74d28291-9db9-7ae2-305d-9b8b3f5fd463/ftk3252456602304584541.pkg" target="_blank" rel="external">http://osxapps.itunes.apple.com/apple-assets-us-std-000001/Purple3/v4/74/d2/82/74d28291-9db9-7ae2-305d-9b8b3f5fd463/ftk3252456602304584541.pkg</a></p>
<p><code>ps</code>:为了安全性最好自己抓包获取此下载地址,后面的 hostname、 path 和自己抓取的链接对应。</p>
<h3 id="下载-pkg文件">下载.pkg文件</h3>
<p>抓取到苹果官方的下载链接后,可以先离线到百度云分流我们可以通过第三方下载工具下载 .pkg 文件,下载工具自备。
<br>下载完成后文件是无法直接安装的,所以我们要准备欺骗 App Store 把静态文件 server 映射到本地。
<br>下面要做的就是搭建本地静态文件 server 替换苹果官方的 server。</p>
<h3 id="搭建静态文件server">搭建静态文件server</h3>
<p>搭建本地静态资源服务的方法太多了,e.g. 现成的就有nginx、apache;程序有 nodejs、python、java etc.
<br>本教程选择熟悉的<a href="http://expressjs.com/" target="_blank" rel="external">express</a>,只需一个中间件即可搞定,搭建node环境,不再赘述。</p>
<h3 id="分析url">分析url</h3>
<p>url格式:
<br>
<figure class="highlight avrasm">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="label">protocol:</span>// hostname[:port]/path[?query]<span class="preprocessor">#hash</span></span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>在这里我们只需要找到 hostname 和 path;</p>
<figure class="highlight cpp">
<table>
<tr>
<td class="code"><pre><span class="line">hostname : osxapps.itunes.apple.com</span><br><span class="line">path : /apple-assets-us-<span class="built_in">std</span>-<span class="number">000001</span>/Purple3/v4/<span class="number">74</span>/d2/<span class="number">82</span>/<span class="number">74</span>d28291-<span class="number">9</span>db9-<span class="number">7</span>ae2-<span class="number">305</span>d-<span class="number">9</span>b8b3f5fd463/</span><br></pre></td>
</tr>
</table>
</figure>
<h3 id="修改hosts">修改hosts</h3>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">sudo vi /etc/hosts</span><br></pre></td>
</tr>
</table>
</figure>
<img src="/2015/10/02/mac-osx-capitan/hosts.png" alt="[hosts]" title="[hosts]">
<p>如此一来,系统 DNS 将把 osxapps.itunes.apple.com 解析到本地。</p>
<h3 id="创建express项目">创建express项目</h3>
<p>万事俱备,只欠exprss。</p>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">$ <span class="built_in">cd</span> your-project // 任意名称</span><br><span class="line">$ mkdir static</span><br><span class="line">$ npm install express</span><br></pre></td>
</tr>
</table>
</figure>
<p>手动将下载好的.pkg文件拷贝到项目的static文件夹,在项目文件夹根目录创建server.js:</p>
<figure class="highlight js">
<table>
<tr>
<td class="code"><pre><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> app = express();</span><br><span class="line"><span class="comment">// 映射资源</span></span><br><span class="line">app.use(<span class="string">'/apple-assets-us-std-000001/Purple3/v4/74/d2/82/74d28291-9db9-7ae2-305d-9b8b3f5fd463/'</span>, express.static(path.resolve(<span class="string">'.'</span>, <span class="string">'static'</span>)));</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">80</span>);</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"Server Listen on port 80."</span>);</span><br></pre></td>
</tr>
</table>
</figure>
<p>启动程序
<br>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">$ <span class="built_in">cd</span> your-project</span><br><span class="line">$ sudo node server.js</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>此时你可以在浏览器中测试,在地址栏输入我们抓取的苹果官方的下载链接,如果一切配置都正确,你应该会看到百兆的下载速度。
<br><a href="http://osxapps.itunes.apple.com/apple-assets-us-std-000001/Purple3/v4/74/d2/82/74d28291-9db9-7ae2-305d-9b8b3f5fd463/ftk3252456602304584541.pkg" target="_blank" rel="external">http://osxapps.itunes.apple.com/apple-assets-us-std-000001/Purple3/v4/74/d2/82/74d28291-9db9-7ae2-305d-9b8b3f5fd463/ftk3252456602304584541.pkg</a></p>
<img src="/2015/10/02/mac-osx-capitan/pkg.jpeg" alt="[下载速度]" title="[下载速度]">
<h3 id="更新_El_Capitan">更新 El Capitan</h3>
<p>打开 App Store 找到 OS X El Capitan 的更新按钮, 点击更新按钮。稍等片刻,下载完成后就会弹出安装页面。
<br>安装过程需要一段时间,冲杯咖啡等待即可。</p>
<p>enjoy yourself …</p>
<h3 id="还原hosts">还原hosts</h3>
<p>记得一定要还原hosts设置,不然以后 App Store 就更新不了,方法就是删除掉上文配置的映射,保存即可。</p>
<h2 id="引申">引申</h2>
<p>如果有vps的话,也下载到自己的vps上,然后将苹果的下载地址解析到vps ,原理相通。</p>
<h2 id="参考">参考</h2>
<p><a href="https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E5%AE%9A%E4%BD%8D%E7%AC%A6" target="_blank" rel="external">URL</a>
<br><a href="http://coolshell.cn/articles/1480.html" target="_blank" rel="external">SimpleHTTPServer</a>
<br><a href="https://zh.wikipedia.org/wiki/Hosts%E6%96%87%E4%BB%B6" target="_blank" rel="external">hosts 文件</a></p>
<hr>
<p>博客:<a href="https://vectorho.github.io" target="_blank" rel="external">https://vectorho.github.io</a>
<br>作者:<a href="https://vectorho.github.io/about/" target="_blank" rel="external">Vector Ho</a></p>]]></content>
<summary type="html">
<![CDATA[<h2 id="前奏">前奏</h2>
<p>总所周知,<a href="#">app store</a> 国内下载速度实在不敢恭维,然而系统升级一般没个一天是搞不定的(心中有一万只草泥马在奔腾ಥ_ಥ)。
<br>但是,你们天真地以为这就能难倒技术控了么?23333…</p>
<p>今天小弟就给看官们带来一次全新体验。</p>
<h2 id="原理">原理</h2>
<p>抓取苹果官方下载链接,离线到第三方服务器分流。再使用第三方下载工具下载系统<code>.pkg</code>文件。搭建本地静态文件<code>server</code>,
<br>将苹果官方的下载地址解析到本地的服务器,从而实现快速下载安装。</p>
<p>抓取工具首推charles(本人和charles不存在隶属关系^.^)。</p>
<p><code>ps</code>:以上适用于 El Capitan 更新慢的用户,有百兆光纤的用户可忽略。</p>]]>
</summary>
<category term="osx" scheme="http://yoursite.com/tags/osx/"/>
<category term="mac" scheme="http://yoursite.com/categories/mac/"/>
</entry>
<entry>
<title><![CDATA[Tips For npm — npm publish]]></title>
<link href="http://yoursite.com/2015/09/27/web-npm_publish/"/>
<id>http://yoursite.com/2015/09/27/web-npm_publish/</id>
<published>2015-09-26T16:43:27.000Z</published>
<updated>2015-09-29T06:07:33.000Z</updated>
<content type="html"><![CDATA[<h2 id="什么是NPM">什么是NPM</h2>
<p><a href="https://www.npmjs.com/" target="_blank" rel="external">npm</a> is the package manager for javascript.
<br>是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。</p>
<p>如果你熟悉ruby的gem,Python的pypi、setuptools,PHP的pear,那么你就知道NPM的作用是什么了。</p>
<p>Nodejs自身提供了基本的模块,但是开发实际应用过程中仅仅依靠这些基本模块则还需要较多的工作。幸运的是,Nodejs库和框架为我们提供了帮助,让我们减少工作量。但是成百上千的库或者框架管理起来又很麻烦,有了NPM,可以很快的找到特定服务要使用的包,进行下载、安装以及管理已经安装的包。
<br>
<a id="more"></a>
</p>
<h2 id="npm安装">npm安装</h2>
<p>安装nodejs,npm默认作为全局模块,由于本文主要讲述发布npm到包管理中心,不再赘述。
<br>建议使用nvm安装并管理nodejs版本。<a href="https://github.com/cnpm/nvm" target="_blank" rel="external">参考资料</a></p>
<h2 id="发布npm模块">发布npm模块</h2>
<p>安装完npm后,打开终端输入npm,马上就能使用npm提供的cli功能,如图:
<br><img src="/2015/09/27/web-npm_publish/npm-cli.png" alt="[npm cli]" title="[npm cli]">
<br>引入正文,如何发布module? npm遵循cmd规范,publish前会读取当前目录package.json,匹配version、github信息 etc. (ps:任何一个npm模块源码根目录都会有一个package.json文件供参考)</p>
<p>btw 首次发布需要先添加本地npm用户信息,添加后本地将缓存当前通过验证的用户。
<br>ps:如果使用nrm工具管理源,需先切回npm官方源:<code>nrm use npm</code>,再次发布跳过此步骤:
<br>
<figure class="highlight coffeescript">
<table>
<tr>
<td class="code"><pre><span class="line">$ sudo <span class="built_in">npm</span> adduser</span><br><span class="line">$ sudo <span class="built_in">npm</span> whoami</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>正式发布:
<br>
<figure class="highlight bash">
<table>
<tr>
<td class="code"><pre><span class="line">$ <span class="built_in">cd</span> your project</span><br><span class="line">$ vi package.json // 修改版本号</span><br><span class="line">$ sudo npm publsh</span><br></pre></td>
</tr>
</table>
</figure>
</p>
<p>至此,我们已经成功把module发布到了 <a href="https://www.npmjs.com" target="_blank" rel="external">https://www.npmjs.com</a>,是不是很简单,快动手把自己的module也贡献出来吧。
<br>btw 假设发布太频繁,<a href="https://www.npmjs.com/" target="_blank" rel="external">npm</a>网站用到了缓存(hold on),稍后便可以看到更新的模块。</p>
<h2 id="引申">引申</h2>
<ul>
<li>发布模块时,需要更新package版本信息,由于本人比较好奇就去研究了npm版本号规则。</li>
<li>module源码托管在git服务提供商,如何提交一个tag/issue,自动发布到<a href="https://www.npmjs.com/" target="_blank" rel="external">npm</a>。</li>
</ul>
<h3 id="关于package版本号">关于package版本号</h3>
<p>npm社区版本号规则采用的是semver(语义化版本),主要规则如下:</p>
<pre><code>版本格式:主版号.次版号.修订号,版号递增规则如下:
主版号:当你做了不相容的 API 修改,
次版号:当你做了向下相容的功能性新增,
修订号:当你做了向下相容的问题修正。
先行版号及版本编译资讯可以加到「主版号.次版号.修订号」的后面,作为延伸。
</code></pre>
<h3 id="关于github托管Hooks">关于github托管Hooks</h3>
<p>其实就是实现git服务提供商给hooks接口,e.g github提供的提交某个issue就会促发hook,实现此hook只需一个脚本去发布到<a href="https://www.npmjs.com/" target="_blank" rel="external">npm</a>。</p>
<p>以github为例,仅给出参考:</p>
<ul>
<li><a href="https://github.com/SegmentFault/deploy-robot" target="_blank" rel="external">https://github.com/SegmentFault/deploy-robot</a></li>
<li><a href="http://blog.coderzh.com/2015/09/13/use-webhook-automated-deploy-hugo/" target="_blank" rel="external">http://blog.coderzh.com/2015/09/13/use-webhook-automated-deploy-hugo/</a></li>
</ul>
<hr>
<p>博客:<a href="https://vectorho.github.io" target="_blank" rel="external">https://vectorho.github.io</a>
<br>作者:<a href="https://vectorho.github.io/about/" target="_blank" rel="external">Vector Ho</a></p>]]></content>
<summary type="html">
<![CDATA[<h2 id="什么是NPM">什么是NPM</h2>
<p><a href="https://www.npmjs.com/">npm</a> is the package manager for javascript.
<br>是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。</p>
<p>如果你熟悉ruby的gem,Python的pypi、setuptools,PHP的pear,那么你就知道NPM的作用是什么了。</p>
<p>Nodejs自身提供了基本的模块,但是开发实际应用过程中仅仅依靠这些基本模块则还需要较多的工作。幸运的是,Nodejs库和框架为我们提供了帮助,让我们减少工作量。但是成百上千的库或者框架管理起来又很麻烦,有了NPM,可以很快的找到特定服务要使用的包,进行下载、安装以及管理已经安装的包。
<br>]]>
</summary>
<category term="npm" scheme="http://yoursite.com/tags/npm/"/>
<category term="web技术" scheme="http://yoursite.com/categories/web%E6%8A%80%E6%9C%AF/"/>
</entry>
</feed>