alicdn边缘节点不稳定导致页面崩溃问题

问题概述

某工作日,线上某用户向客服专员反馈没法正常访问“查看报价页面”,页面内容没有呈现。客服专员收到反馈后,将问题转交给SRE处理。很奇怪的是,SRE访问生产环境“查看报价页面”显示正常,为了进一步分析定位问题,SRE向用户申请了远程操作,将将一些具有价值的信息记录下来,主要有以下两个方面:

  • 用户访问“查看报价页面”存在样式和字体文件没有加载成功;

    alicdn边缘节点不稳定导致页面崩溃问题_第1张图片

  • 没有加载成功的字体和样式文件的请求域名并不是公司的,而是公网免费的域名(at.alicdn.com、g.alicdn.com);

分析与定位

通过上述信息,可以知道用户与SRE访问页面的差异,SRE访问“查看报价页面”可以正常获取所有资源,而用户无法获取部分字体和样式文件。根据浏览器加载渲染原理,部分字体和样式加载失败大概率不会导致页面DOM无法呈现,无法下结论之时,不妨先假设字体和样式文件影响到了DOM渲染。

当无法从表象分析出线上问题原因时,第一步需要在开发环境或者测试环境复现问题场景,然后排查从请求资源到页面渲染的执行过程。

问题的引入点:域名解析

在复现场景之前,需要先知道访问成功和失败之间的差异。通过收集到的信息来看,请求域名解析的IP有明显不同:

  • 正常访问资源,DNS域名解析
Request URL Remote Address
https://at.alicdn.com/t/font_1353866_klyxwbettba.css 121.31.31.251:443
https://g.alicdn.com/de/prismplayer/2.9.21/skins/default/aliplayer-min.css 119.96.90.252:443
https://at.alicdn.com/t/font_2296011_yhl1znqn0gp.woff2 121.31.31.251:443
https://at.alicdn.com/t/font_1353866_klyxwbettba.woff2?t=1639626666505 121.31.31.251:443
  • 生产环境请求资源失败,DNS域名解析

    • at.alicdn.com116.153.65.231
    • g.alicdn.com211.91.241.230

用户和SRE所处地区不同,访问资源时域名解析命中的边缘节点服务也会不同,而at.alicdn.comg.alicdn.com是公网免费的CDN域名,某些边缘节点服务稳定性不够,拉取不到资源也是可能发生的。

问题根本原因:模块加载

开发环境与测试环境复现差异

修改本地hosts,添加用户域名解析的地址映射,在测试环境和开发环境尝试复现。两个环境均不能获取到字体和样式文件,测试环境(https://ec-hwbeta.casstime.com)页面内容没有呈现(复现成功),开发环境页面内容正常呈现(复现失败),分析开始陷入胡同。

开发环境:

alicdn边缘节点不稳定导致页面崩溃问题_第2张图片

测试环境:

alicdn边缘节点不稳定导致页面崩溃问题_第3张图片

这时候就要开始分析了,两个环境复现问题的差异点在哪里?

不难发现,两个环境最主要的区别在于yarn startyarn build的区别,也就是构建配置的区别。

开发环境

1、create-react-app关键构建配置

  • 启用style-loader,默认通过style标签将样式注入到html中;
  • 不启用MiniCssExtractPlugin.loader分离样式和OptimizeCSSAssetsPlugin压缩样式;
  • 启用optimization.splitChunks代码分割;
  • 启用optimization.runtimeChunk抽离webpack运行时代码;
const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [
    isEnvDevelopment && require.resolve('style-loader')
    isEnvProduction && {
      loader: MiniCssExtractPlugin.loader,
      // css is located in `static/css`, use '../../' to locate index.html folder
      // in production `paths.publicUrlOrPath` can be a relative path
      options: paths.publicUrlOrPath.startsWith('.')
        ? { publicPath: '../../' }
        : {},
    },
  ].filter(Boolean);
  
  return loaders;
}

module: {
  rules: [
    {
      oneof: [
        {
          test: cssModuleRegex,
          use: getStyleLoaders({
            importLoaders: 1,
            sourceMap: isEnvProduction && shouldUseSourceMap,
            modules: {
              getLocalIdent: getCSSModuleLocalIdent,
            },
          }),
        },
        {
          test: sassModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 3,
              sourceMap: isEnvProduction && shouldUseSourceMap,
              modules: {
                getLocalIdent: getCSSModuleLocalIdent,
              },
            },
            'sass-loader'
          ),
        },
      ]
    }
  ]
}
  
