深度講解Linux企業級集羣實現方案

今天與你們分享的話題,主要是跟咱們的軟負載集羣和Nginx這個強大的開源應用有關係。軟負載與nginx那些強大的功能,你都掌握了嗎?javascript

當咱們打開手機訪問點評客戶端的時候,訪問商戶的請求是如何到達對應某臺應用服務器的?當有不少XX寬帶的用戶投訴說我大點評某某域名沒法打開可是咱們卻找不出任何問題的時候,咱們就想到會不會是寬帶運營商的問題。php

當咱們準備上線一個新的業務,或者新的功能時候,除了把代碼發佈的線上生產環境的應用服務器外,還須要作什麼工做才能讓咱們的資深吃貨的用戶們能夠訪問到咱們高端大氣上檔次的服務呢?用戶是不可能直接跑到咱們的IDC機房插根網線就來訪問咱們的內部服務器的,咱們答應,電信管理IDC的怪叔叔們也不會答應啊。css

首先,咱們很清楚用戶來訪問咱們的站點,同時經過咱們對外開放的url連接來訪問一些新站點或者新功能的頁面。而後,用戶訪問的域名會經過DNS服務被替換爲咱們對外的IP地址,這樣才能被網絡設備識別,而後將用戶的請求按照一個一個網絡包發給咱們的網絡設備,最後網絡設備收到這些網絡數據包後,會將這些數據整理後轉化爲應用程序能夠理解的數據。html

數據到了咱們的核心網絡設備被轉換爲應用層的數據後,是如何到咱們具體某一臺應用服務器來處理呢,這就牽涉到咱們要講的負載均衡器了。負載均衡器若是是硬件設備的話,那就是咱們常常提到的負載均衡設備,若是是linux服務器上運行的負載均衡軟件,那就是軟負載了,若是是集羣而不是單機的話,那就是傳說中的負載均衡集羣了。前端

當一個吃貨經過瀏覽器或手機APP訪問咱們網站的時候,不管是訪問商戶,添加點評,購買團購,仍是在社區經過私信功能與妹子聊天,全部請求都會通過咱們的F5負載均衡設備按照設定的轉發策略(隨機,權重,最小鏈接等)轉發到特定的某臺應用服務器來處理,而後再將處理結果返回給用戶。java

wKiom1NhAT3xIqdpAAD43rtCzhQ827.jpg

好吧,當咱們說了這個硬件設備時候,是否是要談談以軟件實現的負載均衡功能呢,其實目前在咱們PPE環境(xx機房,之後的雙IDC運行後另外一個生產環境)運行着這樣一套軟負載集羣來處理用戶的請求(固然,如今都是僞造的用戶請求)。node

wKioL1NhASOBUE4oAAEaWwRzzCQ111.jpg

網絡設備和Nginx負載均衡集羣中間的F5做爲流量管理設備,作4層(鏈接層,tcp)流量分發。linux

wKiom1NhAWqBgZGNAAE70_ipu7w168.jpg

軟負載實質上是一組nginx集羣以及容許用戶管理nginx配置文件的一個web端。nginx

wKiom1NhAXrBLo_1AAEhYUrHQmM672.jpg

Nginx ("engine x") 是一個高性能的 HTTP 和 反向代理 服務器,也是一個 IMAP/POP3/SMTP 代理服務器。 Nginx因穩定性、豐富的功能集和低系統資源的消耗而聞名。web

從中咱們能夠看出,nginx至少能夠作web服務器,同時能夠作反向代理服務器,同時又能夠作郵件代理服務器,功能仍是很是豐富的,稍後咱們會對nginx的功能模塊作簡要的介紹。

做爲web服務器,nginx因爲自身的優點,在處理靜態文件上有着絕對的優點,因此也是自然的優秀web服務器軟件。

什麼是反向代理服務器,和咱們平時說的用代理服務器上國外網站又有什麼區別?

有圖有真相,看圖說話

代理服務器呢,就是當我想訪問某個網站的時候由於各類緣由不能直接訪問,我能夠主動或者被動用一臺能夠訪問目標站點的服務器作代理去訪問我想要訪問的站點。

