Nginx+Tomcat集群实现反向代理负载均衡和Session共享

本文来源于https://blog.csdn.net/sunroyi666/article/details/79163088,如果有不当之处,请通知删除。

以前写过一篇Apache+Tomcat集群的文章,最近正好有空,把Nginx+Tomcat也写一下做为比较。

首先,用Nginx和Apache+Tomcat做集群的区别,我就直接从网上找了点资料。

主要区别在于,Apache是同步多进程模型,一个连接对应一个进程。Nginx是异步多进程模型,多个连接对应一个进程,这里的多个连接可以达到上万的级别,所以普遍认为,Nginx的性能更好。

Nginx:

  1. 轻量级,采用 C 进行编写,同样的 web 服务,会占用更少的内存及资源

  2. 抗并发,nginx 以 epoll and kqueue 作为开发模型,处理请求是异步非阻塞的,负载能力比 apache 高很多,而 apache 则是阻塞型的。在高并发下 nginx 能保持低资源低消耗高性能 ,而 apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象。

  3. nginx 处理静态文件好,静态处理性能比 apache 高三倍以上

  4. nginx 的设计高度模块化,编写模块相对简单

  5. nginx 配置简洁,正则配置让很多事情变得简单,而且改完配置能使用 -t 测试配置有没有问题,apache 配置复杂 ,重启的时候发现配置出错了,会很崩溃

  6. nginx 作为负载均衡服务器,支持 7 层负载均衡

  7. nginx 本身就是一个反向代理服务器,而且可以作为非常优秀的邮件代理服务器

  8. 启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动,还能够不间断服务的情况下进行软件版本的升级

  9. 社区活跃,各种高性能模块出品迅速

Apache:
  1. apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache

  2. apache 发展到现在,模块超多,基本想到的都可以找到

  3. apache 更为成熟,少 bug ,nginx 的 bug 相对较多

  4. apache 超稳定

  5. apache 对 PHP 支持比较简单,nginx 需要配合其他后端用

  6. apache 在处理动态请求有优势,nginx 在这方面是鸡肋,一般动态请求要 apache 去做,nginx 适合静态和反向。

  7. apache 仍然是目前的主流,拥有丰富的特性,成熟的技术和开发社区

总结:

两者最核心的区别在于 apache 是同步多进程模型,一个连接对应一个进程,而 nginx 是异步的,多个连接(万级别)可以对应一个进程

一般来说,需要性能的 web 服务,用 nginx 。如果不需要性能只求稳定,更考虑 apache ,后者的各种功能模块实现得比前者,例如 ssl 的模块就比前者好,可配置项多。epoll(freebsd 上是 kqueue ) 网络 IO 模型是 nginx 处理性能高的根本理由,但并不是所有的情况下都是 epoll 大获全胜的,如果本身提供静态服务的就只有寥寥几个文件,apache 的 select 模型或许比 epoll 更高性能。当然,这只是根据网络 IO 模型的原理作的一个假设,真正的应用还是需要实测了再说的。

更为通用的方案是,前端 nginx 抗并发,后端 apache 集群,配合起来会更好。

--------------------------------------------------------------------------------------------------

下面介绍如何配置Nginx+Tomcat集群并做到Session复制:

一. Tomcat配置(Tomcat8):

1.conf/server.xml:

主要注意的是端口号(port)的设置,<Engine>中jvmRoute的设置,<Cluster>的注释放开。

[html] view plain copy
  1. <?xml version='1.0' encoding='utf-8'?>  
  2. <Server port="8075" shutdown="SHUTDOWN">  
  3.   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />  
  4.   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />  
  5.   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />  
  6.   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />  
  7.   
  8.   <GlobalNamingResources>  
  9.     <Resource name="UserDatabase" auth="Container"  
  10.               type="org.apache.catalina.UserDatabase"  
  11.               description="User database that can be updated and saved"  
  12.               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"  
  13.               pathname="conf/tomcat-users.xml" />  
  14.   </GlobalNamingResources>  
  15.   
  16.   <Service name="Catalina">  
  17.     <!-- 多个Tomcat的端口号不能相同,否则无法启动 -->  
  18.     <Connector port="8080" protocol="HTTP/1.1"  
  19.                connectionTimeout="20000" maxHttpHeaderSize="2000000"  
  20.                redirectPort="8444" />  
  21.   
  22.     <Connector port="8072" protocol="AJP/1.3" redirectPort="8444" />  
  23.     <!-- 多个Tomcat的jvmRoute必须要设置且必须相同 -->  
  24.     <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">  
  25.   
  26.       <!-- Cluster原本是被注释掉的,现在要放开 -->  
  27.       <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>  
  28.   
  29.   
  30.       <Realm className="org.apache.catalina.realm.LockOutRealm">  
  31.          
  32.         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"  
  33.                resourceName="UserDatabase"/>  
  34.       </Realm>  
  35.   
  36.       <Host name="localhost"  appBase="webapps"  
  37.             unpackWARs="true" autoDeploy="true" reloadable="true">  
  38.   
  39.         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"  
  40.                prefix="localhost_access_log" suffix=".txt"  
  41.                pattern="%h %l %u %t "%r" %s %b" />  
  42.   
  43.       </Host>  
  44.     </Engine>  
  45.   </Service>  
  46. </Server>  

