注意:最新版的nginx和nginx_upstream_jvm_route有冲突,不能同时安装,降级安装nginx 1.4.7版本的,不安装1.6.0版本的。javascript
相关文件下载见附件。css
前言
其实搭建集群环境选择合适的session共享,服务器一般有两种管理session的方式:replicated sessions和sticky sessions。
第一种是基于复制的session共享,主要功能是修改tomcat的session存储机制,使之可以把session序列化存放到memcached中,表明工具是memcached-session-manager。
第二种是实现基于cookie的“粘性会话”(Sticky Session),(咱们须要作的就是调整高度策略,
让用户在一次会话周期内的全部请求始终转发到一台特定的后端服务器上,这种机制也称为粘滞会话(Sticky Sessions),要实现它的关键在于如何设计持续性调度算法。参见
http://www.xwuxin.com/?p=1958)这个功能是基于nginx的扩展功能开发的一个扩展插件实现的,主要的表明有nginx_upstream_jvm_route。
关于这点,维基百科上面有相应的描述:
负载均衡 写道
http://zh.wikipedia.org/zh-cn/负载均衡_(计算机)
持续性
负载均衡器须要处理的一个重要问题是:如何保存用户会话?若是会话信息保存在后台服务器,用户接下来的请求可能会被分配到不一样的后台服务器,此时用户会话就没法继续。负载均衡器能够缓存用户会话,而后将用户请求分发到不一样的后台服务器。可是这将带来负载均衡器的负载问题。
一个解决方案是将一个用户会话中的全部请求都发送到同一个后台服务器。即persistence或stickiness。这个方法的不足之处在于没法容错failover,若是后台服务器故障,它提供的会话就会没法取得,任何依赖于它的会话都会丢失。这个问题一般与数据中心有关,尽管Web Service是非连结导向的,可是后端的数据库先天上仍是连结导向的。
第二个方案是依据用户名,客户端IP来分配提供服务的服务器,也能够随机分配。由于客户多是经过DHCP,NAT或者Web代理来链接Internet的,其IP地址可能常常变换,这使得这个方案的服务质量没法保障。随机分配由负载均衡器将会话信息存储保存。若是负载均衡器被替换或故障,这些信息可会会丢失;另外(负载均衡器)负载较高的时候,为保证分配表空间不会被耗尽,超时的分配信息必须被删除。随机分配方法也要求客户会维持会话状态,若是客户浏览器禁用了cookies的功能,就会引发问题。优秀的负载均衡器会使用多种持续(会话保持)技术,以免其中某些不能够用时引发故障。
另一个方案是将每一会话信息保存到一个数据库中。因为这个方案会增长数据库的负载,因此这个方案对性能的提升并很差。数据库最好是用来存储会话时间比较长的会话数据。为了不数据库出现单点故障,而且提升其扩展性,数据库一般会复制到多台服务器上,经过负载均衡器来分发请求到数据库服务器上。微软ASP.net中的状态服务器技术就是一个典型的会话数据库的例子。集群中的全部服务器都将它们的会话信息保存到状态服务器上,同时它们能够向状态服务器查询会话数据。
幸运的是有一种更有效的方法,一般客户浏览器能够保存用户的每一个会话信息。例如使用浏览器cookie,对数据加密并加上一个时间戳就能够保证安全了;URL重写。将会话信息存储在客户端一般是首选方案,由于这样负载均衡器就能够灵活的选择后台服务器来处理用户请求。然而这种方法不适应于一些较复杂的电子商务,由于电子商务中会话数据较大,并且须要服务器须要常常从新处理会话信息;与此同时URL重写因为用户能够改变会话流数据而存在安全问题;加密客户端cookies也一直存在着安全方面的争议,除非全部的会话都经过HTTPS,可是HTTPS很容易遭到中间人攻击。
持续性
负载均衡器须要处理的一个重要问题是:如何保存用户会话?若是会话信息保存在后台服务器,用户接下来的请求可能会被分配到不一样的后台服务器,此时用户会话就没法继续。负载均衡器能够缓存用户会话,而后将用户请求分发到不一样的后台服务器。可是这将带来负载均衡器的负载问题。
一个解决方案是将一个用户会话中的全部请求都发送到同一个后台服务器。即persistence或stickiness。这个方法的不足之处在于没法容错failover,若是后台服务器故障,它提供的会话就会没法取得,任何依赖于它的会话都会丢失。这个问题一般与数据中心有关,尽管Web Service是非连结导向的,可是后端的数据库先天上仍是连结导向的。
第二个方案是依据用户名,客户端IP来分配提供服务的服务器,也能够随机分配。由于客户多是经过DHCP,NAT或者Web代理来链接Internet的,其IP地址可能常常变换,这使得这个方案的服务质量没法保障。随机分配由负载均衡器将会话信息存储保存。若是负载均衡器被替换或故障,这些信息可会会丢失;另外(负载均衡器)负载较高的时候,为保证分配表空间不会被耗尽,超时的分配信息必须被删除。随机分配方法也要求客户会维持会话状态,若是客户浏览器禁用了cookies的功能,就会引发问题。优秀的负载均衡器会使用多种持续(会话保持)技术,以免其中某些不能够用时引发故障。
另一个方案是将每一会话信息保存到一个数据库中。因为这个方案会增长数据库的负载,因此这个方案对性能的提升并很差。数据库最好是用来存储会话时间比较长的会话数据。为了不数据库出现单点故障,而且提升其扩展性,数据库一般会复制到多台服务器上,经过负载均衡器来分发请求到数据库服务器上。微软ASP.net中的状态服务器技术就是一个典型的会话数据库的例子。集群中的全部服务器都将它们的会话信息保存到状态服务器上,同时它们能够向状态服务器查询会话数据。
幸运的是有一种更有效的方法,一般客户浏览器能够保存用户的每一个会话信息。例如使用浏览器cookie,对数据加密并加上一个时间戳就能够保证安全了;URL重写。将会话信息存储在客户端一般是首选方案,由于这样负载均衡器就能够灵活的选择后台服务器来处理用户请求。然而这种方法不适应于一些较复杂的电子商务,由于电子商务中会话数据较大,并且须要服务器须要常常从新处理会话信息;与此同时URL重写因为用户能够改变会话流数据而存在安全问题;加密客户端cookies也一直存在着安全方面的争议,除非全部的会话都经过HTTPS,可是HTTPS很容易遭到中间人攻击。
这里先介绍第二种实现方式。html
在服务机器上建立一个目录/home/oracle/http,全部的文件都放在该文件下:
准备工做java
进入到
http://nginx.org/en/download.html,不要下载nginx最新的稳定版本:nginx-1.6.0.tar.gz,下载nginx-1.4.7,下载地址是
http://nginx.org/download/nginx-1.4.7.tar.gz
解压到服务器上,进入到/home/oracle/http/nginx-1.4.7目录下,这里采用的是源代码编译的方式进行安装,参考文档说明,相关的configure设置为:
一个参考的configure示例:
./configure
--sbin-path=/usr/local/nginx/nginx
--conf-path=/usr/local/nginx/nginx.conf
--pid-path=/usr/local/nginx/nginx.pid
--with-http_ssl_module
--with-pcre=../pcre-4.4
--with-zlib=../zlib-1.1.3
全部的代码写到一行。
安装nginx须要安装相应的依赖包,依赖包一共有三个
pcre-8.3五、zlib-1.2.8和nginx-upstream-jvm-route-0.1
分别到
http://sourceforge.net/projects/pcre/、
http://zlib.net/以及
https://code.google.com/p/nginx-upstream-jvm-route/官网上去下载最新版本便可。
下载完成以后解压,其中nginx-1.4.7解压到/home/oracle/http/nginx-1.4.7,pcre-8.35解压到/home/oracle/http/pcre-8.35,zlib-1.2.8解压到/home/oracle/http/zlib-1.2.8,nginx_upstream_jvm_route解压到/home/oracle/http/nginx_upstream_jvm_route。
其中nginx-upstream-jvm-route-0.1.tar.gz包里面的文件是旧的,会致使nginx安装不成功,须要使用nginx_upstream_jvm_route.tar.gz里面的文件替换掉解压出的文件,或者去https://code.google.com/p/nginx-upstream-jvm-route/网站上checkout最新的源代码。
一、准备工做作好以后,进入到/home/oracle/http/nginx-1.4.7目录下,为nginx添加nginx_upstream_jvm_route的模块,运行下面的命令:
patch -p0 < /home/oracle/http/nginx_upstream_jvm_route/jvm_route.patch
若是使用以前的旧的jar包的话会致使patch失败:node
若是失败的话就须要把以前的/home/oracle/http/nginx-1.4.7下面的文件从新解压一份从新patch:nginx
此时就能patch成功,能够进行下面的安装。web
二、patch完成以后就能够安装,这里configure使用下面的命令:
./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目录下面生成相应的文件:正则表达式
三、接下来安装tomcat集群,这里为了演示方便,采用单机多端口的方式进行集群搭建,将一个tomcat包分别解压到/home/oracle/http/
apache-tomcat-a、/home/oracle/http/
apache-tomcat-b、/home/oracle/http/
apache-tomcat-c目录下。
四、首先是/home/oracle/http/nginxserverfiles/nginx.conf配置文件的修改:
#运行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文件夹下。
用下面的内容替换掉index.jsp的内容:
<%@ 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没有影响。