1

Spring-data-redis キャッシュ (1.6.1)、Jredis Client 2.7.3 を使用しています。フェイルオーバーを有効にしたい。redis サーバーがダウンしても、アプリケーションが動作し続けてほしい

1) spring-data xml 構成を使用した場合、redis キャッシュは正常に動作します。バックエンドを呼び出し、データをキャッシュし、2 回目の呼び出しでバックエンドを呼び出しません。ただし、xml 構成を使用してサーバーがダウンしている場合に Redis Exception をキャプチャし、アプリケーションが動作し続けるように null を返す方法がわかりません。十分なドキュメントがありません。(このソリューションは機能しません)

2) Java を使用して redis キャッシュを構成しました。この場合、フェイルオーバーの例外をキャッチできますが、Spring-data-redis は、redis キャッシュが機能している場合でも、バックエンド データベース メソッドを呼び出し続けます。そのため、バックエンド メソッドを呼び出してデータをキャッシュする必要があります。2 番目の呼び出しは、バックエンド データベースに戻るべきではありません。

したがって、すべてのリクエストでバックエンドデータベースメソッドをキャッシュして呼び出しています。

誰かがこの問題に直面したかどうか疑問に思っていました。または、バックエンド Redis サーバーがダウンしている場合に spring-data-redis をフェイルオーバーする方法についてのアイデア。

サンプルコードを添付しました。

ここに AppConfig.java があります

Configuration
@EnableCaching
@ComponentScan("com.mkyong.helloworld.service")
@PropertySource("classpath:/redis.properties")
public class AppConfig extends CachingConfigurerSupport{
    private @Value("${redis.host-name}") String redisHostName;
    private @Value("${redis.port}") int redisPort;
//  private @Value("${cache.expire}") long cacheExpire;
//  private @Value("${cache.name}") String cacheName; 


private Log logger = LogFactory.getLog(getClass());

 @Bean
 public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
     return new PropertySourcesPlaceholderConfigurer();
 }

 @Bean
 JedisConnectionFactory jedisConnectionFactory() {
     JedisConnectionFactory factory = new JedisConnectionFactory();
     factory.setHostName(redisHostName);
     factory.setPort(redisPort);
     factory.setUsePool(true);
     return factory;
 }

 @Bean
 RedisTemplate<Object, Object> redisTemplate() {
     RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
     redisTemplate.setConnectionFactory(jedisConnectionFactory());
     return redisTemplate;
 }

 @Bean
 public CacheManager cacheManager() {
//     return new RedisCacheManager(redisTemplate());
//      logger.debug("Calling Redis CustomRedisCacheManager()" );
//     return new CustomRedisCacheManager(redisTemplate());
     logger.debug("START: operation=cacheManager");
     UUID transactionId = UUID.randomUUID();
     long startTime = System.currentTimeMillis();
     long stopTime=0;
     long elapsedTime=0;
     String userTokenCache1="descCache";
     String userTokenCache2="titleCache";
     //Long expiration = environment.getProperty("cache.default.timeout", Long.class);
     Long expiration = 360000L;
     Map<String, Long> expires = new ConcurrentHashMap<>(1);
     expires.put(userTokenCache1, expiration);
     expires.put(userTokenCache2, expiration);
     CustomRedisCacheManager cacheMgr = new CustomRedisCacheManager( redisTemplate() );
     cacheMgr.setExpires(expires);
//   //cacheMgr.setDefaultExpiration(expires);
//   cacheMgr.setCacheNames(Arrays.asList(userTokenCache));
     stopTime = System.currentTimeMillis();
     elapsedTime = stopTime - startTime;
     logger.debug("Cache Name = " + userTokenCache1 + " cacheExpire=" + userTokenCache1 );
     logger.debug("END: transcation_id=" + transactionId + " operation=cacheManager" + " status=Completed, execution_time=" + elapsedTime );

     return cacheMgr;
 }

// @Bean // important!
// @Override
// public CacheErrorHandler errorHandler() {
//     // configure and return CacheErrorHandler instance
//   CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
//      
//      @Override
//      public void handleCachePutError(RuntimeException exception, Cache cache,
//              Object key, Object value) {
//          // TODO Auto-generated method stub
//          logger.warn("As the Redis Cache server may be down. Unable to save Cache..." );
//      }
//      
//      @Override
//      public void handleCacheGetError(RuntimeException exception, Cache cache,
//              Object key) {
//          // TODO Auto-generated method stub
//          logger.warn("As the Redis Cache server may be down. Fetching Data from the backend..." );
//      
//      }
//      
//      @Override
//      public void handleCacheEvictError(RuntimeException exception, Cache cache,
//              Object key) {
//          // TODO Auto-generated method stub
//          logger.warn("As the Redis Cache server may be down. Unable to evict cache..." );
//      }
//      
//      @Override
//      public void handleCacheClearError(RuntimeException exception, Cache cache) {
//          // TODO Auto-generated method stub
//          logger.warn("As the Redis Cache server may be down. Unable to clear cache..." );
//      }
//  };
//  return cacheErrorHandler;
// }

}

