《Java微服務:實用指南》的 翻譯與改編。指南的前幾部分:
讓我們從抽象的事物開始,到具體的函式庫結束,看看 Java 中微服務的固有問題。
如何讓 Java 微服務具有彈性?
回想一下,當您建立微服務時,您實際上是將 JVM 方法呼叫替換為同步 HTTP 呼叫或非同步訊息傳遞。雖然方法呼叫基本上可以保證完成(除非 JVM 意外關閉),但預設情況下網路呼叫是不可靠的。它可能有效,但也可能因各種原因而無效:網路超載、實施了新的防火牆規則等等。要了解這有何不同,讓我們來看看 BillingService 範例。HTTP/REST 彈性模式
假設客戶可以在您公司的網站上購買電子書。為此,您剛剛實作了一個計費微服務,它可以呼叫您的線上商店來產生實際的 PDF 發票。現在,我們將透過 HTTP 同步呼叫此服務(儘管非同步呼叫此服務更有意義,因為從使用者的角度來看,PDF 生成不必是即時的。我們將在下一個範例中使用相同的範例)部分並查看差異)。@Service
class BillingService {
@Autowired
private HttpClient client;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
httpClient.send(invoiceRequest(user.getEmail(), invoice), responseHandler());
// ...
}
}
總而言之,以下是此 HTTP 呼叫的三種可能結果。
- OK:通話接通,帳戶建立成功。
- DELAY:通話已接通,但完成時間太長。
- 錯誤。通話失敗,您可能發送了不相容的請求,或者係統可能無法運作。
訊息傳遞彈性模式
讓我們仔細看看非同步通訊。我們的 BillingService 程式現在可能看起來像這樣,假設我們使用 Spring 和 RabbitMQ 進行訊息傳遞。為了建立帳戶,我們現在向 RabbitMQ 訊息代理程式發送一條訊息,其中有幾個工作人員正在等待新訊息。這些工作人員會建立 PDF 發票並將其發送給適當的使用者。@Service
class BillingService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
// преобразует счет, например, в json и использует его How тело messages
rabbitTemplate.convertAndSend(exchange, routingkey, invoice);
// ...
}
}
潛在的錯誤現在看起來有點不同,因為您不再像使用同步 HTTP 連線那樣立即收到 OK 或 ERROR 回應。相反,我們可能會遇到三種可能出錯的情況,這可能會引發以下問題:
- 我的消息是否已被員工傳遞和使用?還是丟失了?(用戶不會收到發票)。
- 我的訊息只發送一次嗎?或多次交付但僅處理一次?(用戶將收到多張發票)。
- 配置:從“我是否使用了正確的路由鍵/名稱進行交換”到“我的消息代理是否已正確配置和維護,或者其隊列是否已滿?” (用戶不會收到發票)。
- 如果您使用 ActiveMQ 等 JMS 實現,則可以用速度換取兩階段 (XA) 提交的保證。
- 如果您使用 RabbitMQ,請先閱讀本教學課程,然後仔細考慮一般的確認、容錯和訊息可靠性。
- 也許有人精通配置 Active 或 RabbitMQ 伺服器,尤其是與叢集和 Docker 結合使用(有人嗎?;))
哪個框架是 Java 微服務的最佳解決方案?
一方面,您可以安裝非常受歡迎的選項,例如Spring Boot。它使創建 .jar 檔案變得非常容易,附帶 Tomcat 或 Jetty 等內建 Web 伺服器,並且可以在任何地方快速運行。非常適合建立微服務應用程式。最近,出現了一對專門的微服務框架,Kubernetes或GraalVM,部分受到反應式程式設計的啟發。這裡有一些更有趣的競爭者:Quarkus、Micronaut、Vert.x、Helidon。最後,您必須自己選擇,但我們可以給您一些可能不完全標準的建議:除了 Spring Boot 之外,所有微服務框架通常都被宣傳為速度快得令人難以置信,幾乎可以瞬時啟動,低內存佔用,並且能夠擴展到無限。行銷資料通常具有令人印象深刻的圖形,展示了龐然大物 Spring Boot 旁邊或彼此之間的平台。從理論上講,這可以減輕支持遺留專案的開發人員的緊張,這些專案有時需要幾分鐘才能加載。或在雲端工作的開發人員希望在 50 毫秒內啟動/停止目前所需的盡可能多的微容器。 然而,問題是這些(人為的)裸機啟動時間和重新部署時間幾乎對專案的整體成功沒有貢獻。至少它們的影響遠小於強大的框架基礎設施、強大的文件、社群和強大的開發人員技能。所以最好這樣看:如果到目前為止:- 您讓 ORM 大量運行,為簡單的工作流程產生數百個查詢。
- 您需要無盡的千兆位元組來運行中等複雜的整體應用程式。
- 您有如此多的程式碼並且複雜性如此之高(我們現在不是在談論像 Hibernate 這樣的潛在緩慢啟動器),以至於您的應用程式需要幾分鐘才能加載。
哪些函式庫最適合同步 Java REST 呼叫?
在低階技術方面,您可能最終會得到以下 HTTP 用戶端程式庫之一:Java 的本機 HttpClient(自 Java 11 起)、Apache 的 HttpClient或OkHttp。請注意,我在這裡說「可能」是因為還有其他選項,從老式的JAX-RS 客戶端到現代的WebSocket客戶端。無論如何,趨勢是產生一個 HTTP 客戶端,而不是自己擺弄 HTTP 呼叫。為此,您需要查看OpenFeign專案及其文檔,作為進一步閱讀的起點。異步 Java 訊息傳遞的最佳代理是什麼?
您很可能會遇到流行的ActiveMQ(經典或 Artemis)、RabbitMQ或Kafka。- ActiveMQ 和 RabbitMQ 是傳統的、成熟的訊息代理程式。它們涉及“聰明的經紀人”和“愚蠢的用戶”的互動。
- 從歷史上看,ActiveMQ 具有易於內聯(用於測試)的優點,可以透過 RabbitMQ/Docker/TestContainer 設定來緩解。
- Kafka 不能被稱為傳統的「智慧」經紀人。相反,它是一個相對「愚蠢」的訊息儲存(日誌檔案),需要聰明的消費者來處理。
我可以使用哪些函式庫來測試微服務?
這取決於你的堆疊。如果您部署了 Spring 生態系統,那麼使用該框架的特定工具將是明智的選擇。如果 JavaEE 類似Arquillian。Docker 和非常好的Testcontainers庫可能值得一看,它特別有助於輕鬆快速地設定 Oracle 資料庫以進行本地開發或整合測試。若要模擬測試整個 HTTP 伺服器,請查看Wiremock。若要測試非同步訊息傳遞,請嘗試實作 ActiveMQ 或 RabbitMQ,然後使用Awaitility DSL編寫測試。此外,還使用了所有常用工具 - Junit、TestNG for AssertJ和Mockito。請注意,這不是完整清單。如果您在這裡沒有找到您最喜歡的工具,請在評論部分中發布。如何為所有 Java 微服務啟用日誌記錄?
微服務中的日誌記錄是一個有趣且相當複雜的主題。您現在擁有 n 個日誌文件,而不是可以使用 less 或 grep 命令操作的一個日誌文件,並且您希望它們不要太分散。本文(英文)很好地描述了日誌生態系的特徵。請務必閱讀它,並注意從微服務角度進行集中日誌記錄部分。在實踐中,您會遇到不同的方法:系統管理員編寫某些腳本,收集來自不同伺服器的日誌檔案並將其組合成一個日誌文件,並將它們放在 FTP 伺服器上以供下載。在並行 SSH 會話中執行 cat/grep/unig/sort 組合。這正是 Amazon AWS 所做的,您可以讓您的經理知道。使用Graylog或ELK Stack(Elasticsearch、Logstash、Kibana)等工具我的微服務如何找到彼此?
到目前為止,我們假設我們的微服務彼此了解並且知道相應的 IPS。我們來談談靜態配置。因此,我們的銀行整體 [ip = 192.168.200.1] 知道它需要與風險伺服器 [ip = 192.168.200.2] 通信,該伺服器硬編碼在屬性檔案中。但是,您可以使事情變得更加動態:- 使用基於雲端的配置伺服器,所有微服務都可以從中提取其配置,而不是在其微服務上部署 application.properties 檔案。
- 由於您的服務執行個體可以動態變更其位置,因此值得查看了解您的服務所在位置、它們的 IP 以及如何路由它們的服務。
- 既然一切都是動態的,就會出現新的問題,例如自動選舉領導者:例如,誰是負責某些任務的主人,以免重複處理這些任務?當領導者失敗時誰來取代他?更換的依據是什麼?
如何使用Java微服務組織授權和認證?
這個話題也值得單獨講一個故事。同樣,選項範圍從使用自訂安全框架的硬編碼基本 HTTPS 驗證到使用自己的授權伺服器執行 Oauth2 安裝。如何確保我的所有環境看起來都一樣?
對於沒有微服務的部署來說是這樣,對於有微服務的部署也是如此。嘗試結合使用 Docker/Testcontainers 和 Scripting/Ansible。沒問題:簡單介紹一下 YAML
讓我們暫時遠離庫和相關問題,快速瀏覽一下 Yaml。該文件格式實際上用作“將配置編寫為程式碼”的格式。Ansible 等簡單工具和 Kubernetes 等巨人也使用它。要體驗 YAML 縮排的痛苦,請嘗試編寫一個簡單的 Ansible 文件,並查看需要對該文件進行多少編輯才能使其按預期工作。儘管所有主要 IDE 都支援該格式!之後,再回來閱讀本指南。Yaml:
- is:
- so
- great
那麼分散式事務呢?性能測試?其他主題?
也許有一天,在手冊的未來版本中。現在,僅此而已。和我們在一起!微服務的概念問題
除了 Java 中微服務的具體問題之外,任何微服務專案中都會出現其他問題。它們主要與組織、團隊和管理有關。前端和後端不匹配
前端和後端不匹配是許多微服務專案中非常常見的問題。這是什麼意思?只是在古老的巨石中,Web 介面開發人員有一個特定的來源來獲取數據。在微服務專案中,前端開發人員突然有n個來源可以取得資料。想像一下,您正在使用 Java 創建某種 IoT(物聯網)微服務專案。假設您管理整個歐洲的大地測量機器和工業爐。這些烤箱會定期向您發送溫度等最新資訊。遲早您可能想在管理 UI 中找到烤箱,也許使用「熔爐搜尋」微服務。根據後端對應方應用領域驅動設計或微服務法的嚴格程度,「查找烤箱」微服務可能只會返回烤箱 ID,而不返回其他數據,例如類型、型號或位置。為此,前端開發人員需要使用從第一個微服務收到的 ID 在「取得熔爐資料」微服務中進行一個或 n 個額外呼叫(取決於分頁實作)。 雖然這只是一個簡單的例子,雖然取自一個真實的(!)項目,但它也說明了以下問題:超市已經變得非常受歡迎。那是因為有了它們,你不必去 10 個不同的地方買蔬菜、檸檬水、冷凍披薩和衛生紙。相反,您可以前往一個地方。這樣更容易、更快捷。對於前端和微服務開發人員也是如此。管理階層期望
管理層錯誤地認為,他們現在需要為一個(總體)專案僱用無限數量的開發人員,因為開發人員現在可以完全獨立地彼此工作,每個人都在自己的微服務上工作。最後(發布前不久)只需要進行少量整合工作。 事實上,這種做法是有很大的問題。在下面的段落中,我們將嘗試解釋原因。“較小的部分”並不等於“更好的部分”
如果認為分成 20 個部分的程式碼必然比整個程式碼的品質更高,那將是一個很大的錯誤。即使我們從純粹的技術角度來考慮質量,我們的各個服務仍然可能運行 400 個 Hibernate 查詢來從資料庫中選擇用戶,遍歷不支援的程式碼層。我們再次回到西蒙布朗的名言:如果你不能正確建立單體,那麼就很難建立正確的微服務。談論微服務項目中的容錯能力往往已經為時已晚。以至於有時看到微服務在實際專案中如何運作會讓人感到害怕。原因是 Java 開發人員並不總是準備好在適當的層面上研究容錯、網路和其他相關主題。「部件」本身較小,但「技術部件」較大。想像一下,您的微服務團隊被要求編寫一個用於登入資料庫系統的技術微服務,如下所示:@Controller
class LoginController {
// ...
@PostMapping("/login")
public boolean login(String username, String password) {
User user = userDao.findByUserName(username);
if (user == null) {
// обработка варианта с несуществующим пользователем
return false;
}
if (!user.getPassword().equals(hashed(password))) {
// обработка неверного пароля
return false;
}
// 'Ю-ху, залогинorсь!';
// установите cookies, делайте, что угодно
return true;
}
}
現在你的團隊可能會決定(甚至可能說服業務人員)這一切都太簡單和無聊了,與其編寫一個登入服務,不如編寫一個真正有用的 UserStateChanged 微服務,而不需要任何實際和有形的業務需求。由於目前有些人將 Java 視為恐龍,因此讓我們用時尚的 Erlang 來編寫 UserStateChanged 微服務。讓我們嘗試在某個地方使用紅黑樹,因為 Steve Yegge 寫道,你必須徹底了解它們才能申請 Google。從整合、維護和整體設計的角度來看,這就像在單一整體中編寫義大利麵條式程式碼層一樣糟糕。一個人為的普通例子?這是真實的。然而,這在現實中是可能發生的。
更少的碎片 - 更少的理解
那麼自然會出現關於理解整個系統、其流程和工作流程的問題,但同時,作為開發人員,您只負責處理獨立的微服務[95:login-101:updateUserProfile]。它與上一段一致,但根據您的組織、信任和溝通水平,如果微服務鏈中出現意外故障,這可能會導致很多混亂、聳肩和指責。沒有人會對所發生的事情負起全部責任。這不是不誠實的問題。事實上,連接不同的部分並理解它們在專案整體圖中的位置是非常困難的。通訊及服務
溝通和服務的程度很大程度取決於公司的規模。然而,一般關係是顯而易見的:越多,問題就越多。- 誰運行微服務#47?
- 他們是否剛剛部署了一個新的、不相容的微服務版本?這是在哪裡記錄的?
- 我需要與誰聯繫來請求新功能?
- 在唯一懂得這種語言的人離開公司之後,誰會支援 Erlang 中的微服務?
- 我們所有的微服務團隊不僅使用不同的程式語言,而且還處於不同的時區!我們如何正確協調這一切?
GO TO FULL VERSION