optimization: {
  minimize: isEnvProduction,
  minimizer: [
      // 压缩css
    new OptimizeCSSAssetsPlugin({
      cssProcessorOptions: {
        parser: safePostCssParser,
        map: shouldUseSourceMap
          ? {
              // `inline: false` forces the sourcemap to be output into a
              // separate file
              inline: false,
              // `annotation: true` appends the sourceMappingURL to the end of
              // the css file, helping the browser find the sourcemap
              annotation: true,
            }
          : false,
      },
    })
  ],
  // Automatically split vendor and commons
  // https://twitter.com/wSokra/status/969633336732905474
  splitChunks: {
    chunks: 'all',
    name: false,
  },
  // Keep the runtime chunk separated to enable long term caching
  runtimeChunk: {
    name: entrypoint => `runtime-${entrypoint.name}`,
  },  
}

alicdn边缘节点不稳定导致页面崩溃问题_第4张图片

css-loader在解析样式表中@importurl()过程中,如果index.module.scss中使用@import 引入第三方样式库aliplayer-min.css@import aliplayer-min.css部分和index.module.scss中其余部分将会被分离成两个module,然后分别追加到样式数组中,数组中的每个”样式项“将被style-loader处理使用style标签注入到html

alicdn边缘节点不稳定导致页面崩溃问题_第5张图片

alicdn边缘节点不稳定导致页面崩溃问题_第6张图片

alicdn边缘节点不稳定导致页面崩溃问题_第7张图片

alicdn边缘节点不稳定导致页面崩溃问题_第8张图片

2、执行链路

开发环境的构建配置基本清楚,再来看看执行流程。执行yarn start启用本地服务,localhost:3000访问“查看报价页面”。首先会经过匹配路由,然后react-loadable调用webpack runtime中加载chunk的函数__webpack_require__.e,该函数会根据入参chunkId使用基于promise实现的script请求对应chunk,返回Promise。如果Promise.all()存在一个Promise转变成Promise,那么Promise.all的执行结果就是Promise。因为css chunk是通过style标签注入到html中,所以__webpack_require__.e只需要加载js chunk,当所有的js chunk都请求成功时,Promise.all的执行结果就是Promisefulfilled状态会被react-loadable中的then捕获,更新组件内部状态值,触发重新渲染,执行render函数返回jsx element对象。因此,内容区域正常显示。

alicdn边缘节点不稳定导致页面崩溃问题_第9张图片

alicdn边缘节点不稳定导致页面崩溃问题_第10张图片

生产环境

1、create-react-app关键构建配置

  • 不启用style-loader,默认动态创建link标签注入样式;
  • 启用了MiniCssExtractPlugin.loader分离样式;
  • 启用optimization.splitChunks代码分割;
  • 为了更好的利用浏览器强缓存,设置optimization.runtimeChunk,分离webpack runtime
const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [
    isEnvDevelopment && require.resolve('style-loader')
    isEnvProduction && {
      loader: MiniCssExtractPlugin.loader,
      // css is located in `static/css`, use '../../' to locate index.html folder
      // in production `paths.publicUrlOrPath` can be a relative path
      options: paths.publicUrlOrPath.startsWith('.')
        ? { publicPath: '../../' }
        : {},
    },
  ].filter(Boolean);
  
  return loaders;
}

module: {
  rules: [
    {
      oneof: [
        {
          test: cssModuleRegex,
          use: getStyleLoaders({
            importLoaders: 1,
            sourceMap: isEnvProduction && shouldUseSourceMap,
            modules: {
              getLocalIdent: getCSSModuleLocalIdent,
            },
          }),
        },
        {
          test: sassModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 3,
              sourceMap: isEnvProduction && shouldUseSourceMap,
              modules: {
                getLocalIdent: getCSSModuleLocalIdent,
              },
            },
            'sass-loader'
          ),
        },
      ]
    }
  ]
}

optimization: {
  minimize: isEnvProduction,
  minimizer: [],
  // Automatically split vendor and commons
  // https://twitter.com/wSokra/status/969633336732905474
  splitChunks: {
    chunks: 'all',
    name: false,
  },
  // Keep the runtime chunk separated to enable long term caching
  runtimeChunk: {
    name: entrypoint => `runtime-${entrypoint.name}`,
  },
},

plugins: [
  // Generates an `index.html` file with the 
                    
                    

你可能感兴趣的:(alicdn边缘节点不稳定导致页面崩溃问题)