Redis与Tomcat集群集成

在网站访问量急剧上升时,通常需要使用集群的方法进行横向扩展。
对于没有状态的应用来说,直接用nginx进行处理即可。
但对于有状态的应用来说,比如登录状态等,除了使用nginx进行扩展外,就需要考虑到Session共享的问题了。

大家知道可以用apache+tomcat来实现Session共享,但效率太低了,而且容易出错。
今天说的主要是用nginx+tomcat+redis+tomcat-redis-session-manager的方式实现共享。
原理比较简单:

1、tomcat-redis-session-manage扩展了
org.apache.catalina.valves.ValveBase;
org.apache.catalina.session.ManagerBase;
org.apache.catalina.session.StandardSession;
并通过Tomcat配置,替代了这几个类。

2、Set属性时,用session id作为key,将Tomcat的整个Session拆分为SessionSerializationMetadata+RedisSession然后序列化为byte[],存放到Redis。

3、Get属性时,用session id作为key,从Redis获取byte[],然后反序列化为SessionSerializationMetadata+RedisSession,供Tomcat使用。

配置也很简单:
1、从github下载源码tomcat-redis-session-manager

2、用gradle进行编译

#master分支下面,要把signing段和uploadArchives段删掉,才能正常编译
#release就不需要了
gradle build

3、将三个Jar包拷贝到Tomcat的lib文件夹下

#%TOMCAT_HOME%/lib
tomcat-redis-session-manager-master-2.0.0.jar
commons-pool2-2.2.jar
jedis-2.5.2.jar

4、修改context.xml配置文件,新增下面内容就搞定咯

<!--%TOMCAT_HOME%/conf/context.xml-->
  <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
  <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" 
  host="localhost" port="6379" database="0" maxInactiveInterval="60"/>

好处是:不需要修改应用
坏处是:要耗费一定的时间来(序列化+保存到Redis)、(反序列化+从Redis读取)。

Memcached常用操作

*生产环境建议直接用linux

1、命令行直接运行
1.1、可以直接指定参数运行

#最大16M内存,监听11211端口,最大连接数8
memcached.exe -m 16 -p 11211 -c 8

1.2、可以注册为Windows服务,再运行

#注册为服务
memcached.exe -d install
#开启服务
memcached.exe -d start
#关闭服务
memcached.exe -d stop
#卸载服务
memcached.exe -d uninstall

Continue reading Memcached常用操作

Redis分片(Jedis)

Redis的分片技术一般是通过客户端或代理来实现的

1、用jedis实现分片的时候,服务端不需要做任何配置即可

package com.djhu.redis.test;

import java.util.ArrayList;
import java.util.List;

import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;

public class JedisShardTest
{
	public static void main(String[] args)
	{
		List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>();
		jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6379));
		jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6380));

		ShardedJedis sharded = new ShardedJedis(jedisShardInfoList);
		sharded.set("key01", "a");
		sharded.set("key02", "b");
		sharded.set("key03", "c");
		sharded.set("key04", "d");
		sharded.set("key05", "e");
		
		System.out.println(sharded.get("key03"));
	}
}

2、用Jedis连接池实现分片

package com.djhu.redis.test;

import java.util.ArrayList;
import java.util.List;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.util.Hashing;
import redis.clients.util.Sharded;

public class JedisSharedFactory
{
	// 最大可用连接数,默认值为8,如果赋值为-1则表示不限制
	private static int MAX_TOTAL = 256;
	// 最大空闲连接数,默认值为8
	private static int MAX_IDLE = 32;
	// 最小空闲连接数
	private static int MIN_IDLE = 4;
	// 最大等待连接毫秒数,默认值为-1表示永不超时
	private static int MAX_WAIT = 3000;
	// 连接redis超时时间
	private static int TIMEOUT = 3000;
	// true表示验证连接
	private static boolean TEST_ON_BORROW = true;