2.webapps/ROOT/WEB-INF/web.xml:

添加<distributable />

[html] view plain copy
  1. <?xml version="1.0" encoding="ISO-8859-1"?>  
  2.   
  3. <web-app xmlns="http://java.sun.com/xml/ns/javaee"  
  4.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
  6.                       http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
  7.   version="3.0"  
  8.   metadata-complete="true">  
  9.   
  10.   <display-name>Welcome to Tomcat</display-name>  
  11.   <description>  
  12.      Welcome to Tomcat  
  13.   </description>  
  14. <distributable />   
  15. </web-app>  
3.在webapps/ROOT/下添加testCluster.jsp:

[html] view plain copy
  1. <%@ page contentType="text/html; charset=GBK" %>  
  2. <%@ page import="java.util.*" %>  
  3. <html><head><title>Cluster Test</title></head>  
  4. <body>  
  5. <%  
  6.   //HttpSession session = request.getSession(true);  
  7.   System.out.println(session.getId());  
  8.   out.println("<br> SESSION ID:" + session.getId()+"<br>");    
  9.   // 如果有新的请求,则添加session属性  
  10.   String name = request.getParameter("name");  
  11.   if (name != null && name.length() > 0) {  
  12.      String value = request.getParameter("value");  
  13.      session.setAttribute(name, value);  
  14.   }    
  15.     out.print("<b>Session List:</b>");    
  16.     Enumeration<String> names = session.getAttributeNames();  
  17.     while (names.hasMoreElements()) {  
  18.         String sname = names.nextElement();   
  19.         String value = session.getAttribute(sname).toString();  
  20.         out.println( sname + " = " + value+"<br>");  
  21.         System.out.println( sname + " = " + value);  
  22.    }  
  23. %>  
  24.   <form action="testCluster.jsp" method="post">  
  25.     名称:<input type=text size=20 name="name">  
  26.      <br>  
  27.     值:<input type=text size=20 name="value">  
  28.      <br>  
  29.     <input type=submit value="提交">  
  30.    </form>  
  31.   
  32.    <b>负载均衡测试:此为:Tomcat1上的文件,<font color=red>111111</font><b>  
  33. </body>  
  34. </html>  
在复制到其他Tomcat下时,将下面这段改掉即可
[html] view plain copy
  1. <b>负载均衡测试:此为:Tomcat1上的文件,<font color=red>111111</font><b>  
[html] view plain copy
  1. <b>负载均衡测试:此为:Tomcat2上的文件,<font color=red>222222</font><b>  
4. 启动多个Tomcat。


二. Nginx配置(nginx-1.12.2):

1.conf/nginx.conf:

