注意:最新版的nginx和nginx_upstream_jvm_route有冲突,不能同时安装,降级安装nginx 1.4.7版本的,不安装1.6.0版本的。
相关文件下载见附件。
持续性
负载均衡器需要处理的一个重要问题是:如何保存用户会话?如果会话信息保存在后台服务器,用户接下来的请求可能会被分配到不同的后台服务器,此时用户会话就无法继续。负载均衡器可以缓存用户会话,然后将用户请求分发到不同的后台服务器。但是这将带来负载均衡器的负载问题。
一个解决方案是将一个用户会话中的所有请求都发送到同一个后台服务器。即persistence或stickiness。这个方法的不足之处在于无法容错failover,如果后台服务器故障,它提供的会话就会无法取得,任何依赖于它的会话都会丢失。这个问题通常与数据中心有关,尽管Web Service是非连结导向的,但是后端的数据库先天上还是连结导向的。
第二个方案是依据用户名,客户端IP来分配提供服务的服务器,也可以随机分配。因为客户可能是通过DHCP,NAT或者Web代理来连接Internet的,其IP地址可能经常变换,这使得这个方案的服务质量无法保障。随机分配由负载均衡器将会话信息存储保存。如果负载均衡器被替换或故障,这些信息可会会丢失;另外(负载均衡器)负载较高的时候,为保证分配表空间不会被耗尽,超时的分配信息必须被删除。随机分配方法也要求客户会维持会话状态,如果客户浏览器禁用了cookies的功能,就会引起问题。优秀的负载均衡器会使用多种持续(会话保持)技术,以避免其中某些不可以用时引起故障。
另外一个方案是将每一会话信息保存到一个数据库中。由于这个方案会增加数据库的负载,所以这个方案对性能的提高并不好。数据库最好是用来存储会话时间比较长的会话数据。为了避免数据库出现单点故障,并且提高其扩展性,数据库通常会复制到多台服务器上,通过负载均衡器来分发请求到数据库服务器上。微软ASP.net中的状态服务器技术就是一个典型的会话数据库的例子。集群中的所有服务器都将它们的会话信息保存到状态服务器上,同时它们可以向状态服务器查询会话数据。
幸运的是有一种更有效的方法,通常客户浏览器可以保存用户的每个会话信息。例如使用浏览器cookie,对数据加密并加上一个时间戳就可以保证安全了;URL重写。将会话信息存储在客户端通常是首选方案,因为这样负载均衡器就可以灵活的选择后台服务器来处理用户请求。然而这种方法不适应于一些较复杂的电子商务,因为电子商务中会话数据较大,而且需要服务器需要经常重新处理会话信息;与此同时URL重写由于用户可以改变会话流数据而存在安全问题;加密客户端cookies也一直存在着安全方面的争议,除非所有的会话都通过HTTPS,但是HTTPS很容易遭到中间人攻击。
这里先介绍第二种实现方式。
准备工作
patch -p0 < /home/oracle/http/nginx_upstream_jvm_route/jvm_route.patch
如果使用之前的旧的jar包的话会导致patch失败:
如果失败的话就需要把之前的/home/oracle/http/nginx-1.4.7下面的文件重新解压一份重新patch:
此时就能patch成功,可以进行下面的安装。
./configure --prefix=/home/oracle/http/nginxserverfiles #nginx安装的根路径 --sbin-path=/home/oracle/http/nginxserverfiles/nginx #nginx的bin路经 --conf-path=/home/oracle/http/nginxserverfiles/nginx.conf #nginx的配置文件路径 --pid-path=/home/oracle/http/nginxserverfiles/nginx.pid #nginx运行时生成pid的文件路径 --with-http_ssl_module #编译ssl模块 --with-pcre=../pcre-8.35 #pcre路径,pcre主要是为ngx_http_rewrite_module模块提供正则表达式的支持的 --with-zlib=../zlib-1.2.8 #zlib路径,zlib是ngx_http_gzip_module模块所必需的 --add-module=/home/oracle/http/nginx_upstream_jvm_route #增加一个外部模块,为tomcat集群的session共享提供解决方案
configure完成之后就是make和make install。完成之后就会在/home/oracle/http/nginxserverfiles目录下面生成相应的文件:
#运行NGINX所使用的用户和组 user root; #nginx进程数,建议按照cpu数目来指定,一般为它的倍数,每个进程消耗约10M内存 worker_processes 5; #日志信息 PID文件 error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; pid logs/nginx.pid; #工作模式及连接数上限 events { #使用epoll的I/O模型 use epoll; #该值受系统进程最大打开文件数限制,需要使用命令ulimit -n 查看当前设置 worker_connections 300000; } #设定http服务器,利用它的反向代理功能提供负载均衡支持 http { #这里是您需要修改的地方,修改为您的服务器IP:端口号 srun_id为您在tomcat中所配置的jvmRoute upstream localhost.com{ server localhost.com:18080 srun_id=a; server localhost.com:28080 srun_id=b; server localhost.com:38080 srun_id=c; jvm_route $cookie_JSESSIONID|sessionid reverse; } include mime.types; #设置默认类型是二进制流,若未设置时,比如未加载PHP时,是不予解析,用浏览器访问则出现下载窗口 default_type application/octet-stream; charset UTF-8; #设定请求缓冲 server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 8m; limit_rate 1024k; #access_log logs/access.log main; sendfile on; tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; tcp_nodelay on; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; gzip on; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; server { listen 80; server_name localhost.com; #所有的代理资源存放到存放路径 root /home/oracle/http/www; index index.html index.htm index.jsp; error_page 404 /404.html; charset UTF-8; #access_log logs/host.access.log main; error_page 500 502 503 504 /50x.html; location / { #root html; proxy_pass http://localhost.com; proxy_redirect off; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ .*\.(js|css)?$ { expires 1h; } location /Nginxstatus { #stub_status on; access_log off; } } log_format access '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $http_x_forwarded_for'; }
然后是tomcat的server.xml文件修改,进入到/home/oracle/http/apache-tomcat-a/conf路径,修改server.xml文件:
<?xml version='1.0' encoding='utf-8'?> <Server port="18081" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="18080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="18443" /> <Connector port="18009" protocol="AJP/1.3" redirectPort="18443" /> <Engine name="Catalina" defaultHost="localhost.com" jvmRoute="a"> <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> </Cluster> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost.com" appBase="/home/oracle/http/www" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server>
注意上述文件中的红色字体和红色加粗字体的描述部分,然后编辑web.xml文件,然后在后面添加上下面的内容:
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <distributable/>
然后从tomcat/webapps下面的ROOT文件夹拷贝到/home/oracle/http/www文件夹下。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
session is :
<%
HttpSession sess = request.getSession();
sess.setMaxInactiveInterval(100);
out.print(sess);
%>
<br>
cookie is :
<%
out.print(request.getHeader("Cookie"));
%>
</body>
</html>
然后就可以启动三个tomcat和nginx了,在运行过程中只要保证至少有一个tomcat实例在运行就可以保证session不会丢失,中途关闭重启多台tomcat没有影响。