天天看點

做煎餅果子的N種方式——From Sequential to Reactive

需要注意的是 本文不是真正的讨論如何做煎餅果子。

相信南方人都吃過煎餅果子——一種裹着生菜和火腿腸的雞蛋煎餅。雖然好吃,其制作過程卻也不比漢堡包簡單,讓我們一起來拆解下吧。

做煎餅果子的N種方式——From Sequential to Reactive

圖 1 ——煎餅果子拆解

從上圖(這個?沒有卷起來)我們可以看到,一個煎餅果子分為下面的幾個部分:

最下面的是雞蛋煎餅——雞蛋是打在煎餅上的

中間是一層生菜——若幹

在上面是一根火腿腸——隻有一根

最後把它卷起來,套上食品塑膠袋,就是煎餅果子了,如下圖:

做煎餅果子的N種方式——From Sequential to Reactive

看到如此美味的煎餅果子,是不是很想自己做來吃吃,甚至是去開個店,專門賣煎餅果子呢?當上CEO,迎娶白富美,這都不是夢啊~~。那麼,下面就讓我們一起看看如果做一個煎餅果子吧。

煎餅果子需要下面幾種食材:

面粉——我們需要做煎餅,

雞蛋N枚——取決于你要做的?的數量,一般一個煎餅果子,使用一枚?

生菜若幹——平均一個煎餅果子,使用1到2片生菜,

火腿腸N根——一般,都會加上火腿腸,可選,一般一個煎餅果子,一根火腿腸。

做煎餅果子的N種方式——From Sequential to Reactive

準備好了原材料,讓我們來做煎餅果子吧,

做煎餅果子的N種方式——From Sequential to Reactive
做煎餅果子的N種方式——From Sequential to Reactive
做煎餅果子的N種方式——From Sequential to Reactive

現在送脆餅

做煎餅果子的N種方式——From Sequential to Reactive
做煎餅果子的N種方式——From Sequential to Reactive
做煎餅果子的N種方式——From Sequential to Reactive

從上面的圖解中我們可以看到,流程是這樣的:

面粉 + 水 -> 面漿;面漿 + 烘焙 -> 煎餅

煎餅 + 雞蛋 + 烘焙 -> 雞蛋煎餅

雞蛋煎餅 + 生菜 -> 帶有生菜的雞蛋煎餅

帶有生菜的雞蛋煎餅 + 火腿腸 -> 帶有生菜和火腿腸的雞蛋煎餅

帶有生菜和火腿腸的雞蛋煎餅 + 卷曲 + 切斷 -> 煎餅果子

我們對上面的步驟進行化簡:

面粉 -> 煎餅

煎餅 + 雞蛋 -> 雞蛋煎餅

雞蛋煎餅 + 生菜 + 火腿腸 -> 煎餅果子

我們對上面的步驟繼續化簡

面粉 -> 煎餅 -> 雞蛋 -> 雞蛋煎餅 -> 生菜 -> 火腿腸 -> 煎餅果子

下面讓我們使用代碼先來模拟下,驗證怎麼樣才可以高效的做出煎餅果子吧。

實作代碼:

做煎餅果子的N種方式——From Sequential to Reactive

讓我們運作上面的代碼,其輸出結果如下:

如果我們偶爾做一次,其實還好啦,滿足,不過作為有夢想的程式員,我們要快點,這樣才有競争力啊,對,快,更快。

這就是我們牛逼的異步的方式了,即基于EventLoop/CSP的方式,事情都是一個人做,這個好了接着做下一個,在做煎餅的時候,不會等待煎餅烤熟。而是回去洗菜、準備火腿腸等。

這個時候,我們就會想,是不是多招聘幾個人呢?掐指一算,對,招聘4個人吧:

做煎餅果子的N種方式——From Sequential to Reactive

這裡我們,招聘了4個人,如下所示:

人手多了,讓我們來看看效果吧:

是的,我們變快了~~~,隻需要 5秒,我們投入了這麼大的人力成本不就是要,客戶第一,讓顧客更加快速地買到煎餅果子麼?

上面的方式稍顯老套,作為弄潮兒,最新的技術,搞搞搞,瞧,我們的透明後廚,簡潔着呢:

做煎餅果子的N種方式——From Sequential to Reactive

通過上面的方式,真的好簡潔,一套一套的,讓我們來看看效果吧:

額,竟然還是這樣?

感覺這個人有點多,生意也不夠好,還是換方式搞吧,某某某,你幫幫他吧,某某某,你也别閑着,把XXX也搞了。

不行,這樣不行,一定要從根本上解決問題,對,我們來梳理一下。

開店成功需要幾個要素?!

成本節約,在等待的時候,可以幫忙做點别的事情。

結構優化,效率提升,建立完整的上下行監管機制,消息順達。

精簡語義,使用專用詞溝通,減少自然語言表意不明。

客戶第一,提高服務品質,盡快傳回結果,不要超出購買者的能夠忍受的最長等待時間。

