日處理20億數據,實時用戶行為服務系統架構實踐

来源:http://www.cnblogs.com/wyfjk/archive/2017/08/04/7284053.html
-Advertisement-
Play Games

近幾年旅游市場高速增長,數據量越來越大,並且會持續快速增長。有越來越多的使用需求,對系統的實時性,穩定性也提出了更高的要求。總的來說,當前需求對系統的實時性/可用性/性能/擴展性方面都有很高的要求。 ...


攜程實時用戶行為服務作為基礎服務,目前普遍應用在多個場景中,比如猜你喜歡(攜程的推薦系統)、動態廣告、用戶畫像、瀏覽歷史等等。

以猜你喜歡為例,猜你喜歡為應用內用戶提供潛在選項,提高成交效率。旅行是一項綜合性的需求,用戶往往需要不止一個產品。作為一站式的旅游服務平臺,跨業務線的推薦,特別是實時推薦,能實際滿足用戶的需求,因此在上游提供打通各業務線之間的用戶行為數據有很大的必要性。

攜程原有的實時用戶行為系統存在一些問題,包括:1)數據覆蓋不全;2)數據輸出沒有統一格式,對眾多使用方提高了接入成本;3)日誌處理模塊是web service,比較難支持多種數據處理策略和實現方便擴容應對流量洪峰的需求等。

而近幾年旅游市場高速增長,數據量越來越大,並且會持續快速增長。有越來越多的使用需求,對系統的實時性,穩定性也提出了更高的要求。總的來說,當前需求對系統的實時性/可用性/性能/擴展性方面都有很高的要求。

一、架構

這樣的背景下,我們按照如下結構重新設計了系統:

圖1:實時用戶行為系統邏輯視圖

 

新的架構下,數據有兩種流向,分別是處理流和輸出流。

在處理流,行為日誌會從客戶端(App/Online/H5)上傳到服務端的Collector Service。Collector Service將消息發送到分散式隊列。數據處理模塊由流計算框架完成,從分散式隊列讀出數據,處理之後把數據寫入數據層,由分散式緩存和資料庫集群組成。

輸出流相對簡單,Web Service的後臺會從數據層拉取數據,並輸出給調用方,有的是內部服務調用,比如推薦系統,也有的是輸出到前臺,比如瀏覽歷史。系統實現採用的是Java+Kafka+Storm+Redis+MySQL+Tomcat+spring的技術棧。

1. Java:目前公司內部Java化的氛圍比較濃厚,並且Java有比較成熟的大數據組件

 

2. Kafka/Storm:Kafka作為分散式消息隊列已經在公司有比較成熟的應用,流計算框架Storm也已經落地,並且有比較好的運維支持環境。

 

3. Redis: Redis的HA,SortedSet和過期等特性比較好地滿足了系統的需求。

 

4. MySQL: 作為基礎系統,穩定性和性能也是系統的兩大指標,對比NoSQL的主要選項,比如HBase和ElasticSearch,十億數據級別上MySQL在這兩方面有更好的表現,並且經過設計能夠有不錯的水平擴展能力。

 

目前系統每天處理20億左右的數據量,數據從上線到可用的時間在300毫秒左右。查詢服務每天服務8000萬左右的請求,平均延遲在6毫秒左右。下麵從實時性/可用性/性能/部署幾個維度來說明系統的設計。

二、實時性

 

作為一個實時系統,實時性是首要指標。線上系統面對著各種異常情況。例如如下幾種情況:

1.突發流量洪峰,怎麼應對;
2.出現失敗數據或故障模塊,如何保證失敗數據重試並同時保證新數據的處理;
3.環境問題或bug導致數據積壓,如何快速消解;
4.程式bug,舊數據需要重新處理,如何快速處理同時保證新數據;

 

系統從設計之初就考慮了上述情況。


首先是用storm解決了突發流量洪峰的問題。storm具有如下特性:

圖2:Storm特性

作為一個流計算框架,和早期大數據處理的批處理框架有明顯區別。批處理框架是執行完一次任務就結束運行,而流處理框架則持續運行,理論上永不停止,並且處理粒度是消息級別,因此只要系統的計算能力足夠,就能保證每條消息都能第一時間被髮現並處理。

對當前系統來說,通過storm處理框架,消息能在進入kafka之後毫秒級別被處理。此外,storm具有強大的scale out能力。只要通過後臺修改worker數量參數,並重啟topology(storm的任務名稱),可以馬上擴展計算能力,方便應對突發的流量洪峰。

