本文的环境:
1.CentOs6.4 minimal
2.jdk1.7.0_71
3.Tomcat7.0.65
4.Nginx1.8
5.Redis3.0.5
6.vm11
环境配置不多说了,假设环境都已经装好。我虚拟机的ip地址是192.168.233.129 后面就不再解释了,直接用。
修改nginx配置信息 cd /usr/local/nginx/conf
vim nginx.conf
配置负载均衡 ,增加下面的配置
upstream localhost {
server localhost:8080;
server localhost:8081;
}
server的location 中增加
proxy_pass http://localhost;
ok 重启下nginx cd /usr/local/nginx/sbin
./nginx -s reload
接下来访问http://192.168.233.129
ok 能够正常访问,显示了tomcat默认页,表示配置成功。
nginx 负载均衡默认是用轮询的方式,为了能够看到每次请求都被转发到了不同的tomcat上的效果,我们分别在webapps下ROOT下增加test.jsp 简单的一句话来表示这是tomcat1还是tomcat2。
ok 增加好之后再访问http://192.168.233.129/test.jsp 就显示tomcat1或者tomcat2,刷新页面,就会交替显示tomcat1 tomcat2.
ok 如果能够交替显示表示负载均衡配置成功了。
ok 接下来就要解决session的问题了,打开F12看cookie,发现每次刷新都是新的sessionid,这样肯定是不行的,解决集群环境下的session共享有很多实现的方式,可以通过在upstream下配置 ip_hash;来根据ip指定到固定一台tomcat服务器来解决session的问题。配置完后之后再重启nginx 再刷新页面发现sessionid不变了,无论怎么刷新sessionid都不会变了,而且页面只显示tomcat1,说明现在是固定的访问tomcat1服务器了,而不是轮询的方式。
ok 到这里为止,我都非常的顺利,几乎没什么问题,但在配置redis来管理session的时候,问题就来了,而且还是相当的蛋疼,花了一天的时间终于解决了,也是我写这篇的主要原因。
ok 接下来我们要实现的是用轮询的方式实现session共享,本文用的方式是用redis来集中管理session,当一个请求过来,tomcat是去redis里找这个sessionid是否存在。
首先先把tomcat的依赖包分别拷到tomcat/lib目录下,我一开始用的包是
1.tomcat-redis-session-manager-1.2-tomcat-7.jar
2.jedis-2.1.0.jar
3.commons-pool-1.6.jar
然后分别修改tomcat下的context.xml配置文件,增加如下配置
<Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve" />
<Manager className="com.radiadesign.catalina.session.RedisSessionManager"
host="localhost"
port="6379"
database="0"
maxInactiveInterval="60" />
ok,配置好了,但是在启动tomcat的时候报错了,错误信息如下:
caused by: org.apache.catalina.LifecycleException: Unable to attach to session handling valve; sessions cannot be saved after the request without the valve starting properly.
借助有道词典看的懂错误的大概意思,但是不知道怎么解决,不用怕,把错误信息复制一下到网上查一下,肯定能解决,因为毕竟我又不是第一个人配置这玩意,别人肯定也会碰到这问题。一点都不用慌,嗯,抱着这样的心态上网查了半天,结果居然没有一个人碰到这问题,我简直日了狗了。
于是看看别人的博客看别人是怎么做的,几乎所有的博客都说要去tomcat-redis-session-manager库的主页
https://github.com/jcoleman/tomcat-redis-session-manager
下载源码,修改build.gradle的依赖包版本,改成你自己的版本,然后重新构建项目,用构建生成的jar包。
不就是要编译打包嘛,为什么要弄的这么麻烦,而且也没用过gradle来构建项目,所以我是直接把源码拷到eclipse,注意这个项目有很多分支和tags,有些比较早的版本是依赖common-pool的jar包,我是下的master的分支,所以加的是common-pool2的jar包,要不然会报找不到类的错误,把依赖包加进来,通过export jar文件,来生成jar包。到这里,我的jar包变成了下面的样子
1.common-pool2-2.4.2.jar
2.jedis-2.5.1.jar
3.tomcat-redis-session-manager-1.2-mine.jar(自己通过源码编译生成的)
ok,把这3个jar包去替换原来的3个jar,然后还要修改一下context.xml配置文件,否则会提示找不到类,因为master分支的包名变了,修改后的context.xml文件如下
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="localhost"
port="6379"
database="0"
maxInactiveInterval="60" />
重启tomcat,嗯,感觉这次肯定没问题了。
结果,还是报同样的错,我简直日了狗了,难道真的要用gradle来构建项目才行?这不科学啊,不就是编译几个java类打成jar包吗,改不改build.gradle依赖包版本应该没有半毛钱关系,又不会影响源代码。但是呢,实在没有办法了,还是决定试一下吧。我想gradle应该类似maven应该很简单吧,于是下了个gradle-2.8。通过修改build.gradle的依赖版本,通过 gradle bulid的命令来构建项目,这里也有一些坑,只改依赖版本,根本构建不成功,不过构建过程碰到的问题,网上都找到了
一个是需要把maven私服给注释了,因为没帐号密码,直接让它去maven中央仓库找,或者你可以配开源中国的,速度快点,不过也就才几个包,无所谓拉,懒的搞,另外一个是需要把 signing那一段注释掉。修改后build.gradle文件如下
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'signing'
group = 'com.orangefunction'
version = '2.0.0'
repositories {
mavenCentral()
}
compileJava {
sourceCompatibility = 1.7
targetCompatibility = 1.7
}
dependencies {
compile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '7.0.65'
compile group: 'redis.clients', name: 'jedis', version: '2.5.1'
compile group: 'org.apache.commons', name: 'commons-pool2', version: '2.4.2'
//compile group: 'commons-codec', name: 'commons-codec', version: '1.9'
testCompile group: 'junit', name: 'junit', version: '4.+'
testCompile 'org.hamcrest:hamcrest-core:1.3'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.mockito:mockito-all:1.9.5'
testCompile group: 'org.apache.tomcat', name: 'tomcat-coyote', version: '7.0.65'
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from 'build/docs/javadoc'
}
task sourcesJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}
artifacts {
archives jar
archives javadocJar
archives sourcesJar
}
//signing {
// sign configurations.archives
//}
task copyJars(type: Copy) {
from configurations.runtime
into 'dist'
}
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
//repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
// authentication(userName: sonatypeUsername, password: sonatypePassword)
//}
//repository(url: "https://oss.sonatype.org/content/repositories/snapshots") {
// authentication(userName: sonatypeUsername, password: sonatypePassword)
//}
pom.project {
name 'tomcat-redis-session-manager'
packaging 'jar'
description 'Tomcat Redis Session Manager is a Tomcat extension to store sessions in Redis'
url 'https://github.com/jcoleman/tomcat-redis-session-manager'
issueManagement {
url 'https://github.com:jcoleman/tomcat-redis-session-manager/issues'
system 'GitHub Issues'
}
scm {
url 'https://github.com:jcoleman/tomcat-redis-session-manager'
connection 'scm:git:git://github.com/jcoleman/tomcat-redis-session-manager.git'
developerConnection 'scm:git:git@github.com:jcoleman/tomcat-redis-session-manager.git'
}
licenses {
license {
name 'MIT'
url 'http://opensource.org/licenses/MIT'
distribution 'repo'
}
}
developers {
developer {
id 'jcoleman'
name 'James Coleman'
email 'jtc331@gmail.com'
url 'https://github.com/jcoleman'
}
}
}
}
}
}
其实根本不需要改依赖版本,直接用默认的就可以了,原因我上面说过了。
ok,我们再用gradle build 来构建项目,这次终于成功了,看构建日志会发现,他会去下载依赖包,所以如果用gradle来构建的话,就不需要再另外去下载jar包了,,gradle已经帮你下载了,跟maven类似,也有个本地仓库,在用户目录下\.gradle\caches\modules-2\files-2.1,只要把这jar包拷过去就可以了。
ok 这下总可以了吧,把构建生成的jar包再替换tomcat中的。然后再重启
结果你猜怎么着,没错,还是报同样的错误,我简直又日了狗了,简直奔溃,因为网上根本搜不到这个问题的解决方案
Stack Overflow上也去查了。
试了各种的方法,还去专门深入了解了一下tomcat的原理,都没有什么卵用,好吧,只能根据错误提示,去源码里查原因了,找到出错的那一行,
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
setState(LifecycleState.STARTING);
Boolean attachedToValve = false;
for (Valve valve : getContainer().getPipeline().getValves()) {
if (valve instanceof RedisSessionHandlerValve) {
this.handlerValve = (RedisSessionHandlerValve) valve;
this.handlerValve.setRedisSessionManager(this);
log.info("Attached to RedisSessionHandlerValve");
attachedToValve = true;
break;
}
}
if (!attachedToValve) {
String error = "Unable to attach to session handling valve; sessions cannot be saved after the request without the valve starting properly.";
log.fatal(error);
throw new LifecycleException(error);
}
try {
initializeSerializer();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
log.fatal("Unable to load serializer", e);
throw new LifecycleException(e);
}
log.info("Will expire sessions after " + getMaxInactiveInterval() + " seconds");
initializeDatabaseConnection();
setDistributable(true);
}
看抛出的异常说明attachedToValve是false,说明if (valve instanceof RedisSessionHandlerValve) 这句是false。
ok,为了能够看清这个valve到底是什么东西,于是在这句之前加个日志打印一下看是什么,这里写日志的时候一开始把valve写成value了,我靠,我一直以为是value,长的太他妈像了。
for (Valve valve : getContainer().getPipeline().getValves()) {
log.info("valve======================>" + valve.getClass().getName());
if (valve instanceof RedisSessionHandlerValve) {
this.handlerValve = (RedisSessionHandlerValve) valve;
this.handlerValve.setRedisSessionManager(this);
log.info("Attached to RedisSessionHandlerValve");
attachedToValve = true;
break;
}
}
ok,改完之后,直接用eclipse的export 重新打成jar包来替换tomcat中的,这时候可以看到刚刚添加的日志
INFO: valve======================>org.apache.catalina.core.StandardContextValve
ok,valve 果然不是RedisSessionHandlerValve,看来valve没取到值,是不是在context.xml中配置有问题?
于是又去检查了一遍,发现RedisSessionHandlerValve类名没有写错,于是又去看了一下打印的日志。
这一次看,有惊天大突破,平时waring的日志啊什么的肯定都是不看的,但是这次发现这个waring有点特别
WARNING: No rules found matching 'Context/Value'.
总感觉报错跟这个有关,警告说没有匹配的规则,那应该还是配置文件的问题了,到这里的时候突然想到写日志的时候把valve写成value了,于是去官网确认了一下,到底是value还是valve。
好吧,果然是写错了,简直崩溃,看来下次配置文件应该用复制的,而不是自己手打。把value改成valve之后,再重启tomcat,ok,终于不报错了。把2台tomcat跑起来,测试下redis管理session的效果。
可以看到刷新页面,tomcat1和tomcat2交替显示,并且sessionid不变。
然后去redis 用命令看一下,keys * ,发现存了一个session,ok,到此Nginx负载均衡+Tomcat集群+Redis管理session终于配置成功。
既然是配置文件写错了,那我用之前的3个jar可以吗,于是又把最开始的3个jar包来替换tomcat中的,记得还要改下context.xml配置,因为包名改了,之前说过的。测试了之后发现一点问题也没有,完全可以用最开始的那3个jar包。
最后来个非常非常重要的总结,你前面可以都不看,但是总结你应该看看,因为很重要,所以字体要大点。
总结:用Tomcat通过redis来管理session所需要用到的依赖包tomcat-redis-session-manager.jar,完全没有必要自己去下载源码,然后修改build.gradle的依赖包的版本,再重新构建。真的,我不知道为什么博客几乎所有有关redis管理session的文章都说要自己重新构建,真的没必要。还有的人说如果用jedis-2.1.jar则要用commons-pool jar包,如果要jedis-2.5以上则要用commons-pool2的jar包,真的完全错误。我不知道他们自己有没有测试过。这里说下什么时候用commons-pool,什么时候用commons-pool2,因为commos-pool2的包名发生了变化,所以版本导不对会报找不到类异常。
你可以直接去github里下载已经编译好的jar包,比如tomcat-redis-session-manager-1.2-tomcat-7.jar,他里面依赖的是commons-pool版本的jar包,如果你想用commons-pool2,你可以把master分支源码拉下来自己打jar包,master里依赖的commons-pool2,看RedisSessionManager这个类也知道,
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
他导的是commons-pool2。
所以,最终你可以用我一开始用的3个jar包,也可以用我后来的3个jar包,我都测试过,都没问题。
ok,现在我把这两组jar包都上传,你们可以直接下载我的。
commons-pool2-2.4.2版本之所以比上面那个大那么多是因为,tomcat-redis-session-manager-1.2-mine.jar这个包把依赖包也打进来了,所以,应该只要拷这个到tomcat下就可以了,我没试过,有兴趣的可以试一下。
- 大小: 18.7 KB
- 大小: 19.1 KB
- 大小: 31.5 KB
分享到:
相关推荐
2. **添加session存储库**:在Tomcat服务器中,需要添加一个session存储库,通常是一个Java的HttpSessionListener,以便在session创建或销毁时与Redis进行交互。你可以使用第三方库如`spring-session-data-redis`来...
- **session共享**:在分布式环境中,Redis可以作为session存储,解决跨服务器的session共享问题,确保用户会话的一致性。 - **消息队列**:Redis支持发布/订阅模型,可以作为简单消息队列,用于异步处理任务或...
同时,需要配置session存储路径为redis的地址。 3. **添加共享session的jar文件**:压缩包中的"tomcat6+redis做session共享亲测可用"文件可能包含了一个实现了session共享功能的jar文件,需要将其添加到Tomcat的lib...
我们将配置`Tomcat`使用`Redis`作为session存储,这样每当session数据发生变化时,都会自动同步到`Redis`中。 实现session共享的关键步骤如下: 1. **安装与配置**:首先,我们需要在服务器上安装`Nginx`、`Tomcat...
为了解决这个问题,我们可以使用Redis作为session存储。 Redis是一个高性能的键值存储系统,它可以快速读写大量数据,特别适合存储session这类小数据量但频繁读写的场景。通过配置Nginx和后端应用(如Tomcat),...
将session存储在Redis中,各个Tomcat实例可以共享这些数据。为了实现这一目标,可以使用一个名为`Jedis`的Java客户端库与Redis进行交互。在Tomcat中,可以通过实现`HttpSession`接口的自定义session管理器,或者使用...
"redis缓存服务器Nginx+Tomcat+redis+MySQL实现session会话共享"的主题旨在探讨如何利用这些技术组件来实现这一目标。以下是相关知识点的详细说明: **Redis**:Redis是一个高性能的键值数据存储系统,常用于做缓存...
使用Redis作为session存储,可以避免单个服务器上session数据丢失,同时使得session数据在多台服务器间共享,这对于负载均衡环境至关重要。通过将session ID作为键,session数据作为值存储在Redis中,Nginx可以在...
本案例通过搭建一个基于`Keepalived`、`nginx`、`Tomcat`及`Redis`的服务集群来实现应用服务的高可用性和负载均衡,并通过`Redis`实现了`Tomcat`间的`Session`共享功能。 ### 一、环境简介 - **服务器配置**:两台...
超详细Nginx+Tomcat+Redis搭建高性能负载均衡集群Session共享搭建说明,按文档步骤可轻松搭建并实现session共享
标题 "Tomcat7+Redis+Session 负载之后session 共享 tomcat jar包" 涉及的是在使用Nginx做负载均衡时,如何通过集成Redis来实现Tomcat7服务器之间的Session共享,从而确保用户在不同服务器之间切换时仍然能够保持...
nginx+tomcat实现负载均衡,共享session的两种方式: 1.使用Redis共享session 2.使用tomcat的组播功能。
Nginx+Tomcat+Redis实现应用服务器集群负载均衡和Session共享所需要的所有jar包,包括如下jar包:commons-logging-1.2.jar,commons-pool2-2.6.0.jar,jedis-2.9.0.jar,tomcat-juli-7.0.90.jar,tomcat-redis-...
标题 "nginx+tomcat7+jdk1.7+redis--jar.rar" 提示我们这是一个关于在Web服务器架构中集成Nginx、Tomcat7、Java 1.7和Redis的解决方案,特别是涉及到使用Redis作为Session缓存的配置。描述中强调了在下载和使用这些...
在Tomcat的`context.xml`或`server.xml`中配置`Manager`元素,使用`org.apache.catalina.session.JDBCStore`或`org.apache.catalina.session.redis.RedisStore`(如果使用的是Redis作为session存储)。 3. **引入...
这里我们选择使用REDIS,一个开源、高性能的键值数据存储系统,可以作为共享session存储。通过在TOMCAT中配置session复制或者将session持久化到REDIS,可以确保用户的session会话在所有服务器间保持一致。具体步骤...
nginx2.8.1+tomcat7+redis1.8 session共享,windows环境下,jar包都包含,直接运行即可,把redis服务,tomcat两个,niginx负载平衡,session共享,参考别人的例子,自己总结的。
标题中的"nginx+tomcat8.+redis__session__单点登录、会话共享"涉及到的是在分布式系统中如何实现用户会话(Session)的共享和单点登录(Single Sign-On, SSO)的问题。这里主要涉及到三个核心组件:Nginx、Tomcat和...