您现在的位置是:首页 > 文章详情

Redis主从实现读写分离

日期:2018-11-28点击:658

前言

大家在工作中可能会遇到这样的需求,即Redis读写分离,目的是为了压力分散化。下面我将为大家介绍借助AWS的ELB实现读写分离,以写主读从为例。


实现

引用库文件

    <!-- redis客户端 -->     <dependency>       <groupId>redis.clients</groupId>       <artifactId>jedis</artifactId>       <version>2.6.2</version>     </dependency>

方式一,借助切面

JedisPoolSelector

此类的目的是为读和写分别配置不同的注解,用来区分是主还是从。

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

此类的目的是针对主和从的注解,进行动态链接池调配,即主的使用主链接池,从的使用从连接池。

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.  */@Aspectpublic 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

此类目的是存储当前使用的JedisPool,即上面类赋值后的结果保存。

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

此类目的是对Redis具体的调用,里面包含使用主还是从的方式调用。

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">         <!-- 池中最大链接数 -->         <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="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"/>         <!-- 此处Host配置成ELB地址 -->         <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"));     } }

方式二,依赖注入

与方式一类似,但是需要写死具体使用主的池还是从的池,思路如下: 
放弃注解的方式,直接将主和从的两个链接池注入到具体实现类中。

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">         <!-- 池中最大链接数 -->         <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>


原文链接:https://blog.roncoo.com/article/125946
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章