	//连接池
	private static ShardedJedisPool jedisPool = null;
	public static void initJedisPool()
	{
		try
		{
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(MAX_TOTAL);
			config.setMaxIdle(MAX_IDLE);
			config.setMinIdle(MIN_IDLE);
			config.setMaxWaitMillis(MAX_WAIT);
			config.setTestOnBorrow(TEST_ON_BORROW);
			
			List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>();
			jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6379));
			jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6380));
			jedisPool = new ShardedJedisPool(config, jedisShardInfoList,Hashing.MURMUR_HASH,Sharded.DEFAULT_KEY_TAG_PATTERN);
		} 
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public synchronized static ShardedJedis getConnection()
	{
		try
		{
			if (jedisPool != null)
			{
				ShardedJedis resource = jedisPool.getResource();
				return resource;
			} else
			{
				return null;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}

	public static void returnResource(final ShardedJedis jedis)
	{
		if (jedis != null)
		{
			jedis.close();
		}
	}
	
	public static void main(String[] args)
	{
		initJedisPool();
		ShardedJedis redis = getConnection();
		redis.set("key10", "j");
		redis.set("key11", "k");
		redis.set("key12", "l");
		redis.set("key13", "m");
		redis.set("key14", "n");
		
		System.out.print(redis.get("key12"));
		
		returnResource(redis);
	}
}

Jedis连接Redis3 Cluster

1、源码如下

package com.djhu.redis.test;

import java.util.Set;
import java.util.HashSet;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

public class JedisClusterTest
{
	public static void main(String[] args)
	{
		Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6379));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6380));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6381));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6382));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6383));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 7384));  
        
		//JedisCluster cluster = new JedisCluster(jedisClusterNodes,3000,1000);
        JedisCluster cluster = new JedisCluster(jedisClusterNodes);
		cluster.set("key10", "j");
		cluster.set("key11", "k");
		cluster.set("key12", "l");
		cluster.set("key13", "m");
		cluster.set("key14", "n");
		
		System.out.println(cluster.get("key12"));
		
	}
}

2、如果遇到下面错误,主要是因为建立cluster时,ip用了127.0.0.1。用其他ip重建一下cluster,就可以解决了。

Exception in thread "main" redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:34)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
	at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:29)
	at redis.clients.jedis.JedisCluster.set(JedisCluster.java:75)

Redis3配置Cluster

1、redis3的cluster是基于ruby的,所以要安装好ruby,然后安装redis的gem

gem install redis

2、然后配置6份redis,修改配置如下

节点
dbm6379 dbm6380 dbm6381 dbs6382 dbm6383 dbm6384
配置文件 dbm6379/conf/redis.conf dbm6380/conf/redis.conf dbm6381/conf/redis.conf dbs6382/conf/redis.conf dbm6383/conf/redis.conf dbm6384/conf/redis.conf
port 6379 6380 6381 6382 6383 6384
logfile “/home/neohope/DB/redis-3.0.4/cluster/dbm6379/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6380/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6381/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6382/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6383/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6384/logs/redis_log.log”
dir “/home/neohope/DB/redis-3.0.4/cluster/dbm6379/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6380/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6381/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6382/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6383/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6384/data”
cluster-enabled yes yes yes yes yes yes
cluster-config-file nodes-6379.conf nodes-6380.conf nodes-6381.conf nodes-6382.conf nodes-6383.conf nodes-6384.conf
cluster-node-timeout 15000 15000 15000 15000 15000 15000
cluster-migration-barrier 1 1 1 1 1 1
cluster-require-full-coverage yes yes yes yes yes yes

3、启动redis

#!/bin/sh
~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6379/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6380/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6381/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6382/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6383/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6384/conf/redis.conf & echo $!

4、配置cluster

#!/bin/sh
#这里最好不要用127.0.0.1做地址
~/DB/redis-3.0.4/bin/redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384      

5、测试redis cluster

~/DB/redis-3.0.4/bin/redis-cli -c -p 6379 
set key01 a
set key02 b
set key03 c
set key04 d
dbsize
keys *
get key03

6、关闭redis

#!/bin/sh
~/DB/redis-3.0.4/bin/redis-cli -p 6379 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6380 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6381 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6382 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6383 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6384 shutdown

参考:
redis cluster tutorial

Redis主从数据库(Shell)