對消息的處理storm支持多種數據保證策略,at least once,at most once,exactly once。對實時用戶行為來說,首先是保證數據儘可能少丟失,另外要支持包括重試和降級的多種數據處理策略,並不能發揮exactly once的優勢,反而會因為事務支持降低性能,所以實時用戶行為系統採用的at least once的策略。這種策略下消息可能會重發,所以程式處理實現了冪等支持。

storm的發佈比較簡單,上傳更新程式jar包並重啟任務即可完成一次發佈,遺憾的是沒有多版本灰度發佈的支持。

圖3:Storm架構

在部分情況下數據處理需要重試,比如資料庫連接超時,或者無法連接。連接超時可能馬上重試就能恢復,但是無法連接一般需要更長時間等待網路或資料庫的恢復,這種情況下處理程式不能一直等待,否則會造成數據延遲。實時用戶行為系統採用了雙隊列的設計來解決這個問題。

圖4:雙隊列設計

生產者將行為紀錄寫入Queue1(主要保持數據新鮮),Worker從Queue1消費新鮮數據。如果發生上述異常數據,則Worker將異常數據寫入Queue2(主要保持異常數據)。

這樣Worker對Queue1的消費進度不會被異常數據影響,可以保持消費新鮮數據。RetryWorker會監聽Queue2,消費異常數據,如果處理還沒有成功,則按照一定的策略(如下圖)等待或者重新將異常數據寫入Queue2。

圖5:補償重試策略

另外,數據發生積壓的情況下,可以調整Worker的消費游標,從最新的數據重新開始消費,保證最新數據得到處理。中間未經處理的一段數據則啟動backupWorker,指定起止游標,在消費完指定區間的數據之後,backupWorker會自動停止。(如下圖)

圖6:積壓數據消解

三、可用性

作為基礎服務,對可用性的要求比一般的服務要高得多,因為下游依賴的服務多,一旦出現故障,有可能會引起級聯反應影響大量業務。項目從設計上對以下問題做了處理,保障系統的可用性:

1.系統是否有單點?
2.DB擴容/維護/故障怎麼辦?
3.Redis維護/升級補丁怎麼辦?
4.服務萬一掛瞭如何快速恢復?如何儘量不影響下游應用?

首先是系統層面上做了全棧集群化。kafka和storm本身比較成熟地支持集群化運維;web服務支持了無狀態處理並且通過負載均衡實現集群化;redis和DB方面攜程已經支持主備部署,使用過程中如果主機發生故障,備機會自動接管服務;通過全棧集群化保障系統沒有單點。

另外系統在部分模塊不可用時通過降級處理保障整個系統的可用性。先看看正常數據處理流程:(如下圖)

圖7:正常數據流程

在系統正常狀態下,storm會從kafka中讀取數據,分別寫入到redis和mysql中。服務從redis拉取(取不到時從db補償),輸出給客戶端。DB降級的情況下,數據流程也隨之改變(如下圖)

圖8:系統降級-DB

當mysql不可用時,通過打開db降級開關,storm會正常寫入redis,但不再往mysql寫入數據。數據進入reids就可以被查詢服務使用,提供給客戶端。另外storm會把數據寫入一份到kafka的retry隊列,在mysql正常服務之後,通過關閉db降級開關,storm會消費retry隊列中的數據,從而把數據寫入到mysql中。redis和mysql的數據在降級期間會有不一致,但系統恢復正常之後會通過retry保證數據最終的一致性。redis的降級處理也類似(如下圖)

圖9:系統降級-Redis

唯一有點不同的是Redis的服務能力要遠超過MySQL。所以在Redis降級時系統的吞吐能力是下降的。這時我們會監控db壓力,如果發現MySQL壓力較大,會暫時停止數據的寫入,降低MySQL的壓力,從而保證查詢服務的穩定。

為了降低故障情況下對下游的影響,查詢服務通過Netflix的Hystrix組件支持了熔斷模式(如下圖)。

圖10:Circuit Breaker Pattern

在該模式下,一旦服務失敗請求在給定時間內超過一個閾值,就會打開熔斷開關。在開關開啟情況下,服務對後續請求直接返回失敗響應,不會再讓請求經過業務模塊處理,從而避免伺服器進一步增加壓力引起雪崩,也不會因為響應時間延長拖累調用方。

開關打開之後會開始計時,timeout後會進入Half Open的狀態,在該狀態下會允許一個請求通過,進入業務處理模塊,如果能正常返回則關閉開關,否則繼續保持開關打開直到下次timeout。這樣業務恢復之後就能正常服務請求。

