| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- package com.fuint.common.config;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import lombok.AllArgsConstructor;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
- import org.springframework.cache.annotation.CachingConfigurerSupport;
- import org.springframework.cache.annotation.EnableCaching;
- import org.springframework.cache.interceptor.KeyGenerator;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
- import org.springframework.session.data.redis.config.ConfigureRedisAction;
- import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
- /**
- * 配置redis缓存
- *
- * Created by FSQ
- * CopyRight https://www.fuint.cn
- */
- @Configuration
- @EnableCaching
- @AllArgsConstructor
- @EnableRedisHttpSession
- @ConditionalOnProperty(name = "spring.redis.enabled", havingValue = "true", matchIfMissing = true)
- public class RedisConfig extends CachingConfigurerSupport {
- private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);
- private RedisConnectionFactory redisConnectionFactory;
- @Bean
- public KeyGenerator keyGenerator() {
- return (target, method, params) -> {
- StringBuilder sb = new StringBuilder();
- sb.append(target.getClass().getName());
- sb.append(method.getName());
- for (Object obj : params) {
- sb.append(obj.toString());
- }
- return sb.toString();
- };
- }
- @Bean
- public static ConfigureRedisAction configureRedisAction() {
- return ConfigureRedisAction.NO_OP;
- }
- @Bean
- public ObjectMapper objectMapper() {
- return new ObjectMapper();
- }
- @Bean
- Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
- Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
- Object.class);
- jackson2JsonRedisSerializer.setObjectMapper(objectMapper());
- return jackson2JsonRedisSerializer;
- }
- @Bean
- RedisTemplate<String, Object> redisTemplate(Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer) {
- try {
- RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
- StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
- redisTemplate.setKeySerializer(stringRedisSerializer);
- redisTemplate.setHashKeySerializer(stringRedisSerializer);
- // 测试连接 - 增加重试机制
- testRedisConnectionWithRetry(redisTemplate, 3);
- logger.info("Redis连接配置成功");
- return redisTemplate;
- } catch (Exception e) {
- // 打印详细的Redis连接配置信息
- printRedisConfigInfo();
- logger.error("Redis连接配置失败: {}", e.getMessage(), e);
- // 提供更详细的错误信息
- String errorMsg = buildDetailedErrorMessage(e);
- logger.error(errorMsg);
- // 如果是开发环境,可以选择降级处理而不是直接抛出异常
- String envProfile = System.getProperty("env.profile", "dev");
- if ("dev".equals(envProfile)) {
- logger.warn("开发环境检测到Redis连接失败,系统将继续运行但部分功能可能受限");
- // 返回一个mock的RedisTemplate或者禁用Redis功能
- return createMockRedisTemplate();
- } else {
- throw new RuntimeException("Redis连接配置失败,请检查Redis服务是否启动和配置是否正确", e);
- }
- }
- }
- /**
- * 打印Redis连接配置信息
- */
- private void printRedisConfigInfo() {
- try {
- // 获取Redis连接工厂的配置信息
- if (redisConnectionFactory != null) {
- logger.error("=== Redis连接配置信息 ===");
- logger.error("Redis Host: {}", getRedisHost());
- logger.error("Redis Port: {}", getRedisPort());
- logger.error("Redis Database: {}", getRedisDatabase());
- logger.error("Redis Password: {}", getRedisPassword() != null && !getRedisPassword().isEmpty() ? "******" : "(empty)");
- logger.error("Connection Timeout: {}ms", getRedisTimeout());
- logger.error("=========================");
- }
- } catch (Exception ex) {
- logger.error("获取Redis配置信息失败: {}", ex.getMessage());
- }
- }
- /**
- * 获取Redis主机地址
- */
- private String getRedisHost() {
- try {
- return redisConnectionFactory instanceof org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
- ? ((org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory) redisConnectionFactory).getHostName()
- : "unknown";
- } catch (Exception e) {
- return "unknown";
- }
- }
- /**
- * 获取Redis端口
- */
- private int getRedisPort() {
- try {
- return redisConnectionFactory instanceof org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
- ? ((org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory) redisConnectionFactory).getPort()
- : 6379;
- } catch (Exception e) {
- return 6379;
- }
- }
- /**
- * 获取Redis数据库索引
- */
- private int getRedisDatabase() {
- try {
- return redisConnectionFactory instanceof org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
- ? ((org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory) redisConnectionFactory).getDatabase()
- : 0;
- } catch (Exception e) {
- return 0;
- }
- }
- /**
- * 获取Redis密码
- */
- private String getRedisPassword() {
- try {
- return redisConnectionFactory instanceof org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
- ? ((org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory) redisConnectionFactory).getPassword()
- : null;
- } catch (Exception e) {
- return null;
- }
- }
- /**
- * 获取连接超时时间
- */
- private long getRedisTimeout() {
- try {
- return redisConnectionFactory instanceof org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
- ? ((org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory) redisConnectionFactory).getTimeout()
- : 2000L;
- } catch (Exception e) {
- return 2000L;
- }
- }
- /**
- * 测试Redis连接(带重试机制)
- */
- private void testRedisConnectionWithRetry(RedisTemplate<String, Object> redisTemplate, int maxRetries) {
- Exception lastException = null;
- for (int i = 1; i <= maxRetries; i++) {
- try {
- logger.info("第{}次尝试连接Redis...", i);
- redisTemplate.getConnectionFactory().getConnection().ping();
- logger.info("Redis连接测试成功");
- return; // 成功则返回
- } catch (Exception e) {
- lastException = e;
- logger.warn("第{}次Redis连接尝试失败: {}", i, e.getMessage());
- if (i < maxRetries) {
- try {
- Thread.sleep(1000 * i); // 递增延迟
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- throw new RuntimeException("连接重试被中断", ie);
- }
- }
- }
- }
- // 所有重试都失败
- throw new RuntimeException("Redis连接重试" + maxRetries + "次后仍然失败", lastException);
- }
- /**
- * 构建详细的错误信息
- */
- private String buildDetailedErrorMessage(Exception e) {
- StringBuilder sb = new StringBuilder();
- sb.append("\n=== Redis连接详细错误信息 ===\n");
- // 基础错误信息
- sb.append("错误类型: ").append(e.getClass().getSimpleName()).append("\n");
- sb.append("错误消息: ").append(e.getMessage()).append("\n");
- // 常见问题诊断
- sb.append("\n可能的原因:\n");
- sb.append("1. Redis服务未启动或端口被占用\n");
- sb.append("2. Redis配置的主机地址或端口不正确\n");
- sb.append("3. Redis密码配置错误\n");
- sb.append("4. 网络连接问题或防火墙阻止\n");
- sb.append("5. Redis连接超时设置过短\n");
- sb.append("\n建议解决方案:\n");
- sb.append("1. 检查Redis服务状态: net start redis 或 redis-server\n");
- sb.append("2. 验证端口是否监听: netstat -an | findstr 6379\n");
- sb.append("3. 检查配置文件中的Redis密码是否正确\n");
- sb.append("4. 尝试telnet 127.0.0.1 6379测试连接\n");
- sb.append("===============================\n");
- return sb.toString();
- }
- /**
- * 创建Mock的RedisTemplate用于开发环境降级
- */
- private RedisTemplate<String, Object> createMockRedisTemplate() {
- logger.warn("创建Mock RedisTemplate,Redis相关功能将不可用");
- RedisTemplate<String, Object> mockTemplate = new RedisTemplate<String, Object>() {
- @Override
- public void afterPropertiesSet() {
- // 不执行实际初始化
- logger.debug("Mock RedisTemplate已创建");
- }
- };
- // 设置基本序列化器
- StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
- Jackson2JsonRedisSerializer<Object> jacksonSerializer = jackson2JsonRedisSerializer();
- mockTemplate.setKeySerializer(stringRedisSerializer);
- mockTemplate.setHashKeySerializer(stringRedisSerializer);
- mockTemplate.setDefaultSerializer(jacksonSerializer);
- return mockTemplate;
- }
- }
|