是以,對我們的例子使用Actor模型來進行模組化,然後應用CQRS來減少語義。當然,這裡我們的事件并沒有持久化,算是部分實作。

Actor模型 + Facade門面模式:

做煎餅果子的N種方式——From Sequential to Reactive

其中,使用 Ask 模式,我們接駁了傳統的門店服務,以及基于Actor 模型的店員。而對于我們的店員,我們又使用了Actor模型以及監管機制,同時使用 CQRS 來對指令和事件進行分離。

其中我們有指令:

做煎餅果子的N種方式——From Sequential to Reactive

在我們的門店服務内部,将會對這些指令進行處理,并且産生相應的事件。

做煎餅果子的N種方式——From Sequential to Reactive

再和門店的接駁處,使用了Ask模式:

做煎餅果子的N種方式——From Sequential to Reactive

而對于老闆/店長來說,他肯定是自己不處理的,是以他将任務,派發給了<code>煎餅果子大俠</code>,即:

做煎餅果子的N種方式——From Sequential to Reactive

注意,上面我們又一次使用了Ask模式,而非FSM。

這項任務就到達了我們的煎餅果子大俠了,他的任務可重了,因為他需要等待的東西有:

雞蛋煎餅

洗好的生菜

撕開好的火腿腸

是以:我們的煎餅果子大俠會分别和,雞蛋煎餅太郎、生菜小二哥以及火腿腸大叔形成依賴關系,并且會依賴于他們的結果,才可以做出一個完整的煎餅果子:

做煎餅果子的N種方式——From Sequential to Reactive

當收到老闆的指令的時候,他将任務進行了拆解,分發給了和他合作的其他店員:

做煎餅果子的N種方式——From Sequential to Reactive

這裡,我們搭配使用了Ask模式和Aggregator模式,将從各個部分收集到的結果,進行彙總,并産生了最終的美味的煎餅果子。

需要注意的是,我們在這裡,并沒有看到煎餅是怎麼來的,而是直接看到的是雞蛋煎餅,這就是DDD中的領域分層和依賴了:

我們的雞蛋煎餅太郎和煎餅西施之間是一個強依賴關系:

做煎餅果子的N種方式——From Sequential to Reactive

這裡,我們看到了一個奇怪的地方,即不對稱的逾時設定。因為太郎對西施特别好,是以頂住壓力,不管怎麼催他,他都會給西施說,别着急,慢慢來。

這就引發一個問題了,不正确的逾時設定,可能讓消費者非常不耐煩,本來消費者已經等了30s了,結果你對他說,哎呀,我們的雞蛋煎餅太郎太忙了,然後顧客灰溜溜的走了,丢失了大量的潛在客戶。

即,不合适的逾時配置,會造成服務品質的下降。

讓我們來把店開起來,并且提供服務吧

做煎餅果子的N種方式——From Sequential to Reactive

現在店開起來了,讓我們來看一看運作結果吧。

喔,完美的組織

架構和模式應用!當然,我們這裡沒有對Event進行持久化,這一點兒是不利于回溯的,同時也沒有應用斷路器模式,以及還有多處不合理的逾時配置。

上面的這些方式,都讓我們不難想,如何讓客戶更少的等待,如何提供更好的服務,如果我們的心更加大,如果我們要把店面做大,甚至要開連鎖店,或者開煎餅果子工廠呢?

技術,不是給業務以限制,而是助力其想象。

如果,我們想要将這個模式,複制到更多的場景,甚至開一條生産,N調生産線,如果我們要這些生産線能實作智能的調控,達到最小的資源占用,來達到最大的效力,那麼我們應該怎麼做呢?

對于這個問題,在2013年到2014年,業界也在思考,後來幾經波折,想到了一種基于流的拓撲描述的方式。下面就讓我們使用反應式流的方式,來實作上面的業務吧。

首先來一個不太清晰的例子,這個例子中,我們使用了Akka-Stream——一個ReactiveStream的實作。

做煎餅果子的N種方式——From Sequential to Reactive

在上圖中,我們對抽象進行了下面幾點改進:

我們的消費請求,被抽象為了一個流,這個流,類似于我們做的供給側改革,使用消費請求,來指導我們的生産。

我們的店員,不再是基本的店員了,把他們想成持續提供煎餅,雞蛋煎餅,生菜,和撕開的火腿腸,以及煎餅果子的流,即生産線。

我們可以動态地控制生産速度,如果消費者多,就在能力滿足的情況下,盡量地多生産,在某項能力不能滿足目前需求的情況下,就進行複制這些服務,對其進行複制,以提高更搞的生産力。比如,做煎餅是個比較緩慢的動作,那麼我們可以再加一條生産線,這個生産線專門生産煎餅。

有了這個流,我們發現,煎餅的生産和雞蛋煎餅的生産,總是強依賴的,那麼我們可以将他們部署到臨近的生産線,減少成本。

同上,我們發現雞蛋煎餅和生菜以及火腿腸的産生,也是最終要進行合并使用,那麼我們也可把這三種生産線,排布在一起,這樣降低了将這三種材料,運輸到煎餅果子大哥的時間。

