中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

Lagom框架

發(fā)布于:2021-01-21 14:38:11

0

260

0

lagom 教程 框架

完全不同,但仍然很容易-這就是新的開源微服務(wù)框架Lagom試圖創(chuàng)建的二分法。它與其他框架有什么區(qū)別?處理起來有多容易?這個名字到底是什么意思?

關(guān)于這個名字的意思的問題不容易回答,因為人們不能從字面上翻譯瑞典成語Lagom。根據(jù)維基百科,這個詞的意思是:“足夠,足夠,足夠,恰到好處。”在我們的例子中,這不應(yīng)該是自我表揚,而是對微服務(wù)概念的批評聲明。Lagom建議,我們不要把重點放在“微”上,而要固執(zhí)地遵循“代碼越少越好”的概念,而是從領(lǐng)域驅(qū)動的設(shè)計中考慮“有界上下文”的概念,以找到服務(wù)的邊界。領(lǐng)域驅(qū)動設(shè)計和微服務(wù)的概念接近性可以在Lagom框架的不同位置找到。

Lagom入門

使用Lagom開發(fā)應(yīng)用程序的最簡單方法是借助Maven項目模板。

$ mvn archetype:generate -DarchetypeGroupId=com.lightbend.lagom  -DarchetypeArtifactId=maven-archetype-lagom-java  -DarchetypeVersion=1.1.0

在回答了有關(guān)名稱的問題并切換到新創(chuàng)建的目錄后,您將發(fā)現(xiàn)此處顯示的目錄結(jié)構(gòu)。

cassandra-config hello-api hello-impl integration-tests pom.xml stream-api stream-impl

正如微服務(wù)所應(yīng)該的那樣,已經(jīng)生成了兩個服務(wù),而不是一個服務(wù)。畢竟,服務(wù)之間的交互和通信至少與單個on的實現(xiàn)同等重要(而且常常是更大的挑戰(zhàn))。下面是服務(wù)“hello”和“stream”;每個實現(xiàn)都分為兩個子項目(“api”和“impl”)。

要啟動應(yīng)用程序,只需一個簡單的mvn lagom:runAll。

下載幾次之后,它應(yīng)該在端口9000上運行。這可以通過HTTPIE這樣的命令行工具輕松檢查:

$ http localhost:9000/api/hello/Lagom HTTP/1.1 200 OK Content-Type: text/plainHello, Lagom!

開發(fā)所需的所有組件都有一個特殊性,即它們——項目的服務(wù)、服務(wù)注冊中心、API網(wǎng)關(guān),甚至數(shù)據(jù)庫Cassandra(在嵌入式版本中)——都是通過Maven插件啟動的。不需要在項目外部設(shè)置服務(wù)或數(shù)據(jù)庫。Lagom強調(diào)為開發(fā)人員提供一個交互的環(huán)境的重要性-檢查項目并開始工作。這包括代碼更改將在重新加載后立即生效,而不需要構(gòu)建/部署/重新啟動周期。

服務(wù)API-類型安全和異步

從文件夾結(jié)構(gòu)可以看出,每個服務(wù)都分為一個實現(xiàn)(“-”)和一個API定義(“-”)。后者以編程方式定義服務(wù)的HTTP接口。

public interface HelloService extends Service {

 ServiceCallhello(String id);

 default Descriptor descriptor() {
   return named("hello").withCalls(
       pathCall("/api/hello/:id",  this::hello),
     );
 }
}

在生成器的幫助下,將創(chuàng)建服務(wù)描述,其中請求的路徑將映射到方法調(diào)用上。

此接口不僅是實現(xiàn)的模板;Lagom還生成適當?shù)目蛻魴C庫。在其他Lagom服務(wù)中,這可以通過Google的Guice的依賴注入來實現(xiàn)。這樣,在選擇相應(yīng)的服務(wù)時提供類型安全接口??梢允÷允謩訕?gòu)造HTML請求和直接使用通用http客戶機。

