你說,萬一接口掛了會怎么樣?
還能咋樣,白屏唄。
有沒有不白屏的方案?
有啊,還挺簡單的。
容我細細細細分析。
原因就是接口掛了,拿不到數據了。那把數據儲存起來就可以解決問題。
第一時間反應瀏覽器本地存儲,想起了四兄弟。
特性 | cookie | localStorage | sessionStorage | indexDB |
數據生命周期 | 服務器或者客戶端都可以設置、有過期時間 | 一直存在 | 關閉頁面就清空 | 一直存在 |
數據儲存大小 | 4KB | 5MB | 5MB | 動態,很大 大于250MB |
與服務器通信 | 每次都帶在header中 | 不帶 | 不帶 | 不帶 |
兼容性 | 都支持 | 都支持 | 都支持 | IE不支持,其他主流都支持 |
考慮到需要存儲的數據量,5MB 一定不夠的,所以選擇了 IndexDB。
考慮新用戶或者長時間未訪問老用戶,會取不到緩存數據與陳舊的數據。
因此準備上云,用阿里云存儲,用 CDN 來保障。
總結下:線上 CDN、線下 IndexDB。
圖片
先講講線上 CDN。
通常情況下可以讓后端支撐,本質就是更新策略問題,這里不細說。
我們講講另外一種方案,單獨啟個 Node 服務更新 CDN 數據。
圖片
劫持所有接口,判斷接口狀態與緩存標識。從而進行更新數據、獲取數據、緩存策略三種操作
通過配置白名單來控制接口存與取
axios.interceptors.response.use( async (resp) => { const { config } = resp const { url } = config // 是否有緩存tag,用于更新CDN數據。目前是定時服務在跑,訪問頁面帶上tag if (this.hasCdnTag() && this.isWhiteApi(url)) { this.updateCDN(config, resp) } return resp; }, async (err) => { const { config } = err const { url } = config // 是否命中緩存策略 if (this.isWhiteApi(url) && this.useCache()) { return this.fetchCDN(config).then(res => { pushLog(`cdn緩存數據已命中,請處理`, SentryTypeEnum.error) return res }).catch(()=>{ pushLog(`cdn緩存數據未同步,請處理`, SentryTypeEnum.error) }) } } );
累計接口異常發生 maxCount 次,打開緩存開關,expiresSeconds 秒后關閉。
緩存開關用避免網絡波動導致命中緩存,設置了閥值。
/** 緩存策略*/useCache = () => { if (this.expiresStamp > +new Date()) { const d = new Date(this.expiresStamp) console.warn(` --------------------------------------- --------------------------------------- 啟用緩存中 關閉時間:${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} --------------------------------------- --------------------------------------- `) return true } this.errorCount += 1 localStorage.setItem(CACHE_ERROR_COUNT_KEY, `${this.errorCount}`) if (this.errorCount > this.maxCount) { this.expiresStamp = +new Date() + this.expiresSeconds * 1000 this.errorCount = 0 localStorage.setItem(CACHE_EXPIRES_KEY, `${this.expiresStamp}`) localStorage.removeItem(CACHE_ERROR_COUNT_KEY) return true } return false}
根據 method、url、data 三者來標識接口,保證接口的唯一性
帶動態標識,譬如時間戳等可以手動過濾
/** * 生成接口唯一鍵值*/generateCacheKey = (config) => { // 請求方式,參數,請求地址, const { method, url, data, params } = config; let rawData = '' if (method === 'get') { rawData = params } if (method === 'post') { rawData = JSON.parse(data) } // 返回拼接key return `${encodeURIComponent([method, url, stringify(rawData)].join('_'))}.json`;};
/** * 更新cdn緩存數據*/updateCDN = (config, data) => { const fileName = this.generateCacheKey(config) const cdnUrl = `${this.prefix}/${fileName}` axios.post(`${this.nodeDomain}/cdn/update`, { cdnUrl, data })}
構建定時任務,用 puppeteer 去訪問、帶上緩存標識,去更新 CDN 數據
import schedule from 'node-schedule';const scheduleJob = {};export const xxxJob = (ctx) => { const { xxx } = ctx.config; ctx.logger.info(xxx, 'xxx'); const { key, url, rule } = xxx; if (scheduleJob[key]) { scheduleJob[key].cancel(); } scheduleJob[key] = schedule.scheduleJob(rule, async () => { ctx.logger.info(url, new Date()); await browserIndex(ctx, url); });};export const browserIndex = async (ctx, domain) => { ctx.logger.info('browser --start', domain); if (!domain) { ctx.logger.error('domain為空'); return false; } const browser = await puppeteer.launch({ args: [ '--use-gl=egl', '--disable-gpu', '--no-sandbox', '--disable-setuid-sandbox', ], executablePath: process.env.CHROMIUM_PATH, headless: true, timeout: 0, }); const page = await browser.newPage(); await page.goto(`${domain}?${URL_CACHE_KEY}`); await sleep(10000); // 訪問首頁所有查詢接口 const list = await page.$$('.po-tabs__item'); if (list?.length) { for (let i = 0; i < list.length; i++) { await list[i].click(); } } await browser.close(); ctx.logger.info('browser --finish', domain); return true;};
手動 block 整個 domain,整個頁面正常展示
圖片
線上有 CDN 保證了,線下就輪到 IndexDB 了,基于業務簡單的增刪改查,選用 localForage 三方庫足矣。
axios.interceptors.response.use( async (resp) => { const { config } = resp const { url } = config // 是否有緩存tag,用于更新CDN數據。目前是定時服務在跑,訪問頁面帶上tag if (this.hasCdnTag() && this.isWhiteApi(url)) { this.updateCDN(config, resp) } if(this.isIndexDBWhiteApi(url)){ this.updateIndexDB(config, resp) } return resp; }, async (err) => { const { config } = err const { url } = config // 是否命中緩存策略 if (this.isWhiteApi(url) && this.useCache()) { return this.fetchCDN(config).then(res => { pushLog(`cdn緩存數據已命中,請處理`, SentryTypeEnum.error) return res }).catch(()=>{ pushLog(`cdn緩存數據未同步,請處理`, SentryTypeEnum.error) if(this.isIndexDBWhiteApi(url)){ return this.fetchIndexDB(config).then(res => { pushLog(`IndexDB緩存數據已命中,請處理`, SentryTypeEnum.error) return res }).catch(()=>{ pushLog(`IndexDB緩存數據未同步,請處理`, SentryTypeEnum.error) }) } }) } } );
總結下,優點包括不入侵業務代碼,不影響現有業務,隨上隨用,盡可能避免前端純白屏的場景,成本低。劣勢包括使用局限,不適合對數據實效性比較高的業務場景,不支持 IE 瀏覽器。
接口容災我們也是剛弄不久,有許多細節與不足,歡迎溝通交流。
接口容災本意是預防發生接口服務掛了的場景,我們不會很被動。原來是P0的故障,能被它降低為 P2、P3,甚至在某些場景下都不會有用戶反饋。
本文鏈接:http://www.www897cc.com/showinfo-26-35314-0.html我們一起聊聊前端接口容災
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Vue 3的Teleport特性詳解,你了解幾分?
下一篇: 2024 年 Vue 發展預測