English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Introducción
En el trabajo, es posible que se encuentren con necesidades como la separación de lectura y escritura de Redis, con el objetivo de distribuir la presión. A continuación, les presentaré cómo realizar la separación de lectura y escritura utilizando AWS ELB, tomando como ejemplo la escritura principal y la lectura secundaria.
Implementación
Referencia de biblioteca
<!-- Redis cliente --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.6.2</version> </dependency>
Método uno,Utilizando el enfoque de la sección transversal
JedisPoolSelector
El objetivo de este tipo es configurar diferentes anotaciones para la lectura y escritura, para distinguir entre el principal y el secundario.
package com.silence.spring.redis.readwriteseparation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Creado por keysilence el 16/10/26. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface JedisPoolSelector { String value(); }
JedisPoolAspect
El objetivo de este tipo es realizar la调配 dinámica de la piscina de conexión entre el principal y el secundario, es decir, el principal utiliza la piscina de conexión principal y el secundario utiliza la piscina de conexión secundaria.
package com.silence.spring.redis.readwriteseparation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import redis.clients.jedis.JedisPool; import javax.annotation.PostConstruct; import java.lang.reflect.Method; import java.util.Date; /** * Creado por keysilence el 16/10/26. */ @Aspect public class JedisPoolAspect implements ApplicationContextAware { private ApplicationContext ctx; @PostConstruct public void init() { System.out.println("jedis pool aspectj started @" + new Date()); } @Pointcut("execution(* com.silence.spring.redis.readwriteseparation.util.*.*(..))") private void allMethod() { } @Before("allMethod()") public void before(JoinPoint point) { Object target = point.getTarget(); String method = point.getSignature().getName(); Class classz = target.getClass(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); try { Method m = classz.getMethod(method, parameterTypes); if (m != null && m.isAnnotationPresent(JedisPoolSelector.class)) { JedisPoolSelector data = m .getAnnotation(JedisPoolSelector.class); JedisPool jedisPool = (JedisPool) ctx.getBean(data.value()); DynamicJedisPoolHolder.putJedisPool(jedisPool); } } catch (Exception e) { e.printStackTrace(); } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.ctx = applicationContext; } }
DynamicJedisPoolHolder
El propósito de esta clase es almacenar el JedisPool utilizado actualmente, es decir, el resultado guardado después de asignar la clase superior.
package com.silence.spring.redis.readwriteseparation; import redis.clients.jedis.JedisPool; /** * Creado por keysilence el 16/10/26. */ public class DynamicJedisPoolHolder { public static final ThreadLocal<JedisPool> holder = new ThreadLocal<JedisPool>(); public static void putJedisPool(JedisPool jedisPool) { holder.set(jedisPool); } public static JedisPool getJedisPool() { return holder.get(); } }
RedisUtils
El propósito de este clase es llamar a Redis específicamente, que incluye la forma de llamar usando el maestro o el esclavo.
package com.silence.spring.redis.readwriteseparation.util; import com.silence.spring.redis.readwriteseparation.DynamicJedisPoolHolder; import com.silence.spring.redis.readwriteseparation.JedisPoolSelector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Creado por keysilence el 16/10/26. */ public class RedisUtils { private static Logger logger = LoggerFactory.getLogger(RedisUtils.class); @JedisPoolSelector("maestro") public String setString(final String key, final String value) { String ret = DynamicJedisPoolHolder.getJedisPool().getResource().set(key, value); System.out.println("key:" + key + ",value:" + value + ",ret:" + ret); return ret; } @JedisPoolSelector("esclavo") public String get(final String key) { String ret = DynamicJedisPoolHolder.getJedisPool().getResource().get(key); System.out.println("key:" + key + ",ret:" + ret); return ret; } }
spring-datasource.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 池中最大链接数 --> <property name="maxTotal" value="100"/> <!-- 池中最大空闲链接数 --> <property name="maxIdle" value="50"/> <!-- 池中最小空闲链接数 --> <property name="minIdle" value="20"/> <!-- 当池中链接耗尽,调用者最大阻塞时间,超出此时间将抛出异常。(单位:毫秒;默认为-1,表示永不超时) --> <property name="maxWaitMillis" value="1000"/> <!-- 参考:http://biasedbit.com/redis-jedispool-configuration/ --> <!-- 调用者获取链接时,是否检测当前链接有效性。无效则从链接池中移除,并尝试继续获取。(默认为false) --> <property name="testOnBorrow" value="true" /> <!-- 向链接池中归还链接时,是否检测链接有效性。(默认为false) --> <property name="testOnReturn" value="true" /> <!-- 调用者获取链接时,是否检测空闲超时。如果超时,则会被移除(默认为false) --> <property name="testWhileIdle" value="true" /> <!-- 一次运行检测多少条链接空闲链接线程 --> <property name="numTestsPerEvictionRun" value="10" /> <!-- 检测周期空闲链接线程。如果为负值,表示不运行检测线程。(单位:毫秒,默认为-1) --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 方式获取链接。队列:false;栈:true --> <!--<property name="lifo" value="false" />--> </bean> <bean id="maestro" class="redis.clients.jedis.JedisPool"> <constructor-arg index="0" ref="poolConfig"/> <constructor-arg index="1" value="192.168.100.110" type="java.lang.String"/> <constructor-arg index="2" value="6379" type="int"/> </bean> <bean id="esclavo" class="redis.clients.jedis.JedisPool"> <constructor-arg index="0" ref="poolConfig"/> <!-- Configure the Host here with the ELB address --> <constructor-arg index="1" value="192.168.100.110" type="java.lang.String"/> <constructor-arg index="2" value="6380" type="int"/> </bean> <bean id="redisUtils" class="com.silence.spring.redis.readwriteseparation.util.RedisUtils"> </bean> <bean id="jedisPoolAspect" class="com.silence.spring.redis.readwriteseparation.JedisPoolAspect" /> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
Test
package com.silence.spring.redis.readwriteseparation; import com.silence.spring.redis.readwriteseparation.util.RedisUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Creado por keysilence el 16/10/26. */ public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-datasource.xml"); System.out.println(ctx); RedisUtils redisUtils = (RedisUtils) ctx.getBean("redisUtils"); redisUtils.setString("aaa", "111"); System.out.println(redisUtils.get("aaa")); } }
Method two,Dependency injection
Similar to method one, but it is necessary to specify whether to use the master's pool or the slave's pool, the idea is as follows:
Abandoning the annotation method, directly inject the master and slave connection pools into the specific implementation class.
RedisUtils
package com.silence.spring.redis.readwriteseparation.util; import com.silence.spring.redis.readwriteseparation.DynamicJedisPoolHolder; import com.silence.spring.redis.readwriteseparation.JedisPoolSelector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.JedisPool; /** * Creado por keysilence el 16/10/26. */ public class RedisUtils { private static Logger logger = LoggerFactory.getLogger(RedisUtils.class); private JedisPool masterJedisPool; private JedisPool slaveJedisPool; public void setMasterJedisPool(JedisPool masterJedisPool) { this.masterJedisPool = masterJedisPool; } public void setSlaveJedisPool(JedisPool slaveJedisPool) { this.slaveJedisPool = slaveJedisPool; } public String setString(final String key, final String value) { String ret = masterJedisPool.getResource().set(key, value); System.out.println("key:" + key + ",value:" + value + ",ret:" + ret); return ret; } public String get(final String key) { String ret = slaveJedisPool.getResource().get(key); System.out.println("key:" + key + ",ret:" + ret); return ret; } }
spring-datasource.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 池中最大链接数 --> <property name="maxTotal" value="100"/> <!-- 池中最大空闲链接数 --> <property name="maxIdle" value="50"/> <!-- 池中最小空闲链接数 --> <property name="minIdle" value="20"/> <!-- 当池中链接耗尽,调用者最大阻塞时间,超出此时间将抛出异常。(单位:毫秒;默认为-1,表示永不超时) --> <property name="maxWaitMillis" value="1000"/> <!-- 参考:http://biasedbit.com/redis-jedispool-configuration/ --> <!-- 调用者获取链接时,是否检测当前链接有效性。无效则从链接池中移除,并尝试继续获取。(默认为false) --> <property name="testOnBorrow" value="true" /> <!-- 向链接池中归还链接时,是否检测链接有效性。(默认为false) --> <property name="testOnReturn" value="true" /> <!-- 调用者获取链接时,是否检测空闲超时。如果超时,则会被移除(默认为false) --> <property name="testWhileIdle" value="true" /> <!-- 一次运行检测多少条链接空闲链接线程 --> <property name="numTestsPerEvictionRun" value="10" /> <!-- 检测周期空闲链接线程。如果为负值,表示不运行检测线程。(单位:毫秒,默认为-1) --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 方式获取链接。队列:false;栈:true --> <!--<property name="lifo" value="false" />--> </bean> <bean id="masterJedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg index="0" ref="poolConfig"/> <constructor-arg index="1" value="192.168.100.110" type="java.lang.String"/> <constructor-arg index="2" value="6379" type="int"/> </bean> <bean id="slaveJedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg index="0" ref="poolConfig"/> <constructor-arg index="1" value="192.168.100.110" type="java.lang.String"/> <constructor-arg index="2" value="6380" type="int"/> </bean> <bean id="redisUtils" class="com.silence.spring.redis.readwriteseparation.util.RedisUtils"> <property name="masterJedisPool" ref="masterJedisPool"/> <property name="slaveJedisPool" ref="slaveJedisPool"/> </bean> </beans>
Este es el contenido completo del artículo, esperamos que sea útil para su aprendizaje y que todos los amigos nos apoyen y alivien el tutorial.
Declaración: Este artículo se ha redactado en línea, pertenece al propietario original, el contenido se ha subido de manera autónoma por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha realizado una edición humana y no asume la responsabilidad legal correspondiente. Si encuentra contenido sospechoso de infracción de derechos de autor, le invitamos a enviar un correo electrónico a: notice#w3Declaración: El contenido de este artículo se ha obtenido de la red, pertenece al propietario original, el contenido se ha subido de manera autónoma por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha realizado una edición humana y no asume la responsabilidad legal correspondiente. Si encuentra contenido sospechoso de infracción de derechos de autor, le invitamos a enviar un correo electrónico a: notice#w