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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。