Redis主从实现读写分离
前言
大家在工作中可能会遇到这样的需求,即Redis读写分离,目的是为了压力分散化。下面我将为大家介绍借助AWS的ELB实现读写分离,以写主读从为例。
实现
引用库文件
<!--redis客户端--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.6.2</version> </dependency>
方式一,借助切面
JedisPoolSelector
此类的目的是为读和写分别配置不同的注解,用来区分是主还是从。
packagecom.silence.spring.redis.readwriteseparation;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/**
*Createdbykeysilenceon16/10/26.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceJedisPoolSelector{
Stringvalue();
}
JedisPoolAspect
此类的目的是针对主和从的注解,进行动态链接池调配,即主的使用主链接池,从的使用从连接池。
packagecom.silence.spring.redis.readwriteseparation;
importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.springframework.beans.BeansException;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.ApplicationContextAware;
importredis.clients.jedis.JedisPool;
importjavax.annotation.PostConstruct;
importjava.lang.reflect.Method;
importjava.util.Date;
/**
*Createdbykeysilenceon16/10/26.
*/
@Aspect
publicclassJedisPoolAspectimplementsApplicationContextAware{
privateApplicationContextctx;
@PostConstruct
publicvoidinit(){
System.out.println("jedispoolaspectjstarted@"+newDate());
}
@Pointcut("execution(*com.silence.spring.redis.readwriteseparation.util.*.*(..))")
privatevoidallMethod(){
}
@Before("allMethod()")
publicvoidbefore(JoinPointpoint)
{
Objecttarget=point.getTarget();
Stringmethod=point.getSignature().getName();
Classclassz=target.getClass();
Class<?>[]parameterTypes=((MethodSignature)point.getSignature())
.getMethod().getParameterTypes();
try{
Methodm=classz.getMethod(method,parameterTypes);
if(m!=null&&m.isAnnotationPresent(JedisPoolSelector.class)){
JedisPoolSelectordata=m
.getAnnotation(JedisPoolSelector.class);
JedisPooljedisPool=(JedisPool)ctx.getBean(data.value());
DynamicJedisPoolHolder.putJedisPool(jedisPool);
}
}catch(Exceptione){
e.printStackTrace();
}
}
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
this.ctx=applicationContext;
}
}
DynamicJedisPoolHolder
此类目的是存储当前使用的JedisPool,即上面类赋值后的结果保存。
packagecom.silence.spring.redis.readwriteseparation;
importredis.clients.jedis.JedisPool;
/**
*Createdbykeysilenceon16/10/26.
*/
publicclassDynamicJedisPoolHolder{
publicstaticfinalThreadLocal<JedisPool>holder=newThreadLocal<JedisPool>();
publicstaticvoidputJedisPool(JedisPooljedisPool){
holder.set(jedisPool);
}
publicstaticJedisPoolgetJedisPool(){
returnholder.get();
}
}
RedisUtils
此类目的是对Redis具体的调用,里面包含使用主还是从的方式调用。
packagecom.silence.spring.redis.readwriteseparation.util;
importcom.silence.spring.redis.readwriteseparation.DynamicJedisPoolHolder;
importcom.silence.spring.redis.readwriteseparation.JedisPoolSelector;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
/**
*Createdbykeysilenceon16/10/26.
*/
publicclassRedisUtils{
privatestaticLoggerlogger=LoggerFactory.getLogger(RedisUtils.class);
@JedisPoolSelector("master")
publicStringsetString(finalStringkey,finalStringvalue){
Stringret=DynamicJedisPoolHolder.getJedisPool().getResource().set(key,value);
System.out.println("key:"+key+",value:"+value+",ret:"+ret);
returnret;
}
@JedisPoolSelector("slave")
publicStringget(finalStringkey){
Stringret=DynamicJedisPoolHolder.getJedisPool().getResource().get(key);
System.out.println("key:"+key+",ret:"+ret);
returnret;
}
}
spring-datasource.xml
<?xmlversion="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"> <beanid="poolConfig"class="redis.clients.jedis.JedisPoolConfig"> <!--池中最大链接数--> <propertyname="maxTotal"value="100"/> <!--池中最大空闲链接数--> <propertyname="maxIdle"value="50"/> <!--池中最小空闲链接数--> <propertyname="minIdle"value="20"/> <!--当池中链接耗尽,调用者最大阻塞时间,超出此时间将跑出异常。(单位:毫秒;默认为-1,表示永不超时)--> <propertyname="maxWaitMillis"value="1000"/> <!--参考:http://biasedbit.com/redis-jedispool-configuration/--> <!--调用者获取链接时,是否检测当前链接有效性。无效则从链接池中移除,并尝试继续获取。(默认为false)--> <propertyname="testOnBorrow"value="true"/> <!--向链接池中归还链接时,是否检测链接有效性。(默认为false)--> <propertyname="testOnReturn"value="true"/> <!--调用者获取链接时,是否检测空闲超时。如果超时,则会被移除(默认为false)--> <propertyname="testWhileIdle"value="true"/> <!--空闲链接检测线程一次运行检测多少条链接--> <propertyname="numTestsPerEvictionRun"value="10"/> <!--空闲链接检测线程检测周期。如果为负值,表示不运行检测线程。(单位:毫秒,默认为-1)--> <propertyname="timeBetweenEvictionRunsMillis"value="60000"/> <!--链接获取方式。队列:false;栈:true--> <!--<propertyname="lifo"value="false"/>--> </bean> <beanid="master"class="redis.clients.jedis.JedisPool"> <constructor-argindex="0"ref="poolConfig"/> <constructor-argindex="1"value="192.168.100.110"type="java.lang.String"/> <constructor-argindex="2"value="6379"type="int"/> </bean> <beanid="slave"class="redis.clients.jedis.JedisPool"> <constructor-argindex="0"ref="poolConfig"/> <!--此处Host配置成ELB地址--> <constructor-argindex="1"value="192.168.100.110"type="java.lang.String"/> <constructor-argindex="2"value="6380"type="int"/> </bean> <beanid="redisUtils"class="com.silence.spring.redis.readwriteseparation.util.RedisUtils"> </bean> <beanid="jedisPoolAspect"class="com.silence.spring.redis.readwriteseparation.JedisPoolAspect"/> <aop:aspectj-autoproxyproxy-target-class="true"/> </beans>
Test
packagecom.silence.spring.redis.readwriteseparation;
importcom.silence.spring.redis.readwriteseparation.util.RedisUtils;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
/**
*Createdbykeysilenceon16/10/26.
*/
publicclassTest{
publicstaticvoidmain(String[]args){
ApplicationContextctx=newClassPathXmlApplicationContext("spring-datasource.xml");
System.out.println(ctx);
RedisUtilsredisUtils=(RedisUtils)ctx.getBean("redisUtils");
redisUtils.setString("aaa","111");
System.out.println(redisUtils.get("aaa"));
}
}
方式二,依赖注入
与方式一类似,但是需要写死具体使用主的池还是从的池,思路如下:
放弃注解的方式,直接将主和从的两个链接池注入到具体实现类中。
RedisUtils
packagecom.silence.spring.redis.readwriteseparation.util;
importcom.silence.spring.redis.readwriteseparation.DynamicJedisPoolHolder;
importcom.silence.spring.redis.readwriteseparation.JedisPoolSelector;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importredis.clients.jedis.JedisPool;
/**
*Createdbykeysilenceon16/10/26.
*/
publicclassRedisUtils{
privatestaticLoggerlogger=LoggerFactory.getLogger(RedisUtils.class);
privateJedisPoolmasterJedisPool;
privateJedisPoolslaveJedisPool;
publicvoidsetMasterJedisPool(JedisPoolmasterJedisPool){
this.masterJedisPool=masterJedisPool;
}
publicvoidsetSlaveJedisPool(JedisPoolslaveJedisPool){
this.slaveJedisPool=slaveJedisPool;
}
publicStringsetString(finalStringkey,finalStringvalue){
Stringret=masterJedisPool.getResource().set(key,value);
System.out.println("key:"+key+",value:"+value+",ret:"+ret);
returnret;
}
publicStringget(finalStringkey){
Stringret=slaveJedisPool.getResource().get(key);
System.out.println("key:"+key+",ret:"+ret);
returnret;
}
}
spring-datasource.xml
<?xmlversion="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"> <beanid="poolConfig"class="redis.clients.jedis.JedisPoolConfig"> <!--池中最大链接数--> <propertyname="maxTotal"value="100"/> <!--池中最大空闲链接数--> <propertyname="maxIdle"value="50"/> <!--池中最小空闲链接数--> <propertyname="minIdle"value="20"/> <!--当池中链接耗尽,调用者最大阻塞时间,超出此时间将跑出异常。(单位:毫秒;默认为-1,表示永不超时)--> <propertyname="maxWaitMillis"value="1000"/> <!--参考:http://biasedbit.com/redis-jedispool-configuration/--> <!--调用者获取链接时,是否检测当前链接有效性。无效则从链接池中移除,并尝试继续获取。(默认为false)--> <propertyname="testOnBorrow"value="true"/> <!--向链接池中归还链接时,是否检测链接有效性。(默认为false)--> <propertyname="testOnReturn"value="true"/> <!--调用者获取链接时,是否检测空闲超时。如果超时,则会被移除(默认为false)--> <propertyname="testWhileIdle"value="true"/> <!--空闲链接检测线程一次运行检测多少条链接--> <propertyname="numTestsPerEvictionRun"value="10"/> <!--空闲链接检测线程检测周期。如果为负值,表示不运行检测线程。(单位:毫秒,默认为-1)--> <propertyname="timeBetweenEvictionRunsMillis"value="60000"/> <!--链接获取方式。队列:false;栈:true--> <!--<propertyname="lifo"value="false"/>--> </bean> <beanid="masterJedisPool"class="redis.clients.jedis.JedisPool"> <constructor-argindex="0"ref="poolConfig"/> <constructor-argindex="1"value="192.168.100.110"type="java.lang.String"/> <constructor-argindex="2"value="6379"type="int"/> </bean> <beanid="slaveJedisPool"class="redis.clients.jedis.JedisPool"> <constructor-argindex="0"ref="poolConfig"/> <constructor-argindex="1"value="192.168.100.110"type="java.lang.String"/> <constructor-argindex="2"value="6380"type="int"/> </bean> <beanid="redisUtils"class="com.silence.spring.redis.readwriteseparation.util.RedisUtils"> <propertyname="masterJedisPool"ref="masterJedisPool"/> <propertyname="slaveJedisPool"ref="slaveJedisPool"/> </bean> </beans>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。