時事件聚合器rea:用Python構(gòu)建實(shí)時監(jiān)控與告警系統(tǒng))
如果你在做后端或者數(shù)據(jù)系統(tǒng)相關(guān)的工作大概率遇到過這種狀態(tài)服務(wù)上線了一堆日志散落在各個角落報表永遠(yuǎn)是第二天的領(lǐng)導(dǎo)或者運(yùn)營突然問一句“現(xiàn)在支付成功率是多少”你只能打開幾個終端去數(shù)日志。我去年在某公司內(nèi)部就折騰了這樣一個小項(xiàng)目名字就叫rea全稱是Real-time Event Aggregator實(shí)時事件聚合器。rea 做的事不復(fù)雜把不同服務(wù)產(chǎn)生的結(jié)構(gòu)化事件統(tǒng)一收上來按業(yè)務(wù)規(guī)則做實(shí)時窗口聚合算出大家關(guān)心的指標(biāo)再在指標(biāo)異常時把消息推給負(fù)責(zé)人。文章后面我準(zhǔn)備把整個項(xiàng)目從需求拆解、架構(gòu)設(shè)計、核心實(shí)現(xiàn)到踩坑記錄完整講一遍如果你正在做數(shù)據(jù)采集、監(jiān)控看板或者想自己搭一套輕量級實(shí)時處理管線這里面的思路和代碼應(yīng)該能直接參考。1. 項(xiàng)目背景與需求拆解為什么會有 rea1.1 最初的痛點(diǎn)數(shù)據(jù)很多答案很少項(xiàng)目開始前我面對的情況很典型。支付鏈路的成功率算不出來實(shí)時值因?yàn)橹Ц妒录诮灰追?wù)扣款結(jié)果在賬務(wù)服務(wù)前端埋點(diǎn)在客戶端三個地方的數(shù)據(jù)分散在不同的存儲里。運(yùn)營想第二天看數(shù)據(jù)數(shù)據(jù)分析那邊要凌晨跑批任務(wù)等結(jié)果出來已經(jīng)是早上六七點(diǎn)。而當(dāng)時最常被問到的問題恰恰都是“現(xiàn)在怎么樣”現(xiàn)在下單成功率是多少、現(xiàn)在告警數(shù)多不多、新版功能上線后點(diǎn)擊率有沒有變化。我以前也做過一些臨時腳本從日志文件里 grep 關(guān)鍵詞再用時間戳統(tǒng)計。這種辦法在請求量小的時候勉強(qiáng)能用但事件一多就亂了而且每次都要重新寫一遍解析邏輯完全沒有積累。說到底不是沒有數(shù)據(jù)是缺一條能實(shí)時匯總結(jié)論的鏈路。rea 的核心訴求就是把“散落的事件”變成“實(shí)時的指標(biāo)”。1.2 需求收斂三個核心能力項(xiàng)目一開始很容易貪大想一個系統(tǒng)搞定采集、清洗、計算、存儲、展示、告警、權(quán)限管理。后來我把需求砍到只保留真正要用的三塊第一統(tǒng)一接入。所有來源的事件不管原來是日志、HTTP 回調(diào)還是消息隊列里的數(shù)據(jù)都通過同一個協(xié)議進(jìn)入 rea統(tǒng)一成一種結(jié)構(gòu)。這一步解決了“多系統(tǒng)重復(fù)對接”的麻煩。第二實(shí)時聚合。對特定事件按時間窗口做計數(shù)、求和、平均、去重、分位數(shù)等計算窗口能精細(xì)到 10 秒或 1 分鐘級別結(jié)果要能立刻查到。第三規(guī)則告警。預(yù)設(shè)閾值比如“最近 5 分鐘支付失敗率大于 10%”一旦命中就通過 webhook 推到團(tuán)隊群里替代人工盯日志。查詢展示是后加的但很輕量只做了一個簡單的指標(biāo)接口讓運(yùn)營那邊能用表格工具拉數(shù)。當(dāng)時如果加太多權(quán)限、審計、多租戶的需求項(xiàng)目大概率會爛尾。小團(tuán)隊做內(nèi)部工具先把主鏈路打通比什么功能都重要。1.3 技術(shù)選型思考為什么不直接上重型方案討論方案時難免有人提議用現(xiàn)成的流處理框架加時序數(shù)據(jù)庫再配一套監(jiān)控告警組件。不能說這個思路不對但對當(dāng)時的數(shù)據(jù)規(guī)模不劃算。每天事件量在幾百萬條級別規(guī)則只有十幾條團(tuán)隊就兩三個人還需要同時維護(hù)多個節(jié)點(diǎn)學(xué)習(xí)成本和運(yùn)維成本反而吞掉了業(yè)務(wù)收益。rea 走的是一條中間路線用輕量消息中間件接收事件用 Python 做聚合邏輯用內(nèi)存存儲保存近期窗口結(jié)果定期把快照和明細(xì)落到磁盤。這樣的組合勝在簡單一個進(jìn)程就能跑起來出現(xiàn)問題我能直接看代碼定位不需要翻一堆分布式系統(tǒng)的日志。我列過一個對比也貼在當(dāng)時的方案文檔里對比維度重型方案rea 輕量方案部署節(jié)點(diǎn)數(shù)至少 5 個以上1 個主進(jìn)程學(xué)習(xí)成本需要理解流處理概念和運(yùn)維細(xì)節(jié)只需掌握 Python 和基礎(chǔ)隊列擴(kuò)展能力適合每天億級事件百萬級事件夠用故障排查涉及多組件協(xié)調(diào)鏈路長單進(jìn)程日志清晰迭代速度改一行邏輯可能要重新提交任務(wù)改完重啟即可這個選擇的核心邏輯是控制復(fù)雜度先把業(yè)務(wù)問題解決掉。如果將來事件規(guī)模漲到億級再遷到重型架構(gòu)也不遲而且遷移時聚合規(guī)則和告警邏輯是可以原樣帶過去的。2. 整體架構(gòu)設(shè)計從一個采集口到完整處理管線2.1 模塊劃分與數(shù)據(jù)流向rea 的架構(gòu)沒有特別玄乎數(shù)據(jù)流是單向的接入端收到事件經(jīng)過校驗(yàn)和規(guī)范化放進(jìn)一個有界隊列聚合進(jìn)程從隊列里取事件按事件名和維度分組更新對應(yīng)時間窗口的累加值規(guī)則引擎定時掃描窗口狀態(tài)命中閾值就觸發(fā)告警查詢接口直接讀窗口狀態(tài)返回近實(shí)時指標(biāo)。展開成模塊就是五個部分接入適配器、事件隊列、聚合內(nèi)核、規(guī)則引擎、存儲與查詢。接入適配器負(fù)責(zé)把不同來源的數(shù)據(jù)格式轉(zhuǎn)成統(tǒng)一事件事件隊列起到緩沖作用避免瞬時流量沖垮聚合端聚合內(nèi)核是核心后面重點(diǎn)講規(guī)則引擎不參與計算只做判斷存儲層管窗口狀態(tài)的快照和查詢。我特意沒有引入外部存儲依賴來存每條原始事件只在內(nèi)存里維護(hù)聚合狀態(tài)。因?yàn)橹笜?biāo)計算需要的是窗口內(nèi)的匯總值不是每條明細(xì)明細(xì)另外落一份磁盤文件就夠了平時沒有人會去逐條查。2.2 數(shù)據(jù)接入層統(tǒng)一協(xié)議與適配器接入層要解決的核心問題是格式混亂。有的服務(wù)發(fā) JSON有的發(fā)純文本日志有的通過回調(diào)帶一串加密參數(shù)。rea 的做法是定一個內(nèi)部標(biāo)準(zhǔn)事件結(jié)構(gòu)所有外部數(shù)據(jù)都通過適配器轉(zhuǎn)換成標(biāo)準(zhǔn)結(jié)構(gòu)。標(biāo)準(zhǔn)事件包含五類字段事件名、發(fā)生時間、來源服務(wù)、維度標(biāo)簽、數(shù)值。事件名決定算哪類指標(biāo)維度標(biāo)簽用于拆分統(tǒng)計數(shù)值用于求和或平均。這樣設(shè)計有一個好處新接入一個數(shù)據(jù)源時只需要寫一個適配器函數(shù)不需要改聚合邏輯。之前新業(yè)務(wù)接入平均半天就能完成大部分時間花在溝通字段含義上而不是寫代碼。2.3 聚合內(nèi)核為什么不用每條事件都寫庫聚合最忌諱的做法是每來一條事件就寫一次數(shù)據(jù)庫。之前我用關(guān)系表做過一版每秒一千條事件時數(shù)據(jù)庫寫并發(fā)就把服務(wù)拖垮了。后來改成窗口預(yù)聚合事件只寫內(nèi)存窗口關(guān)閉時才把匯總結(jié)果寫一次。具體做法是用事件名和維度組合作為桶的 key每個桶里保存若干時間窗口的累加值。比如一個桶 key 可能是pay_result:pay_success:dcsz同一秒內(nèi)所有到達(dá)事件累加到一個計數(shù)器上。查詢時直接取桶內(nèi)當(dāng)前窗口的值不用掃描明細(xì)。這條思路其實(shí)就是流式聚合里的“增量更新”rea 只是用最簡單的方式實(shí)現(xiàn)了它。2.4 存儲與查詢熱數(shù)據(jù)在內(nèi)存快照落磁盤窗口聚合結(jié)果屬于熱數(shù)據(jù)讀取頻繁且過期很快放在內(nèi)存里最合適。rea 用一個全局字典保存所有桶的窗口狀態(tài)字典的 key 是維度組合value 是窗口數(shù)組。窗口過期后不是立刻刪除而是把它轉(zhuǎn)成一行匯總記錄寫入磁盤上的日志文件方便以后按小時回看。查詢接口讀取內(nèi)存字典返回當(dāng)前窗口的指標(biāo)值。如果某個維度組合沒有數(shù)據(jù)接口返回空而不是報錯這樣在看板工具里顯示成 0 或 null 都由前端決定。存儲層的核心取舍是為查詢速度犧牲少量一致性。重啟或崩潰時最多丟幾秒窗口數(shù)據(jù)對業(yè)務(wù)影響可以接受。3. 關(guān)鍵技術(shù)細(xì)節(jié)與實(shí)現(xiàn)3.1 統(tǒng)一事件模型字段與約定rea 的事件模型長這樣{ event_name: pay_result, occurred_at: 1727600000000, source: trade-service, dimensions: { result: pay_success, dc: sz }, value: 1, trace_id: 8f3a2b }event_name 用“業(yè)務(wù)動作”而不是“日志級別”來命名比如 pay_result、order_created、button_click。occurred_at 使用毫秒級 UTC 時間戳這是整個系統(tǒng)統(tǒng)一時間的基礎(chǔ)。dimensions 是維度標(biāo)簽的集合聚合時會把整個 dimensions 字典序列化后拼進(jìn)桶 key這意味著不同維度組合會獨(dú)立計算互不干擾。value 默認(rèn)是 1如果需要統(tǒng)計金額就傳具體數(shù)值。時間戳有一個容易被忽略的細(xì)節(jié)客戶端和服務(wù)端時鐘可能不一致。如果客戶端提前兩秒上報按處理時間統(tǒng)計會落到錯誤的窗口。rea 統(tǒng)一用 occurred_at 作為事件時間處理進(jìn)程只負(fù)責(zé)記賬不負(fù)責(zé)打時間戳。3.2 窗口聚合算法滾動窗口與滑動窗口窗口是實(shí)時聚合的基本單位。我實(shí)現(xiàn)了兩種窗口。滾動窗口最直觀固定時間長度比如每分鐘一個桶。實(shí)現(xiàn)上依賴取整函數(shù)把 occurred_at 除以窗口毫秒數(shù)再取整就得到窗口序號同一個窗口序號的事件歸到同一批。代碼如下def window_key(event_time_ms: int, window_ms: int) - int: return event_time_ms // window_ms滑動窗口更復(fù)雜一點(diǎn)比如“最近 5 分鐘”每秒滑動一次。如果每個窗口都單獨(dú)存一份累加值內(nèi)存消耗會很大。rea 的簡化做法是維護(hù)一個按秒拆分的數(shù)組長度等于窗口秒數(shù)每秒一個計數(shù)器。計算窗口總量時把數(shù)組里所有元素相加而不是存多份窗口狀態(tài)。class SlidingWindow: def __init__(self, window_seconds300): self.buckets [0] * window_seconds self.window_seconds window_seconds def add(self, occurred_at_ms: int, delta: float): idx (occurred_at_ms // 1000) % self.window_seconds self.buckets[idx] delta def total(self) - float: return sum(self.buckets)這個實(shí)現(xiàn)把內(nèi)存占用壓到最低代價是總量計算需要遍歷數(shù)組。窗口切成 300 個一秒桶時每次求和是 300 次加法在百萬級事件下完全可接受。3.3 事件亂序與遲到數(shù)據(jù)用延遲緩沖兜底分布式環(huán)境下事件亂序是必然的。A 服務(wù)先發(fā)出事件但因?yàn)榫W(wǎng)絡(luò)重試比 B 服務(wù)的事件晚到如果直接按事件時間累加窗口狀態(tài)就會出現(xiàn)偏差。rea 的解決方案是一個簡單的延遲緩沖器接收到事件后先不立刻累加而是放進(jìn)一個按事件時間排序的待處理列表停留 2 秒。如果事件遲到時間小于緩沖就能歸到正確的窗口。大于緩沖時間的遲到事件會單獨(dú)進(jìn)入“l(fā)ate_events”計數(shù)器。這樣做不是完美方案但能覆蓋大部分亂序情況。想完全處理亂序需要引入水印機(jī)制和更復(fù)雜的窗口觸發(fā)邏輯對這些規(guī)則的場景來說不劃算。3.4 狀態(tài)快照與恢復(fù)重啟不丟全部狀態(tài)聚合狀態(tài)全在內(nèi)存進(jìn)程一重啟就全沒了這不滿足實(shí)用性要求。rea 實(shí)現(xiàn)了兩個輔助機(jī)制窗口快照和事件日志。窗口快照每隔 30 秒全量把內(nèi)存字典寫入本地文件文件名帶時間戳。事件日志則是把進(jìn)入聚合內(nèi)核的每一條事件追加寫入一個滾動文件。重啟時先加載最近一個快照再重放快照之后的事件日志就能恢復(fù)到接近實(shí)時狀態(tài)。這個機(jī)制有點(diǎn)像數(shù)據(jù)庫的 checkpoint binlog只不過做得粗糙直接。實(shí)際操作中我一般直接手動重啟恢復(fù)時間控制在幾秒內(nèi)。涉及丟失的數(shù)據(jù)最多是快照之后、重啟之前到達(dá)的少量事件業(yè)務(wù)上能容忍。4. 實(shí)操過程從零搭起一套最小可用 rea4.1 環(huán)境準(zhǔn)備與依賴選擇rea 原型只用 Python 標(biāo)準(zhǔn)庫加一個輕量 HTTP 處理模塊沒有引入復(fù)雜的流處理框架。要做到快速復(fù)現(xiàn)你需要 Python 3.10 以上環(huán)境能跑python -m http.server就行。我建議再準(zhǔn)備一個本地目錄存快照和事件日志后續(xù)查看調(diào)試方便。因?yàn)椴幌胱屧鸵蕾囂嗤獠糠?wù)消息隊列我用一個簡單的內(nèi)存有界隊列代替。生產(chǎn)環(huán)境可以換成成熟的消息中間件但對驗(yàn)證流程來說不影響核心邏輯。4.2 定義配置與規(guī)則rea 的配置分為兩塊全局配置和規(guī)則配置。全局配置定義端口、窗口大小、快照間隔。規(guī)則配置定義監(jiān)控什么事件、按什么維度聚合、觸發(fā)什么條件。規(guī)則我設(shè)計成 JSON方便接入方自己改。{ rules: [ { name: pay_failure_rate_5min, event_name: pay_result, window_seconds: 300, metric: rate, condition: 0.10 } ] }規(guī)則里 metric 支持 count、sum、avg、rate。rate 會先算出窗口內(nèi)失敗數(shù)除以總數(shù)再和閾值比較。寫規(guī)則時最關(guān)鍵的是想清楚窗口大小太短容易誤報太長反饋慢我通常建議從 5 分鐘起步再微調(diào)。4.3 核心代碼實(shí)現(xiàn)下面是一個濃縮版的核心實(shí)現(xiàn)只保留接入、聚合、規(guī)則判斷三塊邏輯。import json import time import threading from collections import defaultdict from http.server import BaseHTTPRequestHandler, HTTPServer class WindowAggregator: def __init__(self, window_seconds300): self.window_seconds window_seconds self.counters defaultdict(lambda: defaultdict(float)) self.lock threading.Lock() def add(self, event): event_name event[event_name] dim_key json.dumps(event.get(dimensions, {}), sort_keysTrue) bucket event[occurred_at] // 1000 // self.window_seconds with self.lock: self.counters[event_name][dim_key][bucket] event.get(value, 1) def snapshot(self): with self.lock: return json.dumps(self.counters)這個類把事件累加到對應(yīng)窗口桶。注意并發(fā)安全HTTP 線程和聚合線程同時訪問 counters所以加了一把鎖。規(guī)則引擎負(fù)責(zé)掃描窗口狀態(tài)class RuleEngine: def __init__(self, rules, aggregator): self.rules rules self.aggregator aggregator def evaluate(self): now time.time() * 1000 current_bucket now // 1000 // self.aggregator.window_seconds for rule in self.rules: event_name rule[event_name] dim_key rule.get(dim_key) for dims, buckets in self.aggregator.counters.get(event_name, {}).items(): if dim_key and dim_key ! dims: continue total sum(v for k, v in buckets.items() if k current_bucket) if rule[metric] count and total rule[threshold]: print(ftrigger rule: {rule[name]})這里為了展示邏輯閾值直接通過參數(shù)簡化了實(shí)際項(xiàng)目里應(yīng)當(dāng)從 JSON 配置讀取。接入 HTTP 服務(wù)使用標(biāo)準(zhǔn)庫 BaseHTTPRequestHandler接口路徑/collect接收 POST JSON 事件。收到后解析、校驗(yàn)、轉(zhuǎn)為標(biāo)準(zhǔn)事件放進(jìn)隊列。聚合線程每秒從隊列取一批事件調(diào)用 aggregator再執(zhí)行規(guī)則評估。4.4 模擬數(shù)據(jù)與結(jié)果驗(yàn)證為了驗(yàn)證鏈路我寫了腳本模擬一萬次支付事件其中失敗事件約 300 次每次事件隨機(jī)分布在 5 分鐘內(nèi)。模擬完成后調(diào)用指標(biāo)接口返回五個窗口的失敗率和我預(yù)設(shè)的概率基本一致。整個處理流程耗時約 2.8 秒內(nèi)存占用不到 50MB說明這套輕量方案在小規(guī)模數(shù)據(jù)下完全跑得動。壓測數(shù)據(jù)如下事件總量處理耗時峰值內(nèi)存16 核 CPU1 萬2.8 秒45 MB~5%10 萬26.1 秒168 MB~12%100 萬約 5 分鐘1.1 GB~35%一百萬條時內(nèi)存接近 1GB原因是窗口桶和維度 key 數(shù)量多。這是純 Python 實(shí)現(xiàn)的正常表現(xiàn)對于內(nèi)部工具是可接受的。如果數(shù)據(jù)量再漲第一件要做的事不是優(yōu)化代碼而是加維度基數(shù)限制。5. 常見問題與排查實(shí)錄5.1 事件積壓寫入慢拖垮消費(fèi)原型上線后遇到第一個問題是事件積壓。HTTP 接口收到事件后直接落日志文件每一條都觸發(fā)一次磁盤寫入壓力一大處理線程消費(fèi)不過來隊列長度持續(xù)增長。后來把日志寫入改成批量緩沖每攢夠 512 條或者間隔 1 秒才寫一次問題立刻緩解。排查這類問題的思路很簡單先看隊列長度是不是持續(xù)增長如果是就分階段量每段的耗時。當(dāng)時量化結(jié)果是磁盤寫入占了 80% 以上耗時改掉就好。如果你在自建系統(tǒng)建議第一時間就把“批量讀寫”作為默認(rèn)設(shè)計不要寫出逐條落盤的版本。5.2 數(shù)據(jù)亂序兩個服務(wù)的時間差引發(fā)誤統(tǒng)計我用一個模擬客戶端同時發(fā)送事件發(fā)現(xiàn)服務(wù) A 的事件總是比服務(wù) B 晚 3 秒。原因是 A 服務(wù)里的日志時間用的是本機(jī)時間該機(jī)器時鐘慢了 3 秒。結(jié)果按事件時間統(tǒng)計時部分事件被分到錯誤窗口導(dǎo)致指標(biāo)時高時低。解決辦法分兩步客戶端統(tǒng)一改用 rea 下發(fā)的標(biāo)準(zhǔn)時間戳同時保留事件發(fā)生時間rea 內(nèi)部對遲到事件做延遲緩沖覆蓋網(wǎng)絡(luò)抖動和輕微時鐘偏差。如果偏差超過幾分鐘再精確處理就不劃算了應(yīng)該直接從數(shù)據(jù)源修時鐘而不是在聚合層硬扛。5.3 內(nèi)存溢出維度基數(shù)失控事件里有一個維度叫request_id本來是想做鏈路追蹤但被誤加進(jìn)了聚合維度。每條請求的 request_id 都不同導(dǎo)致桶 key 數(shù)量飛速增長內(nèi)存直接暴漲。這個問題很隱蔽因?yàn)闇y試數(shù)據(jù)量小看不出問題一上真實(shí)流量就暴露了。解決方法是兩招一是在規(guī)則配置里顯式聲明參與聚合的維度白名單不在白名單里的字段不拼進(jìn) key二是給桶數(shù)量設(shè)置上限超過上限時輸出告警而不是繼續(xù)建新桶。自那以后我對“維度基數(shù)”就特別敏感制定新規(guī)則前必須先問一句這個維度會有多少個不同的值。5.4 窗口邊界時間取整帶來的誤差另一個經(jīng)典問題是窗口邊界。假設(shè)窗口是 5 分鐘我用occurred_at / 1000 / 300取整剛好整點(diǎn) 12:00:00 的事件會被分到下一個窗口嗎不會整除邏輯是歸到當(dāng)前窗口。但查詢端展示時如果前端自己按“當(dāng)前時間 -5 分鐘”做聚合就跟 rea 的窗口錯位了。這里我踩過坑之后總結(jié)了兩條約定所有時間顯示統(tǒng)一使用 UTC不能直接用本地時間拼字符串所有窗口的起止時間由 rea 接口返回前端只渲染不在查詢端二次計算窗口??缣?、跨小時的邊界問題在統(tǒng)一時間基準(zhǔn)后基本都消失了。6. 落地效果與擴(kuò)展空間6.1 實(shí)際收益從十分鐘到五秒rea 上線之后最直觀的變化是業(yè)務(wù)方問“現(xiàn)在怎么樣”時能在幾秒內(nèi)給出答案。支付成功率、錯誤數(shù)、關(guān)鍵埋點(diǎn)點(diǎn)擊量這些指標(biāo)不再等第二天的報表。告警方面原來靠人工盯日志平均發(fā)現(xiàn)一次異常要十分鐘左右rea 把瓶頸壓到 5 秒級別而且直接在團(tuán)隊群推送比人肉監(jiān)控可靠得多。從成本上看這一整套系統(tǒng)只占一個低配容器每月資源開銷可以忽略不計。對于兩三個人的小組rea 的投入產(chǎn)出比相當(dāng)高。它沒有解決所有數(shù)據(jù)問題但解決了當(dāng)時最痛的那一個。6.2 可擴(kuò)展方向規(guī)則沉淀與多級聚合如果繼續(xù)演進(jìn)我會做三件事。第一把規(guī)則從 JSON 升級成可以熱加載的配置中心業(yè)務(wù)方自助提交規(guī)則不用每次改代碼重啟。第二增加多級聚合能力先按原始維度聚合再按省份、機(jī)房等上級維度合并這樣能省掉大量重復(fù)計算。第三把窗口結(jié)果定時導(dǎo)出到離線數(shù)倉做趨勢分析讓實(shí)時指標(biāo)和歷史報表在同一套口徑下對齊。還有一個方向是預(yù)警自愈。告警觸發(fā)后自動附帶最近十分鐘的趨勢圖鏈接甚至觸發(fā)一個預(yù)置的處置腳本比如限流或者切流量。這些能力并不難加因?yàn)榫酆蠣顟B(tài)已經(jīng)在內(nèi)存里了只是多一步動作。6.3 個人體會簡單系統(tǒng)也能解決真問題做完 rea我最大的體會是不要總想著一步到位。當(dāng)時如果堅持上全家桶方案大概率到現(xiàn)在還在調(diào)集群業(yè)務(wù)問題一個都沒解決。反而是一個單進(jìn)程、代碼能一眼看穿的輕量系統(tǒng)實(shí)實(shí)在在跑了大半年。另外對實(shí)時系統(tǒng)的監(jiān)控、告警、恢復(fù)機(jī)制一定要在一開始預(yù)留好位置。哪怕用最簡單的日志打印、文件快照也比完全沒有強(qiáng)。我就是因?yàn)橐婚_始沒有快照機(jī)制第一次重啟時全狀態(tài)丟失才回頭補(bǔ)的。如果讓我現(xiàn)在重新面對三個字母“rea”我還是會選同樣的路先想清楚要解決誰的問題用最小閉環(huán)跑通再讓系統(tǒng)隨著真實(shí)需求慢慢長。很多系統(tǒng)不是死在性能上而是死在復(fù)雜度失控上。