一,集群和分布式的区别

在进入今天的正题之前,对服务器集群和分布式服务器这两个概念进行简要说明。

服务器集群:服务器集群就是指将很多服务器集中起来一起进行同一种服务,在客户端看来就像是只有一个服务器。集群可以利用多个计算机进行并行计算,从而获得很高的计算速度,也可以用多个计算机做备份,,从而使得任何一个机器坏了整个系统还是能正常运行。

根据上述的信息简单来说:服务器集中对外提供同一种服务,解决了大量用户访问同一服务存在的高并发问题;多台服务器备份,解决了服务器的高可用问题;不至于说一台服务器宕机, 这个服务就瘫痪了!

上文中提到的多台服务器备份,备份的不就是会话信息session吗?也就是今天我们要说的服务器集群环境下session的共享问题。

分布式服务器:随着B/S架构系统的发展,开发一个互联网项目的业务需求也越来越多。如果把这些业务模块都放在一台服务器上来提供服务,这肯定是不现实的,毕竟一台服务器的性能各方面都是有限的。企业普遍会把这些业务模块分别放到不同的服务上来提供服务,来满足大量用户访问不同服务时存在的高并发问题

简单来说服务器集群和分布式服务器的区别:
分布式:项目的不同业务模块(例如:用户模块,购物车模块,支付模块),部署在不同的服务器上
集群:同一个业务(例如:用户模块),部署在多个服务器上

二,会话技术Cookie,Session

估计你们会在想:这个标题党,真正的问题不解决,怎么又扯出个会话技术。难道这就是传说中的程序员修复Bug,真正的问题没有解决,又冒出了另一个问题。

哈哈,不扯皮了。这里主要是为了说明Cookie,Session的联系和区别。

1.什么是会话?

用户开一个浏览器访问页面,访问了很多网站,直到用户关闭浏览器,这整个过程我们称作一次会话。

2.会话过程中的信息存储问题

HTTP是无状态协议:每次都是单独连接,不能保存用户的信息,通俗来说,就是它区分不了到底是哪一个用户在访问网络资源。而Cookie和Session就很好地解决了这个问题,

Cookie:
Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问web资源时,浏览器就会带着用户的信息过去。这样,web资源处理的就是用户各自的数据了。

注意:Cookie是存放在浏览器端,分为会话Cookie和持久性Cookie,默认情况下,Cookie是会话级别的,关闭浏览器就会消失。可以通过设置Cookie的有效时间来实现持久性Cookie存储

Session:
虽然Cookie可以存放用户信息,但cookie存储在浏览器端是以明文方式存放,因此安全性较低。而Session是存放在服务器端的,我们把用户的信息保存在服务器端Session中相对安全,然后把服务器创建的Session对象的Id存在中Cookie中,这样用户在访问服务器资源时,Cookie就会带着SessionId过来,服务器端就会根据这个SessionId来找到对应的Session,从而找到存储在Session中的用户信息。

注意:在访问同一个服务器下的Web资源时,Session只会被创建一次,Sessinon在用户访问第一次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session

三,服务器集群环境下session的共享问题

1.反向代理服务器Nginx

Nginx多在高并发情况下需要使用。其原理就是将用户请求分摊到多个服务器执行,减轻每台服务器的压力,多台服务器(集群)共同完成工作任务,从而提高了数据的吞吐量。

集群节点session共享_tomcat

集群节点session共享_redis_02

正向代理:用户想在某个指定的服务器上获取资源
反向代理:用户只要访问Nginx就可以获取到资源,而不用去关心具体访问的是哪个服务器

Nginx的使用

Nginx的安装过程跳过,主要演示Nginx的一些配置。

打开Nginx的nginx.conf文件,配置如下

集群节点session共享_redis_03

#下面这个配置需要自己添加
	upstream xiaogui_server{
		server localhost:8080;
		server 127.0.0.1:8888;
	}

#修改配置文件,添加  proxy_pass http://xiaogui_server;
 location / {
            root   html;
			proxy_pass http://xiaogui_server;
            index  index.html index.htm;
        }

在服务器tomcat1下的测试页面:

<%@ 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>服务器1111111111111</title>
<style type="text/css">
</style>
</head>
<body>
		<h1>服务器1的页面</h1>
		
		服务器1创建Session的Id:<%=session.getId() %>
		
		<%session.setAttribute("username", "xiaogui");%>