另外,為了防止單個調用方的非法調用對服務的影響,服務也支持了多個維度限流,包括調用方AppId/ip限流和服務限流,介面限流等。

四、性能&擴展

由於線上旅游行業近幾年的高速增長,攜程作為行業領頭羊也蓬勃發展,因此訪問量和數據量也大幅提升。公司對業務的要求是可以支撐10倍容量擴展,擴展最難的部分在數據層,因為涉及到存量數據的遷移。

實時用戶行為系統的數據層包括Redis和MySQL,Redis因為實現了一致性哈希,擴容時只要加機器,並對分配到新分區的數據作讀補償就可以。

MySQL方面,我們也做了水平切分作為擴展的準備,分片數量的選擇考慮為2的n次方,這樣做在擴容時有明顯的好處。因為攜程的mysql資料庫現在普遍採用的是一主一備的方式,在擴容時可以直接把備機拉平成第二台(組)主機。假設原來分了2個庫,d0和d1,都放在伺服器s0上,s0同時有備機s1。擴容只需要如下幾步:

1.確保s0 -> s1同步順利,沒有明顯延遲
2.s0暫時關閉讀寫許可權
3.確認s1已經完全同步s0更新
4.s1開放讀寫許可權
5.d1的dns由s0切換到s1
6.s0開放讀寫許可權

遷移過程利用MySQL的複製分發特性,避免了繁瑣易錯的人工同步過程,大大降低了遷移成本和時間。整個操作過程可以在幾分鐘完成,結合DB降級的功能,只有在DNS切換的幾秒鐘時間會產生異常。

整個過程比較簡單方便,降低了運維負擔,一定程度也能降低過多操作造成類似GitLab式悲劇的可能性。

五、部署

前文提到Storm部署是比較方便的,只要上傳重啟就可以完成部署。部署之後由於程式重新啟動上下文丟失,可以通過Kafka記錄的游標找到之前處理位置,恢復處理。
另外有部分情況下程式可能需要多版本運行,比如行為紀錄暫時有多個版本,這種情況下我們會新增一個backupJob,在backupJob中運行歷史版本。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作者:溫學良 鏈接:https://www.zhihu.com/question/21416727/answer/82511153 來源:知乎 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。 Web伺服器習慣處理靜態頁面,所以需要一個程式來幫忙處理動態請求(如當前時間)。Web ...
  • 學了數組之後,感覺有好多操作需要經常去寫,很不方便,因此自己做了一個工具類,方便調用,方法可能不全,希望大家可以添加,讓我使用也方便一點兒。 ...
  • 前言 本文章整理了鏈表排序的三種方法,分別是快速排序、插入排序、歸併排序。為適應不同用途,先給出常用的int版本,再在此基礎上抽象出類模板。 目錄 一、針對整數的版本(常用) 二、模板版本(適用性廣泛) 總結 參考文章 一、針對整數的版本(常用) 文中鏈表定義: 鏈表相關操作: 三種排序方法: 完整 ...
  • 在HttpServlet中重寫service方法的代碼如下: 什麼是”Last-Modified”?和 If-Modified-Since? 在瀏覽器第一次請求某一個URL時,伺服器端的返回狀態會是200,內容是你請求的資源,同時有一個Last-Modified的屬性標記此文件在服務期端最後被修改的 ...
  • 進行了一些Python基礎知識的學習後,將這些知識進行綜合,實現了名片管理小系統,可以進行數據的增刪改查。 ...
  • 今天,公司里有一個萌萌的妹子問我java 中的comparator是怎麼回事。參數分別是什麼,返回值又是什麼,為此,我寫了一個簡單的程式告訴了她: 執行結果: 通過這個執行結果,我們可以看出一下幾點: 以compare(a1, a2)為例: 1)其中a1 是要添加的數數據, a2是從集合中取出來的與 ...
  • 1.第一個Python程式: print("Hello,world !") #雖然簡單,但是必須寫,為什麼?儀式感 2.python中的變數: 變數就是為了存儲,存儲就是為了後面的過程中的調用。 怎麼來定義變數? name(變數名)= “Gao Han” (值) 變數定義的規則:①變數名只能是 字母 ...
  • 在javaweb開發中,Servlet和Filter是很重要的兩個概念,我們平時進行javaweb開發的時候,會經常和Servlet和Filter打交道,但我們真的瞭解Servlet和Filter嗎? 一、基本概念 Servlet: Servlet 是在WEB伺服器上運行的程式。這個詞是在 Java ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...