Spring+SpringMVC做Redis集群(Sentinel模式)

待我称王封你为后i 2022-07-14 13:22 232阅读 0赞

研究Redis也有一段时间了,在前面的Redis系列文章中,介绍了Redis的安装,集群配置,及节点的增加和删除,但是并未实际的使用到项目中,趁这周末时间,参照项目中实际的使用场景,做了一个Redis集群Spring整合的案例,在介绍案例之前,先简单介绍下Redis集群的方式有哪些
1、单机版 不解释
2、Sentinel 哨兵模式
3、Redis Cluster Redis官方集群方案
4、Redis Sharding集群

正确的说,哨兵模式是一主多从的结构,并不属于真正的集群,真正的集群应该是多主多从的结构,需要有多台不同物理地址的主机,无赖家里只有一台PC,只能使用Sentinel模式来做集群。先来看下Sentinel模式的集群架构图
这里写图片描述
首先需要有一台监控服务器,也就是Sentinel,一台主服务器Master,多台从服务器Slave,具体的配置可以参考另一篇博文《Redis序列之Sentinel》,假设Sentinel,Master,Slave都已经配置好了,对应地址分别为:
Sentinel 127.0.0.1:26379,127.0.0.1:26479
Master 127.0.0.1:10003
Slave 127.0.0.1:10001,127.0.0.1:10002

接下来开始Java端的配置准备工作
1、在Maven的pom.xml文件中,增加redis的引用

  1. <!--如果用1.7.5的版本,启动会有问题因此使用1.5.2的版本-->
  2. <dependency>
  3. <groupId>org.springframework.data</groupId>
  4. <artifactId>spring-data-redis</artifactId>
  5. <version>1.5.2.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>redis.clients</groupId>
  9. <artifactId>jedis</artifactId>
  10. <version>2.9.0</version>
  11. </dependency>

2、增加redis.properties文件,文件中只需要指定sentinel即可(原来一直误认为是配置master和slave),文件内容如下

  1. redis.sentinel.host1=127.0.0.1
  2. redis.sentinel.port1=26379
  3. redis.sentinel.host2=127.0.0.1
  4. redis.sentinel.port2=26479
  5. redis.pool.maxTotal=1024
  6. redis.pool.maxIdle=200
  7. redis.pool.maxWaitMillis=1000
  8. redis.pool.testOnBorrow=true
  9. redis.pool.timeBetweenEvictionRunsMillis=30000
  10. redis.pool.minEvictableIdleTimeMillis=30000
  11. redis.pool.softMinEvictableIdleTimeMillis=10000
  12. redis.pool.numTestsPerEvictionRun=1024
  13. #1000*60*60*1
  14. redis.pool.expire=3600000
  15. redis.pool.unlock=false

3、在Spring的applicationContext.xml文件中,增加Redis的配置

  1. <!-- redis属性文件 -->
  2. <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  3. <property name="locations">
  4. <list>
  5. <value>classpath:config/redis.properties</value>
  6. </list>
  7. </property>
  8. </bean>
  9. <!-- 启动缓存注解功能,否则缓解不会生效 -->
  10. <cache:annotation-driven cache-manager="cacheManager"/>
  11. <!-- redis属性配置 -->
  12. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  13. <property name="maxTotal" value="${redis.pool.maxTotal}" />
  14. <property name="maxIdle" value="${redis.pool.maxIdle}" />
  15. <property name="numTestsPerEvictionRun" value="${redis.pool.numTestsPerEvictionRun}" />
  16. <property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}" />
  17. <property name="minEvictableIdleTimeMillis" value="${redis.pool.minEvictableIdleTimeMillis}" />
  18. <property name="softMinEvictableIdleTimeMillis" value="${redis.pool.softMinEvictableIdleTimeMillis}" />
  19. <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
  20. <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
  21. </bean>
  22. <!-- redis集群配置 哨兵模式 -->
  23. <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  24. <property name="master">
  25. <bean class="org.springframework.data.redis.connection.RedisNode">
  26. <!--这个值要和Sentinel中指定的master的值一致,不然启动时找不到Sentinel会报错的-->
  27. <property name="name" value="mymaster"></property>
  28. </bean>
  29. </property>
  30. <!--记住了,这里是指定Sentinel的IP和端口,不是Master和Slave的-->
  31. <property name="sentinels">
  32. <set>
  33. <bean class="org.springframework.data.redis.connection.RedisNode">
  34. <constructor-arg name="host" value="${redis.sentinel.host1}"></constructor-arg>
  35. <constructor-arg name="port" value="${redis.sentinel.port1}"></constructor-arg>
  36. </bean>
  37. <bean class="org.springframework.data.redis.connection.RedisNode">
  38. <constructor-arg name="host" value="${redis.sentinel.host2}"></constructor-arg>
  39. <constructor-arg name="port" value="${redis.sentinel.port2}"></constructor-arg>
  40. </bean>
  41. </set>
  42. </property>
  43. </bean>
  44. <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.RedisConnectionFactory">
  45. <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
  46. <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
  47. </bean>
  48. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
  49. <property name="connectionFactory" ref="redisConnectionFactory"></property>
  50. </bean>
  51. <!-- 缓存管理器 -->
  52. <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
  53. <constructor-arg ref="redisTemplate" />
  54. </bean>

