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;
// }
// }
}
}