Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CDNフル活用でつくる、高速Webアプリ / Using CDN To Improve Web...

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

CDNフル活用でつくる、高速Webアプリ / Using CDN To Improve Web Performance

Kazunari Hara

October 23, 2019

Transcript

69 slides
  1. CyberAgent Web Team is now on WebパフォーマンスとプロダクトKPIの相関を可視化 する話 https://developers.cyberagent.co.jp/blog/archives/9540/ アクセシビリティへの取り組み

    https://www.cyberagent.co.jp/way/csr/accessibility/ アメブロ2017 – 大規模サービスhttps化 https://developers.cyberagent.co.jp/blog/archives/7743/
  2. “Spike” - a sharp increase in the magnitude or concentration

    of visitors Spike reasons: - Breaking news - Launch days - Disasters - Bots - System issues
  3. Cache strategy - Keep resources static - Define consistent URLs

    - Set long cache time (TTL) - Divide endpoint if the resource changes frequently - Tagging cacheable resources and purging if necessary
  4. Static: - Same for everyone - Cacheable Dynamic: - Variable

    by user/session - Hard to cache efficiently Keep as many resources static as possible
  5. Keep as many resources static as possible Web App Assets:

    Entry Assets: {
 “id”: “12345”, 
 “title”: “voice” 
 }
 

  6. Most resources of こえのブログ are static since the app was

    made by a Client Side Rendering (CSR) App {
 “id”: “12345”, 
 “title”: “voice” 
 }
 
 Fetch data from JS when necessary
  7. Define consistent URLs using HTTP methods GET - Retrieve resource

    information only - Do not change the state of the resource - Return the same result every time
  8. Define resources as cacheable or not Cacheable: - Most GET

    endpoints Not cacheable: - Few GET endpoints with session - Few POST/PUT/DELETE endpoints
  9. URL example GET / GET /editor GET /src/**/*.js GET /images/**/*.{png|jpeg|svg}

    GET /assets/audios/${userId}/${entryId}/ GET /assets/images/${userId}/${entryId}/ GET /api/entry/${userId}/${entryId}/ POST /api/entry/${userId}/ PUT /api/entry/${userId}/${entryId}/ DELETE /api/entry/${userId}/${entryId}/ GET /api/auth/${userId}/status/ Cacheable
  10. Set cache TTL as long as possible Response header for

    CDN: “Surrogate-Control: max-age=” - Default: 2592000 (30 days) - Fallback: 86400 (1 day)
  11. Set cache TTL as long as possible Response header for

    browsers: “Cache-Control: max-age=” - Default: ※ 120 (2 minutes) - Fallback: 86400 (1 day) ※ Enabled longer cache with service worker
  12. Divide endpoint if the resource changes frequently Entry { “id”:

    “12345”, “title”: “voice”, “view-count”: 2946 } Meta data is immutable View count changes frequently
  13. Divide endpoint if the resource changes frequently Response header for

    stale content: “Stale-While-Revalidate:” CDN Origin Update data in background
  14. Purge the tag when resources have been updated New version

    release curl -XPOST -H “Fastly-Key:${KEY}” \n -H “Accept:application/json” \n “https://api.fastly.com/service/${SERVICE}/pu rge/webapp”
  15. Purge the tag when resources have been updated Data updated

    curl -XPOST -H “Fastly-Key:${KEY}” \n -H “Accept:application/json” \n -H “Surrogate-Key:blogger/ameba entry/12345” \n “https://api.fastly.com/service/...” Cloud Filestore
  16. These key ideas are also useful even when your app

    uses server side rendering (SSR) To be continued in the fastly meetup #3
  17. Cloud Filestore Cloud Storage Cloud Functions Stable Serverless with Fastly

    and GCP - High CDN cache coverage - Event-driven execution - Auto scalling - Deployability
  18. Cloud Storage Static files in Cloud Storage - Scalable -

    As an origin server - HMAC Authentication https://docs.fastly.com/en/guides/google-cloud-storage
  19. “deployability” is an important metric for app quality Over 1,000

    web app releasesin 10 month Over 200 VCL deploymentsin 10 month
  20. Browser cache strategy No Service Worker: - Cache-Control header Service

    Worker: - Precaching - Runtime caching Service Worker is available in most modern browsers today
  21. Web app assets are cached with service worker at first

    visit and are never re-fetched until updated // service-worker.js workbox.precaching.precacheAndRoute([ { url: "images/title_service_header.svg", revision: "6a048b548112674c9e65ed" }, { url: "index.html", revision: "6805b47012688211c81521" }, { url: "src/components/voice-app.js", revision: "fdf10df6f530cbce55c8f5" }, ... ]);
  22. Query normalization import querystring; sub vcl_recv { set req.url =

    querystring.sort(req.url); set req.url = querystring.regfilter_except( req.url, "^(language|format|offset|limit)$" ); } https://docs.fastly.com/vcl/query-string-manipulation/
  23. Vary normalization sub vcl_recv { if (req.http.User-Agent !~ "Edge" &&

    req.http.User-Agent ~ "Chrome/([^.]*)") { set req.http.X-UA = "esm"; } else { set req.http.X-UA = "es5"; } } sub vcl_fetch { set beresp.http.Vary = "Accept-Encoding, X-UA"; } https://docs.fastly.com/vcl/query-string-manipulation/
  24. Basic Auth table customer_keys { "Basic a2FrZXJ1OmthZXJ1": "kakeru", "Basic a2FlcnU6a2FrZXJ1":

    "kaeru" } sub vcl_recv { if (!table.lookup( customer_keys, req.http.Authorization )) { error 401 "Restricted"; } } https://docs.fastly.com/en/guides/basic-authentication
  25. Basic Auth sub vcl_error { if (obj.status == 401) {

    set obj.http.Content-Type = "text/html; charset=utf-8"; set obj.http.WWW-Authenticate = "Basic realm=Secured"; synthetic {"<!DOCTYPE..."}; return (deliver); } } https://docs.fastly.com/en/guides/basic-authentication
  26. Access control acl office_ip_ranges { "192.0.2.0"/24; "198.51.100.4"; } sub vcl_recv

    { if (!(client.ip ~ office_ip_ranges)) { error 403 "Forbidden"; } } https://docs.fastly.com/en/guides/about-acls
  27. Routing to different origins sub assets_origin { set req.backend =

    F_assets_origin; set req.http.host = "${api-assets-domain}"; } sub api_origin { set req.backend = F_api_origin; set req.http.host = "${api-org-domain}"; } sub vcl_recv { if (req.url.path ~ "^/assets/") { call assets_origin; } else if (req.url.path ~ "^/api/") { call api_origin; }... }
  28. Adding surrogate key sub vcl_fetch { declare local var.SurrogateKey STRING;

    set var.SurrogateKey = "assets"; if (req.http.x-url ~ "/audios/standard/([a-z0-9-]{3,24})/([a-zA-Z0-9]+)") { set var.SurrogateKey = var.SurrogateKey + " blogger/" + re.group.1 + " entry/" + re.group.2; } set beresp.http.Surrogate-Key = var.SurrogateKey; } Extracting keys from URL path
  29. Modifying response headers sub vcl_deliver { unset resp.http.via; unset resp.http.server;

    add resp.http.Server-Timing = fastly_info.state {", fastly;desc="Edge time";dur="} time.elapsed.msec; set resp.http.Referrer-Policy = "origin-when-cross-origin"; set resp.http.Strict-Transport-Security = "max-age=31536000"; set resp.http.X-Content-Type-Options = "nosniff"; add resp.http.Content-Security-Policy = "default-src 'self';...” add resp.http.Content-Security-Policy = "upgrade-insecure-requests"; } Removing unnecessary headrers Adding useful headers
  30. @herablog Related documents: - アメブロ2019: こえのブログでのPWA https://developers.cyberagent.co.jp/blog/archives/20506/ - Web App

    Checklist 〜高品質のWebアプリケーションをつくる ために〜 https://speakerdeck.com/herablog/web-app-checklist-2019-at-inside-frontend - こえのブログでのPWA ~ PWA編 ~ https://speakerdeck.com/herablog/pwa-night-vol-dot-4 - こえのブログでのPWA ~ 開発現場編 ~ https://speakerdeck.com/herablog/koe-no-blog-pwa - 最新CDN入門 WEB+DB PRESS Vol.109 https://gihyo.jp/magazine/wdpress/archive/2019/vol109

More Decks by Kazunari Hara

Other Decks in Technology