You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo Websocket更新Context时反复重连问题求助

Apollo Websocket反复连接问题(GQLgen后端 + Apollo前端)

问题现象

前端用Apollo管理GraphQL查询与订阅,后端基于GQLgen实现Resolver。发现异常:当后端webSocketInit函数返回新创建的context对象时,前端Websocket会不断重复连接;若直接返回原始ctx,所有功能(包括订阅)均正常运行。

后端代码(GQLgen)

Websocket配置(main.go)

srv.AddTransport(&transport.Websocket{
    Upgrader: websocket.Upgrader{
        ReadBufferSize:  2048,
        WriteBufferSize: 2048,
        // 跨域处理
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    },
    InitFunc: func(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
        return webSocketInit(ctx, initPayload)
    },
    KeepAlivePingInterval: 10 * time.Second,
})

webSocketInit函数

func webSocketInit(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
    // 处理认证token
    ctxNew := context.WithValue(ctx, "token", token)
    return ctxNew, nil
}

前端代码(Apollo Client)

订阅逻辑(client.tsx)

useEffect(() => {
    const unsubscribe = subscribeToMore({
        document: MY_SUBSCRIPTION_GQL,
        updateQuery: (prev, { subscriptionData }: any) => {
            // 处理新数据
            return Object.assign({}, prev, {
                data: newData,
            });
        },
        onError: (e) => {
            console.log(e);
        },
    });
    return () => unsubscribe();
}, []);

Apollo Client配置

const authLink = setContext((_, { headers }) => {
    const token = store.getState().user.token;
    return {
        headers: {
            ...headers,
            authorization: token ? `Bearer ${token}` : '',
        },
    };
});

const wsLink =
    typeof window !== 'undefined'
        ? new GraphQLWsLink(
              createClient({
                  url: 'ws://localhost:8080',
                  lazy: true,
                  connectionParams: () => ({
                      Authorization: store.getState().user.token,
                  }),

                  on: {
                      connected: () => {
                          console.log('connected');
                      },
                      error: (e) => {
                          console.log(e);
                      },
                  },
              })
          )
        : null;

// HTTP 请求链路
const httpLink = new HttpLink({
    uri: `http://localhost:8080/`,
});

const link =
    typeof window !== 'undefined' && wsLink != null
        ? split(
              ({ query }) => {
                  const def = getMainDefinition(query);
                  return def.kind === 'OperationDefinition' && def.operation === 'subscription';
              },
              wsLink,
              authLink.concat(httpLink)
          )
        : authLink.concat(httpLink);

export const client = new ApolloClient({
    link: link,
    cache: new InMemoryCache({
        addTypename: false,
    }),
});

问题分析与解决思路

核心排查点

  1. 隐性错误导致连接失败:webSocketInit中可能存在未捕获的错误(比如token解析失败、类型断言错误),即使代码未显式抛出,隐性错误会让InitFunc返回异常,导致后端拒绝连接,触发前端反复重连。
  2. 上下文传递完整性:context.WithValue本身会继承父上下文值,但GQLgen的Websocket Transport可能依赖原始ctx中的内部绑定状态(如心跳处理器、会话标识),若token处理逻辑无意中丢失这些状态,会导致连接无法维持。

具体解决步骤

  • 添加错误日志与参数校验:在webSocketInit中加入日志和校验,确保前端传递的参数合法:
    import (
        "log"
        "fmt"
        "strings"
    )
    
    func webSocketInit(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
        authToken, ok := initPayload["Authorization"].(string)
        if !ok || authToken == "" {
            log.Printf("Invalid auth payload received: %v", initPayload)
            return ctx, fmt.Errorf("missing or invalid authorization token")
        }
        // 统一处理token格式(去掉Bearer前缀)
        token := strings.TrimPrefix(authToken, "Bearer ")
        ctxNew := context.WithValue(ctx, "token", token)
        return ctxNew, nil
    }
    
  • 对齐前后端参数格式:确认前端connectionParams返回的Authorization字段格式和后端预期一致(比如是否带Bearer前缀)。
  • 测试Lazy模式:暂时将前端lazy: true改为lazy: false,排除懒加载导致的连接时序问题。
  • 验证心跳机制:后端设置了10秒Ping间隔,检查前端是否能正常响应Pong,未正确处理心跳也会触发重连。

内容的提问来源于stack exchange,提问作者Nicole Staline

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 23:15:54