不過,使用客戶機庫并不是強制性的,因為框架將方法調(diào)用映射到HTTP調(diào)用上,HTTP調(diào)用也可以直接調(diào)用,特別是非Lagom服務(wù)。
順便說一句,我們的“hello”小方法不直接傳遞響應(yīng),而是一個ServiceCall。這是一個功能接口。也就是說,我們創(chuàng)建的不是一個簡單的對象,而是一個函數(shù),這個函數(shù)將由相應(yīng)的請求執(zhí)行。我們將類型作為請求(因為user GET call不提交任何數(shù)據(jù),在本例中為“NotUsed”)和響應(yīng)(在本例中為一個簡單的字符串)的類型參數(shù)來傳遞。請求的處理總是異步的–函數(shù)的結(jié)果必須是CompletionStage。Lagom廣泛使用java8特性。簡單的實現(xiàn)如下所示:

public class HelloServiceImpl implements HelloService {
 @Override
 public ServiceCallhello(String id) {
   return request -> {
     CompletableFuture.completedFuture("Hello, " + id);
   };
 }
}

對于一個簡單的GET請求,服務(wù)描述符的增益是有限的。當我們想要在服務(wù)之間異步發(fā)送事件時,它變得更有趣。我們可以在Lagom中通過為ServiceCall選擇不同的類型參數(shù)來實現(xiàn)這一點。如果我們的請求和響應(yīng)類型被定義為source(來自Akka streams庫的類型),那么框架將初始化一個WebSocket鏈接。在這里,服務(wù)抽象可以得分,因為它簡化了WebSockets的工作。就未來版本而言,有計劃支持額外的“發(fā)布/訂閱”模式,以便消息可以放置在總線上,其他服務(wù)可以訂閱它。

public interface StreamService extends Service {

 ServiceCall<Source, Source> stream();

 @Override
 default Descriptor descriptor() {
   return named("stream").withCalls(namedCall("stream", this::stream));
 }
}

內(nèi)置斷路器

假設(shè)我們的服務(wù)在另一個服務(wù)的每個HTTP請求中請求信息。這在預期的時間范圍內(nèi)沒有響應(yīng),這意味著將有一個超時。對這個服務(wù)器的請求不應(yīng)該不斷重復,因為我們從應(yīng)用程序請求了不必要的空閑時間:如果我們很可能得不到響應(yīng),為什么要等待超時?此外,還會有請求累積到服務(wù)。一旦它再次可用,它將受到大量未決請求的炮轟,以至于它將立即屈服。

解決這個問題的可靠方法是斷路器模式[6]。斷路器知道三種狀態(tài):

  • 只要一切正常運行,它就會關(guān)閉

  • 如果達到規(guī)定的錯誤限制(超時、異常),它將在規(guī)定的時間段內(nèi)打開。其他請求將以“CircuitBreakerException”失敗。對于客戶端來說,不會有額外的等待時間,外部服務(wù)甚至不會注意到請求。

  • 一旦設(shè)置的時間段結(jié)束,斷路器將切換到“半開”狀態(tài)?,F(xiàn)在將有一個請求通過。如果成功,斷路器將關(guān)閉-外部系統(tǒng)似乎再次可用。如果失敗,下一輪將以“open”狀態(tài)開始。
    這樣的斷路器已經(jīng)集成到Lagom服務(wù)客戶端中。參數(shù)可通過配置文件進行調(diào)整。

Lagom持久性

證明Lagom與其他微觀框架非常不同的一個方面是事件源框架和CQRS的集成。
對于許多開發(fā)人員來說,使用關(guān)系數(shù)據(jù)庫仍然是“默認情況”,可能與ORM工具有關(guān)。即使這可以在Lagom中實現(xiàn),但是用戶被引導到另一個方向。Lagom中的標準是使用“持久實體”(對應(yīng)于域驅(qū)動設(shè)計中的“聚合根”)。這些持久實體接收消息(命令)。

public class HelloEntity extends PersistentEntity{

