本人关于sse遇到的一些坑

背景

第一次写文章啊,先说明一些背景,up主最近在套壳openai做个chatgpt的小网站,前后端自己做,然后整合一些自己设计的业务,在对话的需要用到用sse,至于sse是个什么东西,websocket知道吧,它是相当于websocket的精简版,被动接收服务器推送过来的数据;

现象

这里主要是写我遇到的问题过程,我的一些思考过程;
当时部署好前后端到腾信云发现有个问题,就是前端sse每一分钟自动超时重连,下面先放下片段代码;

前端接收部分

// 前端建立
const token = getUserInfo()?.token;
      const source = new EventSource(`/sse/subscribe?Authorization=${token}`,{withCredentials:true});
      setBtnLoading(true)
      /**
       * 连接一旦建立,就会触发open事件
       * 另一种写法:source.onopen = function (event) {}
       */
      source.addEventListener('open', function (e) {
        console.log("建立连接。。。");
        setStatus(true);
        submitClick(source)
      }, false);

      /**
       * 客户端收到服务器发来的数据
       * 另一种写法:source.onmessage = function (event) {}
       */
      source.addEventListener('message',  function (e) {
        if (true) {
          setRawData(pre=>{
            let copyObj:ConversationDetail = JSON.parse(JSON.stringify(pre));
            // console.log(copyObj)
            const chatMessage: ChatMessage = copyObj.list[copyObj.list.length - 1];

            chatMessage.content = chatMessage.content + msg;
            copyObj.list[copyObj.list.length - 1] = chatMessage;
            return copyObj;
          })
        }
      });

      /**
       * 如果发生通信错误(比如连接中断),就会触发error事件
       * 另一种写法:source.onerror = function (event) {}
       */
      source.addEventListener('error', function (e) {
        message.error("连接异常")
        if (e.readyState === EventSource.CLOSED) {
          // console.log("连接关闭");
        } else {
          // console.log(e);
        }
        closeSse(source)
      }, false);

后端部分代码

// controller部分

@Value("${sse.timeout:600}")
private Integer sseTimeout;
   
@GetMapping(path = "sse/subscribe", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})
    public SseEmitter subscribe(String Authorization) {
        SysUserDto user = securityService.getUserInfo(Authorization);
        MAssert.notNull(user,"未登录");
        return SseEmitterConnections.connect(Authorization,sseTimeout);
    }

// sse工具类
public static  SseEmitter connect(String key){
        return connect(key,600);
    }

    public static  SseEmitter connect(String key,int sseTimeout) {
        try {
            // 设置超时时间,0表示不过期。默认30秒
            SseEmitter sseEmitter = new SseEmitter(sseTimeout * 1000L);
            // 注册回调
            sseEmitter.onCompletion(completionCallBack(key));
            sseEmitter.onError(errorCallBack(key));
            sseEmitter.onTimeout(timeoutCallBack(key));
            sseEmitterMap.put(key, sseEmitter);
            // 数量+1
            count.getAndIncrement();
            log.info("================ 建立连接  Key为:{}", key);

            return sseEmitter;
        } catch (Exception e) {
            log.info("创建新的SSE连接异常,当前连接Key为:{}", key);
        }
        return null;
    }

这样一看好像没什么问题,其中后端的sseTimeout我设置600秒超时;但是问题来了,前端每一分钟会自己跳进error的回调函数,然后自己重连;

在这里插入图片描述

一开始我觉得应该是nginx代理有问题,因为sse是走nginx的代理,有可能跟nginx里面的keepalive_timeout,proxy_read_timeout,proxy_send_timeout有关系,后面我就根据一番百度和chatgpt,把nginx参数配置如下

	location /sse {
   proxy_buffering off;
    proxy_cache off;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	 proxy_read_timeout 500;  
    	proxy_send_timeout 500;
	 keepalive_timeout  500;
		proxy_pass http://localhost:9100;
	}

这个配置大概就是500s超时,nginx -s reload之后,前端还是一分钟就超时断开,不可能!

后面我就觉得是不是keepalive_timeout,proxy_read_timeout,proxy_send_timeout不生效呢,我就把500全部调成30,然后sse前端30秒就超时了,证明这几个参数是生效的,是不是有什么更上级的东西限制它一分钟就超时呢?防火墙?还是说ssl_session_timeout?

为了更好的排查问题,先把后端9100端口在腾讯云开放出来,然后sse直接连接9100端口,实时上sse还是一分钟超时了,那就证明不是nginx问题。

只能用排除法了,现在排除的是java后端配置问题(java后端是配置600秒超时,本地机子也测试过没问题),排出nginx问题(因为直连后端也是1分钟超时),现在我想到问题就是防火墙了。

但是后面我又百度和gpt了一下,好像防火墙没有方面特定限制,而且它tcp超时默认两个小时,后面我想了一下,会不会是前端代码本来就是有问题呢?我用了crul http://ip:port/sse/subscribe?Authorization=xxx 测试了一下,结果它没有一分钟超时,而是10分钟超时,这个证明不是防火墙是没问题的,也证明服务器的设置是正确的。

似乎种种问题指向前端代码有问题,于是乎我又做了实验,在本地机子启动后端,然后前端直连到本地后端服务,这次惊喜来了,sse居然正常(10分钟才超时重连),这次确实把我愣住了,各种矛盾无法解释无法找到突破口;

再把问题屡一下(正常情况是10分钟超时):
1、前端连接远程sse服务,一分钟超时;
2、curl连接远程sse服务,10分钟超时;
3、前端连接本地sse服务,10分钟超时;

其中第2点证明远程服务和防火墙的设置都没有问题,第3证明前端代码和后端代码都没问题;

然后不了了之几天后,我意外的关掉了本机的vpn,结果发现一切正常,居然是本机的vpn搞的鬼,这么一来似乎都解释的清楚;点3说明前后端代码没问题,点2说明腾讯云没问题,那就证明本机通往服务器的链路出了问题,至于为什么前端连接本地sse服务也正常,只能说明内部的网络它不走vpn。

这个问题真的是搞的心力交瘁,完全找不到突破口,碰了所有壁,各种现象都是自我矛盾,如果不是意外关掉vpn我估计也无法发现问题。

第一次写文章,记录一下自己的坑和思考过程,另外网站的地址是 https://www.crmei.space

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值