sina
負(fù)載均衡:將相同的引用部署到多臺(tái)機(jī)器上讳窟。在集群前增加負(fù)載均衡設(shè)備,實(shí)現(xiàn)流量分發(fā)改执。意思是將負(fù)載(工作任務(wù)裹粤、訪問請(qǐng)求)進(jìn)行平衡噪叙、分?jǐn)偟蕉鄠€(gè)操作單元(服務(wù)器、組件)上進(jìn)行執(zhí)行。是解決高性能、高可用硅卢、擴(kuò)展性的終極解決方案点寥。
中國大陸使用nginx網(wǎng)站用戶有:新浪、網(wǎng)易、 騰訊同廉、小米官網(wǎng)等咒程。以其穩(wěn)定性和高性能等眾多優(yōu)點(diǎn)迅速擴(kuò)大市場(chǎng)稠集。
上圖為Nginx的基本框架晦鞋,可以看到Nginx采用多進(jìn)程的方式,它的好處是:
(1)對(duì)于每個(gè)worker進(jìn)程來說,獨(dú)立的進(jìn)程不需要加鎖。
(2)采用獨(dú)立的進(jìn)程,使得之間互不影響艺普,一個(gè)進(jìn)程退出后缴罗,服務(wù)不會(huì)中斷蛆橡,master進(jìn)程則會(huì)很快啟動(dòng)新的worker進(jìn)程葱轩。
(3)它采用異步非阻塞模式來處理請(qǐng)求猾普,可以同時(shí)處理成千上萬個(gè)請(qǐng)求溜在。
Nginx的upstream目前支持5中分配方式:
(1)輪詢(默認(rèn)):每個(gè)請(qǐng)求按時(shí)間順序逐一分配到不同的后端服務(wù)器上祈,如果后端服務(wù)器down掉皇耗,能自動(dòng)剔除
(2)Weight:指定輪詢幾率呜袁,weight和訪問比率成正比膘融,用于后端服務(wù)器性能不均的時(shí)候脱货。
(3)Ip_hash:每個(gè)請(qǐng)求按訪問ip的hash結(jié)果分配,這樣每個(gè)訪客固定訪問一個(gè)后端服務(wù)器多矮,可以解決session(服務(wù)器利用一種散列表的結(jié)構(gòu)來保存信息,給客戶端返回一個(gè)session id保存于cookie)的問題帐萎。
(4)Fair:按后端服務(wù)器的響應(yīng)時(shí)間來分配,響應(yīng)時(shí)間短的優(yōu)先分配芬骄。
(5)Url_hash:按訪問url的hash結(jié)果來分配請(qǐng)求材彪,使每一個(gè)url定向到同一個(gè)后端服務(wù)器晒屎。
Nginx的內(nèi)存池管理分析
(1)Nginx的內(nèi)存管理燥狰,主要是用來實(shí)現(xiàn)防止內(nèi)存泄漏斜筐、和內(nèi)存碎片。
(2)內(nèi)存池就是一個(gè)單鏈表,擁有獨(dú)立的多個(gè)內(nèi)存池潭苞,內(nèi)存池本身分配小內(nèi)存僧诚,防止碎片慈省,大內(nèi)存直接使用malloc笑窜,多個(gè)大內(nèi)存構(gòu)成單鏈表。
(3)小內(nèi)存一旦分配使用,直到整個(gè)池釋放撇簿,才被回收。
(4)大內(nèi)存差购,通過調(diào)用釋放函數(shù)四瘫,在摧毀池之前,回收欲逃。
Nginx源代碼分析
Nginx由以下幾個(gè)元素組成: 1. worker(進(jìn)程) 2. thread(線程) 3. connection(連接) 4. event(事件) 5. module(模塊) 6. pool(內(nèi)存池) 7. cycle(全局設(shè)置) 8. log(日志)
接下來比較重要的函數(shù)是 ngx_init_cycle()找蜜,這個(gè)函數(shù)初始化系統(tǒng)的配置以及網(wǎng)絡(luò)連接等。如果是多進(jìn)程方式加載的會(huì)繼續(xù)調(diào)用ngx_master_process_cycle()稳析,這是main函數(shù)中調(diào)用的最關(guān)鍵的兩個(gè)函數(shù)洗做。
ngx_init_cycle()實(shí)際上是個(gè)復(fù)雜的初始化函數(shù),首先是加載各子模塊的配置信息彰居、并初始化各組成模塊诚纸。 任何模塊都有兩個(gè)重要接口組成,一個(gè)是create_conf陈惰,一個(gè)是init_conf畦徘。分別是創(chuàng)建配置和初始化配置信息。 首先是內(nèi)核模塊、錯(cuò)誤日志旧烧、配置模塊、事件模塊画髓、時(shí)間內(nèi)核模塊掘剪、EPOLL模塊、http模塊奈虾、http內(nèi)核模塊夺谁、http日志模塊,剩下的模塊都算不上關(guān)鍵肉微。 epoll是比較關(guān)鍵的核心模塊之一匾鸥,Nginx兼容多種IO控制模型,memecached用的是libevent不如Nginx徹頭徹尾是自己實(shí)現(xiàn)的碉纳。
在ngx_init_cycle()中對(duì)模塊初始化完畢后勿负,調(diào)用ngx_open_listening_sockets()函數(shù)對(duì)socket進(jìn)行了初始化。 在listen上80端口以后劳曹,調(diào)用模塊的另外一個(gè)重要接口init_module對(duì)各模塊進(jìn)行初始化奴愉。
ngx_init_cycle()返回后,主要的工作都是在ngx_master_process_cycle()函數(shù)中繼續(xù)進(jìn)行的铁孵。ngx_master_process_cycle()函數(shù)中的重要過程有調(diào)用ngx_start_worker_processes()生成多個(gè)子進(jìn)程锭硼,一般Nginx是多進(jìn)程的。 ngx_start_worker_processes()函數(shù)內(nèi)部調(diào)用ngx_worker_process_cycle()函數(shù)建立每個(gè)進(jìn)程的實(shí)際工作內(nèi)容蜕劝,在這個(gè)函數(shù)中首先調(diào)用ngx_create_thread()初始化各線程檀头。我們知道每個(gè)線程都有一個(gè)啟動(dòng)處理函數(shù),Nginx的線程處理函數(shù)為 ngx_worker_thread_cycle()岖沛,內(nèi)部過程中最重要的是對(duì)ngx_event_thread_process_posted()函數(shù)的調(diào)用暑始,用于實(shí)際處理每一次請(qǐng)求。 在初始化線程結(jié)束后婴削,首先調(diào)用ngx_process_events_and_timers()函數(shù)蒋荚,該函數(shù)繼續(xù)調(diào)用 ngx_process_events接口監(jiān)聽事件,一般情況下對(duì)應(yīng)的函數(shù)是ngx_epoll_process_events()馆蠕,如果使用的是其它種 類的IO模型期升,則應(yīng)該實(shí)現(xiàn)相應(yīng)的實(shí)際函數(shù)。這個(gè)接口負(fù)責(zé)把事件投遞到ngx_posted_events事件隊(duì)列里互躬,并在 ngx_event_thread_process_posted()函數(shù)中進(jìn)行處理播赁。
Nginx是一個(gè)支持負(fù)載均衡的反向代理服務(wù)器,Apache的弱點(diǎn)就在于它的并發(fā)模型是普通的進(jìn)程/線程池吼渡,連接數(shù)和進(jìn)程/線程數(shù)是1:1的容为,因此無論是prefork還是worker模式,都將每一個(gè)連接對(duì)應(yīng)到一個(gè)獨(dú)立的進(jìn)程/線程。 這樣的并發(fā)模型在連接數(shù)不太多(1000以內(nèi))時(shí)還算可以坎背,但在大規(guī)模并發(fā)時(shí)替劈,其進(jìn)程/線程總數(shù)會(huì)非常多。由于Apache本身也比較吃內(nèi)存得滤,所以到了1000以上的并發(fā)時(shí)陨献,服務(wù)器的內(nèi)存基本上也就被吃的差不多了,操作系統(tǒng)也在頻繁地做進(jìn)程/線程的切換懂更,非常吃力眨业。相比之下,更高級(jí)的大型網(wǎng)絡(luò)服務(wù)系統(tǒng)(如電信的智能網(wǎng)系統(tǒng))一般采用進(jìn)程/線程池+狀態(tài)機(jī)的模型——也即連接數(shù)和進(jìn)程/線程數(shù)是m:n的沮协,這樣進(jìn)程/線程總數(shù)就不會(huì)由于連接的增多而增多龄捡,避免了內(nèi)存和調(diào)度切換的開銷,但這種做法對(duì)程序邏輯的要求較高慷暂,需要一個(gè)連接拆分為多個(gè)邏輯狀態(tài)(創(chuàng)建聘殖,讀,寫行瑞,關(guān)閉等就斤,根據(jù)實(shí)際業(yè)務(wù)還可以更加細(xì)化)每個(gè)進(jìn)程/線程處理完某一種狀態(tài)后,需要改變?cè)撨B接的狀態(tài)值蘑辑,后續(xù)狀態(tài)由下一個(gè)空閑的進(jìn)程/線程處理洋机。
Nginx就采用了這樣的并發(fā)模型,對(duì)于連接狀態(tài)的存儲(chǔ)洋魂,Nginx主要采用了這樣一個(gè)復(fù)雜結(jié)構(gòu)绷旗。
struct ngx_connection_s
{
void *data;
ngx_event_t *read;
ngx_event_t *write;
...
}; 結(jié)構(gòu)ngx_event_t存儲(chǔ)了連接IO狀態(tài)的詳細(xì)信息,同時(shí)所有的ngx_event_t組成了兩個(gè)全局的鏈表副砍,以便進(jìn)行存取操作衔肢。
Nginx使用了下面這兩個(gè)函數(shù)來完成每個(gè)進(jìn)程/線程的循環(huán)
1.ngx_locked_post_event 這個(gè)函數(shù)負(fù)責(zé)更新某一個(gè)連接的狀態(tài),在檢查到連接IO狀態(tài)改變(比如通過select)后被調(diào)用豁翎。
2.ngx_event_thread_process_posted 這個(gè)函數(shù)檢查event表角骤,并調(diào)用event對(duì)應(yīng)的handler函數(shù),每次處理1個(gè)event心剥。
Nginx實(shí)踐
Nginx有一個(gè)memcached_module,可以直接從后端的memached服務(wù)器中讀取內(nèi)容, 直接輸出邦尊。通過這個(gè)模塊,可以極大的提升動(dòng)態(tài)頁面的訪問速度。memcached可以通過upstream來從多臺(tái)memcached服務(wù)器中讀取,也可以支持熱備份优烧。很多事情都不是那么完美, 對(duì)于Nginx的這個(gè)module,其最大的問題就是不支持壓縮蝉揍,直接緩存頁面, 非常浪費(fèi)內(nèi)存。