<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官問:重復(fù)的請求如何過濾?

          共 2609字,需瀏覽 6分鐘

           ·

          2021-12-19 10:15

          為什么要做重復(fù)請求的過濾呢?不過濾不行嗎?

          過濾重復(fù)請求很難嗎?加一個請求ID不就好了嗎?

          每個技術(shù)難點的話題,肯定是由一個產(chǎn)品需求引發(fā)的,俗話說:如果沒有產(chǎn)品經(jīng)理,程序員將不需要聽診器,但是會失業(yè)!!

          產(chǎn)生背景

          重復(fù)請求能夠?qū)ο到y(tǒng)造成傷害是架構(gòu)中很難避免的一個設(shè)計問題,一般情況下,讀請求很少會造成致命性的故障,主要是系統(tǒng)的寫請求,很多時候一個重復(fù)寫的動作,會是我們程序員加班的緣由。比如:用戶使用積分兌換物品,重復(fù)的請求會造成用戶積分的重復(fù)扣減,而作為線上系統(tǒng),如果日志等輔助打的不好的話,排查原因其實需要很多時間。

          一般的產(chǎn)品經(jīng)理設(shè)計系統(tǒng)的時候并不會涉及到這類異常情況,但是一旦出現(xiàn)問題,產(chǎn)品經(jīng)理就會找到程序員罵娘,多么悲哀的故事,人家付出5分精力設(shè)計的系統(tǒng),我們卻要花費10分的精力去編碼和維護(hù)。

          重復(fù)的業(yè)務(wù)請求,有的時候?qū)ο到y(tǒng)造成的影響很大,所以程序員在設(shè)計的時候尤其要注意,產(chǎn)生的原因有很多:

          • 黑客進(jìn)行了攔截,人為的重放了請求
          • 客戶端因為某些原因,用戶在很短的時間內(nèi)重放了請求
          • 一些中間件(比如網(wǎng)關(guān))重放了請求
          • 未知的其他情況

          道理很簡單,用一張圖表達(dá)的會更清爽一些

          image

          抽象出來是不是很簡單?但是落地卻并非像這張圖一樣簡單!!

          從這張圖上一眼就可以看到,整個過程的重點難點在于過濾器這個邏輯設(shè)計部分,這部分可以和業(yè)務(wù)代碼融合在一起,有的時候也可以相分離,比如:有的網(wǎng)關(guān)可以內(nèi)嵌腳本(比如:lua),就完全可以做到和業(yè)務(wù)無關(guān),但是通常情況下,落地的代碼卻和業(yè)務(wù)息息相關(guān)。

          客戶端處理

          客戶端處理重復(fù)請求是一種可以有效過濾正常請求的手段,為什么這么說呢?當(dāng)一個用戶正常操作的時候,客戶端完全可以利用loading的方式或者其他過濾重復(fù)手段來達(dá)到目的,比如:當(dāng)用戶點擊一個按鈕的時候,彈出loading窗口方式用戶再次操作。

          再比如:客戶端可以設(shè)置一個類似于布隆過濾的數(shù)據(jù)結(jié)構(gòu),配合對應(yīng)的過濾算法也可以達(dá)到過濾重復(fù)請求的效果。

          不過,客戶端的任何解決方案也只是治標(biāo)不治本,畢竟,客戶端在整個系統(tǒng)架構(gòu)中,是最不可靠的終端。

          請求標(biāo)識

          重復(fù)請求過濾的關(guān)鍵在于過濾器的邏輯設(shè)計,目前最常用,落地最多當(dāng)屬使用請求ID的方式。大體流程如下:

          1. 客戶端發(fā)送請求的時候,會生成隨機(jī)的請求ID,隨著業(yè)務(wù)參數(shù)一起傳送到服務(wù)端
          2. 服務(wù)端會根據(jù)傳送上來的請求ID做是否重復(fù)的判斷

          服務(wù)器的判斷邏輯其實有很多落地方案了,比如最常見的利用redis來存儲請求ID,以下是偽代碼(NetCore):

          public?class?Para
          {
          ????public?string?ReqId{get?;set?;}??

          ????//其他業(yè)務(wù)參數(shù)
          }

          public?bool?IsExsit(Para?p)
          {
          ????//利用redis來判斷當(dāng)前的key是否存在
          ?????bool?isExsit=redisMethond(p.ReqId);
          ?????//如果存在,則說明是重復(fù)請求,如果不存在說明不是重復(fù)請求,并且添加到redis
          ?????if(!isExsit){
          ?????????AddRedis(p.ReqId);
          ?????}
          ?????return?isExsit;

          }

          一般網(wǎng)上的文章都到此為止了,這種方案有沒有問題呢?答案:有

          問題1

          正常的客戶端重復(fù)請求,一般情況下真的會根據(jù)我們寫的代碼過濾掉重復(fù)請求,為什么說一般情況呢?那是因為分布式的原因,極限情況下也會導(dǎo)致重復(fù)的請求到業(yè)務(wù)處理端,比如以下情況:

          1. 請求被路由到了A服務(wù)器,A服務(wù)器會去請求Redis,判斷是否有相同的請求ID存在,如果是第一次請求,Redis會返回不存在
          2. 同樣的時間,客戶端或者黑客重放了同樣的請求,這個請求被路由到了B服務(wù)器,B服務(wù)器同樣會請求Redis來判斷是否存在,這個時候由于A服務(wù)器還沒回寫Redis,所以B服務(wù)器得到的結(jié)果也是不存在該請求
          3. 這樣就導(dǎo)致了業(yè)務(wù)端收到了兩次同樣的請求,會導(dǎo)致業(yè)務(wù)不可預(yù)期的結(jié)果

          可見,一個小小重復(fù)過濾請求,可能還需要分布式鎖的出場才可以

          問題2

          即便請求中加了唯一的請求ID,但是這個ID并沒有安全保證,或者說,這個ID是可以篡改的。當(dāng)黑客攔截到請求,隨便改一下請求ID,在重放就搞定你了。所以,加的請求ID,還需要一個安全機(jī)制來保證安全,不然這個參數(shù)其實意義不大。

          業(yè)務(wù)簽名

          由于單純添加請求ID,并不能解決問題,所以我們需要一種保證請求ID的機(jī)制,目前來看,普遍的落地方案是根據(jù)業(yè)務(wù)參數(shù)生成摘要,也就是所謂的加簽操作。加簽操作可以有效的防止參數(shù)被篡改。如果你做過微信相關(guān)的開發(fā),你會發(fā)現(xiàn)和微信服務(wù)器的交互也是基于加簽操作的。而生成的簽名可以作為請求ID,以下是偽代碼:

          ????//客戶端生成簽名
          ????string?sigh=MD5($"參數(shù)1=值1&參數(shù)2=值2&time=當(dāng)前時間戳")

          以上只是例子,雖然MD5算法有產(chǎn)生重復(fù)數(shù)據(jù)的可能性,但是對于當(dāng)前這個業(yè)務(wù)場景來說足夠了。細(xì)心的同學(xué)會發(fā)現(xiàn),參數(shù)當(dāng)中加了一個時間戳的參數(shù),這個是我故意加的,這個時間戳在這個場景下會出現(xiàn)問題,什么問題呢?

          時間戳問題

          當(dāng)前的請求場景是要過濾重復(fù)的請求,什么樣的請求算是重復(fù)請求呢?關(guān)鍵是這個定義要明確,我看了很多重復(fù)過濾請求的文章,重復(fù)請求這個概念其實定義的不好,這個是和具體業(yè)務(wù)場景相關(guān)的。舉個栗子:當(dāng)用戶一秒內(nèi)重復(fù)點擊某個按鈕算是重復(fù)請求,那10秒內(nèi)重復(fù)點擊呢?用戶一秒之內(nèi)對同一個商品下單算重復(fù)請求,那10秒內(nèi)呢?

          這個定義就涉及到了上面所說的時間戳參數(shù)的問題,時間戳是否要參與生成簽名,要根據(jù)具體的業(yè)務(wù)場景來定義,不過,我還是要建議,請求的參數(shù)中帶上時間戳,無論它參不參與簽名,至于為什么這么做,當(dāng)時間長了你就知道了

          寫在最后

          過濾重復(fù)請求這個需求,并沒有像想象中那么容易,并非只要加上一個請求ID就完事了,它涉及到安全以及分布式的問題,在某些場景下(比如:秒殺)還會涉及到性能以及高可用等非功能性問題,所以那些說:只需要一個請求ID就能過濾的同學(xué),請不要再誤導(dǎo)別人了,技術(shù)是神圣不可侵犯的。

          還是那句話:具體的業(yè)務(wù)影響到具體的代碼實現(xiàn),脫離業(yè)務(wù)講架構(gòu)其實就是耍流氓


          瀏覽 108
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  综合激情网站 | 人人爱摸视频 | 色丁香五月激情 | 成人午夜激情片 | 娱乐网一区二区三区 |