Redis-Cluster集群整合SpringCache

前言

    SpringCache不支持集群,只适用于单机环境,集群环境下,每台服务器的spring cache就会不同步,这样就会产生问题,同时SpringCache是代码级的缓存,实际上还是使用的JVM的内存来缓存对象。Redis 作为一个缓存服务器,是内存级的缓存,它是使用单纯的内存来进行缓存,同时redis设置单独的缓存服务器,所有集群服务器统一访问redis,不会出现缓存不同步的情况。Redis和Springcahe进行整合,既能够方便的缓存对象,提升性能,也可以支持集群环境。当然redis不是必须的,其他缓存也是可以的。

Redis集群的搭建

  关于redis的集群搭建我在前边的文章中已经介绍过了,请参考:Redis-Cluster集群搭建

项目下载

项目参考:Redis-SpringCache

Redis整合Spring

  maven依赖

 <!-- 依赖管理 -->
    <dependencies>
        <!-- 时间操作组件 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.5</version>
        </dependency>
        <!-- Apache工具组件 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.3</version>
        </dependency>
        <!-- Jackson Json处理工具包 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.4.2</version>
        </dependency>
        <!-- httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.3.5</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- 日志处理 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.4</version>
        </dependency>
        <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>4.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>4.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>4.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>4.2.6.RELEASE</version>
            </dependency>
            <!-- 编译程序用的 -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.0</version>
                <scope>provided</scope>
            </dependency>
            <!-- Redis客户端 -->
            <dependency>
                <groupId>redis.clients</groupId> 
                <artifactId>jedis</artifactId>
                <version>2.9.0</version>
            </dependency>
            <!-- redis Spring 基于注解配置 --> 
            <dependency>  
              <groupId>org.springframework.data</groupId>  
              <artifactId>spring-data-redis</artifactId>  
              <version>1.8.1.RELEASE</version>  
            </dependency>
            <!-- Mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.2.8</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.2.2</version>
            </dependency>
            <dependency>
                <groupId>com.github.miemiedev</groupId>
                <artifactId>mybatis-paginator</artifactId>
                <version>1.2.15</version>
            </dependency>
            <!-- MySql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.32</version>
            </dependency>
            <!-- 连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.9</version>
            </dependency>

  redis.properties配置文件

#密码
redis.password=123456
#最大空闲数
redis.maxIdle=100
#最大连接数
redis.maxActive=300
#最大建立连接等待时间
redis.maxWait=1000
#客户端超时时间单位是毫秒
redis.timeout=100000
redis.maxTotal=1000
redis.minIdle=8
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 
redis.testOnBorrow=true

spring.redis.cluster.nodes=192.168.251.129\:7001,192.168.251.129\:7002,192.168.251.129\:7003,192.168.251.129\:7004,192.168.251.129\:7005,192.168.251.129\:7006
spring.redis.cluster.max-redirects=3

  applicationContext-redis.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:p="http://www.springframework.org/schema/p"    
    xmlns:context="http://www.springframework.org/schema/context"    
    xmlns:mvc="http://www.springframework.org/schema/mvc"    
    xmlns:cache="http://www.springframework.org/schema/cache"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans      
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd      
                        http://www.springframework.org/schema/context      
                        http://www.springframework.org/schema/context/spring-context-4.2.xsd      
                        http://www.springframework.org/schema/mvc      
                        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd  
                        http://www.springframework.org/schema/cache   
                        http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">

    <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->    
    <cache:annotation-driven cache-manager="cacheManager" />

    <!-- 引入配置文件 -->  
    <bean id="propertyConfigurer"  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="location" value="classpath:properties/redis.properties" />  
    </bean>

    <!-- jedis 配置-->  
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >  
        <!--最大空闲数-->  
        <property name="maxIdle" value="${redis.maxIdle}" />  
        <!--最大建立连接等待时间-->  
        <property name="maxWaitMillis" value="${redis.maxWait}" />  
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->  
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
    </bean >
    <!--配置文件加载-->  
    <bean id="resourcePropertySource" class="org.springframework.core.io.support.ResourcePropertySource">  
        <constructor-arg name="name" value="redis.properties"/>  
        <constructor-arg name="resource" value="classpath:properties/redis.properties"/>  
    </bean>
    <!--redisCluster配置-->  
    <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">  
        <constructor-arg name="propertySource" ref="resourcePropertySource"/>  
    </bean>  
    <!-- redis服务器中心 -->  
    <bean id="connectionFactory"  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >  
       <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/>  
        <constructor-arg name="poolConfig" ref="poolConfig"/>  
        <property name="password" value="${redis.password}" />  
        <property name="timeout" value="${redis.timeout}" ></property>  
    </bean >  
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >  
        <property name="connectionFactory" ref="connectionFactory" />  
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->  
        <property name="keySerializer" >  
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />  
        </property>  
        <property name="valueSerializer" >  
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />  
        </property>  
        <property name="hashKeySerializer">  
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>  
        </property>  
        <property name="hashValueSerializer">  
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>  
        </property>  
    </bean >  

    <!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value -->    
     <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">    
         <property name="caches">    
            <set>    
                <!-- 这里可以配置多个redis -->  
                <bean class="org.lpl.RedisDemo.redis.RedisCache">    
                     <property name="redisTemplate" ref="redisTemplate" />    
                     <property name="name" value="defaultCache"/>    
                </bean>    
            </set>    
         </property>    
     </bean>
