lettuce重连后业务中断,没有异常抛出ConnectionWatchdog - Reconnecting, last destination was

记录

    线上业务中使用springboot的默认redis连接,当收到业务请求时最后打印日志:

[INFO ] 10:31:45.303 [lettuce-eventExecutorLoop-1-38] i.l.core.protocol.ConnectionWatchdog - Reconnecting, last destination was redis-service/XX.XXX.XXX.XXX:6379
[INFO ] 10:31:45.329 [lettuce-nioEventLoop-4-2] i.l.c.protocol.ReconnectionHandler - Reconnected to redis-service:6379

    然后业务中断,没有其他日志打印,线上日志级别为info

    前面研究了下连接池的各个参数,改完不管三七二十一先上去看看,不过不明白为什么连个错误日志也没有,如果连不上只有来个异常啊,到底被谁吃了,开始跟代码。

    首先看ReconnectionHandler,就是一个断开重连的方法,主要用的netty的api,如果出错都能抛出异常,查找无果,往上层找。

    找到ConnectionWatchdog 的run方法,他本身就声明了抛出exception,而且也没有吃异常,只能再往外找,找到scheduleReconnect()方法,这块开始反过来

reconnectWorkers.submit(() -> {
    ConnectionWatchdog.this.run(attempt);
    return null;
});

    这里重试的执行是使用了一个reconnectWorkers执行的,这个reconnectWorkers是一个EventExecutorGroup对象,也就是这个重试是使用了特定的线程池,警觉起来了哈。

    开始追踪线城池是个啥(弱鸡流程,以前没太看过netty)。reconnectWorkers是在ConnectionWatchdog的初始化时指定的,在ConnectionBuilder这个构造器中。

protected ConnectionWatchdog createConnectionWatchdog() {

    if (connectionWatchdog != null) {
        return connectionWatchdog;
    }

    LettuceAssert.assertState(bootstrap != null, "Bootstrap must be set for autoReconnect=true");
    LettuceAssert.assertState(timer != null, "Timer must be set for autoReconnect=true");
    LettuceAssert.assertState(socketAddressSupplier != null, "SocketAddressSupplier must be set for autoReconnect=true");

    ConnectionWatchdog watchdog = new ConnectionWatchdog(clientResources.reconnectDelay(), clientOptions, bootstrap, timer,
            clientResources.eventExecutorGroup(), socketAddressSupplier, reconnectionListener, connection);

    endpoint.registerConnectionWatchdog(watchdog);

    connectionWatchdog = watchdog;
    return watchdog;
}

    继续找clientResources的写入方式。防线是通过set方式写入,主要区分了两种不同redis的部署方式,我这里没有使用集群,追到redisClient的connectStatefulAsync方法。

    继续找到了AbstractRedisClient这个基类的构造方法

protected AbstractRedisClient(ClientResources clientResources) {

    if (clientResources == null) {
        sharedResources = false;
        this.clientResources = DefaultClientResources.create();
    } else {
        sharedResources = true;
        this.clientResources = clientResources;
    }

    genericWorkerPool = this.clientResources.eventExecutorGroup();
    channels = new DefaultChannelGroup(genericWorkerPool.next());
    timer = (HashedWheelTimer) this.clientResources.timer();
}

    如果为空,则有一个默认方式构造,不为空的话则使用传入的,这里我还往下追了下,不过发现越追越乱,然后回到这重新想了下,玩游戏时候的s/l大法开始起作用,打断点吧还是。

    AbstractRedisClient 的98行this.clientResources = DefaultClientResources.create();打上断点,但是竟然没跑,在这纠结了好久。

    最后在DefaultClientResources的create里打上断点,终于过来了,原来在lettuce的配置类中,默认就配置了这个默认的clientResource,详见LettuceConnectionConfiguration

    然后终于找到了本体的DefaultEventExecutor,这个类继承SingleThreadEventExecutor,然后在SingleThreadEventExecutor里找到了一圈,发现了这个宝贝方法:doStartThread()

try {
    SingleThreadEventExecutor.this.run();
    success = true;
} catch (Throwable t) {
    logger.warn("Unexpected exception from an event executor: ", t);
} finally {

    再看了下,可能就是这里把我得异常都吃了。

    做梦到此为止,不过完全不解决我得问题,确实他把我异常吃了,不过他这个重连策略我肯定是解决不了的,因为:

    我的服务部署在了k8s里,redis是使用service访问的,他的重连使用的是SocketAddress,这个就逆天了,会直接连ip,k8s都懂的,连ip就是扯淡,左右无法,看起来要强制使用jedis了,哪位大神有好的办法吗?

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值