負載平衡的基本概念
負載平衡(load balancing)是一種電腦網路技術,用來在多個電腦(電腦叢集)、網路連線、cpu、磁碟驅動器或其他資源中分配負載,以達到優化資源使用、最大化吞吐率、最小化回應時間、同時避免過載的目的。
使用具有負載平衡的多個伺服器元件,取代單一的元件,可以透過冗餘提高可靠性。負載平衡服務通常是由專用軟體和硬體來完成。
負載平衡最重要的一個應用是利用多台伺服器提供單一服務,有時也稱之為伺服器農場。通常,負載平衡主要應用於web網站,大型的internet relay chat網絡,高流量的文件下載網站,nntp(network news transfer protocol)服務和dns服務。現在負載平衡器也開始支援資料庫服務,稱為資料庫負載平衡器。
對於互聯網服務,負載平衡器通常是一個軟體程序,這個程序偵聽一個外部端口,互聯網用戶可以通過這個端口來訪問服務,而作為負載平衡器的軟體會將用戶的請求轉發給後台內網伺服器,內網伺服器將請求的回應傳回給負載平衡器,負載平衡器再將回應傳送到用戶,這樣就向網際網路用戶隱藏了內網結構,阻止了用戶直接存取後台(內網)伺服器,使得伺服器更加安全,可以阻止對核心網路堆疊和運行在其它連接埠服務的攻擊。
當所有後台伺服器發生故障時,有些負載平衡器會提供一些特殊的功能來處理這種情況。例如轉發請求到一個備用的負載平衡器、顯示一條關於服務中斷的訊息等。負載平衡器讓it團隊可以顯著提高容錯能力。它可以自動提供大量的容量以處理任何應用程式流量的增加或減少。
在下面我們就來看看如何建立具備負載平衡能力的nginx tomcat伺服器組合:
0.前期準備
使用debian環境。安裝nginx(預設安裝),一個web項目,安裝tomcat(預設安裝)等。
1.一份nginx.conf設定檔
# 定义nginx运行的用户 和 用户组 如果对应服务器暴露在外面的话建议使用权限较小的用户 防止被入侵 # user www www; #nginx进程数, 建议设置为等于cpu总核心数 worker_processes 8; #开启全局错误日志类型 error_log /var/log/nginx/error.log info; #进程文件 pid /var/run/nginx.pid; #一个nginx进程打开的最多文件描述数目 建议与ulimit -n一致 #如果面对高并发时 注意修改该值 ulimit -n 还有部分系统参数 而并非这个单独确定 worker_rlimit_nofile 65535; events{ #使用epoll模型提高性能 use epoll; #单个进程最大连接数 worker_connections 65535; } http{ #扩展名与文件类型映射表 include mime.types; #默认类型 default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; #日志 access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; #gzip 压缩传输 gzip on; gzip_min_length 1k; #最小1k gzip_buffers 16 64k; gzip_http_version 1.1; gzip_comp_level 6; gzip_types text/plain application/x-javascript text/css application/xml application/javascript; gzip_vary on; #负载均衡组 #静态服务器组 upstream static.zh-jieli.com { server 127.0.0.1:808 weight=1; } #动态服务器组 upstream zh-jieli.com { server 127.0.0.1:8080; #server 192.168.8.203:8080; } #配置代理参数 proxy_redirect off; proxy_set_header host $host; proxy_set_header x-real-ip $remote_addr; proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 65; proxy_send_timeout 65; proxy_read_timeout 65; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; #缓存配置 proxy_cache_key '$host:$server_port$request_uri'; proxy_temp_file_write_size 64k; proxy_temp_path /dev/shm/jielierp/proxy_temp_path; proxy_cache_path /dev/shm/jielierp/proxy_cache_path levels=1:2 keys_zone=cache_one:200m inactive=5d max_size=1g; proxy_ignore_headers x-accel-expires expires cache-control set-cookie; server{ listen 80; server_name erp.zh-jieli.com; location / { index index; #默认主页为 /index #proxy_pass http://jieli; } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { proxy_cache cache_one; proxy_cache_valid 200 304 302 5d; proxy_cache_valid any 5d; proxy_cache_key '$host:$server_port$request_uri'; add_header x-cache '$upstream_cache_status from $host'; proxy_pass http: //static.zh-jieli.com; #所有静态文件直接读取硬盘 # root /var/lib/tomcat7/webapps/jielierp/web-inf ; expires 30d; #缓存30天 } #其他页面反向代理到tomcat容器 location ~ .*$ { index index; proxy_pass http: //zh-jieli.com; } } server{ listen 808; server_name static; location / { } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { #所有静态文件直接读取硬盘 root /var/lib/tomcat7/webapps/jielierp/web-inf ; expires 30d; #缓存30天 } } }
基本上設定這個文件,就可以實作負載了。但是裡面的各種關係要了解就比較麻煩了。
2.基礎講解
現在假使有一台電腦192.168.8.203這台電腦,上面部署了tomcat,裡面8080埠有j2ee的服務,透過瀏覽器可以正常瀏覽網頁。現在有一個問題tomcat是比較全面的web容器,對靜態網頁的處理,應該是比較費資源的,特別是每次都要從磁碟讀取靜態頁面,然後再回傳。這中間會消耗tomcat的資源,可能會使那些動態頁面解析效能影響。秉承linux哲學,一個軟體只做一件事的原則。 tomcat就應該只處理jsp動態頁面。這裡就用到以前了解的nginx來進行反向代理。第一步代理,實現動靜網頁分離。這個很簡單的。
worker_processes 8; pid /var/run/nginx.pid; worker_rlimit_nofile 65535; events{ use epoll; worker_connections 65535; } http{ include mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; proxy_redirect off; proxy_set_header host $host; proxy_set_header x-real-ip $remote_addr; proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 65; proxy_send_timeout 65; proxy_read_timeout 65; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; server{ listen 80; server_name xxx.com; location / { index index; } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { proxy_pass http: //192.168.8.203:8080; expires 30d; } location ~ .*$ { index index; proxy_pass http: //192.168.8.203:8080; } } } worker_processes 8; pid /var/run/nginx.pid; worker_rlimit_nofile 65535; events{ use epoll; worker_connections 65535; } http{ include mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; proxy_redirect off; proxy_set_header host $host; proxy_set_header x-real-ip $remote_addr; proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 65; proxy_send_timeout 65; proxy_read_timeout 65; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; server{ listen 80; server_name xxx.com; location / { index index; } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { proxy_pass http: //192.168.8.203:8080; expires 30d; } location ~ .*$ { index index; proxy_pass http: //192.168.8.203:8080; } } }
修改nginx的設定檔 /etc/nginx/nginx.conf 預設有個設定檔的。其實大部分都差不多,關鍵還是server段的設定。這裡我設定server段如上所示,其他段複製就可以了。 server段裡面的解釋如下:第35行為監聽本機80埠。 37-39行表示預設主頁,這裡的預設主頁我是index.jsp 對應到我專案中是一個index。這裡依需求可以改為
index index.jsp index.html index.htm index.php
具體可參考其他文章。關鍵的第40行,這個是正規匹配,網路上也有很多介紹。這裡符合我專案中用到的所有靜態網頁後綴。第41行是代理地址。這裡我代理到我的web應用程式。 expires 30d快取為30天,這裡的快取是對應到前端頁面,使用者的cache-control字段,
第44行中那個正規是匹配無後綴的頁面。我專案中jsp頁面是無字尾的。這裡可以根據需要進行修改。同樣代理到192.168.8.203:8080這裡。到這裡你可能會問,我艹,這有毛意思啊?當然不是這樣了。簡單的實作靜動分離,我們可以把第41行進行修改,改為
root /var/lib/tomcat7/webapps/jielierp/web-inf
表示不代理,直接從本地磁碟拿。透過查tomcat日誌可以看到靜態頁面是沒有訪問到的。但這又有一個問題。這樣的彈性不好,對下面要講到的記憶體快取和叢集部署來說都是不友善的,所以又有了下面的這種寫法。再寫一個server段。
server{ listen 808; server_name static; location / { } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { #所有静态文件直接读取硬盘 root /var/lib/tomcat7/webapps/jielierp/web-inf ; expires 30d; #缓存30天 } }
這次監聽808端口,然後上上面的程式碼41行就可以修改為 proxy_pass http://192.168.8.203:808了,到這裡就實現了動靜分離了。如果多台伺服器,就修改對應的ip就可以了。如果發現連線不上的,要檢查防火牆,權限等外部問題,這個設定是這樣的。
如果单纯这样的话,我们会发现页面直接传输过于占用带宽。对应web的优化,这里想到的是通过对页面进行gzip压缩,然后传到用户那里,再解压,这样可以有效的减少带宽。这里就会用到nginx 的gzip模块了。默认的nginx是集成有gzip模块的。只需在http段增加下面配置即可。
gzip on; gzip_min_length 1k; #最小1k gzip_buffers 16 64k; gzip_http_version 1.1; gzip_comp_level 6; gzip_types text/plain application/x-javascript text/css application/xml application/javascript; gzip_vary on;
给个首页看看效果
不要在意请求数不一样,那两个请求是谷歌插件来的。不用觉得我在骗你。
作为假使有很多人访问的网站来说,缓存肯定是很重要的东西了。一开始是想通过插件,让nginx和redis进行合成,然后nginx使用redis来缓存的,但是发现配置起来很麻烦,还要自己下载插件,重新编译nginx,比较麻烦,所以这里觉得用nginx自带的缓存也是不错的选择。虽然效率比不上redis,但是有还是比没有好。nginx默认的缓存是磁盘文件系统的缓存,而不是像redis那样的内存级别的缓存。一开始我以为nginx就只有这样。后来查了写资料,才知道是我太天真了,对linux不是很了解导致的。linux的一切皆文件。原来我们可以把文件缓存到内存对应的linux文件系统中。我说的可能比较难以理解,请自行搜索/dev/shm 这个文件目录。我们把文件缓存到这个文件目录里,其实就相当与内存的缓存了。只不过还是靠文件系统管理。所以比不上自定义格式的redis那样的内存缓存。
在http段进行基本配置
#缓存配置 proxy_cache_key '$host:$server_port$request_uri'; proxy_temp_file_write_size 64k; proxy_temp_path /dev/shm/jielierp/proxy_temp_path; proxy_cache_path /dev/shm/jielierp/proxy_cache_path levels=1:2 keys_zone=cache_one:200m inactive=5d max_size=1g; proxy_ignore_headers x-accel-expires expires cache-control set-cookie; location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { proxy_cache cache_one; proxy_cache_valid 200 304 302 5d; proxy_cache_valid any 5d; proxy_cache_key '$host:$server_port$request_uri'; add_header x-cache '$upstream_cache_status from $host'; proxy_pass http: //192.168.8.203:808; expires 30d; #缓存30天 }
经过这两个的配置就基本能实现了,这里说几个注意项,也是困扰我很久的问题。上面第一段代码第6行,proxy_ignore_headers 如果web项目中的html的head头里面指定
<meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0">
这些不缓存的话,就要加上proxy_ignore_headers的配置项了。还有一点就是/dev/shm下面的文件系统权限默认只给root用户,所以要chmod 777 -r /dev/shm 这样不是很安全的做法,如果实际上线可以给定某个用户组,关于用户组的设置是配置的第一行
user www www;
上面第二段代码的第6行是增加一个header字段方便查看是否击中缓存。
我们rm -rf /dev/shm/jielierp/proxy_* 下面的所有文件(注意这里如果是进行多次测试的话要nginx -s reload 重新读取配置或重启服务,因为你rm -rf只是删除了缓存文件,但是缓存的结构信息还在nginx进程里面,结构还在,如果不重启的话,是会出现访问不到的)
所以要记得重启哦。下面是运行效果
第一次访问
第二次访问,在浏览器中ctrl+shift+r 强制刷新
到这里就可以看到效果了。我们查看一下/dev/shm这个里面
到这里已经快结束了。最后也是比较关键的一个技术点,就是集群,集群,集群。这个就要用到upstream了,看到最开头的配置文件了吗,就是那个
#负载均衡组 #静态服务器组 upstream static { server 127.0.0.1:808 weight=1; server 192.168.8.203:808 weight=1; } #动态服务器组 upstream dynamic { server 127.0.0.1:8080; #server 192.168.8.203:8080; }
上面那个就是集群组了。upstream是关键字,static 和 dynamic是两个服务器集群组的名称。以第一个为例,server 127.0.0.1:808 是服务器地址,后面的weight=1 是权重。有多个就写多个。亲测试过,集群中的一个坏了,不影响系统运行。至于更多的轮询规则,可以参考网上更多的资料。这里不多说。至于怎么使用呢? proxy_pass http://192.168.8.203:808 改为 proxy_pass http://static; 这样即可实现均衡。
到这里就结束了。把上面各个部分根据自己需求配置起来就可以实现单机房负载均衡了。 上面这种做法有一个缺点就是在前面的那一台nginx如果当机,后面所以机器就失去了被访问的能力了,所以需要在前面实现多个nginx多机房的负载。关于这个就是另外一个话题了。目前还没有研究。以后有机会再说了。
上面动态服务器组如果是那种需要保存用户状态的话,会有问题,就是session问题,比如我在server1进行登录后,下一次动态服务器组进行轮询后可能分配到server2,就会造成要重新登录。治标的办法是,配置轮询规则,根据用户请求的ip进行hash,然后分配对应的服务器。具体配置如下:
upstream dynamic{ ip_hash; server 127.0.0.1:8080; server 192.168.0.203:8080; }
这样就可以实现一个用户对应一个服务器节点。这样就不会有重复登录的问题。另一种治本的办法是,利用缓存系统进行session的统一存储管理。
以上是Debian下搭建Nginx和Tomcat伺服器實現負載平衡的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!