</beans>

  RedisCache.class(继承Spring的Cache进行实现重写)

public class RedisCache implements Cache {

    private RedisTemplate<String, Object> redisTemplate;
    private String name;

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        // TODO Auto-generated method stub
        return this.redisTemplate;
    }

    @Override
    public ValueWrapper get(Object key) {
        // TODO Auto-generated method stub
        final String keyf = (String) key;
        Object object = null;
        object = redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {

                byte[] key = keyf.getBytes();
                byte[] value = connection.get(key);
                if (value == null) {
                    return null;
                }
                return toObject(value);

            }
        });
        return (object != null ? new SimpleValueWrapper(object) : null);
    }

    @Override
    public void put(Object key, Object value) {
        // TODO Auto-generated method stub
        final String keyf = (String) key;
        final Object valuef = value;
        final long liveTime = 86400;

        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] keyb = keyf.getBytes();
                byte[] valueb = toByteArray(valuef);
                connection.set(keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1L;
            }
        });
    }
    /**
     * 描述 : <Object转byte[]>. <br>
     * <p>
     * <使用方法说明>
     * </p>
     * 
     * @param obj
     * @return
     */
    private byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }
    /**
     * 描述 : <byte[]转Object>. <br>
     * <p>
     * <使用方法说明>
     * </p>
     * 
     * @param bytes
     * @return
     */
    private Object toObject(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }

    @Override
    public void evict(Object key) {
        // TODO Auto-generated method stub
        final String keyf = (String) key;
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.del(keyf.getBytes());
            }
        });
    }

    @Override
    public void clear() {
        // TODO Auto-generated method stub
        redisTemplate.execute(new RedisCallback<String>() {
            public String doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.flushDb();
                return "ok";
            }
        });
    }

    @Override
    public <T> T get(Object key, Class<T> type) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        // TODO Auto-generated method stub
        return null;
    }
}

  Service业务代码测试

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public int insert(Student student) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    @CacheEvict(value="defaultCache",key="'student_id_'+#id")  
    public int deleteByPrimaryKey(Long id) {
        System.out.println("删除方法执行了");
        return studentMapper.deleteByPrimaryKey(id);
    }

    @Override
    @CachePut(value="defaultCache",key="'student_id_'+#student.id")
    //
    public Student updateByPrimaryKey(Student student) {
        System.out.println("修改方法执行了");
        studentMapper.updateByPrimaryKey(student);
        return student;
    }

    @Override
    @Cacheable(value="defaultCache",key="'student_id_'+#id")//#id  
    public Student selectByPrimaryKey(Long id) {
        System.out.println("查询方法执行了");
        return studentMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<Student> selectList() {
        // TODO Auto-generated method stub
        return studentMapper.selectList();
    }
}

SpringCache注解的简单介绍

@Cacheable

  @Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。

@CachePut

  与@Cacheable相同的是@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

@CacheEvict

   @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。标记在方法上就是执行方法是执行出发缓存清除操作。

参考资料

http://blog.csdn.net/qq_34021712/article/details/70478281
http://blog.csdn.net/wjacketcn/article/details/50945887

猜你喜欢

转载自blog.csdn.net/plei_yue/article/details/78803605