fayeah / blogs

方法论、问题驱动、总结
6 stars 0 forks source link

TroubleShooting Cache in Prod #38

Open fayeah opened 4 years ago

fayeah commented 4 years ago

线上有一个问题,然后在开发环境和测试环境都没有办法重现,那么就是用搜索引擎和Angular代码官方文档各种spike和research。

问题是什么

那么的问题是我们新加的一个navigation(导航)没有显示,但是当用户清除了该页面的缓存,重新刷新页面,问题依然存在。

image

只有当用户彻底清除了浏览器的cache之后,再次刷新页面,才得以看到新的feature。

image

问题之后的初步想法

当出现这个问题的时候,我脑袋里面第一个想法就是version没变。

以前遇到过一个icon在线上无法更新的问题,是因为文件名字没有改变,version也没有改变,所以图片会被浏览器缓存。那么解决的办法就是给图片加上hash,每次部署的时候改变hash值,从而更新图片内容。

Spike/Research

首先,我想要重现这个问题,但是本地始终无法重现。尝试重现的步骤:

  1. 运行了prod的buildnpm run build:prod,会在dist目录下生成bundle文件的列表

image

  1. 进入dist目录,使用http-server启动一个服务npx http-server,然后打开index.html即可看到build之后的网页

当我更新了代码,重复上述的步骤,发现我本地的bundle文件内容是更新了的,所以在本地无法重现的情况下,继续探索。

version 没有改变 我发现在build多次之后有些bundle文件的version改变了,比如main.{version_no}.js,但是有很多bundle文件却没有改变,比如那些我们使用懒加载的模块1.{version_no}.js等,而我去检查了angular的配置文件angular.json,对于version的改变是对于所有都要变的:

"configurations": {
  "production": {
    "optimization": true,
    "outputHashing": "all",        // Define the output filename cache-busting hashing mode.
    "sourceMap": false,
    "extractCss": true,
    "namedChunks": false,
    "aot": true,
    "extractLicenses": true,
    "vendorChunk": false,
    "buildOptimizer": true,
    "serviceWorker": true          // Generates a service worker config for production builds.
  }
}

解决办法 那么目前能想到的方案就是在build之后对bundle文件加一个timestring的query,使得每次build之后所有的bundle文件version都有所改变:

stage('Append version to js file') {
  sh 'cd dist' +
    ' && export version_time=$(date +%Y%m%d%H%M%S)' +
    ' && echo $version_time' +
    ' && sed -i \'s/.html"/.html?v=\'$version_time\'"/g\' ngsw.json' +
    ' && sed -i \'s/.js"/.js?v=\'$version_time\'"/g\' ngsw.json' +
    ' && sed -i \'s/.js"/.js?v=\'$version_time\'"/g\' runtime*.js' +
    ' && sed -i \'s/.js"/.js?v=\'$version_time\'"/g\' index.html'
}​

index.html的Cache-Control 对于index.html来说,我们希望每次build都要重新加载,所以对这个文件我们不需要缓存,而我们使用的是nginx进行代理,所以我们做了一件事情,就是在index.html的header加上no-cache的关键字:

location / {
  try_files $uri $uri/ /index.html;
}

location = /index.html {
  expires 0;
  add_header Cache-Control no-cache;
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  add_header X-Frame-Options "sameorigin";
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";
}​

Service Worker的影响 猜测可能与service worker有关系,但是无从得证。项目确实用到了service worker,但是也只是对静态资源assets/下面的资源进行缓存,而且service worker在得知app整个version变化之后也会重新更新资源。

总结一下,