[html] view plain copy
  1. #user  nobody;  
  2. worker_processes  1;    #工作进程的个数,一般与计算机的cpu核数一致  
  3.   
  4. #错误日志存放路径    
  5. #error_log  logs/error.log;  
  6. #error_log  logs/error.log  notice;  
  7. #error_log  logs/error.log  info;  
  8.   
  9. #指定pid存放文件   
  10. #pid        logs/nginx.pid;  
  11.   
  12. events {  
  13.     #使用网络IO模型linux建议epoll,FreeBSD建议采用kqueue,window下不指定。    
  14.     #use epoll;    
  15.   
  16.     worker_connections  1024;   #单个进程最大连接数(最大连接数=连接数*进程数)    
  17. }  
  18.   
  19.   
  20. http {  
  21.     include       mime.types;   #文件扩展名与文件类型映射表    
  22.     default_type  application/octet-stream; #默认文件类型    
  23.   
  24.     #定义日志格式  
  25.     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '  
  26.     #                  '$status $body_bytes_sent "$http_referer" '  
  27.     #                  '"$http_user_agent" "$http_x_forwarded_for"';  
  28.   
  29.     #access_log  logs/access.log  main;  
  30.   
  31.     sendfile        on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。  
  32.     #tcp_nopush     on;  
  33.   
  34.     #keepalive_timeout  0;    
  35.     keepalive_timeout  65;  #长连接超时时间,单位是秒    
  36.   
  37.     #gzip  on;  #启用Gizp压缩    
  38.   
  39.     #服务器的集群    
  40.     upstream  netitcast.com {  #服务器集群名字     
  41.     #Nginx是如何实现负载均衡的,Nginx的upstream目前支持以下几种方式的分配  
  42.         #1、轮询(默认)  
  43.         #每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。  
  44.         #2、weight  
  45.         #指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。  
  46.         #2、ip_hash  
  47.     #每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。  
  48.         #3、fair(第三方)  
  49.         #按后端服务器的响应时间来分配请求,响应时间短的优先分配。  
  50.         #4、url_hash(第三方)  
  51.         #按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。    
  52.       #ip_hash;   
  53.         server    192.168.8.23:8080  weight=1;#服务器配置   weight是权重的意思,权重越大,分配的概率越大。    
  54.         server    192.168.8.23:8079  weight=2;    
  55.     }     
  56.   
  57.     #当前的Nginx的配置    
  58.     server {  
  59.         listen       8082;  #监听80端口,可以改成其他端口    
  60.         server_name  localhost; #当前服务的域名   
  61.   
  62.         #charset koi8-r;  
  63.   
  64.         #access_log  logs/host.access.log  main;  
  65.   
  66.         location / {  
  67.             proxy_pass http://netitcast.com;    
  68.             proxy_redirect default;    
  69.         proxy_connect_timeout 3; #nginx跟后端服务器连接超时时间(代理连接超时)   
  70.             proxy_send_timeout 30; #后端服务器数据回传时间(代理发送超时)   
  71.             proxy_read_timeout 30; #连接成功后,后端服务器响应时间(代理接收超时)   
  72.             #root   html;  
  73.             #index  index.html index.htm;  
  74.         }  
  75.   
  76.         #error_page  404              /404.html;  
  77.   
  78.         # redirect server error pages to the static page /50x.html  
  79.         #  
  80.         error_page   500 502 503 504  /50x.html;  
  81.         location = /50x.html {  
  82.             root   html;  
  83.         }  
  84.   
  85.         # proxy the PHP scripts to Apache listening on 127.0.0.1:80  
  86.         #  
  87.         #location ~ \.php$ {  
  88.         #    proxy_pass   http://127.0.0.1;  
  89.         #}  
  90.   
  91.         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000  
  92.         #  
  93.         #location ~ \.php$ {  
  94.         #    root           html;  
  95.         #    fastcgi_pass   127.0.0.1:9000;  
  96.         #    fastcgi_index  index.php;  
  97.         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;  
  98.         #    include        fastcgi_params;  
  99.         #}  
  100.   
  101.         # deny access to .htaccess files, if Apache's document root  
  102.         # concurs with nginx's one  
  103.         #  
  104.         #location ~ /\.ht {  
  105.         #    deny  all;  
  106.         #}  
  107.     }  
  108.   
  109.   
  110.     # another virtual host using mix of IP-, name-, and port-based configuration  
  111.     #  
  112.     #server {  
  113.     #    listen       8000;  
  114.     #    listen       somename:8080;  
  115.     #    server_name  somename  alias  another.alias;  
  116.   
  117.     #    location / {  
  118.     #        root   html;  
  119.     #        index  index.html index.htm;  
  120.     #    }  
  121.     #}  
  122.   
  123.   
  124.     # HTTPS server  
  125.     #  
  126.     #server {  
  127.     #    listen       443 ssl;  
  128.     #    server_name  localhost;  
  129.   
  130.     #    ssl_certificate      cert.pem;  
  131.     #    ssl_certificate_key  cert.key;  
  132.   
  133.     #    ssl_session_cache    shared:SSL:1m;  
  134.     #    ssl_session_timeout  5m;  
  135.   
  136.     #    ssl_ciphers  HIGH:!aNULL:!MD5;  
  137.     #    ssl_prefer_server_ciphers  on;  
  138.   
  139.     #    location / {  
  140.     #        root   html;  
  141.     #        index  index.html index.htm;  
  142.     #    }  
  143.     #}  
  144.   
  145. }  
2. 点击nginx.exe,启动Nginx:
Nginx+Tomcat集群实现反向代理负载均衡和Session共享

3.我在Nginx的启动过程中,遇到过一闪而过,启动失败的情况,这时可以看一下错误日志 logs/error.log

提示的错误是:2018/01/25 11:07:24 [emerg] 456476#460620: bind() to 0.0.0.0:8081 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)

这是因为端口被占用了,把8081端口的进程关掉或者换一个其他的端口即可。


三. 测试

我Nginx用的端口是8082,所以输入http://localhost:8082/testCluster.jsp

1.这是第一次显示的画面,Tomcat1代表这是第一个Tomcat的页面

Nginx+Tomcat集群实现反向代理负载均衡和Session共享

2.这是第二次显示的画面,Tomcat2表示这是第二个Tomcat的页面,说明负载均衡已成功

Nginx+Tomcat集群实现反向代理负载均衡和Session共享

3.接下来测试Session复制。根据Tomcat变换前后,SESSION ID都是D07D8BB95B783F8CAA83D94175D24846.jvm1可以判断出,这是同一个Session。

可以输入名称和值,看看在服务器切换后,Session中是否可以保存。

Nginx+Tomcat集群实现反向代理负载均衡和Session共享

4. 可以看到,2=222保存了,但是现在仍然在Tomcat2上,看不出效果,再试一次。

Nginx+Tomcat集群实现反向代理负载均衡和Session共享

5.这次切换到Tomcat1了,而且Session List里面的值都带过来了。再试试Tomcat1能否传回Tomcat2。

Nginx+Tomcat集群实现反向代理负载均衡和Session共享

6.回到Tomcat2以后,SessionList中的值仍然保持,至此Session复制完全成功了。

Nginx+Tomcat集群实现反向代理负载均衡和Session共享