分布式下Session一致性架构举例
一、问题及方案
见这篇文章:分布式下Session一致性问题
二、分布式环境搭建:

系统环境
[root@centos7 ~]# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
[root@centos7 ~]#
2.1 安装jdk
# 下载jdk-8u141-linux-x64.tar.gz
# 创建目录
mkdir -p /opt/java
# 解压
tar -xzvf jdk-8u141-linux-x64.tar.gz -C /opt/java
# 创建链接
ln -s /opt/java/jdk1.8.0_141 /usr/local/jdk
# 设置变量 /etc/profile末尾添加
export JAVA_HOME=/usr/local/jdk
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
# 使变量生效
source /etc/profile
export PATH=$JAVA_HOME/bin:$PATH
# 验证
java -version
2.2 安装tomcat
安装3个tomcat并设置其端口号
# 下载apache-tomcat-8.5.16.tar.gz
# 创建目录
mkdir -p /opt/tomcat
# 解压
tar -xzvf apache-tomcat-8.5.16.tar.gz -C /opt/tomcat
# 清空ROOT目录
rm -rf /opt/tomcat/apache-tomcat-8.5.16/webapps/ROOT/*
# 复制3个tomcat
cd /opt/tomcat
cp -a apache-tomcat-8.5.16 tomcat1
cp -a apache-tomcat-8.5.16 tomcat2
cp -a apache-tomcat-8.5.16 tomcat3
# 修改端口号
sed -i '22s/8005/8'"$i"'05/' $file
sed -i '69s/8080/8'"$i"'80/' $file
sed -i '116s/8009/8'"$i"'09/' $file
修改如下3个端口号

用如下脚本修改:a.sh
#!/bin/sh
for i in {1..3}
do
  file=/opt/tomcat/tomcat"$i"/conf/server.xml
  sed -i '22s/8005/8'"$i"'05/' $file
  sed -i '69s/8080/8'"$i"'80/' $file
  sed -i '116s/8009/8'"$i"'09/' $file
  #sed -i '148s#appBase=".*"#appBase="/data/webapps"#' $file
done
执行脚本修改端口
sh a.sh
执行后,3个tomcat的端口号分别改为
8180,8280,8380
启动并验证这3个tomcat
# 分别向3个tomcat写入一个测试文件a.txt
for i in {1..3};do echo 8"$i"80>/opt/tomcat/tomcat"$i"/webapps/ROOT/a.txt;done
# 启动
for i in {1..3};do /opt/tomcat/tomcat"$i"/bin/startup.sh;done
# 验证
for i in {1..3};do curl 127.0.0.1:8"$i"80/a.txt;done

2.3 安装nginx
# 下载nginx-1.13.9.tar.gz
mkdir -p /opt/nginx/nginx-1.3.13.9
# 创建用户
useradd nginx -s /sbin/nologin -M
# 安装pcre openssl
yum install pcre pcre-devel openssl openssl-devel -y
# 解压、编译安装
tar -xzvf nginx-1.13.9.tar.gz
cd nginx-1.13.9
./configure --user=nginx --group=nginx --prefix=/opt/nginx/nginx-1.13.9 --with-http_stub_status_module --with-http_ssl_module
make
make install
# 创建链接
ln -s /opt/nginx/nginx-1.13.9 /usr/local/nginx
# 设置环境变量
echo 'export PATH=/usr/local/nginx/sbin:$PATH' >> /etc/profile
source /etc/profile
nginx常用命令
# 启动
nginx
# 检查配置文件
nginx -t
# 平滑启动
nginx -s reload
# 检查
netstat -tunlp | grep nginx
遇到的问题:
nginx: [emerg] getpwnam("nginx") failed
未创建nginx用户
配置nginx
/usr/local/nginx/conf/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
# http最外层模块
http {
	# 全局变量参数
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
	upstream web_pool {
		server 127.0.0.1:8180 weight=1;
		server 127.0.0.1:8280 weight=1;
		server 127.0.0.1:8380 weight=2;
	}
	# server相当于虚拟站点
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass  http://web_pool;
            index  index.html index.htm;
        }
    }
}
测试一下
for i in {1..9};do curl 127.0.0.1/a.txt;done

2.4 安装redis
wget http://download.redis.io/releases/redis-4.0.8.tar.gz
mkdir -p /opt/redis
tar -xzvf redis-4.0.8.tar.gz -C /opt/redis/
ln -s /opt/redis/redis-4.0.8 /usr/local/redis
cd /usr/local/redis
make && make install
# 配置让其他机器也可以访问
vi /usr/local/redis/redis.conf
#bind 127.0.0.1
bind 0.0.0.0
操作redis
# 查看redis版本
redis-cli -v
# 启动redis
redis-server redis.conf > /tmp/redis.log 2>&1 &
# or
nohup redis-server redis.conf &
# 监控redis
redis-cli -h 192.168.234.130 -p 6379 monitor
# 关闭redis
redis-cli -h 127.0.0.1 -p 6379 shutdown
客户端
# 连接
redis-cli -h 192.168.5.220
# 查看
keys *
# 清空
flushall
三、问题重现
3.1 web应用
只实现登录功能
/login   登录页面,已登录跳转到/index
/logout  退出
/index   首页,未登录跳转到/login
将这个应用部署到3个tomcat上
重启tomcat
for i in {1..3};do /opt/tomcat/tomcat"$i"/bin/shutdown.sh;done
for i in {1..3};do /opt/tomcat/tomcat"$i"/bin/startup.sh;done


3.2 问题描述
登录界面是8280端口,输入用户名密码点击登录,由于nginx配置的是基于轮询的算法进行分发,8380端口的服务器处理的登录,因此session创建于8380端口,而8280处于非登录状态。
四、解决实现
4.1 session sticky
修改nginx的分发算法改为基于ip
upstream web_pool {
	# 默认是轮询
	ip_hash;
	server 127.0.0.1:8180 weight=1;
	server 127.0.0.1:8280 weight=1;
	server 127.0.0.1:8380 weight=2;
}
修改后重启nginx
nginx -t
nginx -s reload
4.2 session replication
http://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html
tomcat配置server.xml
<!--修改每个tomcat下的conf/server.xml-->
<!--其中Receiver这个结点的端口分别配置为4001,4002,4003-->
<!--channelSendOptions-->
<!--8表示异步发送 2表示确认发送 4表示同步发送 10表示同步+确认-->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
	channelSendOptions="8">
	<Manager className="org.apache.catalina.ha.session.DeltaManager"
		expireSessionsOnShutdown="false"
		notifyListenersOnReplication="true" />
	<Channel className="org.apache.catalina.tribes.group.GroupChannel">
		<!-- 228.0.0.4是主播地址-->
		<Membership className="org.apache.catalina.tribes.membership.McastService"
			address="228.0.0.4"
			port="45564"
			frequency="500"
			dropTime="3000" />
		<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
			address="auto"
			port="4001"
			autoBind="100"
			selectorTimeout="5000"
			maxThreads="6" />
		<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
			<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
		</Sender>
		<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
		<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor" />
	</Channel>
	<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter="" />
	<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
	<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
		tempDir="/tmp/war-temp/"
		deployDir="/tmp/war-deploy/"
		watchDir="/tmp/war-listen/"
		watchEnabled="false" />
	<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
</Cluster>
tomcat的配置web.xml
# 修改每个tomcat下的conf/web.xml不起作用
# 一定要修改每个应用的web.xml,在<web-app>这个节点下添加
<distributable />
4.3 session数据集中存储
数据存储到redis,使用spring-data-redis
把web_spring-redis.xml重命名为web.xml,部署到tomcat即可,得先启动redis参照“安装redis”这一节。
spring-redis.xml
	<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 引入配置文件 -->
	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath*:redis.properties</value>
			</list>
		</property>
	</bean>
	<bean id="redisHttpSessionConfiguration"
		class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
		<property name="maxInactiveIntervalInSeconds" value="600"/>
	</bean>
	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxTotal" value="${redis.pool.maxTotal}" />
		<property name="maxIdle" value="${redis.pool.maxIdle}" />
	</bean>
	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
		destroy-method="destroy">
		<property name="hostName" value="${redis.hostname}" />
		<property name="port" value="${redis.port}" />
		<property name="timeout" value="${redis.timeout}" />
		<property name="usePool" value="true" />
		<property name="poolConfig" ref="jedisPoolConfig" />
	</bean>
</beans>
注意:
- Spring版本的选择 >=4.0.3
 
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.yysue</groupId>
	<artifactId>java-web-login</artifactId>
	<packaging>war</packaging>
	<name>java-web-login</name>
	<version>0.0.1-SNAPSHOT</version>
	<properties>
		<java.version>1.8</java.version>
		<spring.version>4.0.3.RELEASE</spring.version>
	</properties>
	<!-- 依赖 -->
	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.2</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.5.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
			<version>1.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.8.1</version>
		</dependency>
	</dependencies>
	<build>
		<!-- 插件 -->
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
												
											分布式下Session一致性架构举例的更多相关文章
- 分布式下Session一致性问题
		
一.Session一致性问题 1.1 什么是Session 用户使用网站的服务,基本上需要浏览器和web服务器进行多次交互,web服务器如何知道哪些请求是来自哪个会话的? 具体方式为:在会话开始时,分 ...
 - session一致性架构设计
		
什么是session? 由于HTTP协议是无状态的协议,因此它不会去记住上一次浏览器访问服务器时的信息.同一个用户的两次操作,与两个不同用户的操作,对它来说是一样的. 这样虽然满足了互联网web应用的 ...
 - session一致性架构设计实践.
		
一.缘起 什么是session? 服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文. Web开发中,web-server可以自动为同一个浏览器的访问用户自动创建se ...
 - nginx反向代理、负载均衡以及分布式下的session保持
		
[前言]部署服务器用到了nginx,相比较于apache并发能力更强,优点也比其多得多.虽然我的项目可能用不到这么多性能,还是部署一个流行的服务器吧! 此篇博文主要学习nginx(ingine x)的 ...
 - 分布式session一致性问题
		
1.分布式session一致性 :指服务器集群情况下session共享的问题. 2.session的作用:保存服务器(tomcat)与客户端(浏览器)整个通讯的会话基本信息. 3.session应用场 ...
 - 【Distributed】分布式Session一致性问题
		
一.概述 1.1 什么是Session 1.2 Session实现原理 1.3 Session常见问题 Session 保证在那里? 关闭浏览器Session会失效吗 服务器集群之后,Session产 ...
 - 分布式下的session处理方式
		
分布式下的session处理方式 现在的企业级别开发下,分布式的问题是随处可见.今天我们来看看分布式情况下session的处理. 目前的处理方式有以下几种: 1.session黏性.就是说,用户在访问 ...
 - 分布式系统session一致性的问题
		
session的概念 什么是session? 服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文.这样,当用户在应用程序的 Web 页之间跳转时,存储在 Sessio ...
 - 分布式系统中session一致性问题
		
业务场景 在单机系统中,用户登陆之后,服务端会保存用户的会话信息,只要用户不退出重新登陆,在一段时间内用户可以一直访问该网站,无需重复登陆.用户的信息存在服务端的 session 中,session中 ...
 
随机推荐
- mysql学习 | LeetCode数据库简单查询练习
			
力扣:https://leetcode-cn.com/ 力扣网数据库练习:https://leetcode-cn.com/problemset/database/ 文章目录 175. 组合两个表 题解 ...
 - 破解浏览器同源政策利器之JSONP
			
本文是在了解了浏览器的同源规则之后,学习了破解这个规则的一个简单有效的方法->JSONP.主要通过阮一峰老师的博客学习 浏览器的同源规则 有这样一个背景,如果你通过银行的网站进行的取钱的交易,而 ...
 - 基于Vue+Vuex+iView的电子商城网站
			
MALL-VUE 这是一个基于VUE + VUEX + iView做的一个电商网站前端项目, 附带前后端分离实现版本(在forMallServer分支),欢迎fork或star 项目地址: https ...
 - Hadoop伪分布式集群实现SSH免密登录
			
在启动Hadoop时候报了这样一个错误: Starting namenodes on [hadoop] hadoop: (Permission denied (publickey,gssapi-key ...
 - leetcode多线程题目
			
代码附上了力扣没显示出来的测试 按序打印 class Foo { private CountDownLatch latch = new CountDownLatch(1); private Count ...
 - EMS查看邮箱容量限制的方法
			
以Exchange管理员身份打开EMS控制台.在PowerShell命令行提示符下,输入以下命令. Get-MailboxDatabase | FL name,IssueWarningQuota,Pr ...
 - 微信小程序发送验证码功能,验证码倒计时
			
data{ timer:'', countDownNum:'发送验证码', } // 点击验证码倒计时获取验证码 Gain:function(e){ let that = this let count ...
 - 如何科学衡量广告投放效果?HMS Core分析服务助您科学归因
			
日益多元化的广告形式以及投放成本的不断攀升,让广告主们更加关注每一次广告投放带来的实际价值. 然而,广告主一般仅能从平台获得展示.点击.下载等前端效果字段,实际的用户注册.激活等后端深度转化指标并无法 ...
 - docker基础容器中bash: vi: command not found问题解决
			
安装即可 1.apt-get update 2.apt-get install vim
 - jquery 日期插件datePicker使用
			
1.将下载下来的DatePicker压缩包解压后整个放入项目中,不可只引入js和css 2.在html中指定input位置加上class="Wdate"(默认样式不加也可正常显示) ...