寶馬雕車香滿路,另類架構老司機
本系列說明
遙想當年研發們仰視的架構師,一出手一出口必是一套套(?),讓人感覺他們如掃地神僧般深不可測,反駁都找不到趁手的詞彙,雖然當時總覺得哪裡不對。現而今不同了,架構師理論方面的書籍或者視訊資料已然汗牛充棟,整個程式員群體都可以或多或少說上一些架構理論,可能理解不深刻,但是起碼有武器可以拿來跟架構師懟。所以,現在架構師真的沒那麼容易混了。本文(或者系列???...如果我不懶的話)的立意與常規的架構講述套路不同,筆者想通過生活小細節或者小啟發來延伸出一些架構思想,以期讓人有更加深刻的理解。
最近乾貨的出貨量大大降低,實在是精力不太夠(其實正在醞釀,敬請期待)。最近的幾篇都是生活中的靈光一現,寫出來的東東竟然也有不小的啟發性和可讀性。今天要說道的,正是迎著朝陽駕車上班途中偶感所得。

image
# 變道提前打燈
上海中環和羅山高架路上的不少電子警示牌,不厭其煩地提示著" 變道請提前3秒打轉向燈 ",以前看到了也就看到了,因為筆者一直很守法,一般提前5秒就打了,所以此警示從未驚起什麼波瀾。某日清晨腦回路猛然搭住,就想如果變道不打燈被視作違章的話,這個識別的程式邏輯該怎麼設計呢?
砍材不誤磨刀功,不瞭解的領域準備工作還是要充分調研的:
大眾熟知的闖紅燈識別,步驟是通過路口的三個感應線圈觸發攝像頭的三次拍照,命中三次就視為違章,筆者開始也是想當然的認為攝像頭大部分都是靠拍照。
後來跟專業的同學求證, 新一代交通攝像頭 的工作原理其實大約是這樣子的:攝像頭會持續拍攝交通情況的視訊,而在攝像頭所在位置附近會有個本地的儲存器和處理器,視訊就儲存在那裡,而後處理器會基於影象識別或者人工智慧blablabla等技術手段分析視訊中的違章情況,一旦識別出就會擷取視訊生成圖片,最後這些高清截圖會上傳到遠端的市局伺服器儲存,而違章截圖也是從此而來(該資訊只是單方面求得,如有偏差歡迎指正)。
繼續又去查了一下,汽車轉向燈的頻閃間隔大約是1Hz,也就是一秒鐘閃爍一次,當然也有0.5秒閃爍一次的,這個汽車行業和國家並沒有硬性規定。
拋開影象識別的領域不談,剩下的實現難度在哪裡呢?且讓我直接寫三個案例來闡明:
Case 1
變道前的4.01秒開啟轉向燈,到3.01秒的時候轉向燈剛好熄滅,到2.01秒的時候轉向燈再次亮起。

image
Case 2
變道前的4.99秒開啟轉向燈,到3.99秒的時候轉向燈剛好熄滅,到2.99秒的時候轉向燈再次亮起。

