-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
1075 lines (605 loc) · 36.4 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--[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="lifeibo">
<meta name="description" content="致力于高性能网络服务器的优化与研究。">
<meta name="keywords" content="李飞勃 nginx 网络 源码 学习 lifeibo push comet Linux Apache">
<!-- 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://blog.lifeibo.com/index.html">
<link href="/favicon.ico" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/stylesheets/highlight/github.css" rel="stylesheet" type="text/css">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="/javascripts/ender.js"></script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<link href="/atom.xml" rel="alternate" title="李子的博客" type="application/atom+xml">
<!--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" -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-21744925-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">李子的博客</a></h1>
<h2>想想写写</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:blog.lifeibo.com" />
<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>
<li><a href="/about/">About</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2013/01/28/ngx-lua-and-go.html">Ngx_lua与go高并发性能对比</a></h1>
<p class="meta">
<time datetime="2013-01-28T00:46:00+08:00" pubdate data-updated="true">Jan 28<span>th</span>, 2013</time>
<span class="categories">
<a class='category' href='/blog/categories/language/'>language</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2013/01/28/ngx-lua-and-go.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>nginx在处理高并发能力上非常出色,而go作为新时代互联网语言,在设计之初就为实现高并发。</p>
<p>ngx_lua由nginx来处理网络事件,并使用协程来实现非阻塞,从而实现高并发。
go语言级别提供非阻塞的api,同样使用协程来提供高并发处理。</p>
<p>我们来测试对比一下两者的性能。</p>
<pre><code>ngx_lua:Tengine/1.4.3+luajit+ngx_lua
go:go1.0.3
</code></pre>
<p>分别实现512字节的内容的输出,对比在不同并发下的qps。</p>
<p>测试机器:</p>
<pre><code>16core Intel(R) Xeon(R) CPU E5520 @ 2.27GHz
Linux localhost 2.6.18-164.el5 #1 SMP Tue Aug 18 15:51:48 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux
</code></pre>
<p>使用ab进行测试,测试结果如下:</p>
<table>
<thead>
<tr>
<th>短连接 </th>
<th> 100 </th>
<th> 200 </th>
<th> 500 </th>
<th> 1000 </th>
<th> 2000</th>
</tr>
</thead>
<tbody>
<tr>
<td>ngx_lua </td>
<td> qps:17329 us:2.6% sy:2.2% </td>
<td> 17744 </td>
<td> 16443 </td>
<td> 15852 </td>
<td> 13589</td>
</tr>
<tr>
<td>go </td>
<td> qps:16538 us:9.1% sy:3.6% </td>
<td> 16546 </td>
<td> 15988 </td>
<td> 15032 </td>
<td> 13757</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>长连接 </th>
<th> 100 </th>
<th> 200 </th>
<th> 500</th>
</tr>
</thead>
<tbody>
<tr>
<td>ngx_lua </td>
<td> qps:72274 us:13.8% sy:8.5 </td>
<td> 61204 </td>
<td> 61983</td>
</tr>
<tr>
<td>go </td>
<td> qps:39072 us:29% sy:15% </td>
<td> 38688 </td>
<td> 38238</td>
</tr>
</tbody>
</table>
<p><strong>从结果中,可以看出短连接时,两者qps相差不大,而长连接时,两者相差较大。go的cpu占用比ngx_lua要高不少。另外,go在并发数增加的情况下,性能依然出色。</strong></p>
<p>相关测试代码。</p>
<p>lua代码:</p>
<pre><code>ngx.print("aaaaa...512...aaa")
</code></pre>
<p>go 代码:</p>
<pre><code>package main
import (
"net/http"
"log"
"fmt"
"runtime"
)
func handler512(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "keep-alive")
a := []byte("aaaaa...512...aaa")
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(a)))
w.Write(a)
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
http.HandleFunc("/512b", handler512)
log.Fatal(http.ListenAndServe(":8080", nil))
}
</code></pre>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/12/25/nginx-process-exit.html">Nginx问题定位之监控进程异常退出</a></h1>
<p class="meta">
<time datetime="2012-12-25T14:33:00+08:00" pubdate data-updated="true">Dec 25<span>th</span>, 2012</time>
<span class="categories">
<a class='category' href='/blog/categories/nginx/'>nginx</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2012/12/25/nginx-process-exit.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>nginx在运行过程中是否稳定,是否有异常退出过?这里总结几项平时会用到的小技巧。</p>
<p><strong>1. 在error.log中查看是否有signal项,如果有,看看signal是多少。</strong></p>
<p>比如,这是一个异常退出的情况:</p>
<pre><code>$grep signal error.log
2012/12/24 16:39:56 [alert] 13661#0: worker process 13666 exited on signal 11
</code></pre>
<p>如果在进程退出后,有coredump文件产生,则会打出如下日志:</p>
<pre><code>$grep signal error.log
2012/12/24 16:39:56 [alert] 13661#0: worker process 13666 exited on signal 11 (core dumped)
</code></pre>
<p><strong>2. 简单方式,看进程号是否连续</strong></p>
<p>一般来说,在worker进程启动时,其进程号都是连续的(至少相差不是很远),如果有进程退出,其进程号就不一定连续。</p>
<pre><code>$ps aux | grep nginx
lizi 7223 0.0 0.0 74844 2024 ? Ss 13:32 0:00 nginx: master process ./nginx
lizi 7292 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7293 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7294 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7295 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7296 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7297 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7298 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7299 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7300 0.0 0.0 78856 5468 ? S 13:33 0:00 nginx: worker process
lizi 7301 0.0 0.0 78856 5452 ? S 13:33 0:00 nginx: worker process
</code></pre>
<p>可以看到,10个worker进程,基本从7292到7301,进程号连续。<br/>
如下:</p>
<pre><code>$ps aux | grep nginx
nobody 9492 16659 26 09:18 ? 01:10:41 nginx: worker process
root 16659 1 0 Dec24 ? 00:00:00 nginx: master process ./nginx
nobody 16663 16659 11 Dec24 ? 02:41:38 nginx: worker process
nobody 19344 16659 24 10:18 ? 00:50:54 nginx: worker process
nobody 25447 16659 28 07:41 ? 01:43:56 nginx: worker process
</code></pre>
<p>进程号已不再连续,说明nginx可能有工作进程异常退出。</p>
<p><strong>3. 查看dmesg系统消息。</strong></p>
<p>在man手册里面是这么描述dmesg的:</p>
<pre><code>DESCRIPTION
dmesg is used to examine or control the kernel ring buffer.
</code></pre>
<p>查看dmesg是检测系统运行状态的常用手段,通常可以帮我们排查很多问题。当然,如果有进程异常退出,dmesg也可以看到。</p>
<pre><code>$dmesg
nginx[24721]: segfault at 0000000000000001 rip 0000000000000001 rsp 00007ffff58d8180 error 14
nginx[1729]: segfault at 0000000000000190 rip 00000000004c2d27 rsp 00007ffff58d8340 error 4
nginx[22002]: segfault at ffffffffffffffff rip 000000001c959744 rsp 00007fff43caac18 error 6
</code></pre>
<p>rip表示程序退出时的ip寄存器内容,当没有core文件可用时,可根据此值以及反汇编来查找程序core的位置。</p>
<p><strong>4. 打开coredump文件。</strong></p>
<p>一般我们在程序启动前,通过<code>ulimit -c ulimited</code>来设置core文件的大小,也可以修改<code>/etc/security/limits.conf</code>文件,添加如下信息:</p>
<pre><code>admin soft core 1000000
admin hard core 1000000
</code></pre>
<p>也可以直接修改nginx的配置文件,添加如下配置项:</p>
<pre><code>worker_rlimit_core 10000m;
</code></pre>
<p>而此时,在limit系统中,默认coredump文件会写在启动nginx时的目录,如果nginx在启动时worker进程的用户<strong>没有权限写</strong>到这个目录,进程在异常退出时,就无法产生coredump文件。由于nginx启动后,或者是由别人启动,我们无法知道nginx在启动时的目录,也就无法知道core文件的目录。我曾经碰到过这样的问题,通过日志查看,是coredump出来了,但却找不到coredump的文件。</p>
<p>这里有一个小技巧,查看<code>/proc/pid/cwd</code>可以看到进程的工作目录,而core文件会产生在工作目录。</p>
<p>nginx可以配置工作目录来改变默认的工作目录,于是,我们需要配置<code>working_directory</code>为目的工作目录,我们的core文件也会产生在这个目录。</p>
<pre><code>working_directory /path/to/core;
</code></pre>
<p><code>working_directory</code>与编译时指定的<code>--prefix=/path</code>不同,后者表示在配置文件中所用的相对路径所生产的绝对路径。所以,<code>working_directory</code>不会影响到配置的引用路径,而仅仅是为了改变core文件的路径,当然nginx必须有写这个目录的权限,否则无法core出来。</p>
<p>所以,这里,我推荐的做法是,配置<code>worker_rlimit_core</code>与<code>working_directory</code>这两个指令,这样,就不需要修改操作系统的参数就可以正常core出来了。</p>
<p>以上这些是平时用到的一些技巧的总结,大家玩得开心!</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/12/23/vim-sub.html">Vim字符串替换</a></h1>
<p class="meta">
<time datetime="2012-12-23T21:05:00+08:00" pubdate data-updated="true">Dec 23<span>rd</span>, 2012</time>
<span class="categories">
<a class='category' href='/blog/categories/vim/'>vim</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2012/12/23/vim-sub.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>经常使用vim进行字符串替换,每次却只使用全量替换,特地总结一下常用的几个替换字符串的使用。</p>
<pre><code>当前行进行替换
:s/abc/efg/
:s/abc/efg/g
所有行进行替换
:%s/abc/efg/
:%s/abc/efg/g
从第n行开始向下的所有行进行替换,当n为"."时,表示从当前行开始
:n,$s/abc/efg/
:n,$s/abc/efg/g
</code></pre>
<p>上面命令中,最后没有g表示只替换一行中第一次出现的字符串abc为efg。而后面带g的表示当前行的所有abc替换efg。</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2012/12/19/slab-usage-alloc-larger-memory.html">Nginx中slab分配大内存的陷阱</a></h1>
<p class="meta">
<time datetime="2012-12-19T17:57:00+08:00" pubdate data-updated="true">Dec 19<span>th</span>, 2012</time>
<span class="categories">
<a class='category' href='/blog/categories/nginx/'>nginx</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2012/12/19/slab-usage-alloc-larger-memory.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>我们在开发nginx模块时,需要很小心,nginx里面有很多陷阱是我们需要注意的。之前有人提到过slab分配器在使用时,不适合大内存分配,否则会出现分配不出内存的现象。</p>
<p>nginx一般使用slab来管理共享内存,在程序启动时,很分配好需要共享的内存,然后使用slab来进行初始化,之后就交给slab来管理这段内存。slab的源码分析与合适,在我之前的博客里面有分析过。这次我们针对性的看看,为什么会出现分配不出内存的现象。</p>
<p>分配内存时,会调用<code>ngx_slab_alloc_locked</code>,在这个函数里面会先判断size是否大于<code>ngx_slab_max_size</code>,代码如下。</p>
<pre><code>void *
ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)
{
size_t s;
uintptr_t p, n, m, mask, *bitmap;
ngx_uint_t i, slot, shift, map;
ngx_slab_page_t *page, *prev, *slots;
/* 判断大小 */
if (size >= ngx_slab_max_size) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
"slab alloc: %uz", size);
/* 直接分配页 */
page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1)
>> ngx_pagesize_shift);
if (page) {
p = (page - pool->pages) << ngx_pagesize_shift;
p += (uintptr_t) pool->start;
} else {
p = 0;
}
goto done;
}
...
}
</code></pre>
</div>
<footer>
<a rel="full-article" href="/blog/2012/12/19/slab-usage-alloc-larger-memory.html">Read more →</a>
</footer>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2011/12/17/nginx-varibles.html">Nginx源码分析之变量</a></h1>
<p class="meta">
<time datetime="2011-12-17T18:51:00+08:00" pubdate data-updated="true">Dec 17<span>th</span>, 2011</time>
<span class="categories">
<a class='category' href='/blog/categories/nginx/'>nginx</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2011/12/17/nginx-varibles.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>nginx中的变量在nginx中的使用非常的多,正因为变量的存在,使得nginx在配置上变得非常灵活。</p>
<p>我们知道,在nginx的配置文件中,配合变量,我们可以动态的得到我们想要的值。最常见的使用是,我们在写<code>access_log</code>的格式时,需要用到多很多变量。
而这些变量是如何工作的呢?我们可以输出哪些变量?我们又怎么才能输出自己想要的内容呢?当然,我们可能还想知道,如何在我们的模块里面去使用变量,如何添加变量,获取变量的值,以及设置变量的内容?如何使用,以及需要注意些什么?</p>
<p>问题一大堆,那接下来,就让我们一起去一探nginx源码的秘密。</p>
<p>我要讲的内容</p>
<ol>
<li>变量的分类</li>
<li>相关结构</li>
<li>模块中操作变量的函数</li>
<li>变量的实现源码及流程</li>
</ol>
<h2>1. 变量的分类</h2>
<p>站在使用者的角度来看,我们在配置文件中可以看到:</p>
<ol>
<li>set添加的变量(变量名由用户设定)</li>
<li>nginx功能模块中添加的变量,如geo模块(变量名由用户设定)</li>
<li>nginx内建的变量(变量名已由nginx设定好,可以看<code>ngx_http_core_variables</code>结构)</li>
<li>有一定规则的变量,如”<code>http_host</code>”等(有相同前缀,表示某一类变量),我们就称为规则变量吧</li>
</ol>
<p>从这里,也解决我们的问题,在配置<code>access_log</code>时,我们可以配置哪些变量,是否是用户添加的变量,是否是内建变量在<code>ngx_http_core_variables</code>中有,其次,是否是规则变量,另外,如果想输出自己的内容,那只能写模块自己添加一个变量了,或者hack nginx在<code>ngx_http_core_variables</code>中添加一个变量。</p>
</div>
<footer>
<a rel="full-article" href="/blog/2011/12/17/nginx-varibles.html">Read more →</a>
</footer>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2011/09/09/port-reuse.html">端口重用引发的悲剧</a></h1>
<p class="meta">
<time datetime="2011-09-09T17:57:00+08:00" pubdate data-updated="true">Sep 9<span>th</span>, 2011</time>
<span class="categories">
<a class='category' href='/blog/categories/linux/'>linux</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2011/09/09/port-reuse.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>最近做个性能测试,需要在一台机器上启动很多客户端,连接到同一台服务器,我在一台机器上启动了六万个连接,于是,端口被占用完了。按照我的理解,因为我作用端口是作为客户端,应该不会影响到其它进程,于是我放心大胆地去做测试,结果就引发了悲剧。有服务器程序要用到5191端口,却显示端口被占用了,lsof看了下,居然只有我的进程占用了,完全颠覆我的惯性思想。服务端与客户端都有打开<code>SO_REUSEADDR</code>。</p>
<p>我们先来看看<code>SO_REUSEADDR</code>的说明:</p>
<pre><code>SO_REUSEADDR可以用在以下四种情况下。
(摘自《Unix网络编程》卷一,即UNPv1)
1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启
动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但
每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可
以测试这种情况。
3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个soc
ket绑定的ip地址不同。这和2很相似,区别请看UNPv1。
4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的
多播,不用于TCP。
</code></pre>
<p>分析了一下,第二种情况比较相似,但第二种情况是针对同一服务器的多个实例(多个进程),很显然跟我的情况不一样。我是在客户端与服务端间争用端口。 所以,我的情况根本就不适合以上任何一种情况,所以设置<code>SO_REUSEADDR</code>为1是无用的。</p>
<p>再接下来分析:
一个TCP连接需要由四元组来形成,即(<code>src_ip</code>,<code>src_port</code>,<code>dst_ip,dst_port</code>)。
如果有客户端建立了连接(<code>src_ip1</code>,<code>src_port1</code>,<code>dst_ip1</code>,<code>dst_port1</code>),那么,如果我们还有listen在(<code>src_ip1</code>,<code>src_port1</code>),那么当(<code>dst_ip1</code>,<code>dst_port1</code>)发送消息过来,系统应该把消息给谁?所以就说明了客户端占用了某一端口时,该端口就不能被其它进程listen了。</p>
<p>那么,对于有些童鞋,可能还有这样的疑问,是否一台机器就只能建立65535个连接了(端口16位限制)?非也,一个连接由四元组(<code>src_ip</code>,<code>src_port</code>,<code>dst_ip</code>,<code>dst_port</code>)形式,那么当(<code>src_ip</code>,<code>src_port</code>)一定时,变化的(<code>dst_ip</code>,<code>dst_port</code>)就可以建立更多连接了。</p>
<p>可能有些童鞋还有疑问,作为一个服务器监控一个端口,比如80端口,它为什么可以建立上百万个连接?首先要明白一点,当accept出来后的新socket,它所占用的本地端口依然是80端口,很多新手都以为是一个新的随机端口。由四元组就很容易分析到了,同一个(<code>src_ip</code>,<code>src_port</code>),它所对应的(<code>dst_ip</code>,<code>dst_port</code>)可以无穷变化,这样就可以建立很多个客户端的请求了。</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2011/08/29/net-work.html">统计网卡流量</a></h1>
<p class="meta">
<time datetime="2011-08-29T17:57:00+08:00" pubdate data-updated="true">Aug 29<span>th</span>, 2011</time>
<span class="categories">
<a class='category' href='/blog/categories/linux/'>linux</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2011/08/29/net-work.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>显示网卡流量的方法蛮多,一般我们可以通过dstat来查看,但dstat不一定所有的机器都有安装。而我们知道,通过ifconfig可以看到某一网卡发送与接收的字节数,所以我们可以写一个脚本来统计一下。</p>
<p>先看ifconfig:</p>
<pre><code>$ ifconfig eth0
eth0 Link encap:Ethernet HWaddr A4:BA:DB:43:BA:B1
inet addr:10.232.4.34 Bcast:10.232.4.255 Mask:255.255.255.0
inet6 addr: fe80::a6ba:dbff:fe43:bab1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:301707081 errors:0 dropped:1346358 overruns:0 frame:0
TX packets:296718885 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:28485042645 (26.5 GiB) TX bytes:35887266717 (33.4 GiB)
Interrupt:98 Memory:d6000000-d6012800
</code></pre>
<p>我们可以看到rx与tx两个数据,于是我们的脚本出来了:</p>
<pre><code>#!/bin/bash
alias ifconfig="/sbin/ifconfig"
eth=eth0
while true; do
RXpre=$(ifconfig ${eth} | grep bytes | awk '{print $2}'| awk -F":" '{print $2}')
TXpre=$(ifconfig ${eth} | grep bytes | awk '{print $6}' | awk -F":" '{print $2}')
sleep 1
RXnext=$(ifconfig ${eth} | grep bytes | awk '{print $2}'| awk -F":" '{print $2}')
TXnext=$(ifconfig ${eth} | grep bytes | awk '{print $6}' | awk -F":" '{print $2}')
echo RX ----- TX
echo "$((((${RXnext}-${RXpre})/1024)/1024))MB/s $((((${TXnext}-${TXpre})/1024/1024)))MB/s"
done
</code></pre>
<p>脚本暂时比较简单,可以添加一些参数判断,比如多长时间显示一次等等,先看看执行结果:</p>
<pre><code>$ ./a
RX ----- TX
5MB/s 7MB/s
RX ----- TX
5MB/s 7MB/s
RX ----- TX
4MB/s 6MB/s
RX ----- TX
4MB/s 6MB/s
RX ----- TX
</code></pre>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2011/07/07/200-long-connection.html">Http长连接200万尝试及调优</a></h1>
<p class="meta">
<time datetime="2011-07-07T17:57:00+08:00" pubdate data-updated="true">Jul 7<span>th</span>, 2011</time>
<span class="categories">
<a class='category' href='/blog/categories/http/'>http</a>
</span>
<span class="byline author vcard">Posted by <span class="fn">lifeibo</span></span>
| <a href="/blog/2011/07/07/200-long-connection.html#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>对于一个server,我们一般考虑他所能支撑的qps,但有那么一种应用, 我们需要关注的是它能支撑的连接数个数,而并非qps,当然qps也是我们需要考虑的性能点之一。这种应用常见于消息推送系统,也称为comet应用,比如聊天室或即时消息推送系统等。comet应用具体可见我之前的介绍,在此不多讲。对于这类系统,因为很多消息需要到产生时才推送给客户端,所以当没有消息产生时,就需要hold住客户端的连接,这样,当有大量的客户端时,就需要hold住大量的连接,这种连接我们称为长连接。</p>
<p>首先,我们分析一下,对于这类服务,需消耗的系统资源有:cpu、网络、内存。所以,想让系统性能达到最佳,我们先找到系统的瓶颈所在。这样的长连接,往往我们是没有数据发送的,所以也可以看作为非活动连接。对于系统来说,这种非活动连接,并不占用cpu与网络资源,而仅仅占用系统的内存而已。所以,我们假想,只要系统内存足够,系统就能够支持我们想达到的连接数,那么事实是否真的如此?如果真能这样,内核来维护这相当大的数据结构,也是一种考验。</p>
<p>要完成测试,我们需要有一个服务端,还有大量的客户端。所以需要服务端程序与客户端程序。为达到目标,我的想法是这样的:客户端产生一个连接,向服务端发起一个请求,服务端hold住该连接,而不返回数据。</p>
<h2>1. 服务端的准备</h2>
<p>对于服务端,由于之前的假想,我们需要一台大内存的服务器,用于部署nginx的comet应用。下面是我用的服务端的情况:</p>
<pre><code>Summary: Dell R710, 2 x Xeon E5520 2.27GHz, 23.5GB / 24GB 1333MHz
System: Dell PowerEdge R710 (Dell 0VWN1R)
Processors: 2 x Xeon E5520 2.27GHz 5860MHz FSB (16 cores)
Memory: 23.5GB / 24GB 1333MHz == 6 x 4GB, 12 x empty
Disk-Control: megaraid_sas0: Dell/LSILogic PERC 6/i, Package 6.2.0-0013, FW 1.22.02-0612,
Network: eth0 (bnx2):Broadcom NetXtreme II BCM5709 Gigabit Ethernet,1000Mb/s
OS: RHEL Server 5.4 (Tikanga), Linux 2.6.18-164.el5 x86_64, 64-bit
</code></pre>
<p>服务端程序很简单,基于nginx写的一个comet模块,该模块接受用户的请求,然后保持用户的连接,而不返回。Nginx的status模块,可直接用于监控最大连接数。</p>
<p>服务端还需要调整一下系统的参数,在/etc/sysctl.conf中:</p>
<pre><code>net.core.somaxconn = 2048
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_max_syn_backlog = 16384
net.core.netdev_max_backlog = 20000
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_orphans = 131072
/sbin/sysctl -p 生效
</code></pre>
<p>这里,我们主要看这几项:<br/>
<code>net.ipv4.tcp_rmem</code> 用来配置读缓冲的大小,三个值,第一个是这个读缓冲的最小值,第三个是最大值,中间的是默认值。我们可以在程序中修改读缓冲的大小,但是不能超过最小与最大。为了使每个socket所使用的内存数最小,我这里设置默认值为4096。<br/>
<code>net.ipv4.tcp_wmem</code> 用来配置写缓冲的大小。<br/>
读缓冲与写缓冲在大小,直接影响到socket在内核中内存的占用。<br/>
而<code>net.ipv4.tcp_mem</code>则是配置tcp的内存大小,其单位是页,而不是字节。当超过第二个值时,TCP进入pressure模式,此时TCP尝试稳定其内存的使用,当小于第一个值时,就退出pressure模式。当内存占用超过第三个值时,TCP就拒绝分配socket了,查看dmesg,会打出很多的日志“TCP: too many of orphaned sockets”。<br/>
另外<code>net.ipv4.tcp_max_orphans</code>这个值也要设置一下,这个值表示系统所能处理不属于任何进程的socket数量,当我们需要快速建立大量连接时,就需要关注下这个值了。当不属于任何进程的socket的数量大于这个值时,dmesg就会看到”too many of orphaned sockets”。</p>
<p>另外,服务端需要打开大量的文件描述符,比如200万个,但我们设置最大文件描述符限制时,会遇到一些问题,我们在后面详细讲解。</p>
<h2>2. 客户端的准备</h2>
<p>由于我们需要构建大量的客户端,而我们知道,在一台系统上,连接到一个服务时的本地端口是有限的。由于端口是16位整数,也就只能是0到65535,而0到1023是预留端口,所以能分配的只是1024到65534,也就是64511个。也就是说,一台机器只能创建六万多个长连接。要达到我们的两百万连接,需要大概34台客户端。<br/>
当然,我们可以采用虚拟ip的方式来实现这么多客户端,如果是虚拟ip,则每个ip可以绑定六万多个端口,34个虚拟ip就可以搞定。而我这里呢,正好申请到了公司的资源,所以就采用实体机来做了。<br/>
由于系统默认参数,自动分配的端口数有限,是从32768到61000,所以我们需要更改客户端/etc/sysctl.conf的参数:</p>
<pre><code>net.ipv4.ip_local_port_range = 1024 65535
/sbin/sysctl -p
</code></pre>
<p>客户端程序是基于libevent写的一个测试程序,不断的建立新的连接请求。</p>
<h2>3. 由于客户端与服务端需要建立大量的socket,所以我们需要调速一下最大文件描述符。</h2>
<p>客户端,需要创建六万多个socket,我设置最大为十万好了,的在/etc/security/limits.conf中添加:</p>
<pre><code>admin soft nofile 100000
admin hard nofile 100000
</code></pre>
<p>服务端,需要创建200万连接,那我想设置nofile为200万,好,问题来了。<br/>
当我设置nofile为200万时,系统直接无法登陆了。尝试几次,发现最大只能设置到100万。在查过源码后,才知道,原来在2.6.25内核之前有个宏定义,定义了这个值的最大值,为1024*1024,正好是100万,而在2.6.25内核及其之后,这个值是可以通过/proc/sys/fs/nr_open来设置。于是我升级内核到2.6.32。ulimit详细介绍见博文:<a href="http://blog.yufeng.info/archives/1380">老生常谈: ulimit问题及其影响</a>。<br/>
升级内核后,继续我们的调优,如下:</p>
<pre><code>sudo bash -c 'echo 2000000 > /proc/sys/fs/nr_open'
</code></pre>
<p>现在再设置nofile就可以了:</p>
<pre><code>admin soft nofile 2000000
admin hard nofile 2000000
</code></pre>
<h2>4. 最后,在测试的过程中,根据dmesg的系统打出的信息不断调整服务端/sbin/sysctl中的配置,最后我们的测试完成了200万的长连接。</h2>
<p>为了使内存占用尽量减少,我将Nginx的request_pool_size从默认的4k改成1k了。另外,net.ipv4.tcp_wmem与net.ipv4.tcp_rmem中的默认值也设置成4k。</p>
<p>两百万连接时,通过nginx的监控得到数据:<br/>
<img src="/wp-content/uploads/2011/07/abc.jpg" alt="data" /><br/>
两百万连接时系统内存情况:<br/>
<img src="/wp-content/uploads/2011/07/2.png" alt="mem" /></p>
</div>
</article>
<div class="pagination">
<a href="/blog/archives">Blog Archives</a>
</div>
</div>
<aside class="sidebar">
<section>
<h1>Categories</h1>
<ul id="category-list"><li class='category'><a href='/blog/categories/http/'>http (1)</a></li>
<li class='category'><a href='/blog/categories/language/'>language (1)</a></li>
<li class='category'><a href='/blog/categories/linux/'>linux (2)</a></li>
<li class='category'><a href='/blog/categories/nginx/'>nginx (3)</a></li>
<li class='category'><a href='/blog/categories/vim/'>vim (1)</a></li>
</ul>
</section>
<section>
<h1>Recent Posts</h1>
<ul id="recent_posts">
<li class="post">
<a href="/blog/2013/01/28/ngx-lua-and-go.html">ngx_lua与go高并发性能对比</a>
</li>
<li class="post">
<a href="/blog/2012/12/25/nginx-process-exit.html">nginx问题定位之监控进程异常退出</a>
</li>
<li class="post">
<a href="/blog/2012/12/23/vim-sub.html">vim字符串替换</a>
</li>
<li class="post">
<a href="/blog/2012/12/19/slab-usage-alloc-larger-memory.html">nginx中slab分配大内存的陷阱</a>
</li>
<li class="post">
<a href="/blog/2011/12/17/nginx-varibles.html">nginx源码分析之变量</a>
</li>
</ul>