-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.html
More file actions
1553 lines (1030 loc) · 90 KB
/
index.html
File metadata and controls
1553 lines (1030 loc) · 90 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
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>课程格子黑板报</title>
<meta name="author" content="CreatingEV">
<meta name="description" content="第一部分 Rack 什么是rack Rack 为开发 Ruby web 应用提供了一个最小的模块化和适应性接口。通过对 HTTP 请求与响应 的尽可能最简单的方式包装,它统一和提炼 Web 服务器 ,Web 框架,和之间的软件(所谓的中间件)的 API 为单一方法调用。 什么是rack app A …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://creatingev.github.io">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="课程格子黑板报" type="application/atom+xml">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="./javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">课程格子黑板报</a></h1>
<h2>Blog of CreatingEV Dev Team</h2>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="http://google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="q" value="site:creatingev.github.io" />
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2014/01/12/rails-serverhou-bian-yin-cang-de-zhen-xiang/">Rails_server后边隐藏的真相</a></h1>
<p class="meta">
<time datetime="2014-01-12T01:38:40+08:00" pubdate data-updated="true">Jan 12<span>th</span>, 2014</time>
| <a href="/blog/2014/01/12/rails-serverhou-bian-yin-cang-de-zhen-xiang/#disqus_thread"
data-disqus-identifier="http://creatingev.github.io/blog/2014/01/12/rails-serverhou-bian-yin-cang-de-zhen-xiang/">Comments</a>
</p>
</header>
<div class="entry-content"><h2>第一部分 Rack</h2>
<h3>什么是rack</h3>
<p>Rack 为开发 Ruby web 应用提供了一个最小的模块化和适应性接口。通过对 HTTP 请求与响应 的尽可能最简单的方式包装,它统一和提炼 Web 服务器 ,Web 框架,和之间的软件(所谓的中间件)的 API 为单一方法调用。</p>
<h3>什么是rack app</h3>
<p>A Rack application is any Ruby object that responds to the call method, takes a single hash parameter and returns an array containing the response status code, HTTP response headers and the response body as an array of strings.</p>
<p>意思就是,一个包含call(env)方法的对象就能做为一个Rack Web应用,参数env是一个hash,方法的返回值是一个列表,包含三个元素:HTTP状态码(200, 500等),HTTP响应头(Hash),HTTP响应内容(字符串数组)。</p>
<h4>一些具体的例子</h4>
<p>simplest.ru</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># run with rackup</span>
</span><span class='line'><span class="no">Simplest</span> <span class="o">=</span> <span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span> <span class="o">[</span><span class="s2">"200"</span><span class="p">,</span> <span class="p">{</span><span class="s2">"Content-Type"</span> <span class="o">=></span> <span class="s2">"text/plain"</span><span class="p">},</span> <span class="o">[</span><span class="s2">"OK"</span><span class="o">]]</span> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">run</span> <span class="no">Simplest</span>
</span></code></pre></td></tr></table></div></figure>
<p>redirect.rb</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="nb">require</span> <span class="s2">"rack"</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">Redirect</span>
</span><span class='line'> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">call</span> <span class="n">env</span>
</span><span class='line'> <span class="k">if</span> <span class="n">env</span><span class="o">[</span><span class="s2">"QUERY_STRING"</span><span class="o">]</span> <span class="o">=~</span> <span class="sr">/url=redirect/</span>
</span><span class='line'> <span class="o">[</span><span class="s2">"302"</span><span class="p">,</span> <span class="p">{</span><span class="s2">"location"</span> <span class="o">=></span> <span class="s2">"http://qhr.me"</span><span class="p">},</span> <span class="o">[]]</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="o">[</span><span class="s2">"200"</span><span class="p">,</span> <span class="p">{</span><span class="s2">"location"</span> <span class="o">=></span> <span class="s2">"text/plain"</span><span class="p">},</span> <span class="o">[</span><span class="s2">"ok"</span><span class="o">]]</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="ss">Rack</span><span class="p">:</span><span class="ss">:Server</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">app</span><span class="p">:</span> <span class="no">Redirect</span><span class="p">,</span> <span class="ss">environment</span><span class="p">:</span> <span class="s2">"development"</span><span class="p">)</span><span class="o">.</span><span class="n">start</span>
</span></code></pre></td></tr></table></div></figure>
<p>local_dir.rb</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="nb">require</span> <span class="s2">"rack"</span>
</span><span class='line'>
</span><span class='line'><span class="ss">Rack</span><span class="p">:</span><span class="ss">:Server</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">app</span><span class="p">:</span> <span class="ss">Rack</span><span class="p">:</span><span class="ss">:Directory</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"~/"</span><span class="p">))</span><span class="o">.</span><span class="n">start</span>
</span></code></pre></td></tr></table></div></figure>
<p>更多的细节请大家参看<a href="http://guides.ruby-china.org/rails_on_rack.html">http://guides.ruby-china.org/rails_on_rack.html</a>这里就不在重复了。</p>
<h2>第二部分 Server (Webrick举例)</h2>
<p>webrick是ruby标准库中的一个webserver。</p>
<h1>一些基础</h1>
<h3>SizedQueue</h3>
<p>一个线程安全的队列,有大小限制。队列为空的时候,pop操作的线程会被阻塞。队列满的时候,push操作的线程会被阻塞。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">q</span> <span class="o">=</span> <span class="no">SizedQueue</span><span class="o">.</span><span class="n">new</span> <span class="mi">1</span>
</span><span class='line'>
</span><span class='line'><span class="n">q</span><span class="o">.</span><span class="n">push</span> <span class="mi">1</span>
</span><span class='line'>
</span><span class='line'><span class="no">Thread</span><span class="o">.</span><span class="n">start</span> <span class="p">{</span>
</span><span class='line'> <span class="kp">loop</span> <span class="k">do</span>
</span><span class='line'> <span class="nb">puts</span> <span class="n">q</span><span class="o">.</span><span class="n">pop</span>
</span><span class='line'> <span class="nb">sleep</span> <span class="mi">10</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="n">q</span><span class="o">.</span><span class="n">push</span> <span class="mi">2</span>
</span><span class='line'><span class="n">q</span><span class="o">.</span><span class="n">push</span> <span class="mi">3</span>
</span></code></pre></td></tr></table></div></figure>
<h3>TCPServer</h3>
<p>TCP/IP stream型连接的服务器端套接字的类。accept实例方法会受理客户端的连接请求, 返回已连接的TCPSocket的实例。</p>
<h3>IO::select</h3>
<p>多路复用IO。参数列表前三项为输入/输出/异常的IO(或者子类)的实例数组。第四个参数是timeout。第四个参数是timeout可以是整数、Float或nil(省略时的默认值)。指定为nil时,将会一直等到IO变成就绪状态。timeout时将返回nil,除此以外将返回一个包含3个元素的数组,这3个元素分别是等待输入/输出/异常的对象的数组(指定数组的子集)。</p>
<h1>从rack开始</h1>
<p>rack可以简单的理解成ruby frameword 和 webserver 之间的一个通用接口。一份基于rack开发的web服务可以使用rack支持的各种server来运行。rack中的所有server都具有一个叫做run的方法,这个是web server的入口。那么从rack/lib/rack/handler/webrick.rb中可以找到如下代码。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">run</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{})</span>
</span><span class='line'> <span class="n">environment</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">'RACK_ENV'</span><span class="o">]</span> <span class="o">||</span> <span class="s1">'development'</span>
</span><span class='line'> <span class="n">default_host</span> <span class="o">=</span> <span class="n">environment</span> <span class="o">==</span> <span class="s1">'development'</span> <span class="p">?</span> <span class="s1">'localhost'</span> <span class="p">:</span> <span class="s1">'0.0.0.0'</span>
</span><span class='line'>
</span><span class='line'> <span class="n">options</span><span class="o">[</span><span class="ss">:BindAddress</span><span class="o">]</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:Host</span><span class="p">)</span> <span class="o">||</span> <span class="n">default_host</span>
</span><span class='line'> <span class="n">options</span><span class="o">[</span><span class="ss">:Port</span><span class="o">]</span> <span class="o">||=</span> <span class="mi">8080</span>
</span><span class='line'> <span class="n">options</span><span class="o">[</span><span class="ss">:OutputBufferSize</span><span class="o">]</span> <span class="o">=</span> <span class="mi">5</span>
</span><span class='line'> <span class="vi">@server</span> <span class="o">=</span> <span class="o">::</span><span class="ss">WEBrick</span><span class="p">:</span><span class="ss">:HTTPServer</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@server</span><span class="o">.</span><span class="n">mount</span> <span class="s2">"/"</span><span class="p">,</span> <span class="ss">Rack</span><span class="p">:</span><span class="ss">:Handler</span><span class="o">::</span><span class="no">WEBrick</span><span class="p">,</span> <span class="n">app</span>
</span><span class='line'> <span class="k">yield</span> <span class="vi">@server</span> <span class="k">if</span> <span class="nb">block_given?</span>
</span><span class='line'> <span class="vi">@server</span><span class="o">.</span><span class="n">start</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>那么就从WEBrick::HTTPServer开始,看看mount和start方法是怎么工作的。</p>
<h1>进入webrick</h1>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">HTTPServer</span> <span class="o"><</span> <span class="o">::</span><span class="ss">WEBrick</span><span class="p">:</span><span class="ss">:GenericServer</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>这里有必要说说GenericServer。
其中有两个只读的实例变量:listeners, tokens。
listeners是监听连接的socket数组。
tokens是最大连接数量(并发数量)。</p>
<h3>start方法</h3>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'> <span class="k">while</span> <span class="vi">@status</span> <span class="o">==</span> <span class="ss">:Running</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'> <span class="k">if</span> <span class="n">svrs</span> <span class="o">=</span> <span class="no">IO</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="vi">@listeners</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="mi">2</span><span class="o">.</span><span class="mi">0</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="n">svrs</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span>
</span><span class='line'> <span class="n">svrs</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">each</span><span class="p">{</span><span class="o">|</span><span class="n">svr</span><span class="o">|</span>
</span><span class='line'> <span class="vi">@tokens</span><span class="o">.</span><span class="n">pop</span> <span class="c1"># blocks while no token is there.</span>
</span><span class='line'> <span class="k">if</span> <span class="n">sock</span> <span class="o">=</span> <span class="n">accept_client</span><span class="p">(</span><span class="n">svr</span><span class="p">)</span>
</span><span class='line'> <span class="n">sock</span><span class="o">.</span><span class="n">do_not_reverse_lookup</span> <span class="o">=</span> <span class="n">config</span><span class="o">[</span><span class="ss">:DoNotReverseLookup</span><span class="o">]</span>
</span><span class='line'> <span class="n">th</span> <span class="o">=</span> <span class="n">start_thread</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
</span><span class='line'> <span class="n">th</span><span class="o">[</span><span class="ss">:WEBrickThread</span><span class="o">]</span> <span class="o">=</span> <span class="kp">true</span>
</span><span class='line'> <span class="n">thgroup</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">th</span><span class="p">)</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="vi">@tokens</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>start中,是一个循环。当没有请求的时候,主线程会被select阻塞。有请求的时候,针对每个输入就绪的socket,会通过调用socket的accept方法,来产生一个与客户端通信的新socket,而原来的socket依然在端口上监听。</p>
<p>针对每个与客户端通信的socket,webrick会创建一个线程(相关代码在start_thread中,稍后提及)来处理请求,这里@tokens的作用类似信号量,初始化server的时候,会把@tokens用nil填充满,只有能从@token获取到信号的时候,才可以创建线程,获取不到信号的时候,会阻塞主线程,以此控制并发数量。这里参见之前提到的SizedQueue。</p>
<p>每个请求的具体行为,就要继续查看start_thread了。</p>
<h3>start_thread</h3>
<p>这个方法中是一些异常和logger的处理,主要的一句是</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">start_thread</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'> <span class="n">block</span> <span class="p">?</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span> <span class="p">:</span> <span class="n">run</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
</span><span class='line'> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>显而易见,run(sock)就是下个目标。</p>
<h3>run</h3>
<p>这个方法,就要回到::WEBrick::HTTPServer了。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
</span><span class='line'> <span class="k">while</span> <span class="kp">true</span>
</span><span class='line'> <span class="n">res</span> <span class="o">=</span> <span class="no">HTTPResponse</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="p">)</span>
</span><span class='line'> <span class="n">req</span> <span class="o">=</span> <span class="no">HTTPRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="p">)</span>
</span><span class='line'> <span class="n">server</span> <span class="o">=</span> <span class="nb">self</span>
</span><span class='line'> <span class="k">begin</span>
</span><span class='line'> <span class="n">timeout</span> <span class="o">=</span> <span class="vi">@config</span><span class="o">[</span><span class="ss">:RequestTimeout</span><span class="o">]</span>
</span><span class='line'> <span class="k">while</span> <span class="n">timeout</span> <span class="o">></span> <span class="mi">0</span>
</span><span class='line'> <span class="k">break</span> <span class="k">if</span> <span class="no">IO</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="o">[</span><span class="n">sock</span><span class="o">]</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="p">)</span>
</span><span class='line'> <span class="n">timeout</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">if</span> <span class="vi">@status</span> <span class="o">!=</span> <span class="ss">:Running</span>
</span><span class='line'> <span class="n">timeout</span> <span class="o">-=</span> <span class="mi">0</span><span class="o">.</span><span class="mi">5</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">raise</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:EOFError</span> <span class="k">if</span> <span class="n">timeout</span> <span class="o"><=</span> <span class="mi">0</span>
</span><span class='line'> <span class="k">raise</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:EOFError</span> <span class="k">if</span> <span class="n">sock</span><span class="o">.</span><span class="n">eof?</span>
</span><span class='line'> <span class="n">req</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">request_method</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">request_method</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">request_uri</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">request_uri</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">request_http_version</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">http_version</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">keep_alive</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">keep_alive?</span>
</span><span class='line'> <span class="n">server</span> <span class="o">=</span> <span class="n">lookup_server</span><span class="p">(</span><span class="n">req</span><span class="p">)</span> <span class="o">||</span> <span class="nb">self</span>
</span><span class='line'> <span class="k">if</span> <span class="n">callback</span> <span class="o">=</span> <span class="n">server</span><span class="o">[</span><span class="ss">:RequestCallback</span><span class="o">]</span>
</span><span class='line'> <span class="n">callback</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>
</span><span class='line'> <span class="k">elsif</span> <span class="n">callback</span> <span class="o">=</span> <span class="n">server</span><span class="o">[</span><span class="ss">:RequestHandler</span><span class="o">]</span>
</span><span class='line'> <span class="n">msg</span> <span class="o">=</span> <span class="s2">":RequestHandler is deprecated, please use :RequestCallback"</span>
</span><span class='line'> <span class="vi">@logger</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span><span class='line'> <span class="n">callback</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="n">server</span><span class="o">.</span><span class="n">service</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>
</span><span class='line'> <span class="k">rescue</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:EOFError</span><span class="p">,</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:RequestTimeout</span> <span class="o">=></span> <span class="n">ex</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">set_error</span><span class="p">(</span><span class="n">ex</span><span class="p">)</span>
</span><span class='line'> <span class="k">rescue</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:Error</span> <span class="o">=></span> <span class="n">ex</span>
</span><span class='line'> <span class="vi">@logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="n">ex</span><span class="o">.</span><span class="n">message</span><span class="p">)</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">set_error</span><span class="p">(</span><span class="n">ex</span><span class="p">)</span>
</span><span class='line'> <span class="k">rescue</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:Status</span> <span class="o">=></span> <span class="n">ex</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">ex</span><span class="o">.</span><span class="n">code</span>
</span><span class='line'> <span class="k">rescue</span> <span class="no">StandardError</span> <span class="o">=></span> <span class="n">ex</span>
</span><span class='line'> <span class="vi">@logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="n">ex</span><span class="p">)</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">set_error</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
</span><span class='line'> <span class="k">ensure</span>
</span><span class='line'> <span class="k">if</span> <span class="n">req</span><span class="o">.</span><span class="n">request_line</span>
</span><span class='line'> <span class="k">if</span> <span class="n">req</span><span class="o">.</span><span class="n">keep_alive?</span> <span class="o">&&</span> <span class="n">res</span><span class="o">.</span><span class="n">keep_alive?</span>
</span><span class='line'> <span class="n">req</span><span class="o">.</span><span class="n">fixup</span><span class="p">()</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="n">res</span><span class="o">.</span><span class="n">send_response</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
</span><span class='line'> <span class="n">server</span><span class="o">.</span><span class="n">access_log</span><span class="p">(</span><span class="vi">@config</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">break</span> <span class="k">if</span> <span class="vi">@http_version</span> <span class="o"><</span> <span class="s2">"1.1"</span>
</span><span class='line'> <span class="k">break</span> <span class="k">unless</span> <span class="n">req</span><span class="o">.</span><span class="n">keep_alive?</span>
</span><span class='line'> <span class="k">break</span> <span class="k">unless</span> <span class="n">res</span><span class="o">.</span><span class="n">keep_alive?</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h3>req.parse</h3>
<p>从socket读取请求报文,构造request实例。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="n">socket</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@socket</span> <span class="o">=</span> <span class="n">socket</span>
</span><span class='line'> <span class="k">begin</span>
</span><span class='line'> <span class="vi">@peeraddr</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="ss">:peeraddr</span><span class="p">)</span> <span class="p">?</span> <span class="n">socket</span><span class="o">.</span><span class="n">peeraddr</span> <span class="p">:</span> <span class="o">[]</span>
</span><span class='line'> <span class="vi">@addr</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="ss">:addr</span><span class="p">)</span> <span class="p">?</span> <span class="n">socket</span><span class="o">.</span><span class="n">addr</span> <span class="p">:</span> <span class="o">[]</span>
</span><span class='line'> <span class="k">rescue</span> <span class="ss">Errno</span><span class="p">:</span><span class="ss">:ENOTCONN</span>
</span><span class='line'> <span class="k">raise</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:EOFError</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="n">read_request_line</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span>
</span><span class='line'> <span class="k">if</span> <span class="vi">@http_version</span><span class="o">.</span><span class="n">major</span> <span class="o">></span> <span class="mi">0</span>
</span><span class='line'> <span class="n">read_header</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@header</span><span class="o">[</span><span class="s1">'cookie'</span><span class="o">].</span><span class="n">each</span><span class="p">{</span><span class="o">|</span><span class="n">cookie</span><span class="o">|</span>
</span><span class='line'> <span class="vi">@cookies</span> <span class="o">+=</span> <span class="ss">Cookie</span><span class="p">:</span><span class="ss">:parse</span><span class="p">(</span><span class="n">cookie</span><span class="p">)</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="vi">@accept</span> <span class="o">=</span> <span class="no">HTTPUtils</span><span class="o">.</span><span class="n">parse_qvalues</span><span class="p">(</span><span class="nb">self</span><span class="o">[</span><span class="s1">'accept'</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@accept_charset</span> <span class="o">=</span> <span class="no">HTTPUtils</span><span class="o">.</span><span class="n">parse_qvalues</span><span class="p">(</span><span class="nb">self</span><span class="o">[</span><span class="s1">'accept-charset'</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@accept_encoding</span> <span class="o">=</span> <span class="no">HTTPUtils</span><span class="o">.</span><span class="n">parse_qvalues</span><span class="p">(</span><span class="nb">self</span><span class="o">[</span><span class="s1">'accept-encoding'</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@accept_language</span> <span class="o">=</span> <span class="no">HTTPUtils</span><span class="o">.</span><span class="n">parse_qvalues</span><span class="p">(</span><span class="nb">self</span><span class="o">[</span><span class="s1">'accept-language'</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">return</span> <span class="k">if</span> <span class="vi">@request_method</span> <span class="o">==</span> <span class="s2">"CONNECT"</span>
</span><span class='line'> <span class="k">return</span> <span class="k">if</span> <span class="vi">@unparsed_uri</span> <span class="o">==</span> <span class="s2">"*"</span>
</span><span class='line'>
</span><span class='line'> <span class="k">begin</span>
</span><span class='line'> <span class="n">setup_forwarded_info</span>
</span><span class='line'> <span class="vi">@request_uri</span> <span class="o">=</span> <span class="n">parse_uri</span><span class="p">(</span><span class="vi">@unparsed_uri</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@path</span> <span class="o">=</span> <span class="ss">HTTPUtils</span><span class="p">:</span><span class="ss">:unescape</span><span class="p">(</span><span class="vi">@request_uri</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@path</span> <span class="o">=</span> <span class="ss">HTTPUtils</span><span class="p">:</span><span class="ss">:normalize_path</span><span class="p">(</span><span class="vi">@path</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@host</span> <span class="o">=</span> <span class="vi">@request_uri</span><span class="o">.</span><span class="n">host</span>
</span><span class='line'> <span class="vi">@port</span> <span class="o">=</span> <span class="vi">@request_uri</span><span class="o">.</span><span class="n">port</span>
</span><span class='line'> <span class="vi">@query_string</span> <span class="o">=</span> <span class="vi">@request_uri</span><span class="o">.</span><span class="n">query</span>
</span><span class='line'> <span class="vi">@script_name</span> <span class="o">=</span> <span class="s2">""</span>
</span><span class='line'> <span class="vi">@path_info</span> <span class="o">=</span> <span class="vi">@path</span><span class="o">.</span><span class="n">dup</span>
</span><span class='line'> <span class="k">rescue</span>
</span><span class='line'> <span class="k">raise</span> <span class="ss">HTTPStatus</span><span class="p">:</span><span class="ss">:BadRequest</span><span class="p">,</span> <span class="s2">"bad URI `</span><span class="si">#{</span><span class="vi">@unparsed_uri</span><span class="si">}</span><span class="s2">'."</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="sr">/close/io</span> <span class="o">=~</span> <span class="nb">self</span><span class="o">[</span><span class="s2">"connection"</span><span class="o">]</span>
</span><span class='line'> <span class="vi">@keep_alive</span> <span class="o">=</span> <span class="kp">false</span>
</span><span class='line'> <span class="k">elsif</span> <span class="sr">/keep-alive/io</span> <span class="o">=~</span> <span class="nb">self</span><span class="o">[</span><span class="s2">"connection"</span><span class="o">]</span>
</span><span class='line'> <span class="vi">@keep_alive</span> <span class="o">=</span> <span class="kp">true</span>
</span><span class='line'> <span class="k">elsif</span> <span class="vi">@http_version</span> <span class="o"><</span> <span class="s2">"1.1"</span>
</span><span class='line'> <span class="vi">@keep_alive</span> <span class="o">=</span> <span class="kp">false</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="vi">@keep_alive</span> <span class="o">=</span> <span class="kp">true</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>先是解析请求行,再是请求报文头部解析,最后确定keep_alive</p>
<p>回到run。</p>
<p>一般的使用情况下server都是self,lookup_server与virtual_hosts有关。server.service就是self.service,其中,找到了真正的servlet的实例,并调用实例的service方法。其中可以看看mount方法的作用:可以把不同的servlet mount不同的url上,形成一个路由表。</p>
<p>rack的webrick handler就是一个webrick servlet,并且复写了service这个方法。server.service(req, res)调用完毕,那么response的各个属性也就填好了,接着<code>res.send_response(sock)</code>会通过socket来发送数据。</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2014/01/08/chat-server-performance-tune/">一次聊天服务器性能调优经历分享</a></h1>
<p class="meta">
<time datetime="2014-01-08T08:48:28+08:00" pubdate data-updated="true">Jan 8<span>th</span>, 2014</time>
| <a href="/blog/2014/01/08/chat-server-performance-tune/#disqus_thread"
data-disqus-identifier="http://creatingev.github.io/blog/2014/01/08/chat-server-performance-tune/">Comments</a>
</p>
</header>
<div class="entry-content"><p>导读:<br/>
这是我的一次性能调优经历的分享,我的本意不是分享我调优所获得的知识与经验,而是希望可以启发大家的思考。如天放所说,其实最有效完成一个目标的方式是用二分法,逐步找到问题的解决办法。但二分法的本质是趋利避害,是以目标为导向的方法,追求最短时间解决一个问题,追求最易解决问题的办法。二分法可能是高效地解决问题的好办法,但却不是学习的好方法。</p>
<p>“名人哲言”里有一句话,“上帝若為你關了一扇門,必然為你開另一扇門”,在这里,恰恰相反,“当你开了一扇门,你必然会关上另外一扇门”。当你用二分法解决了你的问题,并为此窃喜之时,或许被你关上的无数扇门了有你追求了许久的宝藏,这些宝藏一次次与你擦肩而过。从长远的角度来讲,二分法不见得是最高效的办法。</p>
<p>带着问题去学习,才是一个最高效的学习方式,在我们去完成我们的目标的时候,我们会碰到各种各样的问题,正是这些问题的存在,才让我们逐步减少因我们片段学习(我们的许多知识是通过google得到的)而产生的割裂性,逐渐完整化,系统化。而二分法在许多时候,会让我们规避掉这些问题,导致我们错失最好的学习机会。</p>
<p>在我的这次经验中,我获得了一些看起来很简单,但是却让我受益无穷的经验和知识宝藏,有了这些知识的夯实,我才能慢慢往更深的地方走去。或许可以这么说,二分法是一个解决问题的好办法,但不是一个提高知识深度的好办法。而提高你的知识深度,有时候,需要你违背二分法的原则,脑子缺根筋,在一条未知的路上反复探索,才能到达更深的地步。</p>
<p>理是这么个理,你需要有足够的智慧去区分什么时候需要用二分法,什么时候需要钻牛角尖。其实最简单的办法是看时间的要求,在时间紧张的时候,我们应该追求效率,在时间充裕的时候,我们应该追求深度,对问题穷追不舍。我觉得每个人都应该在快速的节奏下,停下来想一想,去深究一些问题,张弛有度才是一个健康的方式。这也是为什么我希望我们的开发节奏是一大一小穿插的方式进行的原因。(但看起来,大部分人都误用了小版本的用途,小版本最主要的目的是希望你能深究一些你碰到的难题,比如重构,比如UI效果,而现在大家却只用小版本来解决小bug…)</p>
<p>第二件,我想通过这次经历分享的是,面对未知原因的问题的时候,其实每个人都可以解决。用最笨的办法,用最小时候学过的做实验的办法。我们小时候做过许多化学实验,物理实验,老师通常会选一组参照物,最终通过与参照物对比的方式来得出我们的结论。所以我们碰到未知问题的时候,也同样的可以用这样的办法解决,但请注意最重要的一点,要保证参照物与实验对象只有唯一一点不同,这样结论才是正确的,否则实验没有任何意义。所以大胆假设,小心求证才显得如此重要。</p>
<p>有参照物的问题,每个人都有办法去解决,每个人都不要错过这样一个完美的学习的机会。</p>
<p>这次调优经历不见得是最高效,最好的方式,结果也不见得是最好的结果,但我想,希望我的这次抛砖引玉,可以给大家带来更多的思考。</p>
<hr />
<p>ppt内容预览下载地址(可以用上一页,下一页翻页,这样就像ppt了):<a href="http://pan.baidu.com/s/1sjybu8X">http://pan.baidu.com/s/1sjybu8X</a></p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/12/04/splitting-a-fat-model-into-multiple-files/">Splitting a Fat Model Into Multiple Files</a></h1>
<p class="meta">
<time datetime="2013-12-04T18:07:10+08:00" pubdate data-updated="true">Dec 4<span>th</span>, 2013</time>
| <a href="/blog/2013/12/04/splitting-a-fat-model-into-multiple-files/#disqus_thread"
data-disqus-identifier="http://creatingev.github.io/blog/2013/12/04/splitting-a-fat-model-into-multiple-files/">Comments</a>
</p>
</header>
<div class="entry-content"><h2>起因</h2>
<p>Rails的应用内,总有一些超级复杂的model,比如User。而正因是<strong>胖model,瘦controller</strong>这样的指导思想,会导致一些model臃肿不堪,有些model甚至会超过1000行,而一旦超过1000行,一个类的代码就变得难以维护了。所以一段时间内,我们为了不让我们的model太臃肿,我们只能不往User里面写代码,而把一些代码写在其他的model里面。比如写成如下的方式</p>
<pre><code>#exam.rb
class Exam < ActiveRecord::Base
def add_user(user)
end
end
</code></pre>
<p>但这样的方式<code>Exam.add_user(user)</code>明显没有<code>user.join_exam(exam)</code>直观。</p>
<pre><code>#user.rb
class User < ActiveRecord::Base
def join_exam(exam)
end
end
</code></pre>
<p>所以为了兼顾直观与保持model可维护性更高,我查找了一些资料,尝试为我们的model瘦身。</p>
<h2>解决思路</h2>
<p>一个广为流传的办法是<a href="https://gist.github.com/dhh/1014971">Use concerns to keep your models manageable</a>,这是dhh(Creator of Ruby on Rails)大神推荐的方式,已被列入Rails4的规范之中了(<a href="http://ruby-china.org/topics/7709">Rails4重大设计决策:“胖”Model用ActiveSupport::Concern瘦身</a>)。</p>
<p>但使用concern的根本目的不是为了解决单个model的肥胖问题,而是为了让一些model通用的代码片段存在于一个合适的位置,比如commentable,这样但凡有用到comment的model,只要<code>include commentable</code>就可以实现评论相关的功能,这样的实现方式既简单又直观。</p>
<p>如果为model瘦身,主要的问题是,如何分离你的业务逻辑,因为单纯的将代码发在几个小文件中是没有什么意义的(为什么没有意义?因为会增加你查找的难度和理解的难度),这也是为什么<a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/">7 Patterns to Refactor Fat ActiveRecord Models</a>说</p>
<pre><code>“Any application with an app/concerns directory is concerning.”
</code></pre>
<p>的一个重要原因。</p>
<p>所以综合以上思路,最终实践得出了三个可以简化User model的办法</p>
<ul>
<li>为model抽象一些功能性concern,比如Authenticatable, Genderable。</li>
<li>为model分离一些业务相关模块。比如user_post, 比如 user_event</li>
<li>将特殊的功能模块抽象成class</li>
</ul>
<h2>实践</h2>
<p>以下实践均以User model为例</p>
<h3>为model抽象一些功能性concern</h3>
<p>这样做符合concern本身的意图,比如将用户验证的相关操作和相关scope放在Authenticatable的module,这样User就拥有了可拔插的能力,如果想去掉相关的功能,只需要取消include就可以了。</p>
<p>所以,这部分的代码类似于</p>
<pre><code>#app/models/concerns/authenticatable.rb
# -*- encoding : utf-8 -*-
module Authenticatable
extend ActiveSupport::Concern
included do
def self.basic_auth(account, password)
end
end
def has_password?
end
end
</code></pre>
<h3>为model分离一些业务相关模块</h3>
<p>这样做是不太符合concern本身的意图的,但借用concern来表达我们的业务逻辑,也没有什么问题。但这样做最大的问题是,这部分的concern不应该和传统的concern混在一起,这样不同model的业务逻辑容易混乱(至少不好查找)。</p>
<p>所以,我们将这部分代码放在了<code>app/models/concerns/user_concern/</code>中,代码类似如下</p>
<pre><code>#app/models/concerns/user_concern/post.rb
# -*- encoding : utf-8 -*-
module UserConcern
module Post
extend ActiveSupport::Concern
included do
has_many :posts, :dependent => :destroy
has_many :other_posts, :dependent => :destroy
end
end
end
</code></pre>
<h3>将特殊的功能模块抽象成class</h3>
<p>这种办法最受<a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/">7 Patterns to Refactor Fat ActiveRecord Models</a>推崇,所以,可以借鉴一些这边文章提到的一些方法来抽象我们的代码。这部分代码类似于</p>
<pre><code>#app/models/lib/edit_limit.rb
# -*- encoding : utf-8 -*-
class EditLimit
NO_MODIFY_LIMIT_USERS_KEY = "no_modify_limit_users"
MODIFY_LIMIT = 5
MODIFY_LIMIT_KEY = "editable_times_%d"
def initialize(user)
@user = user
end
def self.no_modify_limit_users
Rails.cache.fetch NO_MODIFY_LIMIT_USERS_KEY do
Set.new
end
end
def self.add_no_modify_limit_user(user)
no_limit_user_ids = no_modify_limit_users
no_limit_user_ids << user.id
Rails.cache.write NO_MODIFY_LIMIT_USERS_KEY, no_limit_user_ids
no_limit_user_ids
end
end
</code></pre>
<h3></h3>
<p>最终,我们在user.rb的引用如下所示</p>
<pre><code>#app/models/user.rb
class User < ActiveRecord::Base
include Authenticatable
include UserConcern::Post
end
</code></pre>
<h2>总结</h2>
<p>因为Rails本身的设计,对于一些不太复杂的逻辑与应用,实在无需煞费苦心去寻求瘦身与简化。因为符合Rails本身的设计思想在很大程度上就已经很好的分离了model,view,controller(MVC)三者的职责。而一些复杂的应用,在不考虑更好的可读性的前提下(比如将许多逻辑放置在关联表中),也不会形成太过肥胖的model。所以,只有一些人像我这样对可读性有些洁癖,又想让model保持简单的人,才需要类似的解决办法。</p>
<h2>参考资料</h2>
<ul>
<li><a href="https://gist.github.com/dhh/1014971">Use concerns to keep your models manageable</a></li>
<li><a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/">7 Patterns to Refactor Fat ActiveRecord Models</a></li>
<li><a href="https://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns">Put chubby models on a diet with concerns</a></li>
</ul>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/12/03/something-about-probability/">一定会发生和一定不会发生的事情</a></h1>
<p class="meta">
<time datetime="2013-12-03T06:25:41+08:00" pubdate data-updated="true">Dec 3<span>rd</span>, 2013</time>
| <a href="/blog/2013/12/03/something-about-probability/#disqus_thread"
data-disqus-identifier="http://creatingev.github.io/blog/2013/12/03/something-about-probability/">Comments</a>
</p>
</header>
<div class="entry-content"><p>谈起这个话题,原因是我的一次愚蠢的尝试。我曾经在一个游戏中用虚拟积分玩过这样的游戏,我先用1个积分去投注,如果输了,我就翻倍去投注,直到我输光为止。而我不相信我能连输十盘,我觉得那是一个不可能发生的事情。结果很明显,我为了赚一个积分,把我的所有积分都输光了。所以从这点出发,我觉得有必要去正视什么是一定会发生的事情,什么是一定不会发生的事情,两者的边界是什么?</p>
<h2>一定会发生的事情</h2>
<h3>赌博</h3>
<p>以我上述的例子为准,假设你的人品爆发,碰到赌场了绝对公平的游戏,你赢的概率有50%。所以我们可以计算一下你连输10(或11次,下方是11次的概率)盘,输掉内裤的概率有多大。</p>
<pre><code>连输11次的概率=m¹º=0.0009765625 其中m=0.5
</code></pre>
<p>所以,很明显你连输11盘的概率很低,换言之,你赢的概率达到了99.90%,这个概率达到了千足金的含金量。可问题是,这够稳固吗?这就意味着,你玩1000次,你就要输掉一次,只为了赚1块钱。</p>
<p>ok,你可以说假设你每天玩一次,1000天才会输掉一次,就是说快3年你才输掉一次。这看起来还挺好的。<code>常胜将军</code>。但问题关键是,这是赌博,赌博最重要的一点是趋利,是贪婪,没有人会满足一天只赚一块钱,说不定你一天就会玩上100盘,不到十天,你就输光了你的所有资本。</p>
<p>我称上述的例子为一定会发生的事情,哪怕它的概率达到了99.9%。</p>
<h2>一定不会发生的事情</h2>
<h3>飞机失事</h3>
<p>众所周知,飞机失事的概率是很低的,但到底是一个什么量级的数据呢?</p>
<p>据<a href="http://planecrashinfo.com/cause.htm">Accident statistics</a>的统计,平均340万次飞行才会有一次至少一人死亡的记录,所以全身而退的概率就等于</p>
<pre><code>1-1/3400000 = 0.99999970588
</code></pre>
<p>这个比例就相当于,如果象梁朝伟一样,每天飞到巴黎去喂个鸽子,第二天飞回来(每天一次飞机)。那么9315年才能出现一次(至少一个人死亡)的飞机失事。</p>
<p>所以这个比例在我们看来是几乎不会出现的概率,我们暂时可以称之为一定不会发生的事情。</p>
<h3>UUID(Universally Unique Identifier)</h3>
<p>UUID是计算机上普遍使用的用于唯一标识的标识符。它一般是根据mac地址+时间戳+随机数生成的。</p>
<pre><code>UUID的标准型式包含32个16进位数字,以连字号分为五段,形式为8-4-4-4-12的32个字符。示例:
550e8400-e29b-41d4-a716-446655440000
</code></pre>
<p>所以假设UUID是全随机的,那么它的概率是</p>
<pre><code>16的32次方 = 3.4 x 10的38次方
</code></pre>
<p>所以,也就是说随机两次,重复一次的概率=1/ 3.4*10的38次方</p>
<p>那随机UUID的重复机率是多少呢?</p>
<pre><code>随机产生的UUID(例如说由java.util.UUID类型产生的)的128个比特中,有122个比特是随机产生,4个比特在此版本('Randomly generated UUID')被使用,还有2个在其变体('Leach-Salz')中被使用。利用生日悖论,可计算出两笔UUID拥有相同值的机率约为
p(n)\approx 1-e^{-n^2/{2 \cdot x}}
以下是以x=2122计算出n笔UUID后产生碰撞的机率:
n 机率
68,719,476,736 = 2的36次方 0.0000000000000004 (4 x 10-16)
2,199,023,255,552 = 2的41方 0.0000000000004 (4 x 10-13)
70,368,744,177,664 = 2的46方 0.0000000004 (4 x 10-10)
与被陨石击中的机率比较的话,已知一个人每年被陨石击中的机率估计为170亿分之1[1],也就是说机率大约是0.00000000006 (6 x 10-11),等同于在一年内置立数十兆笔UUID并发生一次重复。换句话说,每秒产生10亿笔UUID,100年后只产生一次重复的机率是50%。如果地球上每个人都各有6亿笔UUID,发生一次重复的机率是50%。
产生重复UUID并造成错误的情况非常低,是故大可不必考虑此问题。
机率也与乱数产生器的质量有关。若要避免重复机率提高,必须要使用奠基于密码学上的假乱数产生器来生成值才行。
</code></pre>
<p>所以,UUID的重复可以认为是一定不可能发生的事情。而这个概率相比飞机失事的概率来说,对我们来说,那真的是非常非常放心了。如果飞机失事的概率达到UUID重复的概率,那就没有空难了。</p>
<h2>两者的边界</h2>
<p>当我们去评估哪一些属于一定会发生,哪些是一定不会发生的时候,有两个指标。一个是单次发生的概率,第二个是你重复的次数。而我们去评估的时候,通常会以相对夸张的次数来评估最终发生的概率。</p>
<p>以飞机失事的概率来计算,假设一个人的寿命是90年,每天坐一班飞机。那么他在有生之年失事的概率<1%。虽然没有绝对安全,但相对更为复杂的生存环境,我觉得这已经是一个完美的概率了。</p>
<p>所以我觉得,飞机失事的概率为分界(1%的最终概率),远大于最终死亡概率的,那么它就可以认为是一定会发生的事情,而远小于最终死亡概率的,那么可以列为安全概率。而在这概率附近的,看你个人自己的看法啦。</p>
<h2>参考资料</h2>
<ul>
<li><a href="http://www.360doc.com/content/11/1126/15/5575132_167527846.shtml">大型客机空难事故率排名</a></li>
<li><a href="http://planecrashinfo.com/cause.htm">Accident statistics</a></li>
<li><a href="http://zh.wikipedia.org/w/index.php?title=%E7%94%9F%E6%97%A5%E6%94%BB%E5%87%BB&variant=zh-cn">生日问题</a></li>
<li><a href="http://zh.wikipedia.org/w/index.php?title=UUID&variant=zh-cn">UUID</a></li>
</ul>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/11/30/what-we-really-care/">我们真正关心的事情</a></h1>
<p class="meta">
<time datetime="2013-11-30T09:13:54+08:00" pubdate data-updated="true">Nov 30<span>th</span>, 2013</time>
| <a href="/blog/2013/11/30/what-we-really-care/#disqus_thread"
data-disqus-identifier="http://creatingev.github.io/blog/2013/11/30/what-we-really-care/">Comments</a>
</p>
</header>
<div class="entry-content"><p>如果你是位新员工,你可能会有一些困惑,我们没有代码规范,却要求你代码应该这么写,而不应该那么写,相同的,我们会建议你有些事情应该这样做,而不是那么做。你不知道到底哪些事情才是最重要的,在不知道优先级的情况下,你可能会犯一些错误,你会让你感到很沮丧。所以,从一个更严谨的角度,我应该把我们真正在乎的事情全都告诉你。
我们真正在乎的事情是:</p>
<ul>
<li>工作态度</li>
<li>效率</li>
<li>创造性</li>
</ul>
<h2>工作态度</h2>
<h3>认真严谨的工作态度</h3>
<p>这件事情,我在之前提过,你可以参考<a href="blog/2013/11/30/an-advise-to-the-new-staff">an advise to the new staff</a>,这是最最重要的事情,这是我们接下来要说的所有事情的前提。</p>
<h3>我们没有程序员,只有工程师</h3>
<p>希望你可以理解这句听上去有点绕口的话。程序员和工程师,在我的理解,两者最大的区别,前者只工作在代码开发阶段,而后者参与一件事情的全部(请以广义的工程师来理解,非片面的IT工程师)。而这点区别决定了,在这里,你不是对你的leader负责,而是对整个产品负责。所以需要要求你:</p>
<ul>
<li>希望你以用户的眼光来看待问题。不管是API接口还是App开发,请以用户的视角来看待问题。不要犯类似于给用户的接口却不关心排序这么低级的错误。</li>
<li>在你的角度,尽你所能推进事情的进展。请记住你不是一个程序员,你可以推动相关人员在这件事情上的进度,(比如推动设计师完成急需的东西)。请不要置身事外,等待事情的进展。</li>
<li>写代码之前先想清楚,先写spec梳理整个功能的开发以及后续。</li>
</ul>
<p>在职责之外,也有属于你的权利</p>
<ul>
<li>你可以强烈坚持你的意见。(但在申诉无效之后,请服从团队的决定)</li>
<li>关于对于产品的建议,你的建议会比其他人的更受重视。</li>
</ul>
<h2>效率</h2>
<p>效率是团队竞争力的根基。围绕效率,有一系列我们推崇的工作方式</p>
<h3>效率、专注——番茄工作法</h3>