实现功能描述:
redis服务器进行Master-slaver-slaver-....主从配置,通过2台sentinel进行failOver故障转移,自动切换,采用该代码完全可以直接用于实际生产环境。
题外话:
一般来说这样的部署足以支持数以百万级的用户,但如果数量实在是太高,此时redis的Master-Slaver主从不一定能够满足,因此进行redis的分片。
本文不讲解redis的分片,但如果你使用了,需要注意的按照另一篇文章的介绍:Sentinel&Jedis看上去是个完美的解决方案,这句话只说对了一半,
在无分片的情况是这样,但我们的应用使用了数据分片-sharing,数据被平均分布到4个不同的实例上,每个实例以主从结构部署,Jedis没有提供
基于Sentinel的ShardedJedisPool,也就是说在4个分片中,如果其中一个分片发生主从切换,应用所使用的ShardedJedisPool无法获得通知,所有
对那个分片的操作将会失败。文章中提出了解决方案,请参考《基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案》
该代码模拟多线程向redis中set/get。
1、maven依赖配置
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.4.1.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.6.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency>
2、redis.properties
# Redis settings #sentinel1的IP和端口 im.hs.server.redis.sentinel1.host=192.168.62.154 im.hs.server.redis.sentinel1.port=26379 #sentinel2的IP和端口 im.hs.server.redis.sentinel2.host=192.168.62.153 im.hs.server.redis.sentinel2.port=26379 #sentinel的鉴权密码 im.hs.server.redis.sentinel.masterName=155Master im.hs.server.redis.sentinel.password=hezhixiong #最大闲置连接数 im.hs.server.redis.maxIdle=500 #最大连接数,超过此连接时操作redis会报错 im.hs.server.redis.maxTotal=5000 im.hs.server.redis.maxWaitTime=1000 im.hs.server.redis.testOnBorrow=true #最小闲置连接数,spring启动的时候自动建立该数目的连接供应用程序使用,不够的时候会申请。 im.hs.server.redis.minIdle=300
3、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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!-- Spring自动将该包目录下标记为@Service的所有类作为spring的Bean --> <context:component-scan base-package="com.gaojiasoft.test.redis" /> <context:property-placeholder location="classpath:conf/redis/redis.properties" /> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="${im.hs.server.redis.maxTotal}" /> <property name="minIdle" value="${im.hs.server.redis.minIdle}" /> <property name="maxWaitMillis" value="${im.hs.server.redis.maxWaitTime}" /> <property name="maxIdle" value="${im.hs.server.redis.maxIdle}" /> <property name="testOnBorrow" value="${im.hs.server.redis.testOnBorrow}" /> <property name="testOnReturn" value="true" /> <property name="testWhileIdle" value="true" /> </bean> <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration"> <property name="master"> <bean class="org.springframework.data.redis.connection.RedisNode"> <property name="name" value="${im.hs.server.redis.sentinel.masterName}"></property> </bean> </property> <property name="sentinels"> <set> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${im.hs.server.redis.sentinel1.host}"></constructor-arg> <constructor-arg name="port" value="${im.hs.server.redis.sentinel1.port}"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${im.hs.server.redis.sentinel2.host}"></constructor-arg> <constructor-arg name="port" value="${im.hs.server.redis.sentinel2.port}"></constructor-arg> </bean> </set> </property> </bean> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:password="${im.hs.server.redis.sentinel.password}"> <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg> <constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="connectionFactory" /> </bean> </beans><strong> </strong>
4、RedisServiceImpl.java
package com.gaojiasoft.test.redis; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service("redisService") public class RedisServiceImpl { private Logger logger = LoggerFactory.getLogger("RedisServiceImpl"); @Autowired RedisTemplate<?, ?> redisTemplate; // 线程池 private static final ThreadPoolExecutor executor = new ThreadPoolExecutor( 256, 256, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new BasicThreadFactory.Builder().daemon(true) .namingPattern("redis-oper-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy()); public void set(final String key, final String value) { redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { connection.set( redisTemplate.getStringSerializer().serialize(key), redisTemplate.getStringSerializer().serialize(value)); logger.debug("save key:" + key + ",value:" + value); return null; } }); } public String get(final String key) { return redisTemplate.execute(new RedisCallback<String>() { @Override public String doInRedis(RedisConnection connection) throws DataAccessException { byte[] byteKye = redisTemplate.getStringSerializer().serialize( key); if (connection.exists(byteKye)) { byte[] byteValue = connection.get(byteKye); String value = redisTemplate.getStringSerializer() .deserialize(byteValue); logger.debug("get key:" + key + ",value:" + value); return value; } logger.error("valus does not exist!,key:"+key); return null; } }); } public void delete(final String key) { redisTemplate.execute(new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) { connection.del(redisTemplate.getStringSerializer().serialize( key)); return null; } }); } /** * 线程池并发操作redis * * @param keyvalue */ public void mulitThreadSaveAndFind(final String keyvalue) { executor.execute(new Runnable() { @Override public void run() { try { set(keyvalue, keyvalue); get(keyvalue); } catch (Throwable th) { // 防御性容错,避免高并发下的一些问题 logger.error("", th); } } }); } }
5、RedisTest.java (Junit测试用例)
package com.gaojiasoft.test.redis; import org.junit.Test; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class RedisTest { private static ConfigurableApplicationContext context; RedisServiceImpl service; @Test public void testSave() throws InterruptedException { context = new ClassPathXmlApplicationContext( "classpath:conf/redis/spring-redis.xml"); service = (RedisServiceImpl) context.getBean("redisService"); int i = 1; while (true) { Thread.sleep(1); try { service.mulitThreadSaveAndFind("" + i); } catch (Exception e) { e.printStackTrace(); } i++; } } }
版权声明:本文为博主原创文章,未经博主允许不得转载。