日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

開(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ù)器或者客戶端都可以設(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。

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

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

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

整體方案

整體流程圖

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

CDN

先講講線上 CDN。

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

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

流程圖

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

劫持邏輯

劫持所有接口,判斷接口狀態(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è)所有查詢接口
  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è)面正常展示

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

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ì)有用戶反饋。

分享到:
標(biāo)簽:接口
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定