摘要:構(gòu)建于四個指導(dǎo)性的原則。在這個文章的其余部分我們將繼續(xù)深入異步邊界的概念。電商領(lǐng)域的一致性的重要性并不是碰巧出現(xiàn)的。性能持久性安全都是的方面。來見識下騎士資本集團在年發(fā)生軟件故障的經(jīng)歷。接下來的分鐘發(fā)生的事情是一場惡夢。
What is Reactive Programming?
為了了解Reactive——從編程范式至其背后的動機,有必要了解現(xiàn)在的開發(fā)者和公司在十年前不曾面對的挑戰(zhàn)。
游戲的改變主要有兩大方面:
硬件的提升
因特網(wǎng)
Why things are different now?
(原文對比了一遍2005年和2014年因特用戶的增長:10億至29.5億;Facebook用戶的增長:550萬至13億; twitter從無至有,0至2.7億用戶)
時代改變了,現(xiàn)在一個網(wǎng)站就需要處理十年前整個因特網(wǎng)的流量。實下,容易發(fā)現(xiàn)軟件對于日常生活越來越重要。也容易發(fā)現(xiàn),以前的一些范式并不再適用于現(xiàn)在,更不適用于將來。
The four Reactive principles
Reactive applications 構(gòu)建于四個指導(dǎo)性的原則。
目標(biāo)是一個responsive的程序
一個responsive的程序一定既scalable又resilent.responsiveness離開了scalability和resilence是不可能實現(xiàn)的
一個message-driven的架構(gòu)是scalale, resilent以及最終實現(xiàn)responsive系統(tǒng)的基礎(chǔ)
讓我們在高層了解下每一個原則,理解為了開發(fā)一個適用于現(xiàn)在環(huán)境的軟件,為什么它們是缺一不可的。
Responsive
當(dāng)我們說一個程序是"responsive"的時候,是在說什么?
A responsive system is quick to react to all users — under blue skies and grey skies—in order to ensure a consistently positive user experience.
一個responsive的系統(tǒng)是對所有用戶都能快速反應(yīng)的——不管晴天陰天——來保證一個始終積極的用戶體驗。
在各種條件下都“快速”而“積級”的用戶體驗,比如外部系統(tǒng)出問題或者流量激增,依賴于Reactive application的兩個特質(zhì):resilence,以及scalability。一個消息驅(qū)動架構(gòu)提供了一個responsive系統(tǒng)的基礎(chǔ)。
為什么消息驅(qū)動架構(gòu)對于responsiveness如此重要?
這個世界是異步的。
假如你現(xiàn)在來杯咖啡,卻發(fā)現(xiàn)糖和奶油沒了。
一種做法是:
開始煮咖啡
去商店買糖和奶沒
回家
啥都齊了,開始喝咖啡
享受生活
另一種做法是:
去商店買糖和奶油
回家
煮咖啡
等著咖啡煮好
忍受咖啡因短缺
崩潰
就像你看到的,一個消息驅(qū)動的架構(gòu)提供給你異步邊界(asynchronous boundary),使你從時間和空間中解放出來。在這個文章的其余部分我們將繼續(xù)深入異步邊界的概念。(這段舉的例子挺好,可是看不出這個例子跟消息驅(qū)動有啥關(guān)系呀)
Consistency at Walmart Canada 一致性之于加拿大亞馬遜
(譯注:這個一致性和分布式系統(tǒng)的一致性不一樣……)
在加入Typesafe公司之前,我是負責(zé)構(gòu)建亞馬遜加拿大的新電商平臺的Play和Scala團隊的技術(shù)leader。
我們的目標(biāo)是提供一致的積極的用戶體驗,不管:
無論什么類型的物理設(shè)備被用來訪問walmart.ca,不設(shè)它是桌面電腦,平板還是移動設(shè)備
當(dāng)前的流量峰值,不管它是由于激增才出來的,還是持久的
一個主要的基礎(chǔ)設(shè)施不可用,比如一整個數(shù)據(jù)中心。
響應(yīng)時間和整體的用戶體驗必須保持一致,不管上面的情況是否發(fā)生。一致性對于你的網(wǎng)站是至關(guān)重要的,因為當(dāng)下,這就是你的品牌。一個不好的用戶體驗是難以被忘記或者忽略的,不管是發(fā)生在線上還是在一個線下的商店。
Responsive retail at Gilt
Gilt的Responsive零售。電商領(lǐng)域的一致性(的重要性)并不是碰巧出現(xiàn)的。比如Gilt也是一樣,Gilt是一個閃購網(wǎng)站,每天中午當(dāng)他們發(fā)布了當(dāng)日的銷售商品時流量就會激增。
讓我考慮下一個閃購網(wǎng)站的用戶體驗。如果你在11:58訪問了Gilt,12:01又訪問了它。那么你希望兩次訪問都有積極的體驗,不管是不是Gilt在12:01那時候流量突增。
Gilt提供始終積極、responsive的用戶體驗,并且通過Reactive來實現(xiàn)這一切。通過這篇ReadWrite interview with Eric Bowman of Gilt 了解Gilt向基于Scala的微服務(wù)架構(gòu)(Scala-based microservices architecture)的遷移。
Resilient
大部分的程序都被設(shè)計和開發(fā)成適用于“晴天”,但是事情可能往壞的方面發(fā)展。每隔幾天就會有報告說一個主要的應(yīng)用不可用,或者由于黑客的攻擊造成服務(wù)不可用、數(shù)據(jù)丟失、或者儲譽受損。
A resilient system applies proper design and architecture principles in order to ensure responsiveness under grey skies as well as blue.
一個彈性的系統(tǒng)采用了一些設(shè)計和架構(gòu)的原則,使得不管外部情況的好壞,都是responsive的
Java和JVM都是用來把一個程序無縫地布署到多個操作系統(tǒng),類似的是,201x年的內(nèi)部互聯(lián)的應(yīng)用程序都是關(guān)于應(yīng)用程序?qū)蛹壍慕M合、連接和安全。(注:這句有點繞……忽略,看下邊的)
現(xiàn)在的程序大都由一定數(shù)量的其它程序組裝而成,通過web service和其它網(wǎng)絡(luò)協(xié)議整合在一起?,F(xiàn)在一個應(yīng)用可能依賴于大量的其它服務(wù)——10,20或者更多,它所依賴的這些服務(wù)在自己的信賴防火墻(trust firewall)之外。它可能同時在為大量外部用戶提供服務(wù)——既包含人,也包括其它系統(tǒng)。
考慮到整合的復(fù)雜性,有多少開發(fā)人員:
分析和建模所有外在的依賴
把整合進的每個服務(wù)的理想響應(yīng)時間整理成文檔,進行性能測試——在峰值和平?!獊砣嬖u估最初的期望
把所有的預(yù)期,失敗情況和其它非功能的需求編碼到系統(tǒng)的核心邏輯中
分析和測試每個服務(wù)的失敗情形的影響
分析每個外部依賴的安全,識別是否整合這個外部依賴是否給系統(tǒng)帶來最的安全隱患
Resiliency is one of the weakest links of even the most sophisticated application, 但是Resilency作為一種事后的思考的情況很快就會終結(jié)?,F(xiàn)代的程序必須在其核心就是resilent的,以保持在現(xiàn)實世界多變的環(huán)境下,而不只是理想情況下,保持responsive。 性能、持久性、安全都是resilency的方面。你的應(yīng)用必須在各個層次都是resilent的。
Message-driven resiliency
在一個消息驅(qū)動的內(nèi)核之上構(gòu)建應(yīng)用的美妙之處在于你自然地得到了很多有價值的構(gòu)造模塊。
Isolation 隔離性是一個自愈(self-heal)的系統(tǒng)所需要的。當(dāng)隔離性在被恰當(dāng)?shù)厥褂茫覀兛梢砸罁?jù)一些因素把不同類型的工作進行分離,比如失效的風(fēng)險、不同的性能、CPU和內(nèi)存的使用,等。一個isolation的組件的失效并不會影響整個系統(tǒng)的responsiveness, 并且給予了失效的那個系統(tǒng)一個被治愈的機會。
Location transparency 位置透明性使得我們與不同集群結(jié)點上的不同進程間的通信就像與同一個VM的進程內(nèi)通信一樣。
A dedicated separate error channel 一個精心設(shè)計的分離的錯誤信息通道使得我們可以把錯誤信號重定向到其它地方,而不是拋回到調(diào)用者面前。(注:這個作用應(yīng)該類型于Akka中的event stream這種東西,使得某些失敗的信息可以被訂閱,使得不光在當(dāng)前是調(diào)用了某個失敗組件的組件可以知道它失敗了,而是所有相關(guān)的組件都可以了解到這個情況,便于他們即是作出響應(yīng))
這些因素幫助我們把健壯的錯誤處理和容錯合并進我們的系統(tǒng)中。Akka的supervisor hierarchies的實現(xiàn)展示了這點。
消息驅(qū)動架構(gòu)提供的這些核心的構(gòu)建模塊有助于resiliency,進而有助于responsiveness——不僅在理想情況下,而且在各種并非理想的、現(xiàn)實世界的情況下。
The 440 million dollar resiliency mistake
來見識下騎士資本集團在2012年發(fā)生軟件故障的經(jīng)歷(software glitch experienced by Knight Capital Group)。在一次軟件升級中,另一個休眠的、被整合在一起程序被不經(jīng)意地喚醒,并且開始放大交易量。
接下來的45分鐘發(fā)生的事情是一場惡夢。
騎士資本的自動交易系統(tǒng)洪水般地向NASDAQ(注:納斯達克)進行錯誤交易,使得公司進入了一個需要付出數(shù)十億美元代價的位置。事后,這家公司花了4.4億美元來扭轉(zhuǎn)局勢。在這個故障中,騎士資本不能停止泛水般的錯誤交易,NASDAQ不得不終止騎士資本來停止這一切。騎士資本的股票在一天跌了63%, 幾乎不能做為一個公司存活了下來,只能等著它的股票恢復(fù)了一些價值之后被一些投資者接管。
騎士資本的系統(tǒng)是高性能的,但是不是resilent的。高性能但是沒有resilience使得問題被放大,就像騎士資本發(fā)現(xiàn)的那樣。他們甚至沒有一個停止開關(guān)來使得自己的系統(tǒng)在出現(xiàn)嚴(yán)重問題時停止交易, 所以,當(dāng)真的出了問題時,他們的自動交易系統(tǒng)在45分鐘內(nèi)耗盡了整個公司的資本。
這就是設(shè)計和開發(fā)只能用于"晴天“的程序的結(jié)果。如今,軟件是我們的私人生活和生意的關(guān)鍵組成。如果我們沒有為”陰天“的情況提前設(shè)計和準(zhǔn)備,那么即使這種情況只存在了不到一個小時,也會給我們造成巨大的損失。
Scalable
Resililency和Scalability手拉手,一起共建始終responsive的程序。
A scalable system is easily upgraded on demand in order to ensure responsiveness under varioius load conditions.
一個可擴展的系統(tǒng)可以在面對不同的負載時輕松地進行升級,來保證respnsiveness
任何一個在網(wǎng)上賣過東西的人都明白一個事實:你的流量最大的時候就是你賣的東西最多的時候。除非流量的突增是由于故意的黑客襲擊,不然期望流量突增是挺好的一件事。在一個突增的流量中,人們愿意花錢給你。
那么如何面對這種流量突增,或者是一個長久的但是大量的流量增長。
首先選擇你的范式(paradigm),然后選擇擁抱這種范式的語言和工具。很多情況下,開發(fā)者只是簡單地選擇語言和框架……。一旦選擇了工具,就很難改回來. 所以,就像你在處理任何一個重大的投資一樣進行這些決定。如果你的技術(shù)選擇基于一些原則和分析,那么你就已經(jīng)領(lǐng)先一步了。
Thread-based limitations to concurrency 基于線程的并發(fā)的不足
一個至關(guān)重要的技術(shù)選擇就是框架的并發(fā)模型。在較高的層次,有兩種并發(fā)模型:
傳統(tǒng)的基于線程的并發(fā)模型,基于調(diào)用棧和共享內(nèi)存
消息驅(qū)動的并發(fā)
一些流行的MVC框架,比如Rails,是蔡于線程的。這類框架的典型特點包括:
共享的可變狀態(tài)
每個請求一個線程
對于可變狀態(tài)的并發(fā)訪問——變量以及對象實例——使用鎖和其它復(fù)雜的同步結(jié)構(gòu)來進行管理
這些特點結(jié)合動態(tài)類型、解釋型語言比如Ruby,會迅速達到性能和擴展性的上限。你可以認為所有本質(zhì)是腳本語言的語言都是如此。
Out or up?
讓我們考慮擴展一個應(yīng)用的不同方式。
向上擴展(scaling up)是關(guān)于最大化的利用一個CPU/服務(wù)器的資源,通常需要購買更強大的,專用的、更貴的機器。
向外擴展(scaling out)是關(guān)于把計算分布到由廉價的commdity機器組成的集群中。這樣在成本上更劃算,但是如果你的系統(tǒng)是基于時間和空間概念的(注:”基于時間的“可以認為程序的各個動作是同步的,需要阻塞,等待,”基于空間的“可以認為是基于一個進程內(nèi)部的內(nèi)存共享,比如不線程間共享的變量),那么向外擴展就非常困難。 就像我們之前提到的那樣,一個消息驅(qū)動的架構(gòu)提供了與時間和空間解耦合所需要的異步邊界(asynchronous boundary),提供了按需向外擴展(scale out on demand)的能力,這種能力也被叫做elasticity. Scaling up是關(guān)于更高效地利用已有的資源,而elasticity是關(guān)于在系統(tǒng)需要改變時按需增加新資源。能夠”scale out, on demand"的能力,就是Reactive programming在擴展性方面的終極目標(biāo)。
Reactive應(yīng)用程序很難構(gòu)建于基于線程的框架之上,想想就知道把一個基于可變狀態(tài),線程和鎖的的程序進行scale out有多困難。開發(fā)人員不僅需要利用單個機器上多核的優(yōu)劣,在某些時候,開發(fā)者也需要利用計算機集群的力量。共享可變狀態(tài)也使得scale up變得困難,即使不是不可能。任何曾嘗試操作兩個線程共享的可變狀態(tài)的人都能夠理解保證線程安全有多復(fù)雜,并且也能夠理解為了確保線程安全所需要花費的多余努力所帶來的性能上的懲罰。
Message-driven
一個消息驅(qū)動的架構(gòu)是Reactive應(yīng)用程序的基礎(chǔ)。一個消息驅(qū)動的程序可以是事件驅(qū)動的(event-driven),基于actor的(actor-based),或者兩者的結(jié)合。
一個事件驅(qū)動的系統(tǒng)基于事件,這些事件被一個或更多監(jiān)聽者監(jiān)聽。這和imperative programming不同,因此調(diào)用者不需要阻塞以等待被調(diào)用的例程的響應(yīng)。事件并不是被導(dǎo)向特定的地址,而是被監(jiān)聽,我們將會更深入地討論其隱含的意義。
基于actor的并發(fā)是消息傳遞架構(gòu)的一種延伸,在基于actor的并發(fā)架構(gòu)中,消息可以跨越線程的邊界或者被傳遞到不同物理機器上的另一個actor的郵箱(mailbox)中。這使得elasticity——scale out on demand——變得可能,因為actor可以被分布到網(wǎng)絡(luò)的各處,但是仍然可以像他們在同一個VM里一樣通信。
消息和事件的不同在于消息是有方向的,而事件只是”發(fā)生“并沒有方向。消息有一個清晰的目的的,而event可以被一個或者多的監(jiān)聽者監(jiān)聽。
讓我們深入探討下事件驅(qū)動和基于消息的并發(fā)。
Evnt-driven concurrency
典型的程序用命令的風(fēng)格開發(fā)—一個命令的線性序列—并且是基于調(diào)用棧(call stack)的。調(diào)用棧的主要功能是追蹤例程(routine,感覺應(yīng)該是函數(shù)呀)的調(diào)用者,在阻塞調(diào)用者的同時執(zhí)行被調(diào)用的例程,當(dāng)執(zhí)行完畢后把控制流交還給調(diào)用者,并且返回執(zhí)行結(jié)果的值(或者不返回)
事件驅(qū)動的程序并不關(guān)注于調(diào)用棧,而是關(guān)注于觸發(fā)事件。事件可以被編碼成消息,放在隊列里,被一個或者更多監(jiān)聽者監(jiān)聽。事件驅(qū)動和命令式的巨大區(qū)別在于調(diào)用者不被阻塞,不用在等待響應(yīng)的時候占據(jù)一個線程(caller does not block and hold onto a thread while waiting for a response)。事件循環(huán)本身可以是單線程的,但是仍然可以實現(xiàn)并發(fā),因為可以在(有時是單線程 的)事件循環(huán)處理新的請求的同時調(diào)用例程來完成工作(注:因為對例程的調(diào)用被搞成了非阻塞的,即不會阻塞event-loop)。不再是阻塞于一個請求,等待它被處理完,現(xiàn)在調(diào)用者的標(biāo)識可以和請求消息本身一起被傳遞,使得(如果被調(diào)用的例程選擇這么做)調(diào)用者可以被回調(diào),在回調(diào)時傳遞響應(yīng)(the caller can be called back with a response)。
選擇一個事件驅(qū)動架構(gòu)的主要結(jié)果是他們可有會遇到一個現(xiàn)象,叫做回調(diào)地獄(callback hell)http://callbackhell.com 。 回調(diào)地獄之所以發(fā)生,是因為消息的接受者是匿名函數(shù)而不是可尋址的接受者(addressable recipients).通常的解決方案僅僅關(guān)注于句法—aka, the Pyramid of Doom—而忽略了推導(dǎo)和調(diào)試代碼中表達的event所產(chǎn)生的后續(xù)事件的困難。
Actor-based concurrency
基于actor的程序以在多個actor之間異步的消息傳遞為中心。
一個Actor由以下的屬性組成:
用于接收消息的mailbox
actor的邏輯,它依賴于模式匹配(pattern matching)來決定這個actor如何處理不同類型的消息
隔離的狀態(tài)(isolated state),而不是共享的狀態(tài)。使用隔離的狀態(tài)來存儲請求之間的上下文。
像事件驅(qū)動的并發(fā)一樣,基于actor的并發(fā)也避開了調(diào)用棧,轉(zhuǎn)向輕量級的消息傳遞。actor可以返回以及發(fā)送消息,甚至傳遞消息給自己。一個消息可以傳遞給自己消息,使得它先處理隊列里的其它消息,然后再去結(jié)束一個長時間運行的請求?;赼ctor的并發(fā)的一個巨大好處是,它不僅可以有事件驅(qū)動的并發(fā)的優(yōu)點,而且可以讓計算跨越網(wǎng)絡(luò)邊界變得更容易,并且可以避免callback hell,因為消息被定向到actor。這是一個強大的概念,有助于構(gòu)造易于設(shè)計、實施、維護的高可擴展性的程序。不用考慮“空間”和“時間”,也不用考慮深度嵌套的回調(diào)函數(shù),你只需要考慮actor之間的消息流動。
另一個基于actor的架構(gòu)的主要好處是組件之間解耦合。調(diào)用者不用阻塞自己的線程等待響應(yīng),因此調(diào)用者可以迅速地轉(zhuǎn)去進行其它工作。被調(diào)用的例程,封裝在一個actor中,只需在必須的回復(fù)調(diào)用者。這開啟了很多可能性,比如分布例程(routines)到一個集群中,因為不再有調(diào)用棧把程序耦合在單一的內(nèi)存中,actor模型可以使布署一個拓撲(topology)只是一個虛擬的配置而不再是編碼在程序里。
Akka是一個基于actor的工具箱和運行時- Typesafe Reactive Platform的一部分,用來構(gòu)造并發(fā)的、分布式的、容錯的,基于actor的程序,運行于JVM。Akka還有其它的一些用于構(gòu)造Reactive application的不可思憶的特性,像是用于rsilience的supervisor hierarchies,以及可以把分布工作來實現(xiàn)擴展性。對Akka的深入研究超出了本文的范圍,但是我強烈推薦訪問Let it Crash blog 獲取更多關(guān)于Akka的內(nèi)容。
我也強烈推薦閱讀Benjamin Erb’s Diploma Thesis,Concurrent Programming for Scalable Web Architectures, 被用于本節(jié)的參考。
Conclusion
以上的內(nèi)容描述了時下軟件開發(fā)的的表面,引導(dǎo)我們認識到為什么Reactive programming不僅僅是一個潮流,而且是現(xiàn)在的軟件開發(fā)者需要學(xué)習(xí)的范式。不管你選擇什么語言和工具,優(yōu)先考慮scalability和resilient來實現(xiàn)responsiveness是唯一能滿足用戶需求的方式。每過一年,這點就會變得更加重要。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72330.html
摘要:補充說明響應(yīng)式編程采用了訂閱觀察者設(shè)計模式,使訂閱者可以將通知主動發(fā)送給各訂閱者。一個響應(yīng)式編程的實現(xiàn)庫是一個庫,它通過使用序列來編寫異步和基于事件的程序。 或許響應(yīng)式布局這個名單大家都聽過或者都自己實現(xiàn)過,那么響應(yīng)式編程是什么呢?下面我們來具體聊一聊。 我的理解 從字面意思上我們可以大致理解為:所有的事件存在于一條事件總線上,所有的事件都可以看作未來某個時間將要發(fā)生的事件流(stre...
摘要:概念響應(yīng)式編程,異步非阻塞就是響應(yīng)式編程,與之相對應(yīng)的是命令式編程。的另外一種實現(xiàn)方式就是消息隊列。非阻塞設(shè)計利用規(guī)范中的實現(xiàn)實現(xiàn)代碼鏈接 注: 本文是由讀者觀看小馬哥公開課視頻過程中的筆記整理而成。更多Spring Framework文章可參看筆者個人github: spring-framework-lesson 。 0. 編程模型與并發(fā)模型 Spring 5實現(xiàn)了一部分Reacti...
摘要:中的常見寫法先看下這段代碼。聲明式編程,就是告訴機器你想要的是什么,讓機器想出如何去做。最獨特的特性之一,是其非侵入性的響應(yīng)式系統(tǒng)。的縮寫將遍歷此對象所有的屬性。這一過程被稱為依賴收集。組件的顯示,數(shù)據(jù)的體現(xiàn)大部分都是由承載,傳遞。 目錄 緣起 Android開發(fā)中的常見寫法 JQuery中的常見寫法 命令式編程 聲明式編程 React中的常見寫法 Vue的常見寫法 你肯定熟悉響應(yīng)...
摘要:原文鏈接編程方法論響應(yīng)式與代碼設(shè)計實戰(zhàn)序,來自于微信公眾號次靈均閣正文內(nèi)容在一月的架構(gòu)和設(shè)計趨勢報告中,響應(yīng)式編程和函數(shù)式仍舊編列在第一季度的早期采納者中。 原文鏈接:《Java編程方法論:響應(yīng)式RxJava與代碼設(shè)計實戰(zhàn)》序,來自于微信公眾號:次靈均閣 正文內(nèi)容 在《2019 一月的InfoQ 架構(gòu)和設(shè)計趨勢報告》1中,響應(yīng)式編程(Reactive Programming)和函數(shù)式...
閱讀 3287·2021-11-24 09:38
閱讀 2161·2021-11-23 09:51
閱讀 1753·2021-10-13 09:39
閱讀 2628·2021-09-23 11:53
閱讀 1410·2021-09-02 15:40
閱讀 3662·2019-08-30 15:54
閱讀 1138·2019-08-30 13:04
閱讀 2569·2019-08-30 11:01