 @Override
 public Behavior initialBehavior(OptionalsnapshotState) {

   /*
    * Das Behavior definiert, wie die Entity auf Kommandos reagiert.
    */
   BehaviorBuilder b = newBehaviorBuilder(
       snapshotState.orElse(new HelloState("Hello", LocalDateTime.now().toString())));

   /*
    * Command handler für UseGreetingMessage.
    */
   b.setCommandHandler(UseGreetingMessage.class, (cmd, ctx) ->ctx.thenPersist(new GreetingMessageChanged(cmd.message),
       evt -> ctx.reply(Done.getInstance())));

   /*
    * Event handler für GreetingMessageChanged..
    */
   b.setEventHandler(GreetingMessageChanged.class,
       evt -> new HelloState(evt.message, LocalDateTime.now().toString()));

   return b.build();
 }
}

您可以清楚地看到這在代碼中是如何表示的。我們非常簡單的實體允許我們更改服務(wù)的歡迎文本。我們擴展了超類PersistentEntity,它需要三個類型參數(shù):命令類型、事件類型和狀態(tài)類型。在本例中,我們將命令定義為一個類UseGreetingMessage,它實現(xiàn)了接口HelloCommand,其實例是不可變的。出于保存類型的目的,可以從Immutables庫返回命令、事件和狀態(tài)。為了節(jié)省自己的一些擊鍵,您可以利用一個庫,例如命令、事件和狀態(tài)的不可變項。

實體響應(yīng)命令的方式是由行為定義的。這在運行時可能會改變。通過這種方式,實體可以實現(xiàn)有限狀態(tài)機——在運行時用一種行為替換另一種行為與機器轉(zhuǎn)換到另一種狀態(tài)相關(guān)。

框架通過initialBevahior獲得初始行為。為了構(gòu)造它,我們將使用builder模式。

首先,我們將CommandHandler定義為我們的命令。如果命令有效并要求更改實體,例如,如果將屬性設(shè)置為新值,則不會立即發(fā)生更改。相反,將創(chuàng)建、保存和發(fā)出一個事件。持久實體的EventHandler(我們還將其與構(gòu)建器一起添加到行為中)對事件作出反應(yīng)并執(zhí)行實際更改。

與關(guān)系數(shù)據(jù)庫中的“更新”相比,一個顯著的區(qū)別是持久實體的當前狀態(tài)不一定要保存。這將僅僅保存在內(nèi)存中(內(nèi)存圖像)。如果有必要恢復狀態(tài),例如在重新啟動應(yīng)用程序之后,將通過回放事件來重建狀態(tài)。當前狀態(tài)的可選保存在模型中稱為“快照”,并不替換事件歷史,只表示“預處理”。如果一個實體在其生命周期內(nèi)經(jīng)歷了數(shù)千次狀態(tài)變化,則不需要從一開始就回放所有事件??梢酝ㄟ^從最新快照開始并僅重復以下事件來創(chuàng)建快捷方式。

Lagom對行為的類型和結(jié)構(gòu)給出的嚴格規(guī)范是為了方便開發(fā)人員轉(zhuǎn)換為這個原則,稱為事件源。我的想法是,我被迫為每個實體指定一個明確的協(xié)議:哪些命令可以被處理,哪些事件可以被觸發(fā),哪些值定義了我的類的狀態(tài)?

包括群集

我可以使用的持久實體的數(shù)量不受單個服務(wù)器的主內(nèi)存的限制。相反,每個Lagom應(yīng)用程序都可以用作分布式應(yīng)用程序。在一個額外實例的啟動過程中,我只需要添加一個已經(jīng)運行的實例的地址,然后它將在那里注冊并與當前實例形成一個集群。持久實體由框架管理,并將在集群內(nèi)自動分布(集群分片)。如果節(jié)點被添加到集群或從集群中移除,框架將重新分發(fā)實例。同樣,它可以恢復從內(nèi)存中刪除的實例(鈍化)。

順便說一句,Lagom最初還沒有開發(fā)出這樣的內(nèi)置特性,即以這種方式將應(yīng)用程序狀態(tài)保存在內(nèi)存中,也可以進行擴展。為此,拉貢依賴阿克卡。這肯定已經(jīng)在任務(wù)關(guān)鍵型應(yīng)用程序中使用,因此任何關(guān)于年輕框架可靠性的擔憂都是沒有根據(jù)的。

