nginx之 nginx_upstream_jvm_route模块

在前面一篇博客中介绍了【Nginx+tomcat 负载均衡、动静分离】,这种架构对于有状态的应用来说会存在一定问题:由于nginx采用round bin算法(轮训调度),固每次刷新页面nginx都会将请求分发到不同的tomcat服务器上,且每次的JSESSIONID都是不一样的。于是,对于有状态的应用来说,同一用户的请求调度到不同后端tomcat服务器,导致session信息丢失。

注:

  • 如果应用是无状态的,那么这种架构还是可行的;
  • 如果后端只有一个tomcat服务器,使用nginx仅为了实现动静分离,这种架构也是可行的;

对于有状态应用来说,这个问题可以通过粘性session的方式来解决。在nginx中,可以通过nginx_upstream_jvm_route(推荐)和ip_hash两种技术实现。

nginx_upstream_jvm_route技术:

1、nginx_upstream_jvm_route简介:

1)nginx_upstream_jvm_route是一个nginx的扩展模块,用来实现基于 Cookie 的 Session Sticky (粘性会话)的功能。简单来说,它是基于cookie中的JSESSIONID来决定将请求发送给后端的哪个server。原理:

  • 一开始请求过来,没有带session信息,jvm_route就根据round robin的算法,发到一台tomcat上面。
  • 将响应的server标识绑定到cookie中的JSESSIONID中,并返回给客户。
  • nginx会根据JSESSIONID来决定由哪个后端server来处理。

2)缺点:
暂时jvm_route模块还不支持默认fair的模式。jvm_route的工作模式和fair是冲突的。对于某个特定用户,当一直为他服务的tomcat宕机后,默认情况下它会重试max_fails的次数,如果还是失败,就重新启用round robin的方式将请求分发到其他tomcat服务器上,而这种情况下就会导致用户的session丢失。

总的说来,jvm_route是通过session_cookie这种方式来实现session粘性,将特定会话附属到特定tomcat上,从而解决session不同步问题,但无法解决宕机后会话转移问题。

2、nginx_upstream_jvm_route 模块安装:

1)下载

wget http://nginx-upstream-jvm-route.googlecode.com/files/nginx-upstream-jvm-route-0.1.tar.gz

在googlecode上nginx_upstream_jvm_route就一个下载地址,是2009年的0.1版本;对于比较新的nginx(1.6以后的)使用这个版本的模块安装在patch –p0 < /path/to/jvm_route.patch 时会报错。解决办法:

注:
由于版本兼容的问题,本例采用nginx-1.6.0(http://nginx.org/download/nginx-1.6.0.tar.gz)和github上的nginx_upstream_jvm_route(https://github.com/nulab/nginx-upstream-jvm-route/archive/master.zip)

2)打补丁:
解压nginx-1.6.0文件,然后进入到nginx-1.6.0目录中,执行下面命令:

[root@centOS1 nginx-1.6.0]# patch -p0 < /usr/local/nginx-upstream-jvm-route-master/jvm_route.patch

3)编译、安装:

[root@centOS1 nginx-1.6.0]# ./configure –prefix=/usr/local/nginx –add-module=/usr/local/nginx-upstream-jvm-route-master/
[root@centOS1 nginx-1.6.0]# make
[root@centOS1 nginx-1.6.0]# make install

3、nginx_upstream_jvm_route配置:

1)修改nginx配置文件中tomcat负载均衡的部分:

1
2
3
4
5
6
7
upstream  tomcats_jvm_route  
        {  
             # ip_hash;   
              server   192.168.33.10:8090 srun_id=tomcat01;   
              server   192.168.33.11:8090 srun_id=tomcat02;  
              jvm_route $cookie_JSESSIONID|sessionid reverse;  
        }

2)修改所有tomcat中server.xml文件:

1
2
3
4
将  
<Engine name="Catalina" defaultHost="localhost" >  
修改为:  
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat01">

3)测试:
在tomcat01和tomcat02的webapps下,ROOT目录中建立a.jsp,内容如下(输出sessionID信息):

1
2
3
4
5
6
7
8
9
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
    <head>
    </head>
    <body>
     tomcat01服务器<br/>
ID:<%=session.getId() %><br/>
    </body>
</html>

我们在浏览器中输入127.0.0.1/a.jsp 就可以看到输出, 并且可以观察到无论刷新多少次,输出结果不变(nginx把用户请求粘到了某一个tomcat上)。通过浏览器F12查看网络信息,可以看到cookie中的jsesisonID值326257DA6DB76F8D2E38F2C4540D1DEA.tomcat1,即带上了某台tomcat的粘性。
参考:http://hanqunfeng.iteye.com/blog/1920994

ip_hash技术:

nginx中的ip_hash技术能够将某个ip的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能建立起稳固的session,ip_hash是在upstream配置中定义的:

1
2
3
4
5
upstream backend {   
    server 127.0.0.1:8080 ;   
    server 127.0.0.1:9090 ;   
    ip_hash;   
    }

尽量不要使用这种方式,因为:

1)nginx不是最前端的服务器:
ip_hash要求nginx一定是最前端的服务器,否则nginx得不到正确ip,就不能根据ip作hash。譬如使用的是squid为最前端,那么nginx取ip时只能得到squid的服务器ip地址,用这个地址来作分流是肯定错乱的。
2)nginx的后端还有其它方式的负载均衡:
假如nginx后端又有其它负载均衡,将请求又通过另外的方式分流了,那么某个客户端的请求肯定不能定位到同一台session应用服务器上。
3)多个外网出口:
很多公司上网有多个出口,多个ip地址,用户访问互联网时候自动切换ip。而且这种情况不在少数。使用 ip_hash 的话对这种情况的用户无效,无法将某个用户绑定在固定的tomcat上 。