原文:https://www.reactivemanifesto.org/glossaryhtml
牛津詞典把「asynchronous(異步的)」定義爲「不一樣時存在或發生的」。 在本宣言的上下文中, 咱們的意思是: 在來自客戶端的請求被髮送到了服務端以後, 對於該請求的處理能夠發生這以後的任意時間點。 對於發生在服務內部的執行過程, 客戶端不能直接對其進行觀察, 或者與之同步。 這是同步處理(synchronous processing)的反義詞, 同步處理意味着客戶端只能在服務已經處理完成該請求以後, 才能恢復它本身的執行。前端
當某個組件正竭力維持響應能力時, 系統做爲一個總體就須要以合理的方式做出反應。 對於正遭受壓力的組件來講, 不管是災難性地失敗, 仍是不受控地丟棄消息, 都是不可接受的。 既然它既不能(成功地)應對(壓力), 又不能(直接地)失敗, 那麼它就應該向其上游組件傳達其正在遭受壓力的事實, 並讓它們(該組件的上游組件)下降負載。 這種回壓(back-pressure)是一種重要的反饋機制, 使得系統得以優雅地響應負載, 而不是在負載下崩潰。 回壓能夠一路擴散到(系統的)用戶, 在這時即時響應性可能會有所下降, 可是這種機制將確保系統在負載之下具備回彈性 , 並將提供信息,從而容許系統自己經過利用其餘資源來幫助分發負載,參見彈性。react
當前計算機爲反覆執行同一項任務而進行了優化: 在(CPU的)時鐘頻率保持不變的狀況下, 指令緩存和分支預測增長了每秒能夠被處理的指令數。 這就意味着,快速連續地將不一樣的任務遞交給相同的CPU核心,將並不能獲益於本有可能獲得的徹底(最高利用率的)性能: 若是有可能,咱們應該這樣構造應用程序, 它的執行邏輯在不一樣的任務之間交替的頻率更低。 這就意味着能夠成批地處理一組數據元素, 這也可能意味能夠在專門的硬件線程(指CPU的邏輯核心)上執行不一樣處理步驟。算法
一樣的道理也適用於對於須要同步和協調的外部資源的使用。 當從單一線程(即CPU核心)發送指令, 而不是從全部的CPU核心爭奪帶寬時, 由持久化存儲設備所提供的I/O帶寬將能夠獲得顯著提升。 使用單一入口的額外的效益,即多個操做能夠被從新排序, 從而更好地適應設備的最佳訪問模式(當今的存儲設備的線性存取性能要優於隨機存取的性能)。數據庫
此外, 批量處理還提供了分攤昂貴操做(如I/O)或者昂貴計算的成本的機會。 例如, 將多個數據項打包到同一個網絡數據包或者磁盤存儲塊中, 從而提升效能並下降使用率。編程
咱們所描述的是一個模塊化的軟件架構, 它(實際上)是一個很是古老的概念, 參見Parnas(1972)。 咱們使用「組件(component)」(參見 C.2.8)這個術語, 由於它和「隔間(compartment)」聯繫緊密, 其意味着每一個組件都是自包含的、封閉的並和其餘的組件相隔離。 這個概念首先適用於系統的運行時特徵, 可是它一般也會反映在源代碼的模塊化結構中。 雖然不一樣的組件可能會使用相同的軟件模塊來執行通用的任務, 可是定義了每一個組件的頂層行爲的程序代碼則是組件自己的一個模塊。 組件邊界一般與問題域中的有界上下文(BoundedContext)緊密對齊。 這意味着,系統設計傾向於反應問題域, 並所以在保持隔離的同時也更加容易演化。 消息協議爲多個有界上下文(組件)之間提供了天然的映射和通訊層。後端
將任務異步地委託給另外一個#組件意味着該任務將會在另外一個組件的上下文中被執行, 舉幾個可能的狀況: 這個被委託的內容甚至可能意味着運行在不一樣的錯誤處理上下文裏,屬於不一樣的線程,來自不一樣的進程,甚至在不一樣的網絡節點上。 委託的目的是將處理某個任務的職責移交給另一個組件, 以便發起委託的組件能夠執行其餘的處理、 或者有選擇性地觀察被委託的任務的進度, 以防須要執行額外的操做(如處理失敗或者報告進度)。緩存
彈性意味着當資源根據需求按比例地減小或者增長時, 系統的吞吐量將自動地向下或者向上縮放, 從而知足不一樣的需求。系統須要具備可伸縮性, 以使得其能夠從運行時動態地添加或者刪除資源中獲益。 所以,彈性是創建在可伸縮性的基礎之上的, 並經過添加自動的資源管理概念對其進行了擴充。網絡
失敗是一種服務內部的意外事件, 會阻止服務繼續正常地運行。 失敗一般會阻止對於當前的、 並可能全部接下來的客戶端請求的響應。 和錯誤相對照, 錯誤是意料之中的,而且針各類狀況進行了處理( 例如, 在輸入驗證的過程當中所發現的錯誤), 將會做爲該消息的正常處理過程的一部分返回給客戶端。 而失敗是意料以外的, 而且在系統可以恢復至(和以前)相同的服務水平以前,須要進行干預。 這並不意味着失敗老是致命的(fatal), 雖然在失敗發生以後, 系統的某些服務能力可能會被下降。 錯誤是正常操做流程預期的一部分, 在錯誤發生以後, 系統將會當即地對其進行處理, 並將繼續以相同的服務能力繼續運行。架構
失敗的例子有: 硬件故障、 因爲致命的資源耗盡而引發的進程意外終止,以及致使系統內部狀態損壞的程序缺陷。
隔離能夠定義爲在時間和空間上的解耦。 在時間上解耦意味着發送者和接收者能夠擁有獨立的生命週期—— 它們不須要同時存在,從而使得相互通訊成爲可能。 經過在組件之間添加異步邊界, 以及經過消息傳遞實現了這一點。 在空間上解耦(定義爲位置透明性)意味着發送者和接收者沒必要運行在同一個進程中。 無論運維部門或者運行時自己決策的部署結構是多麼的高效——在應用程序的生命週期以內,這一切均可能會發生改變。
真正的隔離超出了大多數面向對象的編程語言中的常見的封裝概念, 並使得咱們能夠對下述內容進行劃分和遏制:
組件之間的強隔離性是創建在明肯定義的協議的通訊之上的, 並支持解耦, 從而使得系統更加容易被理解、擴展、測試和演化。
彈性系統須要可以自適應, 並不間斷地對需求的變化作出反應。 它們須要優雅而高效地擴大或者縮減(部署)規模。 極大地簡化這個問題的一個關鍵洞察是:認識到咱們一直都在處理分佈式計算。 不管咱們是在一臺單獨的(具備多個獨立CPU,並經過快速通道互聯(QPI)通訊的)節點之上, 仍是在一個(具備多臺經過網絡進行通訊的獨立節點的)機器集羣之上運行咱們的系統, 都是如此。 擁抱這一事實意味着, 在多核心之上進行垂直縮放和在集羣之上進行水平伸縮並無什麼概念上的差別。
若是咱們全部的組件都支持移動性, 而本地通訊只是一項優化。 那麼咱們根本不須要預先定義一個靜態的系統拓撲和部署結構。 能夠將這個決策留給運維人員或者運行時, 讓他(它)們其能夠根據系統的使用狀況來對其進行調整和優化。
這種經過異步的消息傳遞實現的在空間上的(請參見隔離的定義)解耦, 以及將運行時實例和它們的引用解耦,就是咱們所謂的位置透明性。 位置透明性一般被誤認爲是「透明的分佈式計算」, 然而實際上偏偏相反: 咱們擁抱網絡, 以及它全部的約束——如部分失敗、 網絡分裂、 消息丟失, 以及它的異步性和與生俱來的基於消息的性質,並將它們做爲編程模型中的一等公民, 而不是嘗試在網絡上模擬進程內的方法調用(如RPC、XA等)。 咱們對於位置透明性的觀點與Waldo等人著的A Note On Distributed Computing 中的觀點徹底一致。
消息是指發送到特定目的地的一組特定數據, 事件是組件在達到了某個給定狀態時所發出的信號。 在消息驅動的系統中, 可尋址的接收者等待消息的到來, 並對消息作出反應, 不然只是休眠(即異步非阻塞地等待消息的到來)。 而在事件驅動的系統中, 通知監聽器被附加到了事件源, 以便在事件被髮出時調用它們(指回調)。 這也就意味着, 事件驅動的系統關注於可尋址的事件源, 而消息驅動的系統則着重於可尋址的接收者。 消息能夠包含編碼爲它的有效載荷的事件。
因爲事件消耗鏈的短暫性, 因此在事件驅動的系統中很難實現回彈性 : 當處理過程已經就緒,監聽器已經設置好, 以便於響應結果並對結果進行變換時, 這些監聽器一般都將直接地處理成功或者失敗, 並向原始的客戶端報告執行結果。(這些監聽器)響應組件的失敗, 以便於恢復它(指失敗的組件)的正常功能,而在另一方面, 須要處理的是那些並無與短暫的客戶端請求捆綁在一塊兒的, 可是影響了整個組件的健康情況的失敗。
在併發編程中, 若是爭奪資源的線程並無被保護該資源的互斥所無限期地推遲執行, 那麼該算法則被認爲是非阻塞的。 在實踐中, 這一般縮影爲一個 API, 當資源可用時, 該API將容許訪問該資源, 不然它將會當即地返回, 並通知調用者該資源當前不可用, 或者該操做已經啓動了,可是還沒有完成。 某個資源的非阻塞 API 使得其調用者能夠進行其餘操做, 而不是被阻塞以等待該資源變爲可用。 此外,還能夠經過容許資源的客戶端註冊, 以便讓其在資源可用時,或者操做已經完成時得到通知。
協議定義了在組件之間交換或者傳輸消息的方法與規範。 協議由會話參與者之間的關係、 協議的累計狀態以及容許發送的消息集所構成。 這意味着, 協議描述了會話參與者在什麼時候能夠發送什麼樣的消息給另一個會話參與者。 協議能夠按照其消息交換的形式進行分類, 一些常見的類型是:請求——響應模式、 重複的請求——響應模式(如 HTTP 中)、 發佈——訂閱模式、 以及(反應式)流模式(同時包含(動態地)推送和拉取)。
和本地編程接口相比, 協議則更加通用, 由於它能夠包含兩個以上的參與者, 而且能夠預見到消息交換的進展, 而接口僅僅指定了調用者和接收者之間每次一次的交互過程。
須要注意的是, 這裏所定義的協議只指定了可能會發送什麼樣的消息, 而不是它們應該如何被編碼、解碼(即編解碼), 並且對於使用該協議的組件來講,傳輸機制是透明的。
在不一樣的地方同時地執行一個組件被稱爲複製。 這可能意味着在不一樣的線程或者線程池、 進程、 網絡節點或者計算中心中執行。 複製提供了可伸縮性(傳入的工做負載將會被分佈發到跨組件的多個實例中) 以及回彈性 (傳入的工做負載將會被複制到多個並行地處理相同請求的多個實例中)。 這些方式能夠結合使用, 例如, 在確保該組件的某個肯定用戶的全部相關事務都將由兩個實例執行的同時, 實例的總數則又根據傳入的負載而變化,(參見 彈性)。
在複製有狀態的組件時,必需要當心同步副本之間的狀態數據,不然該組件的客戶則須要知道同步的模式,而且還違反了封裝的目的。一般,同步方案的選擇須要在一致性和可用性之間進行權衡,若是容許被複制的副本能夠在有限的時間段內不一致(最終一致性),那麼將會獲得最佳的可用性,同時,完美的一致性則要求全部的複製副本以一種步調一致(lock-step)的方式來推動它們的狀態。在這兩種「極端」之間存在着一系列的可能解決方案,因此每一個組件都應該選擇最適合於其須要的方式。
組件執行其功能所依賴的一切都是資源, 資源必需要根據組件的須要而進行調配。 這包括 CPU 的分配、 內存以及持久化存儲以及網絡帶寬、 內存帶寬、 CPU 緩存、 內部插座的 CPU 連接、 可靠的計時器以及任務調度服務、 其餘的輸入和輸出設備、 外部服務(如數據庫或者網絡文件系統等)等等。 全部的這些資源都必需要考慮到 彈性和回彈性 , 由於缺乏必需的資源將妨礙組件在被須要時發揮正常做用。
一個系統經過利用更多的計算資源來提高其性能的能力, 是經過系統吞吐量的提高比上資源所增長的比值來衡量的。 一個完美的可伸縮性系統的特色是這兩個數字是成正比的。 所分配的資源加倍也將使得吞吐量翻倍。 可伸縮性一般受限於系統中所引入的瓶頸或者同步點, 參見Amdahl 定律以及 Gunther 的通用可伸縮模型( Amdahl’s Law and Gunther’s Universal Scalability Model)。
系統爲它的用戶或者客戶端提供服務。 系統可大可小, 它們能夠包含許多組件或者只有少數幾個組件(參見 C.2.4)。 系統中的全部組件相互協做,從而提供這些服務。 在不少狀況下, 位於相同系統中的多個組件之間,具備某種客戶端——服務端的對應關係(例如,考慮一下,前端組件依賴於後端組件)。 一個系統中共享着一種通用的回彈性模型, 意即, 某個組件的失敗將會在該系統的內部獲得處理, 並由一個組件委託給另一個組件。 若是系統中的某系列組件的功能、資源或者失敗模型都和系統中的其他部分相互隔離, 那麼將這一系列的組件看做是系統的子系統將更有利於系統設計。
咱們使用這個術語來非正式地指代某個服務的任何消費者,能夠是人類或者其餘服務。