大家好,我是烤鸭:
标题起的有点大了,说是自研,其实就是个封装,不过倒是解决了dns切换的问题(虽然不太优雅)。
背景
之前做活动的时候,用域名链接的redis,当时做了主备集群,在主集群宕机的时候,可以自动链接到备用集群,发现不好使。具体原因可以看下我写的这篇。
https://blog.csdn.net/Angry_Mills/article/details/119218854
还有人留言说已经实现了。
不过也没找到代码,就着这个思路自己开发一个吧。
思路
就跟上面这个评论一样,既然没办法从池化的地方入手,就重建client吧,不过重建的过程一定会有一些损失。
- 初始化redis的bean
- aop切面监听异常
- 达到指定条件,销毁并重建bean(指定异常出现多少次之类的)
开发测试
想测试的话,后边可以直接下载源码。
需要redis集群,搭建可以参考
https://blog.csdn.net/Angry_Mills/article/details/123139680
核心代码在切面,我这里使用aop实现的,这里用的重建逻辑也是比较简单的,判断超时10次,就会重建,为了方便复现,超时时间设置的5ms。
线上中需要考虑实际场景,比如超时时间的设置,以及超时次数,或者可以设置时间窗口(1分钟内超时100次之类的),再进行重建。
@Around("redisApi()")public Object doProfiling(ProceedingJoinPoint point) throws Throwable {log.info("进入aop===================="+errorCount);long initTime = System.currentTimeMillis();long sTime = initTime;String met = null; // 拦截方法名称Object args = null; // 拦截的方法参数Object ret = null; // 拦截方法返回值String clusterName = "";if (point.getTarget() instanceof MagRedisServiceImpl) {clusterName = ((MagRedisServiceImpl) point.getTarget()).getClusterName();}try {if (point.getArgs().length > 0) {//只取第一个参数args = point.getArgs()[0];}MethodSignature methodSignature = (MethodSignature) point.getSignature();met = methodSignature.getName();sTime = System.currentTimeMillis();//单独获取系统时间。减少对系统的交互。毫秒值。ret = point.proceed();} catch (Throwable t) {log.error(t.getMessage(), t);if(t instanceof RedisSystemException || t instanceof QueryTimeoutException && t.getCause() instanceof RedisCommandTimeoutException){log.info("redis 连不上了=========="+errorCount);errorCount ++;if (errorCount == 10) {rebuildRedisTemplate();errorCount = 1;}}throw t;//异常向上抛出} finally {}return ret;}
redis 链接正常的时候,多次访问
宕机redis后,第10次访问触发重建逻辑
切换本地dns后,访问正常
最后说一下
demo和线上使用还是有很大区别的,一般redis超时设置可能在100ms以内,如果设置的次数过多,可能超时的时间很长。
还是设置时间窗口+异常类型+超时次数,比较合理。
像RedisCluster is Down 这种的,可能要优先处理。
不过很少有主备集群同时存在,除非做活动或者比较重要的业务,中间件出问题的概率比服务要低很多(不过出了就是大问题)。
源码下载
https://gitee.com/fireduck_admin/redis-maggie-dns