</body>
</html>

服务器tomcat2下的测试页面

<%@ 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>服务器2222222222222</title>
<style type="text/css">
</style>
</head>
<body>
		<h1>服务器2的页面</h1>
		
		服务器2创建Session的Id:<%=session.getId() %><br/>
		
		服务器1创建的Session中的存入的username:<%session.getAttribute("username");%>
</body>
</html>

启动tomcat1,tomcat2,nginx测试

当访问tomcat1页面时,

集群节点session共享_redis_04

刷新页面,访问tomcat2页面:

集群节点session共享_tomcat_05

****得出结论:使用Nginx访问服务器,由于访问的网址不会发生,所以Cookie中的信息在集群的服务器之间是共享的

根据得出结论,当用户第一次访问服务时,服务器创建Session,这时服务器把Session存放到Redis当中,交由Redis管理。以SessionID作为key,Session对象作为值。当用户再次访问时,不管访问的哪一个服务器,Cookie都会带着这个SessionID过来,根据这个SessionID就可以在Redis中找到对应的Session,这样就实现了Session共享。也就是下面我们要说到的这个方案。

使用nginx+tomcat+redis完成session共享(推荐方式)

集群节点session共享_tomcat_06

redis安装:

下载tomcat-redis-session-manager相应的jar包,主要有三个:

集群节点session共享_集群_07

commons-pool-1.6.jar :主要是来用管理redis
jedis-2.1.0.jar :用来操作redis的客户端
tomcat-redis-session-manager:主要使用redis来管理服务器创建的Session

这三个jar包,网上不太容易找到,好多下载都要收费。而且下载之后还要考虑三个jar之间的兼容性。我把测试成功的这三个jar包放在文章末尾的示例项目中!

把这三个jar复制到tomcat的lib目录下,

集群节点session共享_redis_08

修改tomcat/conf目录下的context.xml,在这个文件中,加入下面配置

<Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve" />
<Manager className="com.radiadesign.catalina.session.RedisSessionManager"
         host="localhost"
         port="6379"
         database="0"
         maxInactiveInterval="60" />

注意:所有集群的tomcat都要进行上述的配置。

启动Redis,tomcat1,tomcat2,Nginx服务,进行测试

集群节点session共享_集群_09

用户再一次刷新页面,

集群节点session共享_session共享_10

我们还可以通过redis的客户端查看:

集群节点session共享_redis_11

由于使用redis的客户端查看,对数据进行了转码,所以会显示成上面的结果。

tomcat的广播机制,完成session共享(不推荐)

修改tomcat/conf下的server.xml文件

集群节点session共享_集群节点session共享_12

把这个注释的部分去掉

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

在项目的web.xml中配置

<distributable/>

在集群的tomcat上配置好以后,重启tomcat即可。

根据用户的IP进行hash计算,让用户访问指定的tomcat

修改Nginx/conf目录下的nginx.conf文件

#下面这个配置需要自己添加
	upstream xiaogui_server{
		server localhost:8080;
		server 127.0.0.1:8888;
		ip_hash;
	}

在上面这个配置中加入ip_hash;即可。

缺点:1.由于局域网对外的IP都是一样的,如果该局域网中的用户都对服务进行访问,根据这个配置会造成局域网内所有的用户都会访问该服务器,这时服务器的访问压力会很大。2.当这个服务器宕机之后,可能被分配到这个服务器上的用户就没有办法进行访问了。

Nginx第三方模块upstream_hash解决session共享

为了解决ip_hash的一些问题,可以使用upstream_hash这个第三方模块,这个模块多数情况下是用作url_hash的,但是并不妨碍将它用来做session共享:

假如前端是squid,他会将ip加入x_forwarded_for这个http_header里,用upstream_hash可以用这个头做因子,将请求定向到指定的后端:

hash $http_x_forwarded_for;

在nginx新版本中可支持读取cookie值,所以也可以改成:

hash $cookie_jsessionid;

最后这种没有做过测试,有兴趣的话,自己可以测试一下。

分享示例项目在码云上的地址:https://gitee.com/xiaoguixiaogege/ClusterShareSession