最近在研究nginx+tomcat的负载均衡功能, 因为需要实现failover时用户无感知的效果,所以我考虑使用tomcat的session同步方式来实现。网上能查到的东西我就直接贴链接了,我把搭建这套系统的过程,与遇到的坑的处理方式说明一下。
我使用的系统环境
Centos6.5 和 Centos7.2
Tomat7.0.29
JDK1.8.0_111-b14
Redis 3.2.6
一. 实现nginx+tomcat的负载均衡
用nginx+tomcat实现负载均衡的方法,只需要在nginx的配置文件中配置相应的upstream设置需要负载均衡的服务器列表就可以了。
怎么配置可以参考这个,这个写的比较全面而且不坑
稍微说一下可能会坑的地方
在http节点下,添加upstream节点。
upstream linuxidc {
server 10.0.6.108:7080;
server 10.0.0.85:8980; }将server节点下的location节点中的proxy_pass配置为:http:// + upstream名称,即“ http://linuxidc”.
location / {
root html;
index index.html index.htm;
proxy_pass http://linuxidc; }
注意一下upstream的名称和在proxy_pass里引用的方法
这一步做完以后,可以试验一下在两台机器上部署同样的项目,通过nginx的负载均衡的机制,刷新页面可以看到被指向到了不同的服务器上。
测试failover的话,在nginx的upstream里
upstream backend {
server 192.168.198.128:8080 weight=1;
server 192.168.198.128:8090 weight=4;
server 192.168.198.128:8091 backup;
}
backup : marks the server as a backup server. It will be passed requests when the primary servers are unavailable.(标记为备用服务器。当主服务器不可用以后,请求会被传给这些服务器。)
当然了,比如说你就两台机器,那么把这两台服务器既配置成主服务器,又配置成backup服务器就可以互为failover了。
这个时候我遇到的一个问题:
我的项目第一个页面是login.html 然后登录成功后会进入到index.html ,发现一直登录不进去。查了日志才发现login.html进的是服务器A, 结果到index.html的时候给我指到服务器B去了,那时候又没弄session同步,自然系统认为你没登录,然后就login页面无限循环了。
可以通过ip_hash保证同一个访问的ip一直会指向到特定的某一个服务器上,除非这个服务器当了,才会切换到另外的服务器。
配置的方式
upstream myCluster {
server 127.0.0.1:7085;
server 192.168.1.234:6085;
server 127.0.0.1:7085 backup;
server 192.168.1.234:6085 backup;
ip_hash;
}
在最后加上ip_hash就可以
二. 实现session同步的方案的选择
做完第一步以后,你可以实现nginx+tomcat的负载均衡了,并且可以failover,但是因为两台机器的tomcat并没有共享session,所以虽然failover自动切换到正常运行的服务器了,但是用户仍然被踢回到了登录页面,如果写了一堆东西没提交的话,肯定要掀桌子了。
那么我们就要考虑一下session同步的方案的选取
memcached+ tomcat 实现session同步
redis+tomcat 实现session同步
terracotta + tomcat 实现session同步
因为我们用的是redis集群,所以就把memcached方案pass掉了,有兴趣的可以参考链接里的博文部署一下。
一开始其实很想使用Terracotta的方案
Terracotta是一款由美国Terracotta公司开发的著名开源Java集群平台。它在JVM与Java应用之间实现了一个专门处理集群功能的抽象层,以其特有的增量检测、智能定向传送、分布式协作、服务器镜像、分片等技术,允许用户在不改变现有系统代码的情况下实现单机Java应用向集群化应用的无缝迁移。使得用户可以专注于商业逻辑的开发,由Terracotta负责实现高性能、高可用性、高稳定性的企业级Java集群。
看这个介绍觉得一股子高大上的感觉,使用的是3.7.7版本的Terracotta服务,但是只支持到tomcat7并且不支持JAVA8,据说是3.7.10以后才支持JAVA8,但是一直找不到哪儿可以下载,有个4.3.3版本的服务但是配置方法和3.x的完全不一样,结果折腾了半天也没搞定,后来觉得还是回来使用redis的方案吧。
三. nginx+tomcat+redis(非集群) 实现session同步的方案
方案就是使用redis-session-manager
GITHUB地址
照着github上说的配置一下就可以
1 . 找到相应的jar包copy到tomcat的lib下面
- commons-pool2-2.2.jar
- jedis-2.5.2.jar
- tomcat-redis-session-manager-XXX.jar
2 . 修改tomcat的conf里的context.xml文件
加上
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="localhost" -- optional: defaults to "localhost" -->
port="6379"
database="0"
maxInactiveInterval="60"
sessionPersistPolicies="PERSIST_POLICY_1,PERSIST_POLICY_2,.."
sentinelMaster="SentinelMasterName"
sentinels="sentinel-host-1:port,sentinel-host-2:port,.." />
3 . 启动tomcat盯一下log
可能会遇到很多问题,比如:
(1) xxx类没找到, 那就是扔到lib下的jar包和你在context.xml配置的RedisSessionManager路径跟jar包里的实际路径不一致。网上找到的包有很多二次开发版的,反编译一下看看是不是这里的问题
(2) 有可能会提示一个 commons-pool的问题,java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool$Config
网上查到的解决方案是把1.6版本的 commons-pool也放到lib下就可以了
(3)作者的官网上说暂时不支持Tomcat8,所以如果想要用tomcat8运行的话,可以参考这篇文章
tomcat8+redis实现session同步
(4)如果你连接的redis是集群的话,那么会出现:
redis.clients.jedis.exceptions.JedisDataException: MOVED 13102 127.0.0.1
MOVED indicates that you’re using Redis Cluster. ShardedJedis is not for Redis Cluster, so you should use JedisCluster instead. Please note that JedisCluster doesn’t have pipeline mode, so you may want to send your operation one by one.
就是ShardedJedis 不能用于Redis集群,要用JedisCluster 代替ShardedJedis 才可以,而且JedisCluster 还没有pipeline 模式,所以一次只能执行一个操作
那么分布式系统想连接redis集群的话怎么办呢?
四. nginx+tomcat+redis集群 实现session同步的方案
1 . 搭建redis-cluster
搭建redis-cluster的方法在网上可以搜到很多,在此就不多说(主要是懒)
搭建redis-cluster的步骤主要根据这篇博客来,写的很好
其中遇到的问题:
(1) 在编译redis的时候出现redis出现问题:
zmalloc.h:50:31: 错误:jemalloc/jemalloc.h:没有那个文件或目录
解决方法
make MALLOC=libc
(2)在执行redis-cli或者redis-trib时候提示
version `GLIBC_2.14’ not found (required by ./redis-cli)
问题原因:
glibc的版本太低了,
解决方法:
升级glibc
2 . redis集群+tomcat解决方案
主要参考下文的方法
redis集群+tomcat解决方案
升级Jedis ,换个专用的redis-session-manager包就可以了
使用到的jar包
http://download.csdn.net/detail/hxsyewlink/9744598
实际使用的时候还是会有很多问题
(1) tomcat报了个错
org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException
据说是ecj版本的问题,升级到4.2.2以后解决,把新的包扔到tomcat的lib下面去
(2)按照文章里的配置,调用本地的redis-cluster没有问题,但是调用远程服务器的redis-cluster就提示
Exception in thread “main” redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
这个就是redis服务器就没连上导致的问题,查询得知
创建redis集群的时候使用的命令是:./redis-trib.rb create –replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
所以重新构建集群使用:./redis-trib.rb create –replicas 1 192.168.0.104:7001 192.168.0.104:7002 192.168.0.104:7003 192.168.0.104:7004 192.168.0.104:7005 192.168.0.104:7006
重构集群的时候也会有问题:
(1) redis集群报错Node is not empty
参考这个文章
折腾到现在,nginx+tomcat+redis集群的方案就算是可以运行了。把技术调研的过程记录了一下,也是绕了很多弯路,希望可以帮助到有需要的人们~