當我主動去找代理服務器去訪問是一種狀況,還有一種比較悲催的狀況,當咱們使用了某些無良寬帶運營商提供的物美價廉,縮水嚴重,還不斷搞各類潛規則的寬帶時候,就會碰到咱們這些吃貨去訪問點評網站的時候,首先是去訪問寬帶運營商局域網的代理服務器,而後代理服務器去訪問點評的網站。這樣作對於寬帶運營商來講,能夠緩存一些數據,這樣就能節省點帶寬,可是對於咱們這些使用寬帶的用戶而言,一則數據不安全,運營商的代理服務器上可能有咱們的豔照也說不定,二則,當點評站點可正常訪問,寬帶運營商代理服務器出現問題的時候,就會收到各類用戶投訴,點評又跪了,這讓吃貨怎麼活?問題是點評活的好好的,用戶卻訪問不到。

wKioL1NhAV7jqiPLAACXJkGloDQ338.jpg

反向代理(Reverse Proxy)是指以代理服務器來接受internet上的鏈接請求,而後將請求轉發給內部網絡上的服務器,並將從服務器上獲得的結果返回給internet上請求鏈接的客戶端,此時代理服務器對外就表現爲一個服務器。

 反向代理服務器能夠看下面圖,nginx就是反向代理的角色,用戶的請求是先發到nginx,而後轉發到後端的tomcat。

wKiom1NhAZbSut7FAACZukjMSpM461.jpg

固然,代理服務器和反向代理服務器的類型不僅web服務一種。

以下是一個簡單的反向代理的配置:

1
2
3
4
5
server_name   qunying.dianping.com;
location / {
proxy_pass test.qunying.liu.dianping.com; //反向代理站。
index index.html index.htm;
}

對於咱們線上的環境,nginx不是做爲典型的反向代理在使用,目前點評java相關web業務服務器上採用的是 nginx (緩存和壓縮,日誌)+tomcat(java容器),充分利用了nginx低系統佔用以及高併發處理的優點。

不少人會有疑問,tomcat也能夠作web容器的啊,改個端口不就能夠直接給用戶提供服務了,並且tomcat也能記錄日誌,不必再放一個nginx啊。

tomcat 前面有沒有必要放一個nginx呢?

術業有專攻,tomcat作web服務器是兼職,作java容器是專職。nginx服務器是專職作web服務器,支持高併發,響應快,擅長處理靜態內容,並且能夠把動態內容交給tomcat處理。用tomcat作web容器響應用戶請求,有可能1分鐘只能處理10個請求,可是用nginx+tomcat一分鐘就可能能夠處理100個請求。

nginx爲何能夠這麼快處理用戶的請求呢?

nginx的進程模型以及系統事件機制

nginx啓動後的進程,如圖所示:

wKioL1NhAYuDpxoQAAFduYmboQ4730.jpg

咱們能夠看到master進程,是以root身份啓動,執行內容爲:/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

worker進程都是以咱們指定的nobody身份運行,其中有一個worker進程是舊的worker進程奔潰後,自動從新建立的,你能找到他嗎?

wKioL1NhAZmwwXNPAADogK6xWTc871.jpg

nginx在啓動後,會有一個master進程多個worker進程master進程主要用來管理worker進程,包含:接收來自外界的信號,向各個worker進程發送信號,監控worker進程的運行狀態,當worker進程退出後(異常狀況下),會自動從新啓動新的worker進程。基本的網絡事件,則是放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。worker進程的個數是能夠設置的,通常咱們會設置與機器cpu核數一致。

咱們要控制nginx,只須要經過kill向master進程發送信號就好了。好比kill -HUP pid,則是告訴nginx,從容地重啓nginx,咱們通常用這個信號來重啓nginx,或從新加載配置,由於是從容地重啓,所以服務是不中斷的。master進程在接收到HUP信號後是怎麼作的呢?首先master進程在接到信號後,會先從新加載配置文件,而後再啓動新的進程,並向全部老的進程發送信號,告訴他們能夠光榮退休了。新的進程在啓動後,就開始接收新的請求,而老的進程在收到來自master的信號後,就再也不接收新的請求,而且在當前進程中的全部未處理完的請求處理完成後,再退出。固然,直接給master進程發送信號,這是比較老的操做方式,nginx在0.8版本以後,引入了一系列命令行參數,來方便咱們管理。好比,./nginx -s reload,就是來重啓nginx,./nginx -s stop,就是來中止nginx的運行。如何作到的呢?咱們仍是拿reload來講,咱們看到,執行命令時,咱們是啓動一個新的nginx進程,而新的nginx進程在解析到reload參數後,就知道咱們的目的是控制nginx來從新加載配置文件了,它會向master進程發送信號,而後接下來的動做,就和咱們直接向master進程發送信號同樣了。

