自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我們一起聊聊前端接口容災(zāi)

開(kāi)發(fā) 前端
總結(jié)下,優(yōu)點(diǎn)包括不入侵業(yè)務(wù)代碼,不影響現(xiàn)有業(yè)務(wù),隨上隨用,盡可能避免前端純白屏的場(chǎng)景,成本低。劣勢(shì)包括使用局限,不適合對(duì)數(shù)據(jù)實(shí)效性比較高的業(yè)務(wù)場(chǎng)景,不支持 IE 瀏覽器。

開(kāi)篇

你說(shuō),萬(wàn)一接口掛了會(huì)怎么樣?

還能咋樣,白屏唄。

有沒(méi)有不白屏的方案?

有啊,還挺簡(jiǎn)單的。

容我細(xì)細(xì)細(xì)細(xì)分析。

原因就是接口掛了,拿不到數(shù)據(jù)了。那把數(shù)據(jù)儲(chǔ)存起來(lái)就可以解決問(wèn)題。

思考

存哪里?

第一時(shí)間反應(yīng)瀏覽器本地存儲(chǔ),想起了四兄弟。

選型對(duì)比

特性

cookie

localStorage

sessionStorage

indexDB

數(shù)據(jù)生命周期

服務(wù)器或者客戶(hù)端都可以設(shè)置、有過(guò)期時(shí)間

一直存在

關(guān)閉頁(yè)面就清空

一直存在

數(shù)據(jù)儲(chǔ)存大小

4KB

5MB

5MB

動(dòng)態(tài),很大

大于250MB

與服務(wù)器通信

每次都帶在header中

不帶

不帶

不帶

兼容性

都支持

都支持

都支持

IE不支持,其他主流都支持

考慮到需要存儲(chǔ)的數(shù)據(jù)量,5MB 一定不夠的,所以選擇了 IndexDB。

考慮新用戶(hù)或者長(zhǎng)時(shí)間未訪問(wèn)老用戶(hù),會(huì)取不到緩存數(shù)據(jù)與陳舊的數(shù)據(jù)。

因此準(zhǔn)備上云,用阿里云存儲(chǔ),用 CDN 來(lái)保障。

總結(jié)下:線上 CDN、線下 IndexDB。

整體方案

整體流程圖

圖片圖片

CDN

先講講線上 CDN。

通常情況下可以讓后端支撐,本質(zhì)就是更新策略問(wèn)題,這里不細(xì)說(shuō)。

我們講講另外一種方案,單獨(dú)啟個(gè) Node 服務(wù)更新 CDN 數(shù)據(jù)。

流程圖

圖片圖片

劫持邏輯

劫持所有接口,判斷接口狀態(tài)與緩存標(biāo)識(shí)。從而進(jìn)行更新數(shù)據(jù)、獲取數(shù)據(jù)、緩存策略三種操作

通過(guò)配置白名單來(lái)控制接口存與取

