简化软件集成:一个Apache Camel教程
|
軟件很少(如果有的話)存在于信息真空中。至少,這是我們軟件工程師可以為我們開發的大多數應用程序做出的假設。
在任何規模上,每種軟件都以某種方式與其他軟件進行通信,出于各種原因:從某處獲取參考數據,發送監控信號,與其他服務保持聯系,同時作為分布式的一部分系統等等。
簡化軟件集成:一個Apache Camel教程
在本教程中,您將了解集成大型軟件的一些最大挑戰,以及Apache Camel如何輕松解決這些難題。
問題:系統集成的體系結構設計
在您的軟件工程中,您可能至少做了一次以下操作:
1.確定應啟動數據發送的業務邏輯片段。
2.在相同的應用程序層,根據收件人的期望寫入數據轉換。
3.將數據封裝在適合通過網絡傳輸和路由的結構中。
4.使用適當的驅動程序或客戶端SDK打開到目標應用程序的連接。
5.發送數據并處理響應。
為什么這是一個不好的行為?
雖然你只有這種幾個連接,它仍然是可管理的。隨著系統之間關系的增加,應用程序的業務邏輯與集成邏輯混合在一起,即集成數據,補償兩個系統之間的技術差異,并通過SOAP,REST或更多異常請求將數據傳輸到外部系統。
如果您要集成多個應用程序,那么在這樣的代碼中追溯依賴關系的整個畫面是非常困難的:數據產生在哪里以及哪些服務使用它?您將有許多地方集成邏輯重復,以引導。
有了這樣的方法,雖然這個任務在技術上已經完成,但是我們在集成的可維護性和可伸縮性方面遇到了很大的問題。這個系統中數據流的快速重組幾乎是不可能的,更不用說更深層次的問題,比如缺少監視,斷路,數據恢復等等。
當將軟件集成到一個相當大的企業的范圍時,這一點尤為重要。要處理企業集成,就意味著要與一組應用程序一起工作,這些應用程序運行在廣泛的平臺上,并且存在于不同的位置。在這樣一個軟件環境中,數據交換是相當苛刻的。它必須符合行業的高安全標準,并提供可靠的數據傳輸方式。在企業環境中,系統集成需要一個獨立的、全面的架構設計。
本文將向您介紹軟件集成面臨的獨特困難,并為集成任務提供一些經驗驅動的解決方案。我們將熟悉Apache Camel,這是一個有用的框架,可以減輕集成開發人員頭痛的最壞情況。我們將以駱駝如何幫助建立由Kubernetes提供支持的微服務集群中的通信為例。
整合困難
解決該問題的一個廣泛使用的方法是在應用程序中分離一個集成層。它可以存在于同一個應用程序中,也可以作為一個獨立運行的專用軟件 - 在后一種情況下稱為中間件。
在開發和支持中間件時,您通常會遇到什么問題?一般來說,你有以下關鍵點:
所有數據通道在一定程度上都不可靠。數據強度低到中等時,可能不會出現由此不可靠性引起的問題。從應用程序內存到下面的緩存和設備的每個存儲級別都可能出現故障。只有大量的數據才會出現一些罕見的錯誤。即使成熟的生產就緒供應商產品也有未解決的與數據丟失有關的錯誤跟蹤器問題。一個中間件系統應該能夠通知你這些數據的傷亡,并及時提供消息重新傳遞。
應用程序使用不同的協議和數據格式。這意味著集成系統是數據轉換和適配器到其他參與者的帷幕,并利用了各種技術。這些方法可以包括簡單的REST API調用,但也可以訪問隊列代理,通過FTP發送CSV命令,或者將數據批量拖到數據庫表中。這是一張長長的單子,它不會變短的。
數據格式和路由規則的變化是不可避免的。應用程序開發過程中的每個步驟都會改變數據結構,這通常會導致集成數據格式和轉換的變化。有時候,重組企業數據流的基礎設施變化是必要的。例如,引入一個驗證參考數據的單點時,可能會發生這些更改,這些參考數據必須處理整個公司的所有主數據條目。有了N系統,我們最終可能N^2在它們之間有最大的連接,所以必須應用更改的地方的數量增長得相當快。這將像雪崩一樣。為了保持可維護性,中間件層必須通過多種路由和數據轉換提供清晰的依賴關系圖。
在設計集成和選擇最合適的中間件解決方案時,應該牢記這些想法。處理這個問題的可能方法之一是利用企業服務總線(ESB)。但是主要供應商提供的ESB通常過于沉重,而且往往比他們的價值更麻煩:ESB幾乎不可能快速啟動,它的學習曲線相當陡峭,而且它的靈活性被犧牲于一長串的功能和內置工具。在我看來,輕量級的開源集成解決方案要優越得多 - 它們更具彈性,易于部署到云中,并且易于擴展。
軟件集成并不容易。今天,當我們構建微服務架構并處理大量的小型服務時,我們對于它們應該如何有效溝通也抱有很高的期望。
企業集成模式
正如所料,像一般的軟件開發一樣,數據路由和轉換的發展涉及重復的操作。經過一段時間的處理整合問題的專業人員對這方面的經驗進行了總結和系統化。在結果中,有一組稱為企業集成模式的提取模板,用于設計數據流。這些整合方法在Gregor Hophe和Bobby Wolfe的同名書中有描述,這很像“四人幫”的書,但是在膠合軟件方面。
舉一個例子,規范化模式引入了一個組件,它將具有不同數據格式的語義相同的消息映射到單個規范模型,或者聚合器是一個將一系列消息合并為一個的EIP。
由于它們是用于解決架構問題的技術無關的抽象,所以EIP有助于編寫一個架構設計,它不會深入到代碼級別,而是足夠詳細地描述數據流。這種描述整合路線的符號不僅使設計簡潔,而且在解決與各業務領域的團隊成員的整合任務的背景下,設置了一個通用的術語和通用的語言,這是非常重要的。
介紹Apache Camel
集成路由被寫成由塊組成的管道。它創建了一個完全透明的圖像來幫助追蹤數據流。
駱駝有許多流行的API適配器。例如,從Apache Kafka獲取數據,監控AWS EC2實例,與Salesforce集成 - 所有這些任務都可以使用現成的組件來解決。
幾年前,我正在一個大型食品雜貨零售網絡中建立一個企業集成體系,商店分布廣泛。我從一個專有的ESB解決方案開始,后來證明這個方案過于繁瑣。然后,我們的團隊遇到了Apache Camel,在做了一些“概念驗證”工作之后,我們很快地將所有的數據流改寫成了Camel路由。
Apache Camel可以被描述為一個“中介路由器”,它是一個面向消息的中間件框架,實現了我熟悉的EIP列表。它利用這些模式,支持所有常見的傳輸協議,并且包含了大量有用的適配器。駱駝能夠處理大量的集成例程,而無需編寫自己的代碼。
除此之外,我會選出下面的Apache Camel特性:
集成路由被寫成由塊組成的管道。它創建了一個完全透明的圖像來幫助追蹤數據流。
Camel有許多流行的API適配器。例如,從Apache Kafka獲取數據,監控AWS EC2實例,與Salesforce集成 - 所有這些任務都可以使用現成的組件來解決。
Apache Camel路由可以用Java或Scala DSL編寫。(XML配置也可用,但過于冗長,調試功能更差)。它不會對通信服務的技術堆棧施加限制,但是如果您使用Java或Scala編寫,則可以將Camel嵌入到應用程序中獨立運行。
Camel使用的路由符號可以用下面的簡單偽代碼來描述:
|
|
的Source,Transformer以及Destination是指由其uri指向實現組件的端點。
是什么讓Camel解決了我之前描述的整合問題?我們來看一下。首先,路由和轉換邏輯現在只能用于專門的Apache Camel配置。其次,通過簡潔自然的DSL結合EIP的使用,出現了系統之間的依賴關系圖。它由易理解的抽象構成,路由邏輯易于調整。最后,我們不必編寫轉換代碼的堆,因為適當的適配器可能已經包含在內。
我應該補充一點,Apache Camel是一個成熟的框架,并定期更新。它有一個偉大的社區和相當龐大的知識庫。
它確實有它自己的缺點。駱駝不應該被視為一個復雜的整合套件。這是一個沒有高級功能(如業務流程管理工具或活動監視器)的工具箱,但可用于創建此類軟件。
替代系統可能是,例如Spring Integration或Mule ESB。對于Spring Integration來說,盡管它被認為是輕量級的,但根據我的經驗,把它放在一起并編寫大量的XML配置文件可能會變得異常復雜,并且不是一個簡單的出路。Mule ESB是一個功能強大且功能強大的工具集,但顧名思義,它是一種企業服務總線,因此它屬于不同的權重類別。Mule可以與Fuse ESB進行比較,Fuse ESB是一款基于Apache Camel的類似產品,具有豐富的功能。對我來說,使用Apache Camel來粘貼服務是一件不容易的事情。它很容易使用,并產生一個干凈的描述,在什么地方,同時,它的功能足夠建設復雜的集成。
編寫一個示例路線
我們開始編寫代碼。我們將從一個同步數據流開始,這個數據流將消息從單一來源路由到收件人列表。路由規則將用Java DSL編寫。
我們將使用Maven構建項目。首先將以下依賴項添加到pom.xml:
|
|
或者,應用程序可以建立在camel-archetype-java原型之上。
Camel路徑定義在RouteBuilder.configure方法中聲明。
| public void configure() { Redeliveries(0)); Order.class) // unmarshal JSON to Order class containing List <OrderItem> process one by one ("Handling Drink"); ("dessertStation"). log("Handling Dessert"); ("hotMealStation"). log("Handling Hot Meal"); ("coldMealStation"). log("Handling Cold Meal"); .log("Handling Something Other"); |
在這個定義中,我們創建了一個從JSON文件中獲取記錄的路徑,將它們拆分成條目,并根據消息內容路由到一組處理程序。
讓我們在準備好的測試數據上運行它。我們將得到輸出:
|
camel-1) started in 10.716 seconds BodyType: com.antongoncharov.camel.example.model .OrderItem, Body: OrderItem{id='1', type='Drink', name='Americano', qty='1'}] BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='2', type='Hot Meal', name='French Omelette', qty='1'}] BodyType: com.antongoncharov.camel.example. model. OrderItem, Body: OrderItem{id='3', type='Hot Meal', name='Lasagna', qty='1'}] BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='4', type='Hot Meal', name='Rice Balls', qty='1'}] BodyType: com.antongoncharov.camel.example.model. OrderItem, Body: OrderItem{id='5', type='Dessert', name='Blueberry Pie', qty='1'}] |
正如所料,Camel路由消息到目的地。
數據傳輸選擇
在上面的示例中,組件之間的交互是同步的,并通過應用程序內存執行。但是,當我們處理不共享內存的單獨應用程序時,還有更多的通信方式:
1.文件交換。一個應用程序產生共享數據文件供另一個使用。這是老派精神的生存之地。這種溝通方式帶來了諸多后果:缺乏交易和一致性,性能較差,系統之間的孤立協調。許多開發人員最終編寫了自制的集成解決方案,使這個過程或多或少地可以管理。
2.通用數據庫。讓應用程序將他們希望共享的數據存儲在單個數據庫的通用模式中。設計統一模式和處理并發訪問表是這種方法最突出的挑戰。與文件交換一樣,這很容易成為永久的瓶頸。
3.遠程API調用。提供一個接口,允許應用程序與另一個正在運行的應用程序進行交互,如典型的方法調用。應用程序通過API調用共享功能,但是它在過程中緊密耦合它們。
4.消息。讓每個應用程序連接到一個通用的消息傳遞系統,并使用消息異步交換數據和調用行為。發送者和接收者都不必同時啟動并運行消息。
有更多的交互方式,但是我們應該記住,從廣義上講,有兩種類型的交互:同步和異步。第一個就像在你的代碼中調用一個函數 - 執行流程將一直等待,直到它執行并返回一個值。使用異步方法,相同的數據通過中間消息隊列或訂閱主題發送。異步遠程函數調用可以作為請求 - 回復EIP來實現。
異步消息傳遞不是萬能的,它涉及到一定的限制。您很少在網絡上看到消息API; 同步REST服務更受歡迎。但是消息中間件被廣泛用于企業內部網或分布式系統后端基礎設施。
使用消息隊列
讓我們的示例異步。管理隊列和訂閱主題的軟件系統稱為消息代理。這就像一個表和列的RDBMS。隊列用作點對點集成,而主題用于與許多接收者的發布 - 訂閱通信。我們將使用Apache ActiveMQ作為JMS消息代理,因為它是可靠且可嵌入的。
添加以下依賴項。有時activemq-all,向項目中添加包含所有ActiveMQ jar 的過度,但我們會保持我們的應用程序的依賴關系不復雜。
|
|
然后以編程方式啟動代理。在Spring Boot中,通過插入spring-boot-starter-activemqMaven依賴關系,我們得到了一個自動配置。
使用以下命令運行新的消息代理,只指定連接器的端點:
|
|
并將以下配置片段添加到configure方法體:
|
|
現在我們可以使用消息隊列來更新前面的例子。隊列將自動創建消息傳遞。
| public void configure() { liveries(0)); // unmarshal JSON to Order class containing List<OrderItem> to process one by one log("Drinks"); ("dessertAsync").log("Dessert"); ("coldMealAsync").log("Cold Meals"); .log("Others"); |
好了,現在交互已經變得異步了。這些數據的潛在消費者在準備好時可以訪問它。這是一個松耦合的例子,我們試圖在一個被動的架構中實現。其中一項服務不可用將不會阻止其他服務。而且,消費者可以并行地從隊列中縮放和讀取。隊列本身可以擴展和分區。持久隊列可以將數據存儲在磁盤上,等待處理,即使所有參與者都關閉了。因此,這個系統更容錯。
一個驚人的事實是,CERN使用Apache Camel和ActiveMQ來監視大型強子對撞機(LHC)的系統。還有一個有趣的碩士論文解釋了為這個任務選擇合適的中間件解決方案。所以,正如他們在主題演講中所說:“沒有JMS-沒有粒子物理學!”
監控
在前面的例子中,我們創建了兩個服務之間的數據通道。這是架構中一個額外的潛在失敗點,所以我們必須照顧它。我們來看看Apache Camel提供的監視功能。基本上,它通過JMX提供有關其路由的統計信息。ActiveMQ以相同的方式公開隊列統計信息。
我們打開應用程序中的JMX服務器,使其能夠使用命令行選項運行:
|
ctor=true inName=org.apache.camel registryPort=1099 =camel |
現在運行該應用程序,以便該路線已完成其工作。打開標準jconsole工具并連接到應用程序進程。連接到網址service:jmx:rmi:///jndi/rmi://localhost:1099/camel。轉到MBeans樹中的org.apache.camel域。
我們可以看到,關于路由的一切都在控制之中。我們有正在進行的消息的數量,錯誤計數和隊列中的消息計數。這些信息可以通過流水線連接到一些監視工具集,如Graphana或Kibana。你可以通過實現知名的ELK棧來做到這一點。
還有一個可插拔和可擴展的Web控制臺提供了一個用戶界面,用于管理駱駝的ActiveMQ,和更多的人,叫hawt.io。
測試路線
Apache Camel具有相當廣泛的功能,可以用模擬組件編寫測試路由。這是一個強大的工具,但是為了測試而編寫單獨的路由是一個耗時的過程。在生產線上運行測試而不修改管線會更有效率。駱駝有這個功能,可以使用AdviceWith組件來實現。
讓我們在我們的示例中啟用測試邏輯并運行示例測試。
|
|
測試類是:
|
|
現在運行與應用程序的測試mvn test。我們可以看到,我們的路線已經成功地通過了測試建議。沒有消息通過實際的隊列傳遞,測試已經通過。
| INFO | Route: main started and consuming from: file://testInbox |
今天的一個集成問題是應用程序不再是靜態的。在云基礎架構中,我們同時處理在多個節點上運行的虛擬服務。它使得微服務架構能夠與小型,輕量級服務網絡相互作用。這些服務的壽命是不可靠的,我們必須動態地發現它們。
將云服務合并在一起是Apache Camel可以解決的任務。特別有趣的是,由于EIP的風格和駱駝有足夠的適配器和支持多種協議的事實。最近的2.18版本添加了ServiceCall組件,該組件引入了調用API并通過集群發現機制解析其地址的功能。目前,它支持Consul,Kubernetes,Ribbon等。可以很容易地找到代理的一些例子,其中ServiceCall用Consul配置。我們將在這里使用Kubernetes,因為這是我最喜歡的集群解決方案。
整合架構如下:
該Order服務和Inventory服務將是一個簡單的Spring Boot應用程序返回靜態數據。我們不是綁定在這里的一個特定的技術堆棧。這些服務正在產生我們想要處理的數據。
訂購服務控制器:
| @RestController |
它以如下格式產生數據:
| [{"id":1,"items":[2,3,4]},{"id":2,"items":[5,3]}] |
該Inventory服務控制器是完全類似Order服務的:
| @RestController |
InventoryStorage是保存數據的通用存儲庫。在這個例子中,它返回靜態預定義的對象,這些對象被封送到下面的格式。
| [{"id":1,"name":"Laptop","description":"Up to 12-hours battery life","price":499.9},{"id":2,"name" |
讓我們編寫一個連接它們的網關路由,但在這個步驟中沒有ServiceCall:
|
/json")) =true") json")) true") |
現在想象一下,每個服務不再是一個特定的實例,而是一個運行一個實例的云。我們將使用Minikube在本地嘗試Kubernetes集群。
配置網絡路由以在本地查看Kubernetes節點(
|
in minikube? {print $2}’? previous command? for testing ? |
給出的示例適用于Mac / Linux環境):
使用Dockerfile配置將服務包裝在Docker容器中,如下所示:
|
-jar /app.jar |
構建并將服務映像推送到Docker注冊表。現在運行本地Kubernetes集群中的節點。
Kubernetes.yaml部署配置:
| apiVersion: extensions/v1beta1 |
將這些部署作為集群中的服務公開:
|
|
現在我們可以檢查請求是否由集群中隨機選擇的節點提供服務。curl -X http://192.168.99.100:30517/info依次運行幾次以訪問minikube NodePort以獲得公開的服務(使用您的主機和端口)。在輸出中,我們看到我們已經實現了請求平衡。
| Inventory Service UUID = 22f8ca6b-f56b-4984-927b-cbf9fcf81da5 |
添加camel-kubernetes和camel-netty4-http依賴項目的pom.xml。然后將ServiceCall組件配置為使用共享路徑定義中的所有服務調用的Kubernetes主節點發現:
|
new KubernetesConfiguration(); .168.64.2:8443"); /antongoncharov/.minikube/client.crt"); /antongoncharov/.minikube/client.key"); ServiceCallConfigurationDefinition(); rviceDiscovery(kubernetesCon figuration)); |
ServiceCall EIP完善了Spring Boot。大多數選項可以直接在application.properties文件中配置。
使用ServiceCall組件授權Camel 路由:
| rest("/orders") |
我們還啟動了路線中的斷路器。這是一個集成掛鉤,允許在發送錯誤或收件人不可用的情況下暫停遠程系統調用。這旨在避免級聯系統故障。Hystrix組件通過實現斷路器模式來幫助實現這一點。
讓我們運行它并發送測試請求; 我們會得到這兩個服務聚合的響應。
| [{"id":1,"items":[{"id":2,"name":"Monitor","description":"27-inch, response time: 7ms","price":200 |
結果如預期。
其他用例
我展示了Apache Camel如何在一個集群中集成微服務。這個框架的其他用途是什么?一般來說,在基于規則的路由可能是解決方案的任何地方都是有用的。例如,Apache Camel可以成為Eclipse Kura適配器的物聯網中間件。它可以處理來自各種組件和服務的日志信號的監視,就像在CERN系統中一樣。它也可以是企業級SOA的集成框架,也可以是批量數據處理的管道,雖然它在這方面與Apache Spark沒有很好的競爭。
結論
你可以看到系統集成不是一個簡單的過程。我們很幸運,因為收集了很多經驗。正確應用它來構建靈活和容錯的解決方案非常重要。
為了確保正確的應用,我建議有一個重要的集成方面的清單。必須具備的項目包括:
1.是否有單獨的集成層?
2.是否有集成測試?
3.我們知道預期的峰值數據強度嗎?
4.我們是否知道預期的數據交付時間?
5.消息相關性是否重要?如果序列中斷?
6.我們應該以同步還是異步的方式來做?
7.格式和路由規則更頻繁地變化在哪里?
8.我們有辦法監督這個過程嗎?
在本文中,我們嘗試了Apache Camel,這是一個輕量級集成框架,可幫助您在解決集成問題時節省時間和精力。正如我們所展示的,它可以作為一個工具,支持相關的微服務體系結構,全面負責微服務之間的數據交換。
from:?http://www.uml.org.cn/zjjs/201801181.asp
總結
以上是生活随笔為你收集整理的简化软件集成:一个Apache Camel教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 7 源码学习系列(一)——St
- 下一篇: Apache Camel:基于企业集成模