worker進程是如何處理咱們的http請求的?

master(master進程會先創建好須要listen的socket)--------fork生成子進程workers,繼承socket(此時workers子進程們都繼承了父進程master的全部屬性,固然也包括已經創建好的socket,固然不是同一個socket,只是每一個進程的這個socket會監控在同一個ip地址與端口,這個在網絡協議裏面是容許的)------當一個鏈接進入,產生驚羣現象(驚羣現象:指一個fd的事件被觸發後,等候這個fd的全部線程/進程都被喚醒。雖然都被喚醒,可是隻有一個會去響應。)。

Nginx對驚羣現象的處理共享鎖

nginx提供了一個accept_mutex這個東西,從名字上,咱們能夠看這是一個加在accept上的一把共享鎖。有了這把鎖以後,同一時刻,就只會有一個進程在accpet鏈接,這樣就不會有驚羣問題了。accept_mutex是一個可控選項,咱們能夠顯示地關掉,默認是打開的。

worker進程工做

一個worker進程在accept這個鏈接以後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開鏈接,這樣一個完整的請求就是這樣的了。咱們能夠看到,一個請求,徹底由worker進程來處理,並且只在一個worker進程中處理

採用這種方式的好處:

1)節省鎖帶來的開銷。對於每一個worker進程來講,獨立的進程,不須要加鎖,因此省掉了鎖帶來的開銷,同時在編程以及問題查上時,也會方便不少

2)獨立進程,減小風險。採用獨立的進程,可讓互相之間不會影響,一個進程退出後,其它進程還在工做,服務不會中斷,             master進程則很快從新啓動新的worker進程。固然,worker進程的異常退出,確定是程序有bug了,異常退出,會致使當前worker上的全部請求失敗,不過不會影響到全部請求,因此下降了風險。

Nginx的事件處理機制,採用異步非阻塞事件處理機制,一個worker進程只有一個主線程,經過異步非阻塞的事件處理機制,實現了循環處理多個準備好的事件,從而實現輕量級和高併發。      

異步非阻塞事件處理機制:

同步和異步的概念,這兩個概念與消息的通知機制有關.同步的狀況下,是由處理消息者本身去等待消息是否被觸發,而異步的狀況下是由觸發機制來通知處理消息者。

阻塞和非阻塞,這兩個概念與程序等待消息(無所謂同步或者異步)時的狀態有關.

當讀寫事件沒有準備好時,就放入epoll裏面。若是有事件準備好了,那麼就去處理;若是事件返回的是EAGAIN,那麼繼續將其放入epoll裏面。從而,只要有事件準備好了,咱們就去處理,只有當全部時間都沒有準備好時,纔在epoll裏面等着。這樣,咱們就能夠併發處理大量的併發了,固然,這裏的併發請求,是指未處理完的請求,線程只有一個,因此同時能處理的請求固然只有一個了,只是在請求間進行不斷地切換而已,切換也是由於異步事件未準備好,而主動讓出的。這裏的切換是沒有任何代價,你能夠理解爲循環處理多個準備好的事件。

與多線程相比,這種事件處理方式是有很大的優點的,不須要建立線程,每一個請求佔用的內存也不多,沒有上下文切換,事件處理很是的輕量級。併發數再多也不會致使無謂的資源浪費(上下文切換)。更多的併發數,只是會佔用更多的內存而已.

以前咱們提到nginx的負載均衡功能,那麼和LVS的負載均衡有什麼區別呢?

負載均衡分爲:

L4 switch(四層交換),即在OSI第4層工做,就是TCP層啦。此種Load Balance不理解應用協議(如HTTP/FTP/MySQL等等)。例子:LVS,F5

L7 switch(七層交換),OSI的最高層,應用層。此時,該Load Balancer能理解應用協議。例子:haproxy,MySQL Proxy

不少Load Balancer(例如F5)既能夠作四層交換,也能夠作七層交換。

LVS 工做在網絡4層僅作請求分發之用沒有流量,可配置性低,幾乎可對全部應用作負載均衡,對網絡依賴大,沒有健康檢查機制。