寫作和閱讀分開

雖然在SQL數(shù)據(jù)庫中很容易從數(shù)據(jù)模型請求任何信息,但在事件源的情況下卻不可能。我們只能訪問實體并使用主鍵請求狀態(tài)。因為我們只有一個事件日志而沒有關(guān)系數(shù)據(jù)模型,所以通過二級索引進行查詢是不可能的。

為了實現(xiàn)這一點,應(yīng)用了CQRS體系結(jié)構(gòu)(命令查詢責任分離,用于進一步閱讀:CQRS旅程)。這里的基本原理是不同的數(shù)據(jù)模型用于讀寫。在我們的例子中,這意味著我們的事件日志是寫端。。它可以用來重建實體,但我們不會對此執(zhí)行任何查詢。相反,我們還從事件生成一個read sidefrom。Lagom已經(jīng)提供了ReadSideProcessor。與persistentities類結(jié)合發(fā)生的每個事件也將被處理并用于創(chuàng)建讀取端。這是為閱讀而優(yōu)化的,不允許直接寫。

這種體系結(jié)構(gòu)方法不僅具有技術(shù)優(yōu)勢,因為在許多應(yīng)用程序中,讀取和寫入頻率非常不同,并且使用這種方法可以獨立地進行縮放。它還提供了一些新的可能性。由于從不刪除保存的事件,因此可以在讀取端添加新的結(jié)構(gòu),即所謂的投影。這些可以被歷史事件填滿,因此不僅可以提供未來的信息,也可以提供過去的信息。

CQRS允許在讀取端使用不同的技術(shù),并根據(jù)用例進行調(diào)整??梢韵胂蟮氖牵m然Lagom還不支持,但是可以構(gòu)建一個SQL read side并繼續(xù)使用可用的工具,但同時提供一個ElasticSearch數(shù)據(jù)庫來進行快速搜索,并將事件發(fā)送到Spark Streaming進行分析。

重要的是要記住,讀取端將被異步刷新,并帶有延遲(寫入端和讀取端之間的“最終一致性”)。強一致性僅在持久性級別上在該模型中可用。

不幸的是,目前它只適用于商業(yè)閉源產(chǎn)品導體。開源社區(qū)正在為Kubernetes和Consul開發(fā)實現(xiàn),或者可以使用基于靜態(tài)配置的ServiceLocator,但不建議在生產(chǎn)環(huán)境中使用。

結(jié)論

Lagom遵循一條有趣的道路,是一個非凡的框架。它在技術(shù)基礎(chǔ)上有著根本的不同:一切都是異步的,它基于發(fā)送命令,而持久化是根據(jù)事件源來完成的。這為服務(wù)的可伸縮性帶來了巨大的優(yōu)勢,但對于大多數(shù)開發(fā)人員(包括來自javaee領(lǐng)域的每個人)來說,這意味著需要重新思考。隨著編程語言的變化,總是會擔心生產(chǎn)力會暫時下降,因為開發(fā)人員無法恢復熟悉的實踐和資源。我們的情況也是如此。

Lagom試圖通過給開發(fā)者一條清晰的路徑來防止這種情況。如果我遵循Lagom中服務(wù)實現(xiàn)和持久化的教科書方法的文檔,我將能夠構(gòu)建一個反應(yīng)式系統(tǒng)——完全基于消息傳遞并能夠集群,甚至可能在沒有意識到的情況下。

在相對較新的微服務(wù)領(lǐng)域,標準尚未建立。我們必須看看哪些框架能夠經(jīng)得起時間的考驗。與javaee和Spring的老熟人不同的是,Lagom為這個系統(tǒng)注入了新的活力,并使一個完全不同的體系結(jié)構(gòu)處于平衡狀態(tài)。那些希望嘗試一些新東西并且對可伸縮分布式系統(tǒng)感興趣的人會發(fā)現(xiàn)Lagom很有用。