image
Case 3
需求只提到了提前3秒這個下限值,那上限值應該是多少呢?我可否提前5秒打轉向,甚至提前1分鐘打燈呢?很明顯間隔太久的轉向燈完全沒有意義,所以這個需求描述是有缺陷的。
剖析解讀
Case 1 & 2兩個案例類似,都在3秒前打燈了所以肯定不違章,但是3秒這個時點上轉向燈都是熄滅狀態。視訊分析所需的時間視窗當然不能卡在3秒這個時點上,那麼需要放大到多少合適呢?案例1的時間視窗必須不小於3.02秒,案例2不小於4秒。注意,這裡說的是時間視窗的下限。
而Case 3主要說的是時間視窗的上限,因為我不可能把時間視窗設定到無限大,按照交通實際情況,1分鐘開外的轉向警示基本無意義了。經查交通法規裡確實沒有這方面的描述,看來對於“提前”這件事情大家都知道很難量化的定義它。
再考慮一下 需求設計 問題:
( 架構師的基本素質之一 -- 質疑需求&明確需求 )
1. 是選擇提前“時間”合適還是提前“距離”合適?選擇“時間”就會出現上面的時間視窗臨界問題。選擇“距離”,在不同的車速狀況下,“車速”越快打轉向燈的提前距離就應該越大,若是需要再嚴謹些的描述,這個“車速”應該是前後車的“相對速度”,想想是不是如此?
-
對於汽車的轉向燈頻閃頻率國家似乎並沒有一個統一標準,那就意味著有長有短,進一步造成時間視窗的不可確定性。
-
閃爍次數前文也簡單提過,基於頻閃頻率的最小閃爍次數也需要保證,否則連2S的時間視窗都看不到轉向燈亮起過,這視訊沒法分析了。
另外,基於3個案例假設我們設定時間視窗為變道前的[-5s, -3s],表示這個時間區間內必須出現過轉向燈的亮起狀態。如果某個極端情況轉向燈在[-5s,-3s]僅亮起一次,(-3s, 0) 再也沒有亮起過,也就是轉向燈總共就閃了一次而已,警示作用很不明顯,那麼對於轉向燈的最小閃爍次數是否有要求呢?經筆者對自己車子的多次測試,開啟一下轉向即使馬上關閉,轉向燈也至少閃爍3次。(對測試當日跟我車屁股後面的司機師傅們深表同情,你們可能一直在罵前面那個傻叉為什麼一直在打轉向?)
解決方案
透過問題看本質無非就是 時間視窗的臨界設定問題 。
首先簡單放大時間視窗這麼低階的方案肯定為架構師所不齒,因為再大的時間視窗都可能會存在臨界問題,而且時間視窗越大處理器分析的壓力就越大。
這裡我要祭出本人一直以來作為架構師灰常灰常重要但是很容易被忽視的一條原則:“ 能用規範化和標準化來解決的問題,就不要再花更多的成本用在工程手段上 ”。此原則在創業公司尤其適用。
比如費盡九牛二虎之力研發了一個元資料管理系統,可以對業務系統裡各類風格迥異千奇百怪格式的日誌進行解析,後果是這個元資料系統可能永遠跟不上業務系統更新的速度,而你只能跟在屁股後面不停的修正這個元資料系統。如果寫個日誌元件,規定讓所有的業務系統必須整合,直接打印出規範的統一標準的日誌來,元資料系統的設計就可以相當簡單了。
再比如運維耗盡全力為了實現基於容器化的持續部署,不得不為每個業務系統不統一的變數宣告方式編寫大量的yaml配置檔案,為什麼不能讓業務系統統一改造成規範的變數配置方式呢?相信這才是一勞永逸且最省力的方式吧。
回到轉向燈的問題上來,我的解決方案首選壓根就不是技術方案,而是先把上面提到的所有不確定的問題先制定出標準來。比如閃爍頻率,最少閃爍次數等。
解決方案我會用軟體產品的幾個典型場景來類比,讓大家自己去聯想吧。
銀行日終跑批
銀行支付類金融系統都會有日切,做一些停業記賬、清算、會計日期切換等操作,而這個視窗就算時間足夠的短,也不得不停止某些業務比如轉賬,這段時間也就是銀行日切黑暗期。有的銀行在晚上 9-11點,有的在凌晨1-2點,不一而足。首先對於龐大的資訊系統要做好 時間的完全同步和一致 是基本不可能的,所以時間視窗可能產生交叉,由此出現任何的賬務資料異常,都是很可怕的一件事情。
TCP的滑動視窗實現
前面描述的所有細節都是基於變道這個時點來往前倒推,如果用類似TCP的滑動視窗方式呢?比如我們就設定5秒的滑動視窗,交通攝像頭我們估算一秒100幀的水平,那麼5秒的窗口裡就有500幀影象需要處理。對滑動視窗內的每幀影象進行識別,標示出當前轉向燈亮暗的狀態,當某一幀識別出變道動作,那隻要把[-5s, -3s)區間內的影象識別的轉向燈狀態做個簡單的運算即可,就算區間再大一些也完全不是問題。
當然轉向燈閃爍間隔是一秒的話連續100幀影象都應該是一樣的狀態,如果只是做轉向燈的識別那麼可以把粒度放寬直接跳過其他的99幀,如果也做其他的違章識別另當別論。
隨著處理器給出分析結果,視窗向後滑動,繼續處理新幀和等待處理器的應答,如此迴圈往復。
另外,流式資料處理框架Spark Streaming也採用了滑動視窗實現方式。
Minimum Window Substring演算法
最小覆蓋子串演算法,也是從滑動視窗延伸的一個演算法,當然也有資料結構的小竅門。因為要取最小,所以也涉及到如何收縮視窗讓子串最小。請考慮如何讓收縮視窗讓視訊分析的區間更小呢?
演算法講解參見
ofollow,noindex">https://www.jianshu.com/p/ce80b4c07c22
分散式鎖的臨界問題
分散式鎖的實現方案很多,市面上以Zookeeper, Redis實現的居多。不管是哪種,都要考慮這麼一個問題:持鎖的程序在釋放鎖之前崩潰了,導致鎖一直在無法釋放的狀態。為了避免這種情況,一般會設定一個鎖失效時間,這個時長設定一般要比99%的持鎖時間久,那麼就引入另一個問題,假如持鎖的程序A就是不小心抖動了一下導致持鎖時長超過了失效時長,而失效機制導致鎖的釋放就意味著程序B也可以持有這個鎖。A B兩個程序同時持有鎖,我就問你怕不怕!我就問你慌不慌!不然的話,或者你的系統允許這種極端情況的發生,有一定的 容錯 能力,大不了最後再補救。或者你需要一個 雙向的****鎖檢測 機制,客戶端在邏輯鏈路上的關鍵節點不斷檢測鎖的剩餘時間,然後基於自己估算的 仍需時間 延長鎖的失效時間,反過來,服務端也可以檢測客戶端的持鎖時長,在超時那一刻,通知客戶端必須做出鎖失效/快速失敗的回滾處理等等。這些細節都不是那麼容易操作的,所以我也就丟擲個思路,拿走不謝。
移步 《理解鎖以及分散式鎖》
# 路面積水問題
當日是個雨後的早晨,四處都有積水。
這是發生在“變道轉向”之後再次讓我陷入思考的第二件事。