nginx的7層(應用層),因此它能夠針對http應用自己來作分流策略,好比針對域名、目錄結構等,對網絡依賴小,可檢測服務器內部錯誤。

 

nginx能夠根據URL進行負載均衡的請求轉發,而LVS只能根據ip:port進行請求轉發

通常狀況下,LVS會被放在最前端作負載均衡,nginx可做爲lvs的節點服務器。

前面咱們也提到過nginx實現郵件代理服務器的功能,通常使用nginx作郵件代理服務器的場景很少。

很不幸,nginx最先也是被看成郵件代理服務器來開發的。

--with-mail - 啓用 IMAP4/POP3/SMTP 代理模塊

安裝時須要注意的庫依賴:

gzip模塊須要 zlib 庫  
rewrite模塊須要 pcre 庫  
ssl 功能須要openssl庫

咱們nginx通常安裝在:/usr/local/nginx 目錄,nginx的安裝目錄結構以下圖所示

/usr/local/nginx

├── conf(配置文件目錄)

│   ├── fastcgi.conf

│   ├── fastcgi.conf.default

│   ├── fastcgi_params

│   ├── fastcgi_params.default

│   ├── hosts

│   ├── koi-utf

│   ├── koi-win

│   ├── mime.types

│   ├── mime.types.default

│   ├── nginx_app.conf(應用相關配置段 server段)

