使用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
相关产品推荐
相关产品推荐

