RedisConfig.java 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package cn.hobbystocks.auc.common.config;
  2. import io.lettuce.core.ClientOptions;
  3. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  6. import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
  7. import org.springframework.cache.annotation.CachingConfigurerSupport;
  8. import org.springframework.cache.annotation.EnableCaching;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.data.redis.connection.RedisConnectionFactory;
  12. import org.springframework.data.redis.connection.RedisSentinelConfiguration;
  13. import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
  14. import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
  15. import org.springframework.data.redis.core.RedisTemplate;
  16. import org.springframework.data.redis.core.script.DefaultRedisScript;
  17. import org.springframework.data.redis.serializer.StringRedisSerializer;
  18. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  19. import com.fasterxml.jackson.annotation.JsonTypeInfo;
  20. import com.fasterxml.jackson.annotation.PropertyAccessor;
  21. import com.fasterxml.jackson.databind.ObjectMapper;
  22. import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
  23. /**
  24. * redis配置
  25. *
  26. * @author ruoyi
  27. */
  28. @Configuration
  29. @EnableCaching
  30. public class RedisConfig extends CachingConfigurerSupport {
  31. @Bean
  32. @ConditionalOnProperty(name = "hobbystocks.redis.sentinel.nodes")
  33. public LettuceConnectionFactory redisConnectionFactory(RedisProperties redisProperties, @Value("${hobbystocks.redis.sentinel.nodes:}") String hosts) {
  34. // 创建 Redis 哨兵配置
  35. RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
  36. .master(redisProperties.getSentinel().getMaster()); // 设置主节点名称,确保与哨兵配置一致
  37. for (String host : hosts.split(",")) {
  38. sentinelConfig.sentinel(host.split(":")[0], Integer.parseInt(host.split(":")[1]));
  39. }
  40. // 如果 Redis 服务器设置了密码,则可以在此配置
  41. sentinelConfig.setPassword(redisProperties.getPassword());
  42. sentinelConfig.setDatabase(redisProperties.getDatabase());
  43. // 配置连接池参数
  44. GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
  45. poolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive()); // 最大连接数
  46. poolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle()); // 最大空闲连接数
  47. poolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle()); // 最小空闲连接数
  48. // 配置客户端选项,启用自动重连
  49. ClientOptions clientOptions = ClientOptions.builder()
  50. .autoReconnect(true)
  51. .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
  52. .build();
  53. // 创建 Lettuce 连接池配置
  54. LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
  55. .poolConfig(poolConfig)
  56. .clientOptions(clientOptions)
  57. .clientOptions(ClientOptions.builder()
  58. .pingBeforeActivateConnection(true) // 在激活连接前发送 ping
  59. .build())
  60. .build();
  61. // 创建并配置 Lettuce 连接工厂
  62. return new LettuceConnectionFactory(sentinelConfig, clientConfig);
  63. }
  64. @Bean
  65. @SuppressWarnings(value = { "unchecked", "rawtypes" })
  66. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
  67. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  68. template.setConnectionFactory(connectionFactory);
  69. FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
  70. ObjectMapper mapper = new ObjectMapper();
  71. mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  72. mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
  73. serializer.setObjectMapper(mapper);
  74. // 使用StringRedisSerializer来序列化和反序列化redis的key值
  75. template.setKeySerializer(new StringRedisSerializer());
  76. template.setValueSerializer(serializer);
  77. // Hash的key也采用StringRedisSerializer的序列化方式
  78. template.setHashKeySerializer(new StringRedisSerializer());
  79. template.setHashValueSerializer(serializer);
  80. template.afterPropertiesSet();
  81. return template;
  82. }
  83. @Bean
  84. public DefaultRedisScript<Long> limitScript()
  85. {
  86. DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
  87. redisScript.setScriptText(limitScriptText());
  88. redisScript.setResultType(Long.class);
  89. return redisScript;
  90. }
  91. /**
  92. * 限流脚本
  93. */
  94. private String limitScriptText()
  95. {
  96. return "local key = KEYS[1]\n" +
  97. "local count = tonumber(ARGV[1])\n" +
  98. "local time = tonumber(ARGV[2])\n" +
  99. "local current = redis.call('get', key);\n" +
  100. "if current and tonumber(current) > count then\n" +
  101. " return tonumber(current);\n" +
  102. "end\n" +
  103. "current = redis.call('incr', key)\n" +
  104. "if tonumber(current) == 1 then\n" +
  105. " redis.call('expire', key, time)\n" +
  106. "end\n" +
  107. "return tonumber(current);";
  108. }
  109. }