│   ├── nginx.conf(nginx公用配置信息 events,http段

│   ├── nginx.conf.default

│   ├── nginx_status.conf

│   ├── proxy.conf

│   ├── scgi_params

│   ├── scgi_params.default

│   ├── uwsgi_params

│   ├── uwsgi_params.default

│   └── win-utf

├── html

│   ├── 50x.html

│   └── index.html

├── logs -> /data/applogs/nginx

├── sbin(nginx程序目錄)

│   └── nginx

└── tmpdir

   ├── client_body_temp

   ├── fastcgi_temp

   ├── proxy_temp

   │   ├── 0

   │   │   └── 01

   │   ├── 1

   │   │   ├── 00

   │   │   └── 01

   │   ├── 2

   │   │   ├── 00

   │   │   └── 01

   │   ├── 3

   │   │   ├── 00

   │   │   └── 01

   │   ├── 4

   │   │   ├── 00

   │   │   └── 01

   │   ├── 5

   │   │   ├── 00

   │   │   └── 01

   │   ├── 6

   │   │   ├── 00

   │   │   └── 01

   │   ├── 7

   │   │   ├── 00

   │   │   └── 01

   │   ├── 8

   │   │   ├── 00

   │   │   └── 01

   │   └── 9

   │       └── 00

   ├── scgi_temp

   └── uwsgi_temp

nginx基本配置文件

#運行用戶(worker進程屬主)
user nobody;
#啓動進程,設置成和cpu的數量相等
過多的worker數,只會致使進程相互競爭cpu資源,從而帶來沒必要要的上下文切換
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
worker_processes  4;
#全局錯誤日誌及PID文件
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
#工做模式及鏈接數上限
events {
    #epoll是多路複用IO(I/O Multiplexing)中的一種方式,
    #僅用於linux2.6以上內核,能夠大大提升nginx的性能
    use   epoll;
    #單個後臺worker process進程的最大併發連接數  
    worker_connections  1024;
    # 併發總數是 worker_processes 和 worker_connections 的乘積
    # 即 max_clients = worker_processes * worker_connections
    # worker_connections 值的設置跟物理內存大小有關
    # 由於併發受IO約束,max_clients的值須小於系統能夠打開的最大文件數
    # 系統能夠打開的最大文件數和內存大小成正比
    # $ cat /proc/sys/fs/file-max
    # 併發鏈接總數小於系統能夠打開的文件句柄總數,這樣就在操做系統能夠承受的範圍以內
    # worker_connections 的值需根據 worker_processes 進程數目和系統能夠打開的最大文件總數進行適當地進行設置
    # 根據主機的物理可用CPU和可用內存進行配置
}
http {
    #設定mime類型,類型由mime.type文件定義
    include    mime.types;
    default_type  application/octet-stream;
    #設定日誌格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;
    #sendfile 指令指定 nginx 是否調用 sendfile 函數(zero copy 方式)來輸出文件,
    #對於普通應用,必須設爲 on,
    #若是用來進行下載等應用磁盤IO重負載應用,可設置爲 off,以平衡磁盤與網絡I/O處理速度,下降系統的uptime.
    sendfile     on;
    #tcp_nopush     on;
    #鏈接超時時間
    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay     on;
    #開啓gzip壓縮
    gzip  on;
    gzip_disable "MSIE [1-6].";
    #設定請求緩衝
    client_header_buffer_size    128k;
    large_client_header_buffers  4 128k;
    #設定虛擬主機配置
    server {
        #偵聽80端口
        listen    80;
        #定義使用 www.nginx.cn訪問
        server_name  www.nginx.cn;
        #定義服務器的默認網站根目錄位置
        root html;
        #設定本虛擬主機的訪問日誌
        access_log  logs/nginx.access.log  main;
        #默認請求
        location / {
                     
            #定義首頁索引文件的名稱
            index index.php index.html index.htm; 
        }
        # 定義錯誤提示頁面
        error_page   500 502 503 504 /50x.html;
        location = /50x.html {
        }
        #靜態文件,nginx本身處理
        location ~ ^/(images|javascript|js|css|flash|media|static)/ {
                     
            #過時30天,靜態文件不怎麼更新,過時能夠設大一點,
            #若是頻繁更新,則能夠設置得小一點。
            expires 30d;
        }
        #PHP 腳本請求所有轉發到 FastCGI處理. 使用FastCGI默認配置.
        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
        #禁止訪問 .htxxx 文件
            location ~ /.ht {
            deny all;
        }
    }
}

 

線上的一個應用的配置文件:

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
user                              nobody nobody;
worker_processes                  4;
worker_rlimit_nofile              51200;
error_log                         logs/error.log  notice;
pid                               /var/run/nginx.pid;
events {
  use                             epoll;
  worker_connections              51200;
}
http {
  server_tokens                   off;
  include                         mime.types;
  include                         proxy.conf;
  default_type                    application/octet-stream;
  charset                         utf-8;
  client_body_temp_path           /usr/local/nginx/tmpdir/client_body_temp 1 2;
  proxy_temp_path                 /usr/local/nginx/tmpdir/proxy_temp 1 2;
  fastcgi_temp_path               /usr/local/nginx/tmpdir/fastcgi_temp 1 2;
  uwsgi_temp_path                 /usr/local/nginx/tmpdir/uwsgi_temp 1 2;
  scgi_temp_path                  /usr/local/nginx/tmpdir/scgi_temp 1 2;
  ignore_invalid_headers          on;
  server_names_hash_max_size      256;
  server_names_hash_bucket_size   64;
  client_header_buffer_size       8k;
  large_client_header_buffers     4 32k;
  connection_pool_size            256;
  request_pool_size               64k;
  output_buffers                  2 128k;
  postpone_output                 1460;
  client_header_timeout           1m;
  client_body_timeout             3m;
  send_timeout                    3m;
  sendfile                        on;
  tcp_nodelay                     on;
  tcp_nopush                      off;
  reset_timedout_connection       on;
  keepalive_timeout               20 5;
  keepalive_requests              100;
  gzip                            on;
  gzip_http_version               1.1;
  gzip_vary                       on;
  gzip_proxied                    any;
  gzip_min_length                 1024;
  gzip_comp_level                 6;
  gzip_buffers                    16 8k;
  gzip_proxied                    expired no-cache no-store private auth no_last_modified no_etag;
  gzip_types                      text/plain application/x-javascript text/css application/xml application/json;
  gzip_disable                    "MSIE [1-6]\.(?!.*SV1)";
  include                         nginx_app.conf; #與應用有關的server配置
  include                         nginx_status.conf; #單機nginx訪問統計配置

 

Nginx配置文件分爲好多塊,常見的從外到內依次是「http」、「server」、「location」等等,缺省的繼承關係是從外到內,也就是說內層塊會自動獲取外層塊的值做爲缺省值。

nginx的目錄瀏覽功能:

在server里加上以下三行便可。  
autoindex on;  
autoindex_localtime on;  
autoindex_exact_size off;

nginx登陸驗證

在location段內添加:  
auth_basic "phoenix slb admin ";  
auth_basic_user_file /data/appdatas/phoenix-slb-passwd;
本文內容選自51CTO學院講師馬哥博客,更多Linux集羣資料點擊: http://edu.51cto.com/course/course_id-368.html
相關文章
相關標籤/搜索