axios.interceptors.response.use(
      async (resp) => {
        const { config } = resp
        const { url } = config
        // 是否有緩存tag,用于更新CDN數(shù)據(jù)。目前是定時(shí)服務(wù)在跑,訪問(wèn)頁(yè)面帶上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緩存數(shù)據(jù)已命中,請(qǐng)?zhí)幚韅, SentryTypeEnum.error)
            return res
          }).catch(()=>{
           pushLog(`cdn緩存數(shù)據(jù)未同步,請(qǐng)?zhí)幚韅, SentryTypeEnum.error)
          })
        }
      }
    );

緩存策略

累計(jì)接口異常發(fā)生 maxCount 次,打開(kāi)緩存開(kāi)關(guān),expiresSeconds 秒后關(guān)閉。

緩存開(kāi)關(guān)用避免網(wǎng)絡(luò)波動(dòng)導(dǎo)致命中緩存,設(shè)置了閥值。

/*
* 緩存策略
*/
useCache = () => {
  if (this.expiresStamp > +new Date()) {
    const d = new Date(this.expiresStamp)
    console.warn(`
    ---------------------------------------
    ---------------------------------------
    啟用緩存中
    關(guān)閉時(shí)間:${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
}

唯一標(biāo)識(shí)

根據(jù) method、url、data 三者來(lái)標(biāo)識(shí)接口,保證接口的唯一性

帶動(dòng)態(tài)標(biāo)識(shí),譬如時(shí)間戳等可以手動(dòng)過(guò)濾

/**
 * 生成接口唯一鍵值
*/
generateCacheKey = (config) => {
  // 請(qǐng)求方式,參數(shù),請(qǐng)求地址,
  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`;
};

更新數(shù)據(jù)

/**
 * 更新cdn緩存數(shù)據(jù)
*/
updateCDN = (config, data) => {
  const fileName = this.generateCacheKey(config)
  const cdnUrl = `${this.prefix}/${fileName}`
  axios.post(`${this.nodeDomain}/cdn/update`, {
    cdnUrl,
    data
  })
}

Node定時(shí)任務(wù)

構(gòu)建定時(shí)任務(wù),用 puppeteer 去訪問(wèn)、帶上緩存標(biāo)識(shí),去更新 CDN 數(shù)據(jù)

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);
  // 訪問(wèn)首頁(yè)所有查詢(xún)接口
  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;
};

效果

手動(dòng) block 整個(gè) domain,整個(gè)頁(yè)面正常展示

圖片圖片

IndexDB

線上有 CDN 保證了,線下就輪到 IndexDB 了,基于業(yè)務(wù)簡(jiǎn)單的增刪改查,選用 localForage 三方庫(kù)足矣。

axios.interceptors.response.use(
      async (resp) => {
        const { config } = resp
        const { url } = config
        // 是否有緩存tag,用于更新CDN數(shù)據(jù)。目前是定時(shí)服務(wù)在跑,訪問(wèn)頁(yè)面帶上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緩存數(shù)據(jù)已命中,請(qǐng)?zhí)幚韅, SentryTypeEnum.error)
            return res
          }).catch(()=>{
           pushLog(`cdn緩存數(shù)據(jù)未同步,請(qǐng)?zhí)幚韅, SentryTypeEnum.error)
           if(this.isIndexDBWhiteApi(url)){
             return this.fetchIndexDB(config).then(res => {
              pushLog(`IndexDB緩存數(shù)據(jù)已命中,請(qǐng)?zhí)幚韅, SentryTypeEnum.error)
              return res
            }).catch(()=>{
             pushLog(`IndexDB緩存數(shù)據(jù)未同步,請(qǐng)?zhí)幚韅, SentryTypeEnum.error)
            })
           }
          })
        }
      }
    );

總結(jié)

總結(jié)下,優(yōu)點(diǎn)包括不入侵業(yè)務(wù)代碼,不影響現(xiàn)有業(yè)務(wù),隨上隨用,盡可能避免前端純白屏的場(chǎng)景,成本低。劣勢(shì)包括使用局限,不適合對(duì)數(shù)據(jù)實(shí)效性比較高的業(yè)務(wù)場(chǎng)景,不支持 IE 瀏覽器。

接口容災(zāi)我們也是剛弄不久,有許多細(xì)節(jié)與不足,歡迎溝通交流。

接口容災(zāi)本意是預(yù)防發(fā)生接口服務(wù)掛了的場(chǎng)景,我們不會(huì)很被動(dòng)。原來(lái)是P0的故障,能被它降低為 P2、P3,甚至在某些場(chǎng)景下都不會(huì)有用戶(hù)反饋。

責(zé)任編輯:武曉燕 來(lái)源: 政采云技術(shù)
相關(guān)推薦

2024-11-27 08:47:12

2021-08-27 07:06:10

IOJava抽象

2024-02-20 21:34:16

循環(huán)GolangGo

2023-08-04 08:20:56

DockerfileDocker工具

2022-05-24 08:21:16

數(shù)據(jù)安全API

2023-08-10 08:28:46

網(wǎng)絡(luò)編程通信

2023-09-10 21:42:31

2023-06-30 08:18:51

敏捷開(kāi)發(fā)模式

2025-03-13 05:00:00

2022-02-14 07:03:31

網(wǎng)站安全MFA

2022-06-26 09:40:55

Django框架服務(wù)

2022-10-28 07:27:17

Netty異步Future

2023-04-26 07:30:00

promptUI非結(jié)構(gòu)化

2022-04-06 08:23:57

指針函數(shù)代碼

2023-12-28 09:55:08

隊(duì)列數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)

2022-11-12 12:33:38

CSS預(yù)處理器Sass

2024-02-26 00:00:00

Go性能工具

2023-07-27 07:46:51

SAFe團(tuán)隊(duì)測(cè)試

2025-03-27 02:00:00

SPIJava接口

2022-07-29 08:17:46

Java對(duì)象內(nèi)存
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)