本系列說明
遙想當(dāng)年研發(fā)們仰視的架構(gòu)師,一出手一出口必是一套套(?),讓人感覺他們?nèi)鐠叩厣裆闵畈豢蓽y(cè),反駁都找不到趁手的詞匯,雖然當(dāng)時(shí)總覺得哪里不對(duì)?,F(xiàn)而今不同了,架構(gòu)師理論方面的書籍或者視頻資料已然汗牛充棟,整個(gè)程序員群體都可以或多或少說上一些架構(gòu)理論,可能理解不深刻,但是起碼有武器可以拿來(lái)跟架構(gòu)師懟。所以,現(xiàn)在架構(gòu)師真的沒那么容易混了。本文(或者系列???...如果我不懶的話)的立意與常規(guī)的架構(gòu)講述套路不同,筆者想通過生活小細(xì)節(jié)或者小啟發(fā)來(lái)延伸出一些架構(gòu)思想,以期讓人有更加深刻的理解。
最近干貨的出貨量大大降低,實(shí)在是精力不太夠(其實(shí)正在醞釀,敬請(qǐng)期待)。最近的幾篇都是生活中的靈光一現(xiàn),寫出來(lái)的東東竟然也有不小的啟發(fā)性和可讀性。今天要說道的,正是迎著朝陽(yáng)駕車上班途中偶感所得。
# 變道提前打燈
上海中環(huán)和羅山高架路上的不少電子警示牌,不厭其煩地提示著"變道請(qǐng)?zhí)崆?秒打轉(zhuǎn)向燈",以前看到了也就看到了,因?yàn)楣P者一直很守法,一般提前5秒就打了,所以此警示從未驚起什么波瀾。某日清晨腦回路猛然搭住,就想如果變道不打燈被視作違章的話,這個(gè)識(shí)別的程序邏輯該怎么設(shè)計(jì)呢?
砍材不誤磨刀功,不了解的領(lǐng)域準(zhǔn)備工作還是要充分調(diào)研的:
大眾熟知的闖紅燈識(shí)別,步驟是通過路口的三個(gè)感應(yīng)線圈觸發(fā)攝像頭的三次拍照,命中三次就視為違章,筆者開始也是想當(dāng)然的認(rèn)為攝像頭大部分都是靠拍照。
后來(lái)跟專業(yè)的同學(xué)求證,新一代交通攝像頭的工作原理其實(shí)大約是這樣子的:攝像頭會(huì)持續(xù)拍攝交通情況的視頻,而在攝像頭所在位置附近會(huì)有個(gè)本地的存儲(chǔ)器和處理器,視頻就存儲(chǔ)在那里,而后處理器會(huì)基于圖像識(shí)別或者人工智能blablabla等技術(shù)手段分析視頻中的違章情況,一旦識(shí)別出就會(huì)截取視頻生成圖片,最后這些高清截圖會(huì)上傳到遠(yuǎn)程的市局服務(wù)器存儲(chǔ),而違章截圖也是從此而來(lái)(該信息只是單方面求得,如有偏差歡迎指正)。
繼續(xù)又去查了一下,汽車轉(zhuǎn)向燈的頻閃間隔大約是1Hz,也就是一秒鐘閃爍一次,當(dāng)然也有0.5秒閃爍一次的,這個(gè)汽車行業(yè)和國(guó)家并沒有硬性規(guī)定。
拋開圖像識(shí)別的領(lǐng)域不談,剩下的實(shí)現(xiàn)難度在哪里呢?且讓我直接寫三個(gè)案例來(lái)闡明:
Case 1
變道前的4.01秒開啟轉(zhuǎn)向燈,到3.01秒的時(shí)候轉(zhuǎn)向燈剛好熄滅,到2.01秒的時(shí)候轉(zhuǎn)向燈再次亮起。
Case 2
變道前的4.99秒開啟轉(zhuǎn)向燈,到3.99秒的時(shí)候轉(zhuǎn)向燈剛好熄滅,到2.99秒的時(shí)候轉(zhuǎn)向燈再次亮起。
Case 3
需求只提到了提前3秒這個(gè)下限值,那上限值應(yīng)該是多少呢?我可否提前5秒打轉(zhuǎn)向,甚至提前1分鐘打燈呢?很明顯間隔太久的轉(zhuǎn)向燈完全沒有意義,所以這個(gè)需求描述是有缺陷的。
剖析解讀
Case 1 & 2兩個(gè)案例類似,都在3秒前打燈了所以肯定不違章,但是3秒這個(gè)時(shí)點(diǎn)上轉(zhuǎn)向燈都是熄滅狀態(tài)。視頻分析所需的時(shí)間窗口當(dāng)然不能卡在3秒這個(gè)時(shí)點(diǎn)上,那么需要放大到多少合適呢?案例1的時(shí)間窗口必須不小于3.02秒,案例2不小于4秒。注意,這里說的是時(shí)間窗口的下限。
而Case 3主要說的是時(shí)間窗口的上限,因?yàn)槲也豢赡馨褧r(shí)間窗口設(shè)置到無(wú)限大,按照交通實(shí)際情況,1分鐘開外的轉(zhuǎn)向警示基本無(wú)意義了。經(jīng)查交通法規(guī)里確實(shí)沒有這方面的描述,看來(lái)對(duì)于“提前”這件事情大家都知道很難量化的定義它。
再考慮一下需求設(shè)計(jì)問題:
(架構(gòu)師的基本素質(zhì)之一 -- 質(zhì)疑需求&明確需求)
1. 是選擇提前“時(shí)間”合適還是提前“距離”合適?選擇“時(shí)間”就會(huì)出現(xiàn)上面的時(shí)間窗口臨界問題。選擇“距離”,在不同的車速狀況下,“車速”越快打轉(zhuǎn)向燈的提前距離就應(yīng)該越大,若是需要再嚴(yán)謹(jǐn)些的描述,這個(gè)“車速”應(yīng)該是前后車的“相對(duì)速度”,想想是不是如此?
對(duì)于汽車的轉(zhuǎn)向燈頻閃頻率國(guó)家似乎并沒有一個(gè)統(tǒng)一標(biāo)準(zhǔn),那就意味著有長(zhǎng)有短,進(jìn)一步造成時(shí)間窗口的不可確定性。
閃爍次數(shù)前文也簡(jiǎn)單提過,基于頻閃頻率的最小閃爍次數(shù)也需要保證,否則連2S的時(shí)間窗口都看不到轉(zhuǎn)向燈亮起過,這視頻沒法分析了。
另外,基于3個(gè)案例假設(shè)我們?cè)O(shè)定時(shí)間窗口為變道前的[-5s, -3s],表示這個(gè)時(shí)間區(qū)間內(nèi)必須出現(xiàn)過轉(zhuǎn)向燈的亮起狀態(tài)。如果某個(gè)極端情況轉(zhuǎn)向燈在[-5s,-3s]僅亮起一次,(-3s, 0) 再也沒有亮起過,也就是轉(zhuǎn)向燈總共就閃了一次而已,警示作用很不明顯,那么對(duì)于轉(zhuǎn)向燈的最小閃爍次數(shù)是否有要求呢?經(jīng)筆者對(duì)自己車子的多次測(cè)試,開啟一下轉(zhuǎn)向即使馬上關(guān)閉,轉(zhuǎn)向燈也至少閃爍3次。(對(duì)測(cè)試當(dāng)日跟我車屁股后面的司機(jī)師傅們深表同情,你們可能一直在罵前面那個(gè)傻叉為什么一直在打轉(zhuǎn)向?)
解決方案
透過問題看本質(zhì)無(wú)非就是時(shí)間窗口的臨界設(shè)定問題。
首先簡(jiǎn)單放大時(shí)間窗口這么低級(jí)的方案肯定為架構(gòu)師所不齒,因?yàn)樵俅蟮臅r(shí)間窗口都可能會(huì)存在臨界問題,而且時(shí)間窗口越大處理器分析的壓力就越大。
這里我要祭出本人一直以來(lái)作為架構(gòu)師灰?;页V匾呛苋菀妆缓鲆暤囊粭l原則:“能用規(guī)范化和標(biāo)準(zhǔn)化來(lái)解決的問題,就不要再花更多的成本用在工程手段上”。此原則在創(chuàng)業(yè)公司尤其適用。
比如費(fèi)盡九牛二虎之力研發(fā)了一個(gè)元數(shù)據(jù)管理系統(tǒng),可以對(duì)業(yè)務(wù)系統(tǒng)里各類風(fēng)格迥異千奇百怪格式的日志進(jìn)行解析,后果是這個(gè)元數(shù)據(jù)系統(tǒng)可能永遠(yuǎn)跟不上業(yè)務(wù)系統(tǒng)更新的速度,而你只能跟在屁股后面不停的修正這個(gè)元數(shù)據(jù)系統(tǒng)。如果寫個(gè)日志組件,規(guī)定讓所有的業(yè)務(wù)系統(tǒng)必須集成,直接打印出規(guī)范的統(tǒng)一標(biāo)準(zhǔn)的日志來(lái),元數(shù)據(jù)系統(tǒng)的設(shè)計(jì)就可以相當(dāng)簡(jiǎn)單了。
再比如運(yùn)維耗盡全力為了實(shí)現(xiàn)基于容器化的持續(xù)部署,不得不為每個(gè)業(yè)務(wù)系統(tǒng)不統(tǒng)一的變量聲明方式編寫大量的yaml配置文件,為什么不能讓業(yè)務(wù)系統(tǒng)統(tǒng)一改造成規(guī)范的變量配置方式呢?相信這才是一勞永逸且最省力的方式吧。
回到轉(zhuǎn)向燈的問題上來(lái),我的解決方案首選壓根就不是技術(shù)方案,而是先把上面提到的所有不確定的問題先制定出標(biāo)準(zhǔn)來(lái)。比如閃爍頻率,最少閃爍次數(shù)等。
解決方案我會(huì)用軟件產(chǎn)品的幾個(gè)典型場(chǎng)景來(lái)類比,讓大家自己去聯(lián)想吧。
銀行日終跑批
銀行支付類金融系統(tǒng)都會(huì)有日切,做一些停業(yè)記賬、清算、會(huì)計(jì)日期切換等操作,而這個(gè)窗口就算時(shí)間足夠的短,也不得不停止某些業(yè)務(wù)比如轉(zhuǎn)賬,這段時(shí)間也就是銀行日切黑暗期。有的銀行在晚上 9-11點(diǎn),有的在凌晨1-2點(diǎn),不一而足。首先對(duì)于龐大的信息系統(tǒng)要做好時(shí)間的完全同步和一致是基本不可能的,所以時(shí)間窗口可能產(chǎn)生交叉,由此出現(xiàn)任何的賬務(wù)數(shù)據(jù)異常,都是很可怕的一件事情。
TCP的滑動(dòng)窗口實(shí)現(xiàn)
前面描述的所有細(xì)節(jié)都是基于變道這個(gè)時(shí)點(diǎn)來(lái)往前倒推,如果用類似TCP的滑動(dòng)窗口方式呢?比如我們就設(shè)定5秒的滑動(dòng)窗口,交通攝像頭我們估算一秒100幀的水平,那么5秒的窗口里就有500幀圖像需要處理。對(duì)滑動(dòng)窗口內(nèi)的每幀圖像進(jìn)行識(shí)別,標(biāo)示出當(dāng)前轉(zhuǎn)向燈亮暗的狀態(tài),當(dāng)某一幀識(shí)別出變道動(dòng)作,那只要把[-5s, -3s)區(qū)間內(nèi)的圖像識(shí)別的轉(zhuǎn)向燈狀態(tài)做個(gè)簡(jiǎn)單的運(yùn)算即可,就算區(qū)間再大一些也完全不是問題。
當(dāng)然轉(zhuǎn)向燈閃爍間隔是一秒的話連續(xù)100幀圖像都應(yīng)該是一樣的狀態(tài),如果只是做轉(zhuǎn)向燈的識(shí)別那么可以把粒度放寬直接跳過其他的99幀,如果也做其他的違章識(shí)別另當(dāng)別論。
隨著處理器給出分析結(jié)果,窗口向后滑動(dòng),繼續(xù)處理新幀和等待處理器的應(yīng)答,如此循環(huán)往復(fù)。
另外,流式數(shù)據(jù)處理框架Spark Streaming也采用了滑動(dòng)窗口實(shí)現(xiàn)方式。
Minimum Window Substring算法
最小覆蓋子串算法,也是從滑動(dòng)窗口延伸的一個(gè)算法,當(dāng)然也有數(shù)據(jù)結(jié)構(gòu)的小竅門。因?yàn)橐∽钚。砸采婕暗饺绾问湛s窗口讓子串最小。請(qǐng)考慮如何讓收縮窗口讓視頻分析的區(qū)間更小呢?
算法講解參見
分布式鎖的臨界問題
分布式鎖的實(shí)現(xiàn)方案很多,市面上以Zookeeper, Redis實(shí)現(xiàn)的居多。不管是哪種,都要考慮這么一個(gè)問題:持鎖的進(jìn)程在釋放鎖之前崩潰了,導(dǎo)致鎖一直在無(wú)法釋放的狀態(tài)。為了避免這種情況,一般會(huì)設(shè)置一個(gè)鎖失效時(shí)間,這個(gè)時(shí)長(zhǎng)設(shè)定一般要比99%的持鎖時(shí)間久,那么就引入另一個(gè)問題,假如持鎖的進(jìn)程A就是不小心抖動(dòng)了一下導(dǎo)致持鎖時(shí)長(zhǎng)超過了失效時(shí)長(zhǎng),而失效機(jī)制導(dǎo)致鎖的釋放就意味著進(jìn)程B也可以持有這個(gè)鎖。A B兩個(gè)進(jìn)程同時(shí)持有鎖,我就問你怕不怕!我就問你慌不慌!不然的話,或者你的系統(tǒng)允許這種極端情況的發(fā)生,有一定的容錯(cuò)能力,大不了最后再補(bǔ)救?;蛘吣阈枰粋€(gè)雙向的****鎖檢測(cè)機(jī)制,客戶端在邏輯鏈路上的關(guān)鍵節(jié)點(diǎn)不斷檢測(cè)鎖的剩余時(shí)間,然后基于自己估算的仍需時(shí)間延長(zhǎng)鎖的失效時(shí)間,反過來(lái),服務(wù)端也可以檢測(cè)客戶端的持鎖時(shí)長(zhǎng),在超時(shí)那一刻,通知客戶端必須做出鎖失效/快速失敗的回滾處理等等。這些細(xì)節(jié)都不是那么容易操作的,所以我也就拋出個(gè)思路,拿走不謝。
# 路面積水問題
當(dāng)日是個(gè)雨后的早晨,四處都有積水。
這是發(fā)生在“變道轉(zhuǎn)向”之后再次讓我陷入思考的第二件事。
示意圖如上,我要說的積水問題必須是在高架上下閘道口的坡面上的。為什么我不說更常見的平坦路面的積水區(qū)呢?因?yàn)樽鳛橐粋€(gè)有素質(zhì)的老司機(jī),經(jīng)過平坦路面的積水區(qū)時(shí)路邊若是有行人,必然速度放緩慢慢前行,以防積水潑濺到路人。而在高架坡面上因?yàn)橐曇爸锌床坏礁呒艿紫碌男熊嚮蛘咝腥耍行r(shí)候下意識(shí)的就沖過去,高架下方“爭(zhēng)渡爭(zhēng)渡驚起一灘鷗鷺”。
這里還是友情提醒一下,駕車但凡涉水不管什么情況都要放緩,這對(duì)別人對(duì)自己都沒壞處。
那么這個(gè)故事到底對(duì)架構(gòu)有借鑒意義呢?請(qǐng)大家思考:
路面為什么會(huì)有積水?
積水情況是否有監(jiān)控?
我只是高架路面設(shè)計(jì)師,如何想得到高架下的行車或行人?
路面為什么會(huì)有積水
原因無(wú)非兩個(gè),路面不平整或者沒有傾斜度,排水系統(tǒng)不給力。對(duì)于路面施工的質(zhì)量問題,沒什么好說的,施工和驗(yàn)收環(huán)節(jié)都沒做到位。而依賴的排水系統(tǒng)不給力,對(duì)于高架施工隊(duì)來(lái)說這個(gè)鍋好像是可以甩給排水系統(tǒng)團(tuán)隊(duì)的。
無(wú)需多言,這里我要類比的軟件架構(gòu)類別是“服務(wù)治理”。很明顯高架路面系統(tǒng)依賴了排水系統(tǒng),那我們就來(lái)說道說道限流、路由、熔斷、降級(jí)這四塊。
限流比較容易理解,我的排水管道就這么粗,你拿盆往里灌肯定噴出來(lái),所以超出排水管道處理能力的水就不收了。那多的這些水怎么處理呢?不知道大家有沒有聽過“海綿城市”,通過一些特殊材料或特殊管道設(shè)計(jì),將這些水先蓄在某些地方,以后隨時(shí)可以拿來(lái)澆澆公園的花草啥的也算有效利用,架構(gòu)里這就是常見的“削峰”,一般通過MQ隊(duì)列來(lái)實(shí)現(xiàn)。當(dāng)然還有更簡(jiǎn)單粗暴的就是水從哪里來(lái)回哪里去,我路邊整幾排燒紅的鐵棒,水一沾上就變?yōu)檎羝舭l(fā)掉了(好像發(fā)現(xiàn)了什么了不得的發(fā)明), 架構(gòu)里對(duì)應(yīng)的就是“快速失敗”,既然系統(tǒng)已經(jīng)處理不了你了,就直接給你個(gè)失敗應(yīng)答完事。
路由就是把水引流到不同的排水口(多個(gè)排水口可以理解成排水系統(tǒng)的分布式多節(jié)點(diǎn)),污水和雨水是不同的排水通道,這個(gè)可以叫灰度或A/BTest;大雨會(huì)啟動(dòng)更多的排水口進(jìn)行排水,或者易積水路段使用大的排水口,這個(gè)叫“彈性擴(kuò)容”或者“權(quán)重路由”。
熔斷就像家里的保險(xiǎn)絲,當(dāng)排水系統(tǒng)達(dá)到自己能力的上限,水再也進(jìn)去不去的時(shí)候(響應(yīng)超時(shí)或持續(xù)報(bào)錯(cuò)),排水口可以考慮自動(dòng)關(guān)閉。等排水系統(tǒng)緩過勁來(lái)之后,可以打開排水口繼續(xù)接受排水,這里需要不時(shí)的去探測(cè)排水系統(tǒng)是否恢復(fù),決定何時(shí)再開啟排水口。此處的熔斷就是為了保護(hù)排水系統(tǒng)本身,不要因?yàn)榇罅康乃嗳雽?dǎo)致排水管道的破裂脫節(jié)之類的惡劣情況發(fā)生。
降級(jí)之前要先分級(jí),在排水壓力很大的情況下,可以讓關(guān)鍵核心路段的積水先排掉別讓交通完全癱瘓,其他邊緣路段就自我犧牲一下先關(guān)閉主動(dòng)排水口,也就是說在排水能力有限的情況下,把優(yōu)先權(quán)讓給關(guān)鍵核心,這種是資源搶占式的降級(jí);假設(shè)平日的排水是在管道流動(dòng)的過程中加入了過濾雜質(zhì)的功能,以防大形體的垃圾進(jìn)入排水系統(tǒng)堵塞住出水口,那在暴雨天氣排水壓力很大的情況下,是否可以考慮關(guān)停過濾功能,或者把過濾的標(biāo)準(zhǔn)放寬,這樣排水的過程必然順暢很多,這種是裁剪枝節(jié)、降低失敗率、提高關(guān)鍵流程吞吐量的降級(jí)。
積水情況是否有監(jiān)控
這個(gè)問題其實(shí)有一定的迷惑性,積水這種表象級(jí)別的監(jiān)控絕對(duì)不是我們的目的,而是應(yīng)該看更深一層,如何監(jiān)控到路面的凹陷凸起。類比到軟件系統(tǒng),如果沒有監(jiān)控,我們對(duì)它們將一無(wú)所知。可悲的是,有相當(dāng)比例的工程師對(duì)于系統(tǒng)上線后的感知,仍舊是通過是否有異常日志或者業(yè)務(wù)故障通知,才意識(shí)到自己系統(tǒng)的問題。實(shí)際上軟件產(chǎn)品的交付物其中一個(gè)很重要的特性就是“可監(jiān)控”,對(duì)于一個(gè)無(wú)法從內(nèi)到外看個(gè)透徹的黑匣子,是相當(dāng)恐怖的一件事情。
那么到底應(yīng)該監(jiān)控什么呢?除了對(duì)自身業(yè)務(wù)或技術(shù)指標(biāo)的監(jiān)控,對(duì)于依賴方的監(jiān)控同樣重要。不能只盯著自己高架路面的平整度,還要監(jiān)控你依賴的排水系統(tǒng)處理能力,如果處理速度越來(lái)越慢,那就要考慮告警并介入了。
細(xì)節(jié)為王,眼界放寬
開篇我說過現(xiàn)在的架構(gòu)師不好混,實(shí)際上作為一個(gè)經(jīng)驗(yàn)老到的架構(gòu)師,他的能力就深藏在一些細(xì)節(jié)和眼界上,而這些都不是吃快餐養(yǎng)成的“準(zhǔn)”架構(gòu)師們短時(shí)間內(nèi)可以望其項(xiàng)背的。就像上面的問題“我只是高架路面設(shè)計(jì)師,如何想得到高架下的行車或行人?” 話說沒吃過豬肉也見過豬跑,我就不信你沒有在雨天有積水的馬路上走過,或者親身經(jīng)歷或者看到別人被積水坑害過。
回到軟件架構(gòu)上,有些細(xì)節(jié)可能讓你空想你是想不出來(lái)的,但是你從初級(jí)工程師成長(zhǎng)起來(lái)肯定踩到過某些坑,而這些坑都應(yīng)該在你的架構(gòu)設(shè)計(jì)里避免。一個(gè)標(biāo)準(zhǔn)的系統(tǒng)應(yīng)用特性,很多人可以張嘴就來(lái):可擴(kuò)展性、高可靠、高可用、低延遲、BLABLABLA,又有多少人真正的對(duì)“可運(yùn)維”“可監(jiān)控”“可排障”“可持續(xù)交付”等在概要設(shè)計(jì)初期就有考慮呢?
# 課間不休息了,我再啰嗦兩句
到這里,發(fā)現(xiàn)自己就半小時(shí)的車程感悟了這么多,也真的挺難為自己的。
總結(jié)性陳述:其實(shí)主要就扯了臨界時(shí)間窗口、服務(wù)治理兩個(gè)架構(gòu)設(shè)計(jì)經(jīng)常遇到的點(diǎn),結(jié)合**提前打轉(zhuǎn)向燈 **和 防止****路面積水兩個(gè)場(chǎng)景做了發(fā)散性的對(duì)比,其他一些只言片語(yǔ)的架構(gòu)思想僅為個(gè)人心得,如有異議隨時(shí)歡迎來(lái)懟。
就此擱筆,有緣再見。