<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 為 Mail.Ru Group 的電子郵件服務實施反垃圾郵件的貓捉老鼠的故事,以及 Tarantool 與此相關的內容 > 原文: [http://highscalability.com/blog/2016/8/30/the-cat-and-mouse-story-of-implementing-anti-spam-for-mailru.html](http://highscalability.com/blog/2016/8/30/the-cat-and-mouse-story-of-implementing-anti-spam-for-mailru.html) ![](https://img.kancloud.cn/fb/d1/fbd1e1ddcb807ab802759cd4e17894c8_974x487.png) 大家好! 在本文中,我想向您介紹一個為 Mail.Ru Group 的電子郵件服務實現反垃圾郵件系統的故事,并分享我們在此項目中使用 [Tarantool](https://tarantool.org/) 數據庫的經驗:Tarantool 的任務是什么 ,我們面臨的局限性和整合問題,陷入的陷阱以及我們最終如何得到啟示。 讓我從簡短的回溯開始。 大約十年前,我們開始為電子郵件服務引入反垃圾郵件。 我們的第一個過濾解決方案是卡巴斯基反垃圾郵件和 RBL(*實時黑洞列表-*是與垃圾郵件發送有關的 IP 地址的實時列表)。 這樣可以減少垃圾郵件的發送,但是由于系統的慣性,我們無法足夠快地(即實時)抑制垃圾郵件的郵寄。 另一個未滿足的要求是速度:用戶應該以最小的延遲接收經過驗證的電子郵件,但是集成解決方案的速度還不足以趕上垃圾郵件發送者。 垃圾郵件發件人發現垃圾郵件未送達時,他們會很快更改其行為模型和垃圾郵件內容的外觀。 因此,我們無法忍受系統的慣性,而是開始開發自己的垃圾郵件過濾器。 我們的第二個系統是 MRASD-Mail.Ru 反垃圾郵件守護程序。 實際上,這是一個非常簡單的解決方案。 客戶的電子郵件到達 [Exim](http://www.exim.org) 郵件服務器,并通過充當主要過濾器的 RBL 到達,然后到達 MRASD,在那里發生了所有不可思議的事情。 反垃圾郵件守護程序將郵件分解為幾部分:標頭和正文。 然后,它使用基本算法對每個片段進行歸一化,例如對字符大小寫進行歸一化(全部以小寫或大寫形式),將外觀相似的符號轉換為特定形式(例如,使用一個符號表示俄語和英語“ O”)等 標準化后,守護程序提取了所謂的“實體”或電子郵件簽名。 我們的垃圾郵件過濾器會分析電子郵件的不同部分,并在發現任何可疑內容時將其阻止。 例如,我們可以為“ viagra”一詞定義一個簽名,所有包含該詞的消息都會被阻止。 實體也可以是 URL,圖像,附件等。 在反垃圾郵件檢查過程中完成的另一件事是為已驗證的電子郵件計算指紋。 計算為少數棘手的哈希函數的指紋是郵件的獨特特征。 基于計算的哈希值和收集的哈希統計信息,反垃圾郵件系統可以將郵件過濾為垃圾郵件或讓其通過。 當哈希值或實體達到某個頻率閾值時,服務器開始阻止所有匹配的電子郵件。 為此,我們維護了統計數據(計數器)來跟蹤遇到某個實體的頻率,接收者抱怨該實體的頻率,并設置了一個實體標志 SPAM / HAM(在與垃圾郵件相關的術語中,“ ham”與“ 垃圾郵件”,表示經過驗證的電子郵件不包含垃圾內容。 MRASD 的核心部分是使用 C ++實現的,而其相當多的業務邏輯是使用解釋性語言 Lua 來實現的。 正如我已經說過的那樣,垃圾郵件發送者是非常有活力的人,他們會很快改變其行為。 我們的目標是對垃圾郵件發送者方面的每項更改做出快速響應,這就是為什么我們使用解釋性語言實施業務邏輯的原因(對于 Lua,我們不必每次都在所有服務器上重新編譯系統并進行更新)。 另一個要求是速度:Lua 中的代碼在性能測試中顯示出良好的結果。 最后,很容易與 C ++中的代碼集成。 ![](https://img.kancloud.cn/ab/8e/ab8e9f7679d10541d01de987ebcd2d78_1000x709.png) 上面的方案說明了我們的反垃圾郵件過濾器的簡化工作流程:電子郵件從發件人發送到我們的郵件服務器; 如果該消息已成功通過主過濾器(1),則它進一步進入 MRASD(2)。 MRASD 將其檢查結果返回到郵件服務器(3),并根據這些結果將郵件傳遞到收件人的“垃圾郵件”文件夾或收件箱中。 MRASD 允許我們將未過濾的垃圾郵件數量減少十倍。 隨著時間的流逝,我們不斷改進系統:添加了新的子系統和組件,引入了新的工具。 因此,系統不斷發展,變得更加復雜,反垃圾郵件任務也變得更加多樣化。 這些變化無助于影響我們的技術堆棧。 這就是本故事的下一部分。 **技術堆棧的演進** 在電子郵件服務時代的曙光中,消息流以及消息內容比今天明顯稀缺。 但是工具和計算能力的選擇也較差。 從上面描述的 MRASD 的“父母”模型可以看出,有必要存儲各種統計數據。 這些數據的相當一部分是“熱”的(即經常使用),這對數據存儲系統提出了某些要求。 結果,我們選擇了 MySQL 作為“冷”數據的存儲系統,但對于“熱”統計數據仍然不確定。 我們分析了所有可能的解決方案(它們的性能和功能適用于“熱”數據,但不適用于關鍵任務數據),最終到達 [Memcached](https://memcached.org/) -那時,此解決方案已經足夠穩定。 但是我們在存儲“熱”和關鍵數據方面仍然存在問題。 與任何其他緩存解決方案一樣,Memcached 也有其局限性,包括不進行復制以及緩存關閉(并刷新)后的長時間預熱時間。 我們的進一步搜索將我們帶到了[京都內閣](http://fallabs.com/kyotocabinet/),這是一個非關系鍵值數據庫系統。 時間過去了,電子郵件工作量增加了,反垃圾郵件工作量也增加了。 出現了需要存儲更多數據的新服務(Hadoop,Hypertable)。 順便說一句,當今的高峰處理工作量達到每分鐘 55 萬封電子郵件(如果我們每天計算平均值,則每分鐘約有 35 萬封電子郵件),每天要分析的日志文件量超過 10 TB。 讓我們回到過去:盡管工作量不斷增加,但我們對數據處理(加載,保存)的要求卻保持不變。 有一天,我們意識到京都議定書無法管理所需的數據量。 此外,我們需要一種存儲系統,它具有針對“熱”和關鍵數據的更廣泛功能。 也就是說,是時候到處尋找更好的替代方案了,這些替代方案將更靈活,更易于使用,并具有更高的性能和故障轉移功能。 那時,一個名為 Tarantool 的 NoSQL 數據庫在我們公司中獲得了普及。 Tarantool 是公司內部開發的,完全滿足了我們的“ wannas”。 順便說一句,我最近一直在修改我們的服務,當我遇到最早的 Tarantool 版本之一時 [Tarantool / Silverbox](http://www.slideshare.net/fuenteovehuna/tarantool-silverbox) ,我感到自己是一名考古學家。 我們決定嘗試一下 Tarantool,因為它的基準測試涵蓋了我們所需的數據量(我當時沒有確切的工作量數據),并且也滿足了我們對內存使用的要求。 另一個重要因素是項目團隊位于隔壁,我們可以使用 JIRA 快速提出功能請求。 我們是決定在他們的項目中嘗試使用 Tarantool 的先驅者之一,我認為我們向 Tarantool 邁出的第一步受到了其他先驅者的積極經驗的鼓舞。 那就是我們的“ Tarantool 時代”開始的時候。 我們積極地將 Tarantool 引入到我們的反垃圾郵件體系結構中。 今天,我們有了基于 Tarantool 的隊列,用于存儲各種統計數據的高工作量服務:用戶信譽,發件人 IP 信譽,用戶可信度(“業力”統計信息)等。我們目前的活動是將升級的數據存儲系統集成到我們的 實體統計處理器。 您可能想知道為什么我們只針對我們的反垃圾郵件項目專注于一個數據庫解決方案,而不考慮遷移到其他存儲。 嗯,事實并非如此。 我們也考慮和分析競爭系統,但是暫時,Tarantool 可以很好地處理項目中所需的所有任務和工作負載。 引入新的(未知的,以前未使用的)系統總是有風險的,并且會花費大量時間和資源。 同時,Tarantool 是我們(以及許多其他項目)的知名工具。 我們的開發人員和系統管理員已經了解使用和配置 Tarantool 的所有知識,以及如何充分利用它。 另一個優勢是 Tarantool 的開發團隊不斷改進其產品并提供良好的支持(這些人正在隔壁工作,這很不錯:))。 當我們仍在實施另一個基于 Tarantool 的解決方案時,我們獲得了所有必要的幫助和支持(我稍后會告訴您)。 接下來,我將為您概述我們的反垃圾郵件項目中使用 Tarantool 的幾個系統,這些系統將涉及我們面臨的問題。 **我們使用 Tarantool** 的系統概述 **業力** **業力**是一個數字值,表示用戶的信任度。 它最初是為不需要復雜的依賴系統的用戶提供的通用“胡蘿卜和棍子”系統的基礎。 業力是基于從其他用戶信譽系統接收到的數據的匯總值。 業力系統背后的想法很簡單:每個用戶都有其業力-越高,我們對這個用戶的信任就越高; 越低,我們在反垃圾郵件檢查期間評估他們的電子郵件時就越嚴格。 例如,如果發件人發送的電子郵件中包含可疑內容,并且發件人的業障評分很高,則此類郵件會打入收件人的收件箱。 較低的業障評級將是反垃圾郵件系統的悲觀因素。 這個系統使我想到了老師在學校考試中查閱的一本考勤書。 參加所有班級的學生只會得到幾個額外的問題,然后去度假,而錯過許多班級的學生則必須回答很多問題才能獲得高分。 ![](https://img.kancloud.cn/61/06/610652a7b1e62d0411d94f3aeb0d1733_974x740.png) 用于存儲與業力相關的數據的 Tarantool 在單個服務器上工作。 下圖說明了每分鐘一個這樣的實例執行的請求數。 ![](https://img.kancloud.cn/af/87/af8797828d31a50c827cc500391ab0b4_878x462.png) **RepIP / RepUser** **RepIP** 和 **RepUser** (信譽 IP 和信譽用戶)是一種高工作負載服務,用于處理與具有特定 IP 以及與發送者(用戶)的活動和動作有關的統計信息 與用戶在一段時間內使用電子郵件服務的強度有關的統計信息。 該系統使我們知道用戶已發送了多少電子郵件,其中有多少已被閱讀,以及有多少被標記為垃圾郵件。 該系統的優勢在于,它提供了時間軸,而不是用戶活動的快照。 為什么這對于行為分析很重要? 想象一下,您在沒有任何交流的情況下搬到了國外,而您的所有朋友都留在家里。 然后,幾年后,您的小屋里有了網線。 哇! 您瀏覽到自己喜歡的社交網絡的網站,然后看到朋友的照片-嗯,他已經改變了很多……您可以從該照片中獲得多少信息? 我想不是太多。 現在想象一下,您觀看了一個視頻,該視頻顯示了您的朋友變遷,結婚等等……-那是一段簡短的傳記片段。 我敢打賭,在第二種情況下,您會更好地了解朋友的生活。 數據分析也是如此:我們擁有的信息越多,我們就可以更好地評估用戶的行為。 我們可以注意到發件人的郵件活動趨勢,了解發件人的習慣。 根據這種統計信息,為每個用戶和 IP 地址分配“信任等級分數”和一個特殊標志。 此標志用于主要過濾器中,該過濾器甚至可以在垃圾郵件到達我們的郵件服務器之前過濾掉多達 70%的垃圾郵件。 這個百分比說明信譽服務的重要性。 這就是為什么此服務需要最大的性能和容錯能力的原因。 這就是為什么我們在這里使用 Tarantool 的原因。 ![](https://img.kancloud.cn/b7/2b/b72b0a502ef9c9eb73e6fa9cc57082f9_1000x709.png) 信譽統計信息存儲在兩臺服務器上,每臺服務器有四個 Tarantool 實例。 下圖說明了每分鐘 RepIP 請求的平均數量。 ![](https://img.kancloud.cn/48/4e/484e755d1fd1502782b469ce37cc919a_750x394.png) 在實施信譽服務時,Tarantool 遇到了許多配置問題。 與我們之前討論的系統不同,RepIP / RepUser 的數據包更大:這里的平均包大小為 471,97 字節(最大大小為 16 KB)。 從邏輯上講,數據包包括兩個部分:一個小的“基本”部分(標志,匯總統計信息)和一個很大的統計部分(詳細的按動作統計信息)。 對整個數據包進行尋址會導致大量的網絡使用,因此加載和保存記錄會花費更多時間。 許多系統僅需要數據包的基本部分,但是如何將其從元組中剝離(“元組”是 Tarantool 的記錄術語)? 這是存儲過程很方便的地方。 我們在 Tarantool 的 **init.lua** 文件中添加了所需的函數,并從客戶端對其進行了調用(從 Tarantool 1.6 版開始,您可以使用純 C 語言編寫存儲過程)。 **1.5.20 之前的 Tarantool 版本存在問題** 說我們從未遇到過 Tarantool 問題是錯誤的。 是的,我們有一些。 例如,在計劃的重新啟動后,Tarantool 客戶端(超過 500 個)由于超時而無法重新連接。 當出現故障后,下次嘗試重新連接的嘗試被延遲了一段時間后,我們嘗試引入漸進式超時,但這無濟于事。 我們發現,問題在于 Tarantool 在其事件循環的每個周期內僅接受一個連接請求,盡管有數百個請求在等待。 我們有兩種選擇:安裝新的 Tarantool 版本(1.5.20 或更高版本)或修改 Tarantool 的配置(禁用 *io_collect_interval* 選項可以解決此問題)。 Tarantool 開發人員很快修復了此錯誤,因此 Tarantool 1.6 或 1.7 將不會提供它。 **RepEntity-實體信譽** 當前,我們正在集成一個用于存儲實體統計信息(URL,圖像,附件等)的新組件。 **RepEntity** 。 RepEntity 的目的類似于已經討論的 RepIP / RepUser 的目的:它提供有關實體行為的詳細信息,這是我們的反垃圾郵件過濾器的決策標準。 借助 RepEntity 統計信息,我們可以根據電子郵件的實體過濾出垃圾郵件。 例如,郵件可能包含可疑的 URL(例如,其中可能包含垃圾內容或導致[網絡釣魚](https://en.wikipedia.org/wiki/Phishing)網站),而 RepEntity 可以幫助我們更快地注意到和阻止此類郵件。 怎么樣? 我們可以看到該 URL 的動態發送,并且可以檢測到其行為的變化,這對于“固定”計數器是不可能的。 除了不同的數據包格式外,RepEntity 和 RepIP 系統之間的基本區別是 RepEntity 在我們的服務上產生了明顯更高的工作負載(處理和存儲的數據量更大,請求數量也更多)。 一封電子郵件最多可以包含一百個實體(最多 10 個 IP 地址),對于這些實體中的大多數,我們必須加載并保存完整的統計信息包。 值得注意的是,數據包由特殊的聚合器保存,該聚合器首先等待累積足夠的統計信息。 因此,所有這些都意味著數據庫系統要承擔更多的工作量,并且需要準確的設計和實現。 **讓我強調一下,由于某些項目限制,對于 RepEntity,我們使用了 Tarantool 1.5(因此,我將繼續撰寫此版本)。** 首先,我們估計了存儲所有統計信息所需的內存量。 為了更好地說明此任務的重要性,讓我帶來一些數字:在預期的工作量下,將數據包增加 1 個字節意味著將數據總量增加 1 GB。 如您所見,我們的任務是以最緊湊的方式將數據存儲在一個元組中(正如我已經說過的,我們無法將整個數據包存儲在一個元組中,因為我們經常要求僅檢索一部分數據包數據) 。 要計算要存儲在 Tarantool 中的數據量,我們還需要考慮: * 存儲索引的額外空間 * 在元組中存儲數據大小的額外空間(1 個字節) * 最大元組大小的限制為 1 MB(對于 1.7 版,請參見 [http://tarantool.org/doc/book/box/limitations.html](http://tarantool.org/doc/book/box/limitations.html) ) Tarantool 增加了各種請求(讀取,插入,刪除)的數量,從而產生超時錯誤。 我們的調查表明,在頻繁插入和刪除的情況下,Tarantool 啟動了一個復雜的樹重新平衡過程(我們所有的索引都是 TREE 類型的)。 Tarantool 中的樹索引具有棘手的自平衡邏輯,只有在滿足某些“不平衡”條件時才會啟動。 因此,當一棵樹“失去足夠的平衡”時,Tarantool 啟動了重新平衡過程,這使得 Tarantool 變得口吃。 在日志中,我們發現消息*資源暫時不可用(errno:11)*在幾秒鐘后消失了。 但是,盡管發生了這些錯誤,客戶端仍無法獲取請求的數據。 Tarantool 團隊的同事提出了一個解決方案:嘗試使用其他類型的樹索引 AVLTREE,該索引在每次插入/刪除/更改時都會重新平衡。 實際上,盡管重新平衡呼叫的數量有所增加,但其總成本卻更低。 在更新架構并重新啟動數據庫之后,該問題永遠消失了。 我們面臨的另一個問題是清理過時的記錄。 不幸的是,Tarantool(據我所知,對于 1.7 版也是如此)不允許為某個記錄定義 TTL(生存時間),而忘記了它,而所有清理活動都委托給了數據庫。 好了,您仍然可以自己使用 Lua 和 [box.fiber](http://stable.tarantool.org/doc/mpage/stored-procedures.html#sp-box-fiber) 實現所需的清理邏輯。 從好的方面來說,這提供了更大的靈活性:您可以定義復雜的清除條件,而不僅僅是基于 TTL 的條件。 但是,要正確實現清除邏輯,您需要注意一些細微差別。 我實施的第一根清潔纖維使 Tarantool 的速度非常慢。 事實是,我們可以刪除的數據量大大少于記錄的總數。 為了減少要刪除的記錄候選者的數量,我引入了基于所需字段的二級索引。 之后,我實現了一個遍歷所有候選對象的光纖(其上次修改的時間戳早于指示的時間戳),檢查了其他清除條件(例如,當前是否為記錄設置了“正在進行寫入”標志),并且 如果滿足所有條件,則光纖會刪除該記錄。 當我在零工作負載下測試邏輯時,一切工作正常。 在低工作量的情況下也很好。 但是當我將工作量增加到預期水平的一半時,我遇到了問題。 我的請求開始失敗,并顯示超時錯誤。 我知道我一定已經溜了。 當我深入研究這個問題時,我意識到我對光纖的工作方式有一個錯誤的認識。 在我的世界中,光纖是一個獨立的線程,對接收和處理客戶端請求沒有任何影響(上下文切換除外)。 但是不久,我發現我的光纖使用了與請求處理線程相同的事件循環。 這樣,我在一個循環中遍歷大量記錄,而又不刪除任何內容,只是阻塞了事件循環,因此未處理客戶端請求。 為什么提到刪除操作? 您會看到,每次刪除某些記錄時,都會發生一次 yield 操作,該操作解除了事件循環的阻塞并允許處理下一個事件。 在這一點上,我得出的結論是,如果執行了一些 N 操作(其中 N 是一個經驗推導的值,我取 N = 100)而沒有產生屈服,則有必要強制屈服(例如,使用 *wrap.sleep( 0)*)。 要記住的另一件事是,記錄刪除可能會觸發索引更新,因此在遍歷記錄時我可能會丟失一些要刪除的數據。 這是另一個解決方案。 在一個周期中,我可以選擇一小部分元素(小于 1000 個)并遍歷這些元素,刪除需要的元素,并跟蹤最后一個未刪除的元素。 在下一次迭代中,我將從最后一個未刪除的元素中選擇另外一小部分元素。 我們也嘗試過實施一種解決方案,該解決方案將來可以平滑地進行分片,但是此嘗試失敗了:實現的機制開銷太大,因此我們暫時放棄了分片。 希望我們可以在較新的 Tarantool 版本中找到重新分片功能。 這是一些性能提示。 要提高 Tarantool 的性能,您可以禁用* .xlog 文件。 但是請記住,在這種情況下,Tarantool 僅作為高速緩存工作,并具有所有的限制(我的意思是沒有復制,重新啟動后需要很長的預熱時間)。 這里的解決方法是不時制作快照,并在需要時使用它來還原數據。 如果一臺計算機上有多個 Tarantool 實例,則可以將每個實例“精確定位”到某個 CPU 內核以提高性能。 但是,如果您說 12 個物理內核,那么開始 12 個實例就不好了,因為每個 Tarantool 實例連同執行線程也都有一個 WAL 線程。 所需功能: * 分片。 * 基于集群的方法,可以進行動態集群配置,例如在添加節點或節點出現故障的情況下派上用場,類似于 MongoDB(mongos)和 Redis(redis sentinel)。 * 可以為記錄清除定義 TTL(生存時間)。 **的結論** Tarantool 是我們反垃圾郵件系統的基石,我們許多重要的高工作量服務都基于此。 現成的[連接器](http://stable.tarantool.org/doc/mpage/connectors.html)使輕松將 Tarantool 與使用不同編程語言實現的組件集成在一起成為可能。 Tarantool 的成功歷史悠久:多年來,在我們的反垃圾郵件項目中使用 Tarantool 以來,我們在操作或穩定性方面都沒有遇到嚴重問題。 但是要充分利用該數據庫,您需要考慮一些配置上的細微差別(在這里,我們一直在談論 Tarantool 1.5 版的細微差別)。 關于我們未來計劃的幾句話: * 在我們的項目中增加基于 Tarantool 的服務的數量。 * 遷移到 Tarantool 1.7。 * 開始使用 Vinyl 引擎,尤其是對于 RepEntity,那里的真正“熱”數據量并不大。 [關于 HackerNews](https://news.ycombinator.com/item?id=12397570) 保存 反垃圾郵件系統內部郵件處理的平均延遲是多少? 是否有關于典型請求分解的任何分析信息?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看