<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>

          程序員過(guò)關(guān)斬將--重復(fù)的請(qǐng)求并不好過(guò)濾

          共 3059字,需瀏覽 7分鐘

           ·

          2021-04-29 00:55

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

          過(guò)濾重復(fù)請(qǐng)求很難嗎?加一個(gè)請(qǐng)求ID不就好了嗎?

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

          產(chǎn)生背景

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

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

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

          • 黑客進(jìn)行了攔截,人為的重放了請(qǐng)求
          • 客戶端因?yàn)槟承┰颍脩粼诤芏痰臅r(shí)間內(nèi)重放了請(qǐng)求
          • 一些中間件(比如網(wǎng)關(guān))重放了請(qǐng)求
          • 未知的其他情況

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

          image

          抽象出來(lái)是不是很簡(jiǎn)單?但是落地卻并非像這張圖一樣簡(jiǎn)單!!

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

          客戶端處理

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

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

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

          請(qǐng)求標(biāo)識(shí)

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

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

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

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

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

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

          }

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

          問(wèn)題1

          正常的客戶端重復(fù)請(qǐng)求,一般情況下真的會(huì)根據(jù)我們寫的代碼過(guò)濾掉重復(fù)請(qǐng)求,為什么說(shuō)一般情況呢?那是因?yàn)榉植际降脑颍瑯O限情況下也會(huì)導(dǎo)致重復(fù)的請(qǐng)求到業(yè)務(wù)處理端,比如以下情況:

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

          可見,一個(gè)小小重復(fù)過(guò)濾請(qǐng)求,可能還需要分布式鎖的出場(chǎng)才可以

          問(wèn)題2

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

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

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

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

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

          時(shí)間戳問(wèn)題

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

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

          寫在最后

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

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

          推薦閱讀:

          Kafka原理篇:圖解kakfa架構(gòu)原理

          架構(gòu)設(shè)計(jì)方法論

          從面試角度一文學(xué)完 Kafka

          數(shù)據(jù)庫(kù)跟緩存的雙寫一致性

          全網(wǎng)最詳盡的負(fù)載均衡原理圖解


          關(guān)號(hào)互聯(lián)網(wǎng)全棧架構(gòu)價(jià)

          瀏覽 52
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  51妺妺嘿嘿午夜成人A片 | 成人肏屄网站 | 日韩X X免费 | 亚洲中文无吗 | 国产麻豆一区二区三区 |