ここに HelloWorldServica.java があります

@Service
public class HelloWorldService {

    private static final Logger logger = LoggerFactory.getLogger(HelloWorldService.class);

     @Cacheable(value="descCache")
    public String getDesc() {

        logger.debug("getDesc() is executed!");

        return "Gradle + Spring MVC Hello World Example";

    }
     @Cacheable(value="titleCache")
    public String getTitle(String name) {

        logger.debug("getTitle() is executed! $name : {}", name);

        if(StringUtils.isEmpty(name)){
            return "Hello World";
        }else{
            return "Hello " + name;
        }

    }

}

WelcomeController.java

@Controller
public class WelcomeController {

    private final Logger logger = LoggerFactory.getLogger(WelcomeController.class);
    private final HelloWorldService helloWorldService;
    ApplicationContext ctx = null;

   @Autowired
    public WelcomeController(HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
        ctx = new AnnotationConfigApplicationContext(AppConfig.class);

   }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(Map<String, Object> model) {

        logger.debug("index() is executed!");

        model.put("title", helloWorldService.getTitle(""));
        model.put("msg", helloWorldService.getDesc());

        return "index";
    }

    @RequestMapping(value = "/hello/{name:.+}", method = RequestMethod.GET)
    public ModelAndView hello(@PathVariable("name") String name) {

        logger.debug("hello() is executed - $name {}", name);

        ModelAndView model = new ModelAndView();
        model.setViewName("index");

        model.addObject("title", helloWorldService.getTitle(name));
        model.addObject("msg", helloWorldService.getDesc());

        return model;

    }

    class CustomRedisCacheManager extends RedisCacheManager {
    private static Log logger = LogFactory.getLog(CustomRedisCacheManager.class);
    public CustomRedisCacheManager(RedisTemplate redisTemplate) {
        super(redisTemplate);
    }
    @Override
    public Cache getCache(String name) {
        return new RedisCacheWrapper(super.getCache(name));
    }