image
示意圖如上,我要說的積水問題必須是在 高架上下閘道口的坡面 上的。為什麼我不說更常見的平坦路面的積水區呢?因為作為一個有素質的老司機,經過平坦路面的積水區時路邊若是有行人,必然速度放緩慢慢前行,以防積水潑濺到路人。而在高架坡面上因為視野中看不到高架底下的行車或者行人,有些時候下意識的就衝過去,高架下方“爭渡爭渡驚起一灘鷗鷺”。
這裡還是友情提醒一下,駕車但凡涉水不管什麼情況都要放緩,這對別人對自己都沒壞處。
那麼這個故事到底對架構有借鑑意義呢?請大家思考:
-
路面為什麼會有積水?
-
積水情況是否有監控?
-
我只是高架路面設計師,如何想得到高架下的行車或行人?
路面為什麼會有積水
原因無非兩個,路面不平整或者沒有傾斜度,排水系統不給力。對於路面施工的質量問題,沒什麼好說的,施工和驗收環節都沒做到位。而依賴的排水系統不給力,對於高架施工隊來說這個鍋好像是可以甩給排水系統團隊的。
無需多言,這裡我要類比的軟體架構類別是“ 服務治理 ”。很明顯高架路面系統依賴了排水系統,那我們就來說道說道限流、路由、熔斷、降級這四塊。
限流比較容易理解,我的排水管道就這麼粗,你拿盆往裡灌肯定噴出來,所以超出排水管道處理能力的水就不收了。那多的這些水怎麼處理呢?不知道大家有沒有聽過“海綿城市”,通過一些特殊材料或特殊管道設計,將這些水先蓄在某些地方,以後隨時可以拿來澆澆公園的花草啥的也算有效利用,架構裡這就是常見的“ 削峰 ”,一般通過MQ佇列來實現。當然還有更簡單粗暴的就是水從哪裡來回哪裡去,我路邊整幾排燒紅的鐵棒,水一沾上就變為蒸汽蒸發掉了(好像發現了什麼了不得的發明), 架構裡對應的就是“ 快速失敗 ”,既然系統已經處理不了你了,就直接給你個失敗應答完事。
路由就是把水引流到不同的排水口(多個排水口可以理解成排水系統的分散式多節點),汙水和雨水是不同的排水通道,這個可以叫 灰度或A/BTest ;大雨會啟動更多的排水口進行排水,或者易積水路段使用大的排水口,這個叫“ 彈性擴容 ”或者“ 權重路由 ”。
熔斷就像家裡的保險絲,當排水系統達到自己能力的上限,水再也進去不去的時候( 響應超時 或 持續報錯 ),排水口可以考慮自動關閉。等排水系統緩過勁來之後,可以開啟排水口繼續接受排水,這裡需要不時的去探測排水系統是否恢復,決定何時再開啟排水口。此處的熔斷就是為了保護排水系統本身,不要因為大量的水灌入導致排水管道的破裂脫節之類的惡劣情況發生。
降級之前要先分級,在排水壓力很大的情況下,可以讓關鍵核心路段的積水先排掉別讓交通完全癱瘓,其他邊緣路段就自我犧牲一下先關閉主動排水口,也就是說在排水能力有限的情況下,把優先權讓給關鍵核心,這種是 資源搶佔式 的降級;假設平日的排水是在管道流動的過程中加入了過濾雜質的功能,以防大形體的垃圾進入排水系統堵塞住出水口,那在暴雨天氣排水壓力很大的情況下,是否可以考慮關停過濾功能,或者把過濾的標準放寬,這樣排水的過程必然順暢很多,這種是 裁剪枝節、降低失敗率、提高關鍵流程吞吐量 的降級。
積水情況是否有監控
這個問題其實有一定的迷惑性,積水這種表象級別的監控絕對不是我們的目的,而是應該看更深一層,如何監控到路面的凹陷凸起。類比到軟體系統,如果沒有監控,我們對它們將一無所知。可悲的是,有相當比例的工程師對於系統上線後的感知,仍舊是通過是否有異常日誌或者業務故障通知,才意識到自己系統的問題。實際上軟體產品的交付物其中一個很重要的特性就是“ 可監控 ”,對於一個無法從內到外看個透徹的黑匣子,是相當恐怖的一件事情。
那麼到底應該監控什麼呢?除了對自身業務或技術指標的監控,對於依賴方的監控同樣重要。不能只盯著自己高架路面的平整度,還要監控你依賴的排水系統處理能力,如果處理速度越來越慢,那就要考慮告警並介入了。
細節為王,眼界放寬
開篇我說過現在的架構師不好混,實際上作為一個經驗老到的架構師,他的能力就深藏在一些細節和眼界上,而這些都不是吃快餐養成的“準”架構師們短時間內可以望其項背的。就像上面的問題“ 我只是高架路面設計師,如何想得到高架下的行車或行人? ” 話說沒吃過豬肉也見過豬跑,我就不信你沒有在雨天有積水的馬路上走過,或者親身經歷或者看到別人被積水坑害過。
回到軟體架構上,有些細節可能讓你空想你是想不出來的,但是你從初級工程師成長起來肯定踩到過某些坑,而這些坑都應該在你的架構設計裡避免。一個標準的系統應用特性,很多人可以張嘴就來:可擴充套件性、高可靠、高可用、低延遲、BLABLABLA,又有多少人真正的對 “可運維”“可監控”“可排障”“可持續交付” 等在概要設計初期就有考慮呢?
# 課間不休息了,我再囉嗦兩句
到這裡,發現自己就半小時的車程感悟了這麼多,也真的挺難為自己的。
總結性陳述:其實主要就扯了 臨界時間視窗 、 服務治理 兩個架構設計經常遇到的點,結合**提前打轉向燈 **和 防止****路面積水 兩個場景做了發散性的對比,其他一些隻言片語的架構思想僅為個人心得,如有異議隨時歡迎來懟。
就此擱筆,有緣再見。