注意:最新版的nginx和nginx_upstream_jvm_route有冲突,不能同时安装,降级安装nginx 1.4.7版本的,不安装1.6.0版本的。
相关文件下载见附件。
前言
其实搭建集群环境选择合适的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很容易遭到中间人攻击。
这里先介绍第二种实现方式。
在服务机器上创建一个目录/home/oracle/http,所有的文件都放在该文件下:
准备工作
进入到 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设置为:
http://nginx.org/en/docs/configure.html
一个参考的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.35、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最新的源代码。
1、准备工作做好之后,进入到/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失败:
如果失败的话就需要把之前的/home/oracle/http/nginx-1.4.7下面的文件重新解压一份重新patch:
此时就能patch成功,可以进行下面的安装。
2、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目录下面生成相应的文件:
3、接下来安装tomcat集群,这里为了演示方便,采用单机多端口的方式进行集群搭建,将一个tomcat包分别解压到/home/oracle/http/ apache-tomcat-a、/home/oracle/http/ apache-tomcat-b、/home/oracle/http/ apache-tomcat-c目录下。
4、首先是/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没有影响。