1、基本配置如下

数据库 master slave01 slave02
配置文件 redis.master.conf redis.slave01.conf redis.slave02.conf
ip地址 localhost localhost localhost
端口 6379 6380 6381
logfile “D:/Database/Redis2.8/mirror/master/logs/redis_log.txt” “D:/Database/Redis2.8/mirror/slave01/logs/redis_log.txt” “D:/Database/Redis2.8/mirror/slave02/logs/redis_log.txt”
dir “D:/Database/Redis2.8/mirror/master/data/” “D:/Database/Redis2.8/mirror/slave01/data/” “D:/Database/Redis2.8/mirror/slave02/data/”
slaveof localhost 6379 localhost 6379
slave-serve-stale-data yes yes
slave-read-only yes yes
slave-priority 100 100
maxheap 1073741824 1073741824
heapdir D:\Database\Redis2.8\mirror\slave01\heap D:\Database\Redis2.8\mirror\slave02\heap

2、启动

redis-server.exe D:\Database\Redis2.8\mirror\redis.master.conf
redis-server.exe D:\Database\Redis2.8\mirror\redis.slave01.conf
redis-server.exe D:\Database\Redis2.8\mirror\redis.slave02.conf

3、测试

D:\Database\Redis2.8>redis-cli
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> set key01 a
OK
127.0.0.1:6379> exit

D:\Database\Redis2.8>redis-cli -p 6380
127.0.0.1:6380> get key01
"a"
127.0.0.1:6380> set key02 b
(error) READONLY You can't write against a read only slave.
127.0.0.1:6380> exit

D:\Database\Redis2.8>redis-cli -p 6381
127.0.0.1:6381> get key01
"a"
127.0.0.1:6381> set key02 b
(error) READONLY You can't write against a read only slave.
127.0.0.1:6381> exit

4、关闭

redis-cli -p 6380 shutdown
redis-cli -p 6381 shutdown
redis-cli -p 6379 shutdown

redis-server注册为服务(Shell)

1、注册服务

#loglevel 分为debug, notice, warning三级
redis-server.exe --service-install D:\Database\Redis2.8\db\redis.windows.conf --loglevel notice

2、启动服务

redis-server --service-start

3、停止服务

redis-server --service-stop

4、卸载服务

redis-server --service-uninstall

Jedis使用连接池(Java)

package com.djhu.redis.test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisFactory
{
	// 最大可用连接数,默认值为8,如果赋值为-1则表示不限制
	private static int MAX_TOTAL = 256;
	// 最大空闲连接数,默认值为8
	private static int MAX_IDLE = 32;
	// 最小空闲连接数
	private static int MIN_IDLE = 4;
	// 最大等待连接毫秒数,默认值为-1表示永不超时
	private static int MAX_WAIT = 3000;
	// 连接redis超时时间
	private static int TIMEOUT = 3000;
	// true表示验证连接
	private static boolean TEST_ON_BORROW = true;

	//连接池
	private static JedisPool jedisPool = null;
	public static void initJedisPool(String IP, int port, String password)
	{
		try
		{
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(MAX_TOTAL);
			config.setMaxIdle(MAX_IDLE);
			config.setMinIdle(MIN_IDLE);
			config.setMaxWaitMillis(MAX_WAIT);
			config.setTestOnBorrow(TEST_ON_BORROW);
			
			jedisPool = new JedisPool(config, IP, port, TIMEOUT, password);
		} 
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public synchronized static Jedis getConnection()
	{
		try
		{
			if (jedisPool != null)
			{
				Jedis resource = jedisPool.getResource();
				return resource;
			} else
			{
				return null;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}

	public static void returnResource(final Jedis jedis)
	{
		if (jedis != null)
		{
			jedis.close();
		}
	}
	
	public static void main(String[] args)
	{
		initJedisPool("localhost",6379,null);
		Jedis redis = getConnection();
		redis.select(1);
		//redis.set("key01", "a");
		//redis.set("key02", "b");
		//redis.set("key03", "c");
		System.out.print(redis.dbSize());
		
		returnResource(redis);
	}
}