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

使用NextJS 13.4流式特性+React Suspense时出现connection closed错误求助

NextJS 13.4 App Router流式加载多数据时出现连接关闭错误

问题描述

使用NextJS 13.4的App Router,通过Suspense实现流式加载且加载超过两个数据时,会出现「connection closed」错误。本地开发环境无此问题,但部署到Vercel后触发。

相关代码

API请求封装

export const serverApi = async <T>(url: string, init?: RequestInit): Promise<T> => { 
  const response = await fetch(BASE_URL.API + url, init); 
  if (!response.ok) { 
    throw new Error(response.statusText); 
  } 
  return await (response.json() as Promise<T>); 
};

文章数据API调用

export const getArticles = async () => { 
  return await serverApi<Article[]>(API.ARTICLES, { cache: "no-store" }); 
}; 

export const getArticle = async (id: string) => { 
  return await serverApi<Article>(API.ARTICLE_WITH_ID(id), { cache: "no-store" }); 
};

Header组件实现

const Header = async () => { 
  return (
    <header className="relative w-full flex items-center justify-center">
      <div />
      <HeaderNavigation />
      <Suspense fallback={<HeaderProfileSpinner />}> 
        {/* @ts-ignore */}
        <HeaderProfile />
      </Suspense>
    </header>
  ); 
}; 

const HeaderProfile = async () => { 
  const cookie = getCookie(); 
  const queryClient = getQueryClient(); 
  await queryClient.prefetchQuery(USER_QUERY_KEYS.me(), () => getUserMe(cookie)); 
  const dehydratedState = dehydrate(queryClient); 
  return (
    <Hydrate state={dehydratedState}>
      <HeaderProfileImage />
    </Hydrate>
  ); 
};

环境信息

  • API服务采用NextJS Page Router开发
  • ORM使用Prisma,数据库为Supabase托管的PostgreSQL
  • 部署平台为Vercel

问题推测

「connection closed」错误应该是某个流式请求完成时服务器主动中断了连接,但这与流式加载并行渲染多内容的预期不符。

解决方案建议

1. 调整缓存策略减少并发请求

当前cache: "no-store"会强制每次请求都重新获取数据,可能触发Vercel的连接限制。可以尝试改为缓存策略:

// 使用短时间重验证缓存
export const getArticles = async () => { 
  return await serverApi<Article[]>(API.ARTICLES, { next: { revalidate: 60 } }); 
};

2. 优化并发请求数量

Vercel服务器函数对并发连接数有上限,过多并行流式请求可能导致连接提前关闭。可以合并Suspense边界,或用Promise.all批量处理请求:

// 示例:批量预取数据
const HeaderProfile = async () => { 
  const cookie = getCookie(); 
  const queryClient = getQueryClient(); 
  // 用Promise.all并行处理多个预取请求
  await Promise.all([
    queryClient.prefetchQuery(USER_QUERY_KEYS.me(), () => getUserMe(cookie)),
    // 其他需要预取的请求
  ]);
  const dehydratedState = dehydrate(queryClient); 
  return (
    <Hydrate state={dehydratedState}>
      <HeaderProfileImage />
    </Hydrate>
  ); 
};

3. 调整Prisma连接池大小

Postgres连接池默认大小为5,并发请求过多时会耗尽连接,导致API请求失败。修改prisma/schema.prisma:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  pool_size = 10 // 根据业务量调整,最大不超过Supabase的连接限制
}

4. 简化Server Component中的数据获取逻辑

在Server Component中使用React Query预取再脱水的方式可能增加额外开销,建议直接获取数据:

const HeaderProfile = async () => { 
  const cookie = getCookie(); 
  const user = await getUserMe(cookie);
  return <HeaderProfileImage user={user} />; 
};

5. 检查Vercel函数超时设置

Vercel服务器函数默认超时10秒,若请求处理时间过长会被强制关闭。可以在Vercel控制台的函数设置中延长超时时间,或优化API接口的响应速度(如添加数据库索引、优化查询逻辑)。

内容的提问来源于stack exchange,提问作者Mark Kim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:35:02