我是靠谱客的博主 大胆砖头,最近开发中收集的这篇文章主要介绍sprintboot redis异常处理CacheErrorHandler详解以及性能问题分析redis配置CacheErrorHandler实现日志情况解决方法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

最近考虑redis异常处理,网上很多资料都是实现了CacheErrorHandler进行处理的,但是忽略了一个性能问题,下面介绍一下。

redis配置

其他配置忽略,只关心下面配置:

# 连接超时时间(毫秒)
spring.redis.timeout=400

CacheErrorHandler实现

如下:https://www.cnblogs.com/zhizhao/p/10151287.html

/**
     * redis数据操作异常处理 这里的处理:在日志中打印出错误信息,但是放行
     * 保证redis服务器出现连接等问题的时候不影响程序的正常运行,使得能够出问题时不用缓存
     *
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache,
                                            Object key, Object value) {
                RedisErrorException(exception, key);
            }
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache,
                                            Object key) {
                RedisErrorException(exception, key);
            }
            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache,
                                              Object key) {
                RedisErrorException(exception, key);
            }
            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                RedisErrorException(exception, null);
            }
        };
        return cacheErrorHandler;
    }
    protected void RedisErrorException(Exception exception,Object key){
        log.error("redis异常:key=[{}]", key, exception);
    }

日志情况

当redis出现异常时,CacheErrorHandler捕获异常进行处理:

[DEBUG] [i.l.c.c.PooledClusterConnectionProvider] getConnection(READ, 14016)
[DEBUG] [i.l.c.protocol.DefaultEndpoint] [channel=0x2cfb4178, /10.4.10.13:62530 -> /10.106.157.104:9012, epid=0x8] writeToDisconnectedBuffer() buffering (disconnected) command ClusterCommand [command=AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], redirections=0, maxRedirections=5]
[DEBUG] [i.l.c.protocol.DefaultEndpoint] [channel=0x2cfb4178, /10.4.10.13:62530 -> /10.106.157.104:9012, epid=0x8] write() done
[ERROR] [c.l.n.m.c.JedisClusterConfig] redis异常:key=[....], exception=Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 400 millisecond(s)

....
读取db
....

[DEBUG] [i.l.c.c.PooledClusterConnectionProvider] getConnection(WRITE, 14016)
[DEBUG] [i.l.c.protocol.DefaultEndpoint] [channel=0x2cfb4178, /10.4.10.13:62530 -> /10.106.157.104:9012, epid=0x8] writeToDisconnectedBuffer() buffering (disconnected) command ClusterCommand [command=AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], redirections=0, maxRedirections=5]
[DEBUG] [i.l.c.protocol.DefaultEndpoint] [channel=0x2cfb4178, /10.4.10.13:62530 -> /10.106.157.104:9012, epid=0x8] write() done
[ERROR] [c.l.n.m.c.JedisClusterConfig] redis异常:key=[....], exception=Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 400 millisecond(s)

    可以发现,当redis异常时,虽然使用CacheErrorHandler类捕获了异常,让程序正常运行获取数据库信息。但是会出现getConnection(READ, 14016)和getConnection(WRITE, 14016)两个获取链接等待超时。

    一般情况是,redis出现异常后,getConnection(WRITE, 14016)几乎也会失败,所以用CacheErrorHandler类捕获异常会导致多等待getConnection(WRITE, 14016)400 millisecond(s)。

解决方法

方法1

    不使用CacheErrorHandler类捕获异常,再函数外部使用try{} catch(Exception e) {}自己捕获异常,避免多等待getConnection(WRITE, 14016)400 millisecond(s)。

try {
//获取redis数据
data = getRedisData();
} catch (Exception e) {
if (e instanceof QueryTimeoutException ||
e instanceof RedisCommandTimeoutException || e instanceof RedisCommandExecutionException) {
//redis 异常处理
} else {
return error;
}
}

缺点就是不清楚有多少Exception,可能遗漏

 

方法2

参考:https://my.oschina.net/JasonZhang/blog/994028

public class AppCacheErrorHandler implements CacheErrorHandler{
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object o) {
if(e instanceof JedisConnectionException || e instanceof RedisConnectionFailureException){
logger.warn("redis has lose connection:",e);
return;
}
throw e;
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object o, Object o1) {
if(e instanceof JedisConnectionException || e instanceof RedisConnectionFailureException){
logger.warn("redis has lose connection:",e);
return;
}
throw e;
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object o) {
throw e;
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
throw e;
}
}
@Override
public CacheErrorHandler errorHandler() {
return new AppCacheErrorHandler();
}

作用就是将RuntimeException(注意,必须是RuntimeException,不能是Exception) 抛出去。

我优化了一下:

添加自定义MyRedisException:

public class MyRedisException extends RuntimeException {
public MyRedisException(){
super();
}
}

 

/**
     * redis数据操作异常处理 这里的处理:在日志中打印出错误信息,但是放行
     * 保证redis服务器出现连接等问题的时候不影响程序的正常运行,使得能够出问题时不用缓存
     *
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache,
                                            Object key, Object value) {
                RedisErrorException(exception, key);
            }
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache,
                                            Object key) {
                RedisErrorException(exception, key);
            }
            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache,
                                              Object key) {
                RedisErrorException(exception, key);
            }
            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                RedisErrorException(exception, null);
            }
        };
        return cacheErrorHandler;
    }
    protected void RedisErrorException(Exception exception,Object key){
        log.error("redis异常:key=[{}]", key, exception);
thow new MyRedisException();
    }

捕获MyRedisException:

try {
//获取redis数据
data = getRedisData();
} catch (Exception e) {
if (e instanceof MyRedisException) {
//redis 异常处理
} else {
return error;
}
}

这样,所有redis异常都能捕获了,不用再多等待getConnection(WRITE, 14016)400 millisecond(s)。

最后

以上就是大胆砖头为你收集整理的sprintboot redis异常处理CacheErrorHandler详解以及性能问题分析redis配置CacheErrorHandler实现日志情况解决方法的全部内容,希望文章能够帮你解决sprintboot redis异常处理CacheErrorHandler详解以及性能问题分析redis配置CacheErrorHandler实现日志情况解决方法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(80)

评论列表共有 0 条评论

立即
投稿
返回
顶部