特别说明:
1、Sentinel的集群模型,在Java端配置时,只需要指定有哪些Sentinel就可以了,不需要指定Master和Slave,之前不清楚,导致启动一直报找不到可用的Sentinel
2、在Spring配置文件中一定要增加<cache:annotation-driven cache-manager="cacheManager"/>注解声明,否则缓存不会生效,之前缓存一直不生效的原因就是这个
3、<cache:annotation-driven/>的cache-manager属性默认值是cacheManager,默认情况下可以不指定,如果我们的定义的CacheManager名称不是cacheManager,就需要显示指定;
<cache:annotation-driven/> 的另一个属性是model,可选值是proxy和aspectj,默认是proxy,当model=proxy时,只有当缓存方法在声名的类外部被调用时,spring cache才会生效,换句话说就是如果在同一个类中一个方法调用当前类的缓存方法,缓存不生效,而model=aspectj时就不会有这个问题;另外model=proxy时,@Cacheable等注解加在public方法上,缓存才会生效,加在private上是不会生效的,而model=aspectj时也不会有这个问题
<cache:annotation-driven/> 还可以指定一个proxy-target-class属性,表示是否要代理class,默认为false。@Cacheable、@cacheEvict等也可以标注在接口上,这对于基于接口的代理来说是没有什么问题的,但要注意的是当设置proxy-target-class为true或者mode为aspectj时,是直接基于class进行操作的,定义在接口上的@Cacheable等Cache注解不会被识别到,对应的Spring Cache也不会起作用

以上准备工作完成后,就可以在Java代码中使用Redis了,很多人是以硬编码的方式在代码中使用缓存,这种对业务代码的侵入性是很强的,不方便后期的维护和扩展,这里使用注解方式,只需要在类或方法名上增加注解就可以了

  1. @Override
  2. @Cacheable(value="user")
  3. public UserVO selectUserById(String userId) throws Exception {
  4. return userDao.selectUserById(userId);
  5. }

现在把Tomcat服务启动起来,发现Sentinel已经加载进来了,显示127.0.0.1:10003是Master

  1. 一月 02, 2017 11:53:09 下午 redis.clients.jedis.JedisSentinelPool initSentinels
  2. 信息: Trying to find master from available Sentinels...
  3. 一月 02, 2017 11:53:14 下午 redis.clients.jedis.JedisSentinelPool initSentinels
  4. 信息: Redis master running at 127.0.0.1:10003, starting Sentinel listeners...
  5. 一月 02, 2017 11:53:14 下午 redis.clients.jedis.JedisSentinelPool initPool
  6. 信息: Created JedisPool to master at 127.0.0.1:10003

第二次调用selectUserById方法,已经从缓存中取值,再没有请求DB查询了

  1. INFO 2017-01-02 23:57:14 http-bio-8080-exec-3 com.bug.controller.user.UserController.login 24-line login method start,the parameter is :
  2. DEBUG 2017-01-02 23:57:15 http-bio-8080-exec-3 org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource.getCacheOperations 109-line Adding cacheable method 'selectUserById' with attribute: [CacheableOperation[public com.bug.model.user.UserVO com.bug.service.user.impl.UserServiceImpl.selectUserById(java.lang.String) throws java.lang.Exception] caches=[user] | key='' | condition='' | unless='']
  3. DEBUG 2017-01-02 23:57:16 http-bio-8080-exec-3 org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection 125-line Opening RedisConnection
  4. DEBUG 2017-01-02 23:57:16 http-bio-8080-exec-3 org.springframework.data.redis.core.RedisConnectionUtils.releaseConnection 205-line Closing Redis Connection

自定义Key生成器
默认生成器是SimpleKeyGenerator,生成的Key是经过HashCode转换过的,不能通过Key清除指定接口的缓存,因此需要我们自己实现Key生成器,增加Key生成器实现类CacheKeyGenerator,并实现KeyGenerator接口,代码如下:

  1. public class CacheKeyGenerator implements KeyGenerator {
  2. @Override
  3. public Object generate(Object target, Method method, Object... params) {
  4. StringBuilder key = new StringBuilder();
  5. key.append(target.getClass().getName());
  6. key.append(".");
  7. key.append(method.getName());
  8. return key.toString();
  9. }
  10. }

这里Key的生成规则是类的限定名+方法名,可以确保在同一项目中,key不会重名,如果还需要把查询条件也作为Key的一部分,可以把params加进来

Key生成器需要配置在applicationContext.xml文件中,如下

  1. <!-- 启动缓存注解功能,否则缓解不会生效,并自定义Key生成策略 -->
  2. <cache:annotation-driven cache-manager="cacheManager" key-generator="cacheKeyGenerator"/>
  3. <bean id="cacheKeyGenerator" class="com.bug.common.CacheKeyGenerator"></bean>

发表评论

表情:
评论列表 (有 0 条评论,232人围观)

还没有评论,来说两句吧...

相关阅读