解决K8S多实例AI回答消息丢失及错乱问题

1. 现象描述

发布到生产环境[K8S server端服务2个pod, web端1个pod]后我们询问AI,发现AI回答卡死,查看日志是因为多实例问题导致SseEmitter异常:No emitter found for client

2. 分布式锁解决无法回复问题

主要通过Redis主题订阅消息监听机制实现

@Component
@Slf4j
public class SseServer {

    private final Map<String, SseEmitter> EMITTERS = new ConcurrentHashMap<>();
    private final RedissonClient redissonClient;
    private RTopic topic;

    @Autowired
    public SseServer(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @PostConstruct
    public void init() {
        topic = redissonClient.getTopic("ai-price-sse-channel");

        // 订阅 Redis 频道,接收消息并推送给本地客户端
        topic.addListener(String.class, (channel, message) -> {
            EMITTERS.forEach((account, emitter) -> {
                try {
                    if (message.startsWith(account + "@:@")) {
                        emitter.send(message.replaceFirst(account + "@:@", ""));
                    }
                } catch (IOException e) {
                    emitter.completeWithError(e);
                    EMITTERS.remove(account);
                }
            });
        });
    }

    public SseEmitter subscribe(String token) {
        String account = TokenUtils.getClaimsFromToken(token).getAccount();
        SseEmitter emitter = new SseEmitter(0L);
        EMITTERS.put(account, emitter);

        emitter.onCompletion(() -> {
            System.out.println("SSE connection completed for client: " + account);
            EMITTERS.remove(account);
        });

        try {
            emitter.send(SysConstants.SSE_START);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        emitter.onTimeout(() -> {
            System.out.println("SSE connection timed out for client: " + account);
            emitter.complete();
            EMITTERS.remove(account);
        });
        return emitter;
    }

//    public static void sendMsg(String account, Object message) {
//        SseEmitter emitter = EMITTERS.get(account);
//        if (emitter != null) {
//            try {
//                emitter.send(message);
//            } catch (IOException e) {
//                EMITTERS.remove(account);
//                emitter.completeWithError(e);
//            }
//        } else {
//            System.out.println("No emitter found for client: " + account);
//        }
//    }

    public void sendMsg(String account, Object message) {
        try {
            // 发布消息到 Redis 频道,格式可以自定义,例如 "account:message"
            topic.publish(account + "@:@" + message.toString());
        } catch (Exception e) {
            log.error("Failed to publish message to Redis: {}", e.getMessage());
        }
    }

    public String closeConnection() {
        String account = TokenUtils.getClaimsFromToken().getAccount();
        SseEmitter emitter = EMITTERS.remove(account);
        if (emitter != null) {
            emitter.complete();
            return "Connection closed for client: " + account;
        }
        return "No active connection found for client: " + account;
    }
}

3. K8S会话粘滞机制

通过上述,多实例确实解决了无法回复问题,但是发现会出现AI重复回答问题,原因是什么同一客户端发送到不同的后端服务实例导致,我们可以通过配置K8S来解决

---
apiVersion: v1
kind: Service
metadata:
  name: fangjiatong-server
  namespace: ai
spec:
  type: NodePort
  # K8S会话粘滞,解决豆包乱回答问题
  sessionAffinity: ClientIP

上述是配置发布的K8S service中,下面的配置在同一集群的命名空间中,假设您的web与server端会与同一K8S集群

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fangjiatong-ingress
  namespace: ai
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "SESSIONID"
    nginx.ingress.kubernetes.io/session-cookie-expires: "3600"
spec:
  rules:
    - host: fangjiatong-server.ai
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: fangjiatong-server
                port:
                  number: 8080

在这里插入图片描述
OK,已解决,欢迎关注算法小生公众号~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法小生Đ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值