    protected static class RedisCacheWrapper implements Cache {
        private final Cache delegate;
        public RedisCacheWrapper(Cache redisCache) {
            logger.debug("Start:RedisCacheWrapper()" );
            UUID transactionId = UUID.randomUUID();
            long startTime = System.currentTimeMillis();
            long stopTime=0;
            long elapsedTime=0;
            Assert.notNull(redisCache, "'delegate' must not be null");
            this.delegate = redisCache;
            stopTime = System.currentTimeMillis();
            elapsedTime = stopTime - startTime;
            logger.info(" transcation_id=" + transactionId + " operation=RedisCacheWrapper" + " status=Completed, execution_time (ms)=" + elapsedTime );
            logger.debug("End:RedisCacheWrapper()" );

        }
        @Override
        public Cache.ValueWrapper get(Object key) {
            logger.debug("As the Redis Cache server may be down. Unable to save Cache..." );
            try {
                delegate.get(key);
            }
            catch (Exception e) {
                try {
                    return handleErrors(e);
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
            return null;
        }
        @Override
        public void put(Object key, Object value) {
            try {
                delegate.put(key, value);
            }
            catch (Exception e) {
                try {
                    handleErrors(e);
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        }
        @Override
        public String getName() {
            // TODO Auto-generated method stub
            return null;
        }
        @Override
        public Object getNativeCache() {
            // TODO Auto-generated method stub
            return null;
        }
        @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;
        }
        @Override
        public void evict(Object key) {
            // TODO Auto-generated method stub

        }
        @Override
        public void clear() {
            // TODO Auto-generated method stub

        }

        // implement clear(), evict(key), get(key, type), getName(), getNativeCache(), putIfAbsent(key, value) accordingly (delegating to the delegate).
        protected <T> T handleErrors(Exception e) throws Exception {
            UUID transactionId = UUID.randomUUID();
            long startTime = System.currentTimeMillis();
            long stopTime=0;
            long elapsedTime=0;
            logger.debug("Exception Thrown" + e.getMessage() );
            if (e instanceof RuntimeException ) 
            {
                stopTime = System.currentTimeMillis();
                elapsedTime = stopTime - startTime;
                logger.info(" transcation_id=" + transactionId + " operation=Redis Cache" + " status=Redis cahce may be down, return null, cause=" + e.getMessage() + " execution_time=" + elapsedTime );
                return null;
            } else {
                throw e;
            }
//          else if (<something different>) { // act appropriately }
//              else {
//                  throw e;
//              }
//          }
        }

    }
4

1 に答える 1

0

私の悪い..私はこの問題を修正しました。get 関数から null を返していました

   class CustomRedisCacheManager extends RedisCacheManager {
    private static Log logger = LogFactory.getLog(CustomRedisCacheManager.class);
    public CustomRedisCacheManager(RedisTemplate redisTemplate) {
        super(redisTemplate);
    }
    @Override
    public Cache getCache(String name) {
        return new RedisCacheWrapper(super.getCache(name));
    }

    protected static class RedisCacheWrapper implements Cache {
        private final Cache delegate;
        public RedisCacheWrapper(Cache redisCache) {
            logger.debug("Start: operation=RedisCacheWrapper" );
            UUID transactionId = UUID.randomUUID();
            long startTime = System.currentTimeMillis();
            long stopTime=0;
            long elapsedTime=0;
            Assert.notNull(redisCache, "'delegate' must not be null");
            this.delegate = redisCache;
            stopTime = System.currentTimeMillis();
            elapsedTime = stopTime - startTime;
            logger.debug(" transcation_id=" + transactionId + " operation=RedisCacheWrapper" + " status=Completed, execution_time (ms)=" + elapsedTime );
            logger.debug("End: operation=RedisCacheWrapper" );

        }
        @Override
        public Cache.ValueWrapper get(Object key) {
            logger.debug("Start: operation=get(Object key)" );

            System.out.println("Start: get(Object key)" );
            try {
                Cache.ValueWrapper valueWrapper=delegate.get(key);
                System.out.println("End: get(Object key) returning valueWrapper" );
                logger.debug("End: operation=get(Object key)" );
                ***return valueWrapper;***
            }
            catch (Exception e) {
                try {
                    return handleErrors(e);
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                    return null;
                }
            }

        }
        @Override
        public void put(Object key, Object value) {
            logger.debug("Start: operation=put" );
            try {
                delegate.put(key, value);
                logger.debug("End: operation=put" );
            }
            catch (Exception e) {
                try {
                    handleErrors(e);
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        }
        @Override
        public String getName() {
            // TODO Auto-generated method stub
            return delegate.getName();
//          return null;
        }
        @Override
        public Object getNativeCache() {
            // TODO Auto-generated method stub
            return delegate.getNativeCache();

//          return null;
        }
        @Override
        public <T> T get(Object key, Class<T> type) {
            // TODO Auto-generated method stub
            return delegate.get(key, type);
            //return null;
        }
        @Override
        public ValueWrapper putIfAbsent(Object key, Object value) {
            // TODO Auto-generated method stub
            return delegate.putIfAbsent(key, value);
        }
        @Override
        public void evict(Object key) {
            // TODO Auto-generated method stub

            logger.info("Carch is evicted");
        }
        @Override
        public void clear() {
            // TODO Auto-generated method stub
            logger.info("Carch is clear");

        }

        // implement clear(), evict(key), get(key, type), getName(), getNativeCache(), putIfAbsent(key, value) accordingly (delegating to the delegate).
        protected <T> T handleErrors(Exception e) throws Exception {
            logger.debug("Start: operation=handleErrors" );
            UUID transactionId = UUID.randomUUID();
            long startTime = System.currentTimeMillis();
            long stopTime=0;
            long elapsedTime=0;
            logger.debug("Exception Thrown" + e.getMessage() );

            if (e instanceof RuntimeException ) 
            {
                stopTime = System.currentTimeMillis();
                elapsedTime = stopTime - startTime;
                logger.debug(" transcation_id=" + transactionId + " operation=handleErrors" + " status=Redis cahce may be down, return null, cause=" + e.getMessage() + " execution_time=" + elapsedTime );
                return null;
            } else {
                logger.debug(" transcation_id=" + transactionId + " operation=handleErrors" + " status=Redis cahce may be down, Some Other exception, cause=" + e.getMessage() + " execution_time=" + elapsedTime );
                throw e;
            }
//          else if (<something different>) { // act appropriately }
//              else {
//                  throw e;
//              }
//          }
        }

    }
}
于 2016-01-05T18:27:38.980 に答える