如果我們發現煎餅果子大哥這個隻做煎餅的過程很慢了,這個時候我們可以隻在這個流程節點上,進行複制,進而加快煎餅果子的卷曲和打包過程。

我們如果一個發貨視窗發不過來,我們可以多開兩個門店/物流發貨視窗。

我們可以使用類似于由倉庫直接發貨的方式,将生産好的熱騰騰的煎餅果子,直接交給購買者,而不用通過我們的店長或者店面,在消息模式中,這叫做Forward模式。

即,我們描述了依賴,而再有了這樣的依賴之後,基于我們的需求和供應資訊。我們可以處處都進行複制流程,複制處理節點,進而做到最優化地讓熱騰騰的煎餅果子到達使用者的手裡。

當然,如果我們實在是,實在是不能再擴充生産線了,那麼我們就會進行回壓,回壓的時候,我們在最前面,就會讓客戶等待,或者在滿足SLA的情況下,進行一些政策,但是,我們整體的服務品質,依然是那麼得好,RS,就是雙向的流,控制流,資料流。

即,類似于下面的結構:

做煎餅果子的N種方式——From Sequential to Reactive

我們将生産好的結果,直接遞交給了消費者。

即,可以做到圖中的任意一個方框内的結構拓撲,都是可以單獨地進行複制,和調控的,甚至是智能地進行調控:)。智慧物流,我們也有智慧服務。

有了上面的實作,我們可以使用現在的一些語義化的工具來進行描述,比如:

做煎餅果子的N種方式——From Sequential to Reactive

好了,我們再看一種:

做煎餅果子的N種方式——From Sequential to Reactive

然後,我們再看一種:

做煎餅果子的N種方式——From Sequential to Reactive

然後仔細一下對比:

做煎餅果子的N種方式——From Sequential to Reactive

我們都需要煎餅Flow/Source,而且從煎餅Flow變成了雞蛋煎餅Flow/Source

做煎餅果子的N種方式——From Sequential to Reactive

我們都還需要生菜Flow/Source,以及火腿腸Flow/Source。

做煎餅果子的N種方式——From Sequential to Reactive

我們從雞蛋煎餅Flow/Source 、生菜Flow/Source以及火腿腸Flow/Source,建構了一條煎餅果子的Flow/Source。如果我們把Flow/Source看做生産線,喔喔,我們根據三個現有的Flow/Source,建構了一條煎餅果子的Flow/Source。

最後,我們隻從裡面拿了一個煎餅果子出來:)

做煎餅果子的N種方式——From Sequential to Reactive

上面我們還有一點,就是異步和并發怎麼控制?我如何并發動做一些事情呢?

做煎餅果子的N種方式——From Sequential to Reactive

或者

做煎餅果子的N種方式——From Sequential to Reactive

非常簡單,如果我們想要同步的呢?去掉紅框中的部分就好了。

也就是說,我們描繪了整個服務的編排,然後我們便可以友善地對任意特定的子拓撲進行優化了。開辟新的生産線,或者通過複制某個處理的過程來對某個流程進行并發執行,以提高其生産效率,當然在真的擴不了的情況下,保護我們的系統,保障我們SLA。

那麼,我們如何不斷地擷取我們的美味煎餅果子呢?早上喜歡吃煎餅果子的人太多,都要瘋掉了,好,請看:

做煎餅果子的N種方式——From Sequential to Reactive
剩下的留作練習:)

隻要我們的請求不斷,我們的

做煎餅果子的N種方式——From Sequential to Reactive

就會持續的産生結果。多麼簡單直接,而且非常的優雅。複用這樣的模式,開連鎖店,開工廠,智能化的工業生産,人生巅峰不是夢。

拓展思考?

基于這個例子,我們可以看到,如果我們的服務代碼,通過上面的方式來編寫,那麼勢必更加地簡潔優雅,而且我們也具備了更細膩的控制力,并且也有了更高屋建瓴的拓撲、思維模組化以及全局優化的可能。同時,我們的服務,都是由反應式流中流轉的信号量進行驅動的,進而實作了動态的推拉結合——對,沒錯,控制論中的知識:)

我相信,通過面向流的程式設計,從資料first,切換服務編排,請求/響應拓撲first的思維,将會大大地提高我們對鍊路的了解能力。同時,有了這些工具,我們常見的反應式設計模式,都可以非常友善地應用,并完全可以結合FP以及DDD的一些思路,打造更加清晰、明了、性能優異的系統。而且,我們僅僅描述了服務的編排,進而産生拓撲,而剩下的事情,隻需要動動手指,也許手指都不需要動:),這一切,都将會有下一代的架構來智能地保證。

從上面,大家已經看到了,我們的程式設計模式,是如何一步一步地從傳統的方式,變成我們的Reactive 化的方式。通過Reactive 的方式,我們可以友善的實作服務編排,有了服務編排之後,我們可以做更多的事情,服務将會是更加智能化的,而非一成不變,提升了客戶體驗、資源使用率,并降低了資源的浪費。

繼續閱讀