Next.js 服务端渲染超时导致生产事故

前言

前几天公司平台首页崩溃了 504 Time-out,这里记录一下问题排查和解决的过程。

排查和解决

排查过程

  • step 1:检查测试环境、开发环境未出现问题;
  • step 2:检查生产环境流水线,没有最新部署,排除前端新部署代码影响;
  • step 3:检查生产环境其他页面/about-us页面展示正常,但是部分接口报错超时。猜测是因为生产环境接口超时导致首页的服务端请求返回超时,导致首页返回超时;
  • step 4:本地开发调用生产环境接口,将所有在getServerSideProps中发起的请求全部注释后,首页可以正常加载。

问题定位

首页开启了SSR渲染,getServerSideProps中的请求使用了简单的fetch请求,只对普通错误做了兜底,但没有考虑到请求超时的问题,请求超时导致了页面返回超时,首页报了504错误。

解决方案

优化方案

  • 放弃使用SSR渲染,全部采用客户端请求渲染;
  • 修改现有fetch,通过AbortControllersetTimeout手动添加超时处理;
  • getServerSideProps的请求方式从fetch迁移到axios,借助axios本身自带的超时配置,添加接口超时处理。

方案比较

  • 放弃SSR不可取,因为客户端并发请求返回顺序的问题可能会导致首页的入场动画混乱,且失去了部分首屏性能优化;
  • 修改fetch方法,需要手写大量代码,且AbortController是在Node.js 14.17.0+开始支持,当然也可以不去放弃掉请求,可以通过setTimeoutPromise.race提前reject,不过需要因为是自行实现,风险大。
  • axios是十分成熟的请求库,且项目中已经引入,通过axios为请求添加超时处理十分方便。

实际解决方案

fetch迁移到axios,借助axios已有的超时处理配置解决当前问题,添加首页对getServerSideProps中请求超时的兜底,服务端请求超时后,立即返回空数组。

首页所有的数据消费组件继续兜底,当从getServerSideProps中获取的数据为空数组时,会在客户端再发一次请求,尝试从客户端获取数据。

js 复制代码
import axios from 'axios';

const ssrAxios = axios.create({
  timeout: 3000,
  timeoutErrorMessage: 'Request timed out'
});

export default ssrAxios;
tsx 复制代码
export const getServerSideProps = async () => {
  const addr = process.env.API_ADDR;

  // 使用 Promise.allSettled 获取所有请求的结果
  const [
    list1Res,
    // ... ,
    list2Res,
  ] = await Promise.allSettled([
    ssrAxios.get(`${addr}/api/list1`),
    // ... ,
    ssrAxios.get(`${addr}/api/list2`),
  ]);

  // 处理每个请求的结果
  const list1 = handleFetchResult(list1Res, 'list1');
  // ... ;
  const list2 = handleFetchResult(list2Res, 'list2');

  return {
    props: {
      list1: list1 ?? [],
      // ... ,
      list2: list2 ?? []
    },
  };
};

// Helper function to handle fetch results
const handleFetchResult =  (result: PromiseSettledResult<any>, key: string) => {
  if (result.status === 'rejected') {
    console.error(`Failed to fetch ${key}:`, result.reason);
    return [];
  }

  const { data, success } = result.value.data;
  if (!success) {
    console.error(`Failed to fetch ${key}`);
    return [];
  }

  switch (key) {
    case 'list1':
      return data?.result || [];
	// ... ;
    case 'list2':
      return data?.result || [];
    default:
      return [];
  }
};

问题探究

问题猜测

  • 接口性能瓶颈 :生产环境接口响应时间波动大,SSR并发请求存在短板
  • SSR 超时机制缺失:未设置合理的请求超时阈值,导致慢请求影响页面的生成和响应
  • 错误处理不完善:前端错误处理考虑不全面,未区分超时错误与常规错误,只做了无返回或返回错误的处理,没有对请求超时做兜底。

验证猜想

本地模拟接口返回超时,首页会无法成功加载。添加超时处理兜底后,首页其他内容可以正常展示。

询问deepseek后,得知Next.jsgetServerSideProps没有内置超时处理机制,因此长时间运行的getServerSideProps可能导致页面无法及时返回,甚至触发部署平台(如Vercel)的超时错误。

结论

首页开启了SSR渲染,Next.js 必须在完成getServerSideProps中的所有操作后才能生成页面并返回给客户端。 需要在得到所有需要的props后,才会返回整个页面。因为生产环境某个接口请求超时,并且没有正确处理,可能会导致整个getServerSideProps的执行时间延长,超过服务器的响应时间限制,从而使得页面无法及时返回,出现超时错误。

思考改进

思考

虽然是因为后端服务崩了,接口请求超时导致的问题,但是因为部分服务崩掉导致整个前端首页无法成功加载,作为前端开发人员的我还是有很大责任的。开发时我在对可能出现的错误进行兜底时,需要尽可能地考虑全面。

改进

服务端请求优化,错误处理添加对请求超时的处理。

① 首次访问使用SSR获取最新数据;

② 当SSR超时时自动降级到 CSR版本 ;

③ 保持首屏加载性能的同时提升可用性。

相关推荐
LYFlied10 分钟前
【算法解题模板】-【回溯】----“试错式”问题解决利器
前端·数据结构·算法·leetcode·面试·职场和发展
composurext10 分钟前
录音切片上传
前端·javascript·css
程序员小寒10 分钟前
前端高频面试题:深拷贝和浅拷贝的区别?
前端·javascript·面试
狮子座的男孩15 分钟前
html+css基础:07、css2的复合选择器_伪类选择器(概念、动态伪类、结构伪类(核心)、否定伪类、UI伪类、目标伪类、语言伪类)及伪元素选择器
前端·css·经验分享·html·伪类选择器·伪元素选择器·结构伪类
zhougl99616 分钟前
Vue 中的 `render` 函数
前端·javascript·vue.js
听风吟丶18 分钟前
Spring Boot 自动配置深度解析:原理、实战与源码追踪
前端·bootstrap·html
跟着珅聪学java18 分钟前
HTML中设置<select>下拉框默认值的详细教程
开发语言·前端·javascript
IT_陈寒19 分钟前
JavaScript 性能优化:5个被低估的V8引擎技巧让你的代码提速50%
前端·人工智能·后端
想睡好25 分钟前
setup
前端·javascript·html
光影少年36 分钟前
react navite相比较传统开发有啥优势?
前端·react.js·前端框架