English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Implementation de la séparation de lecture/écriture maître-esclave dans Redis

Préambule

Dans le travail, vous pourriez rencontrer des besoins tels que la séparation de lecture et d'écriture de Redis, qui vise à diffuser la pression. Je vais vous présenter aujourd'hui comment réaliser la séparation de lecture et d'écriture avec l'ELB d'AWS, prenant l'exemple de l'écriture en maître et la lecture en esclave.

Mise en œuvre

Référence des fichiers de bibliothèque

  <!-- Client Redis -->
  <dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>2.6.2</version>
  </dependency>

Méthode 1,Avec l'aide de la couche de couper

JedisPoolSelector

L'objectif de ce type est de configurer des annotations différentes pour la lecture et l'écriture, pour distinguer le maître et l'esclave.

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;
/**
 * Created by keysilence on 16/10/26.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JedisPoolSelector {
  String value();
}

JedisPoolAspect

L'objectif de ce type est de réaliser une allocation dynamique du pool de connexion entre le maître et l'esclave, c'est-à-dire que le maître utilise le pool de connexion principal et l'esclave utilise le pool de connexion secondaire.

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;
/**
 * Created by keysilence on 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

Le but de cette classe est de stocker le JedisPool utilisé actuellement, c'est-à-dire le résultat de l'affectation de la classe ci-dessus.

package com.silence.spring.redis.readwriteseparation;
import redis.clients.jedis.JedisPool;
/**
 * Created by keysilence on 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

Le but de cette classe est d'appeler spécifiquement Redis, en incluant l'utilisation de l'appel principal ou secondaire.

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;
/**
 * Created by keysilence on 16/10/26.
 */
public class RedisUtils {
  private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);
  @JedisPoolSelector("master")
  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("slave")
  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">
    <!-- Nombre maximum de liens dans le pool -->
    <property name="maxTotal" value="100"/>
    <!-- Nombre maximum de liens inactifs dans le pool -->
    <property name="maxIdle" value="50"/>
    <!-- Nombre minimum de liens inactifs dans le pool -->
    <property name="minIdle" value="20"/>
    <!-- Lorsque tous les liens du pool sont épuisés, le temps maximum de blocage du appelant, au-delà duquel une exception sera lancée. (Unité : millisecondes ; valeur par défaut :-1, ce qui signifie jamais de dépasser le délai d'attente) (Unité : millisecondes ; valeur par défaut : -->
    <property name="maxWaitMillis" value="1000"/>
    <!-- Référence : http://biasedbit.com/redis-jedispool-configuration/ -->
    <!-- Lorsque l'appelant obtient un lien, doit-il vérifier la validité du lien actuel. Si le lien est invalide, il sera supprimé du pool de liens et une tentative de récupération continue sera effectuée. (par défaut : faux) -->
    <property name="testOnBorrow" value="true" />
    <!-- Lorsque l'appelant restitue un lien au pool, doit-il vérifier la validité du lien. (par défaut : faux) -->
    <property name="testOnReturn" value="true" />
    <!-- Lorsque l'appelant obtient un lien, doit-il détecter l'expiration des liens inactifs. Si l'expiration intervient, il sera supprimé (par défaut : faux) -->
    <property name="testWhileIdle" value="true" />
    <!-- Combien de liens doit détecter le thread de détection des liens inactifs à chaque exécution -->
    <property name="numTestsPerEvictionRun" value="10" />
    <!-- Période de détection du thread de détection des liens inactifs. Si la valeur est négative, cela signifie que le thread de détection ne s'exécute pas. (Unité : millisecondes, valeur par défaut :-1) -->
    <property name="timeBetweenEvictionRunsMillis" value="60000" />
    <!-- Méthode d'obtention du lien. File d'attente : faux ; pile : vrai -->
    <!--<property name="lifo" value="false" />-->
  </bean>
  <bean id="master" 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="slave" class="redis.clients.jedis.JedisPool">
    <constructor-arg index="0" ref="poolConfig"/>
    <!-- Here the Host configuration is set to 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;
/**
 * Created by keysilence on 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;
/**
 * Created by keysilence on 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">
    <!-- Nombre maximum de liens dans le pool -->
    <property name="maxTotal" value="100"/>
    <!-- Nombre maximum de liens inactifs dans le pool -->
    <property name="maxIdle" value="50"/>
    <!-- Nombre minimum de liens inactifs dans le pool -->
    <property name="minIdle" value="20"/>
    <!-- Lorsque tous les liens du pool sont épuisés, le temps maximum de blocage du appelant, au-delà duquel une exception sera lancée. (Unité : millisecondes ; valeur par défaut :-1, ce qui signifie jamais de dépasser le délai d'attente) (Unité : millisecondes ; valeur par défaut : -->
    <property name="maxWaitMillis" value="1000"/>
    <!-- Référence : http://biasedbit.com/redis-jedispool-configuration/ -->
    <!-- Lorsque l'appelant obtient un lien, doit-il vérifier la validité du lien actuel. Si le lien est invalide, il sera supprimé du pool de liens et une tentative de récupération continue sera effectuée. (par défaut : faux) -->
    <property name="testOnBorrow" value="true" />
    <!-- Lorsque l'appelant restitue un lien au pool, doit-il vérifier la validité du lien. (par défaut : faux) -->
    <property name="testOnReturn" value="true" />
    <!-- Lorsque l'appelant obtient un lien, doit-il détecter l'expiration des liens inactifs. Si l'expiration intervient, il sera supprimé (par défaut : faux) -->
    <property name="testWhileIdle" value="true" />
    <!-- Combien de liens doit détecter le thread de détection des liens inactifs à chaque exécution -->
    <property name="numTestsPerEvictionRun" value="10" />
    <!-- Période de détection du thread de détection des liens inactifs. Si la valeur est négative, cela signifie que le thread de détection ne s'exécute pas. (Unité : millisecondes, valeur par défaut :-1) -->
    <property name="timeBetweenEvictionRunsMillis" value="60000" />
    <!-- Méthode d'obtention du lien. File d'attente : faux ; pile : vrai -->
    <!--<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>

C'est tout pour cet article, j'espère que cela aidera à votre apprentissage, et j'espère que vous soutiendrez également le tutoriel criant.

Déclaration : Le contenu de cet article est partagé sur Internet, propriété de l'auteur original, le contenu est partagé par les utilisateurs d'Internet, ce site ne détient pas de propriété, n'a pas été traité par l'éditeur humain et n'assume pas de responsabilité juridique. Si vous trouvez du contenu suspect de droits d'auteur, veuillez envoyer un e-mail à : notice#w3Déclaration : Le contenu de cet article est tiré du réseau, propriété de l'auteur original, le contenu est contribué et téléchargé par les utilisateurs d'Internet, ce site ne détient pas de propriété, n'a pas été traité par l'éditeur humain et n'assume pas la responsabilité des responsabilités juridiques. Si vous trouvez du contenu suspect de droits d'auteur, veuillez envoyer un e-mail à : notice#w

Vous pourriez aussi aimer