聶永的博客

          記錄工作/學習的點點滴滴。

          100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣

          前言

          每一種該語言在某些極限情況下的表現一般都不太一樣,那么我常用的Java語言,在達到100萬個并發連接情況下,會怎么樣呢,有些好奇,更有些期盼。
          這次使用經常使用的順手的netty NIO框架(netty-3.6.5.Final),封裝的很好,接口很全面,就像它現在的域名 netty.io,專注于網絡IO。
          整個過程沒有什么技術含量,淺顯分析過就更顯得有些枯燥無聊,準備好,硬著頭皮吧。

          測試服務器配置

          運行在VMWare Workstation 9中,64位Centos 6.2系統,分配14.9G內存左右,4核。
          已安裝有Java7版本:

          java version "1.7.0_21"
          Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
          Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
          

          在/etc/sysctl.conf中添加如下配置:

          fs.file-max = 1048576
          net.ipv4.ip_local_port_range = 1024 65535
          net.ipv4.tcp_mem = 786432 2097152 3145728
          net.ipv4.tcp_rmem = 4096 4096 16777216
          net.ipv4.tcp_wmem = 4096 4096 16777216
          
          net.ipv4.tcp_tw_reuse = 1
          net.ipv4.tcp_tw_recycle = 1
          

          在/etc/security/limits.conf中添加如下配置:

               *	soft nofile 1048576
               *	hard nofile 1048576
          

          測試端

          測試端無論是配置還是程序和以前一樣,翻看前幾篇博客就可以看到client5.c的源碼,以及相關的配置信息等。

          服務器程序

          這次也是很簡單吶,沒有業務功能,客戶端HTTP請求,服務端輸出chunked編碼內容。

          入口HttpChunkedServer.java:

          唯一的自定義處理器HttpChunkedServerHandler.java:

          啟動腳本start.sh

          達到100萬并發連接時的一些信息

          每次服務器端達到一百萬個并發持久連接之后,然后關掉測試端程序,斷開所有的連接,等到服務器端日志輸出在線用戶為0時,再次重復以上步驟。在這反反復復的情況下,觀察內存等信息的一些情況。以某次斷開所有測試端為例后,當前系統占用為(設置為list_free_1):

                            total       used       free     shared    buffers     cached
               Mem:         15189       7736       7453          0         18        120
               -/+ buffers/cache:       7597       7592
               Swap:         4095        948       3147
          

          通過top觀察,其進程相關信息

              PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                       
             4925 root      20   0 8206m 4.3g 2776 S  0.3 28.8  50:18.66 java
          

          在啟動腳本start.sh中,我們設置堆內存為6G。

          ps aux|grep java命令獲得信息:

            root      4925 38.0 28.8 8403444 4484764 ?     Sl   15:26  50:18 java -server...HttpChunkedServer 8000
          

          RSS占用內存為4484764K/1024K=4379M

          然后再次啟動測試端,在服務器接收到online user 1023749時,ps aux|grep java內容為:

            root      4925 43.6 28.4 8403444 4422824 ?     Sl   15:26  62:53 java -server...
          

          查看當前網絡信息統計

            ss -s
            Total: 1024050 (kernel 1024084)
            TCP:   1023769 (estab 1023754, closed 2, orphaned 0, synrecv 0, timewait 0/0), ports 12
          
            Transport Total     IP        IPv6
            *    1024084   -         -        
            RAW     0         0         0        
            UDP     7         6         1        
            TCP     1023767   12        1023755  
            INET    1023774   18        1023756  
            FRAG    0         0         0    
          

          通過top查看一下

            top -p 4925
            top - 17:51:30 up  3:02,  4 users,  load average: 1.03, 1.80, 1.19
            Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
            Cpu0  :  0.9%us,  2.6%sy,  0.0%ni, 52.9%id,  1.0%wa, 13.6%hi, 29.0%si,  0.0%st
            Cpu1  :  1.4%us,  4.5%sy,  0.0%ni, 80.1%id,  1.9%wa,  0.0%hi, 12.0%si,  0.0%st
            Cpu2  :  1.5%us,  4.4%sy,  0.0%ni, 80.5%id,  4.3%wa,  0.0%hi,  9.3%si,  0.0%st
            Cpu3  :  1.9%us,  4.4%sy,  0.0%ni, 84.4%id,  3.2%wa,  0.0%hi,  6.2%si,  0.0%st
            Mem:  15554336k total, 15268728k used,   285608k free,     3904k buffers
            Swap:  4194296k total,  1082592k used,  3111704k free,    37968k cached
          
              PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                       
             4925 root      20   0 8206m 4.2g 2220 S  3.3 28.4  62:53.66 java
          

          四核都被占用了,每一個核心不太平均。這是在虛擬機中得到結果,可能真實服務器會更好一些。 因為不是CPU密集型應用,CPU不是問題,無須多加關注。

          系統內存狀況

            free -m
                         total       used       free     shared    buffers     cached
            Mem:         15189      14926        263          0          5         56
            -/+ buffers/cache:      14864        324
            Swap:         4095       1057       3038
          

          物理內存已經無法滿足要求了,占用了1057M虛擬內存。

          查看一下堆內存情況

            jmap -heap 4925
            Attaching to process ID 4925, please wait...
            Debugger attached successfully.
            Server compiler detected.
            JVM version is 23.21-b01
          
            using parallel threads in the new generation.
            using thread-local object allocation.
            Concurrent Mark-Sweep GC
          
            Heap Configuration:
               MinHeapFreeRatio = 40
               MaxHeapFreeRatio = 70
               MaxHeapSize      = 6442450944 (6144.0MB)
               NewSize          = 629145600 (600.0MB)
               MaxNewSize       = 629145600 (600.0MB)
               OldSize          = 5439488 (5.1875MB)
               NewRatio         = 2
               SurvivorRatio    = 1
               PermSize         = 52428800 (50.0MB)
               MaxPermSize      = 52428800 (50.0MB)
               G1HeapRegionSize = 0 (0.0MB)
          
            Heap Usage:
            New Generation (Eden + 1 Survivor Space):
               capacity = 419430400 (400.0MB)
               used     = 308798864 (294.49354553222656MB)
               free     = 110631536 (105.50645446777344MB)
               73.62338638305664% used
            Eden Space:
               capacity = 209715200 (200.0MB)
               used     = 103375232 (98.5863037109375MB)
               free     = 106339968 (101.4136962890625MB)
               49.29315185546875% used
            From Space:
               capacity = 209715200 (200.0MB)
               used     = 205423632 (195.90724182128906MB)
               free     = 4291568 (4.0927581787109375MB)
               97.95362091064453% used
            To Space:
               capacity = 209715200 (200.0MB)
               used     = 0 (0.0MB)
               free     = 209715200 (200.0MB)
               0.0% used
            concurrent mark-sweep generation:
               capacity = 5813305344 (5544.0MB)
               used     = 4213515472 (4018.321487426758MB)
               free     = 1599789872 (1525.6785125732422MB)
               72.48054631000646% used
            Perm Generation:
               capacity = 52428800 (50.0MB)
               used     = 5505696 (5.250640869140625MB)
               free     = 46923104 (44.749359130859375MB)
               10.50128173828125% used
          
            1439 interned Strings occupying 110936 bytes.
          

          老生代占用內存為72%,較為合理,畢竟系統已經處理100萬個連接。

          再次斷開所有測試端,看看系統內存(free -m)

                         total       used       free     shared    buffers     cached
            Mem:         15189       7723       7466          0         13        120
            -/+ buffers/cache:       7589       7599
            Swap:         4095        950       3145
          

          記為list_free_2

          list_free_1list_free_2兩次都釋放后的內存比較結果,系統可用物理已經內存已經降到7589M,先前可是7597M物理內存。
          總之,我們的JAVA測試程序在內存占用方面已經,最低需要7589 + 950 = 8.6G內存為最低需求內存吧。

          GC日志

          我們在啟動腳本處設置的一大串參數,到底是否達到目標,還得從gc日志處獲得具體效果,推薦使用GCViewer

          GC事件概覽:
          gc_eventdetails

          其它:
          gc_total_1 gc_total_2 gc_total_3

          總之:

          • 只進行了一次Full GC,代價太高,停頓了12秒。
          • PartNew成為了停頓大戶,導致整個系統停頓了41秒之久,不可接受。
          • 當前JVM調優喜憂參半,還得繼續努力等

          小結

          Java與與Erlang、C相比,比較麻煩的事情,需要在程序一開始就得準備好它的堆棧到底需要多大空間,換個說法就是JVM啟動參數設置堆內存大小,設置合適的垃圾回收機制,若以后程序需要更多內存,需停止程序,編輯啟動參數,然后再次啟動。總之一句話,就是麻煩。單單JVM的調優,就得持續不斷的根據檢測、信息、日志等進行適當微調。

          • JVM需要提前指定堆大小,相比Erlang/C,這可能是個麻煩
          • GC(垃圾回收),相對比麻煩,需要持續不斷的根據日志、JVM堆棧信息、運行時情況進行JVM參數微調
          • 設置一個最大連接目標,多次測試達到頂峰,然后釋放所有連接,反復觀察內存占用,獲得一個較為合適的系統運行內存值
          • Eclipse Memory Analyzer結合jmap導出堆棧DUMP文件,分析內存泄漏,還是很方便的
          • 想修改運行時內容,或者稱之為熱加載,默認不可能
          • 真實機器上會有更好的反映

          吐槽一下:
          JAVA OSGI,相對比Erlang來說,需要人轉換思路,不是那么原生的東西,總是有些別扭,社區或商業公司對此的修修補補,不過是實現一些面向對象所不具備的熱加載的企業特性。

          測試源代碼,下載just_test

          posted on 2013-05-13 11:16 nieyong 閱讀(62657) 評論(9)  編輯  收藏 所屬分類: C1M

          評論

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2013-05-17 16:17 小孟

          LZ你好,按照你的程序及配置,部署到linux機子上測試,發現online user 大概到300就上不去了,能否指點下問題在哪里,甚是苦惱啊 系統信息為 虛擬機centos6.5 內存8G Cpu 4核  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2013-05-19 16:23 nieyong

          @小孟
          問題太模糊,很難清晰定位。服務器端,需要修改兩處:
          1. 編輯/etc/security/limits.conf文件,添加如下內容
          * soft nofile 1048576
          * hard nofile 1048576

          2. 在/etc/sysctl.conf中添加如下配置:

          fs.file-max = 1048576
          net.ipv4.ip_local_port_range = 1024 65535
          net.ipv4.tcp_mem = 786432 2097152 3145728
          net.ipv4.tcp_rmem = 4096 4096 16777216
          net.ipv4.tcp_wmem = 4096 4096 16777216

          net.ipv4.tcp_tw_reuse = 1
          net.ipv4.tcp_tw_recycle = 1

          在測試端,也需要執行兩處:
          1. 編輯/etc/security/limits.conf文件,添加如下內容

          * soft nofile 1048576
          * hard nofile 1048576
          2. 在/etc/sysctl.conf中添加如下配置:

          fs.file-max = 1048576
          net.ipv4.ip_local_port_range = 1024 65535
          net.ipv4.tcp_mem = 786432 2097152 3145728
          net.ipv4.tcp_rmem = 4096 4096 16777216
          net.ipv4.tcp_wmem = 4096 4096 16777216

          net.ipv4.tcp_tw_reuse = 1
          net.ipv4.tcp_tw_recycle = 1

          測試端最重要配置也就是
          fs.file-max = 1048576
          net.ipv4.ip_local_port_range = 1024 65535

          具體問題,請具體定位。  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2013-05-20 17:32 小孟

          @nieyong
          謝謝你的回復 我今天重新安裝了系統 換成64位centos 發現連接數上去了 看來服務器系統還是非常關鍵的  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2013-07-12 10:58 we

          Java與C,erlang的玩法都不一樣,有些比較是片面的  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2013-10-02 15:35 daquan

          請問100萬的并發是用什么工具產生的?一共用了幾臺壓力機?
          我的壓力機在win平臺使用loadrunner模擬1w并發就已經卡的不行了,根本沒法跑到100w  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2013-11-21 11:58 Ronrey

          請問100萬的并發是用什么工具產生的?一共用了幾臺壓力機?您這個壓力測試工具是什么?  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣[未登錄] 2013-12-17 13:04 HEDY

          100萬并發連接至少要16臺客戶機才能完全實現!!  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣[未登錄] 2013-12-17 13:09 HEDY

          準確的來說是16塊網卡  回復  更多評論   

          # re: 100萬并發連接服務器筆記之Java Netty處理1M連接會怎么樣 2014-03-07 21:42 海邊沫沫

          開啟一個Java虛擬機,無法避免的問題就是一次Full GC時間太長。
          如果分成10個Java虛擬機,每個只配置1G內存,就不會有Full GC太占時間的問題了。  回復  更多評論   

          公告

          所有文章皆為原創,若轉載請標明出處,謝謝~

          新浪微博,歡迎關注:

          導航

          <2013年5月>
          2829301234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678

          統計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 安平县| 温泉县| 南康市| 铜陵市| 元朗区| 浑源县| 黑水县| 民权县| 惠州市| 航空| 泰来县| 潼南县| 健康| 宝坻区| 塔河县| 罗山县| 洛宁县| 临湘市| 双流县| 保靖县| 邻水| 新安县| 沂源县| 乌审旗| 牡丹江市| 南投市| 萝北县| 琼结县| 澄迈县| 汨罗市| 铁力市| 贵定县| 浦县| 大安市| 夏河县| 明水县| 龙川县| 西贡区| 郴州市| 伊吾县| 石林|