分布式下Session一致性架构举例

一、问题及方案

见这篇文章:分布式下Session一致性问题

二、分布式环境搭建:

image

系统环境

[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个端口号

image

用如下脚本修改: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
image

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
image

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应用

java-web-login

只实现登录功能

/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
image
image

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

见应用java-web-login

把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>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容