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