JavaRush /Java Blog /Random-TW /Java 微服務指南。第 3 部分:一般問題

Java 微服務指南。第 3 部分:一般問題

在 Random-TW 群組發布
《Java微服務:實用指南》的 翻譯與改編。指南的前幾部分: 讓我們從抽象的事物開始,到具體的函式庫結束,看看 Java 中微服務的固有問題。 Java 微服務指南。 第 3 部分:一般問題 - 1

如何讓 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:通話已接通,但完成時間太長。
  • 錯誤。通話失敗,您可能發送了不相容的請求,或者係統可能無法運作。
任何程式都應該處理錯誤情況,而不僅僅是成功的情況。這同樣適用於微服務。即使在開始部署和發佈各個微服務後,您需要付出額外的努力來確保 API 的所有已部署版本都相容。 Java 微服務指南。 第 3 部分:一般問題 - 2一個有趣的例子是延遲的情況。例如,響應者的微服務硬碟已滿,回應時間不再是 50 毫秒,而是 10 秒。當您遇到一定的負載,導致 BillingService 的無回應開始在您的系統中級聯時,情況會變得更加有趣。作為一個說明性的例子,想像一下廚房慢慢地啟動了所有餐廳服務員的「區塊」。本節顯然無法提供微服務彈性主題的詳盡概述,但它可以提醒開發人員,這實際上是在首次發布之前需要解決而不是忽略的問題(根據經驗,這種情況發生的頻率比不是)。如下)。Netflix 的 Hystrix 是一個可以幫助您考慮延遲和容錯的流行函式庫。使用其文件更深入地了解該主題。

訊息傳遞彈性模式

讓我們仔細看看非同步通訊。我們的 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 回應。相反,我們可能會遇到三種可能出錯的情況,這可能會引發以下問題:
  1. 我的消息是否已被員工傳遞和使用?還是丟失了?(用戶不會收到發票)。
  2. 我的訊息只發送一次嗎?或多次交付但僅處理一次?(用戶將收到多張發票)。
  3. 配置:從“我是否使用了正確的路由鍵/名稱進行交換”到“我的消息代理是否已正確配置和維護,或者其隊列是否已滿?” (用戶不會收到發票)。
每個單獨的非同步微服務彈性模式的詳細描述超出了本指南的範圍。然而,有跡象表明,方向是正確的。此外,它們將依賴訊息傳遞技術。例子:
  • 如果您使用 ActiveMQ 等 JMS 實現,則可以用速度換取兩階段 (XA) 提交的保證。
  • 如果您使用 RabbitMQ,請先閱讀本教學課程,然後仔細考慮一般的確認、容錯和訊息可靠性。
  • 也許有人精通配置 Active 或 RabbitMQ 伺服器,尤其是與叢集和 Docker 結合使用(有人嗎?;))

哪個框架是 Java 微服務的最佳解決方案?

一方面,您可以安裝非常受歡迎的選項,例如Spring Boot。它使創建 .jar 檔案變得非常容易,附帶 Tomcat 或 Jetty 等內建 Web 伺服器,並且可以在任何地方快速運行。非常適合建立微服務應用程式。最近,出現了一對專門的微服務框架,KubernetesGraalVM,部分受到反應式程式設計的啟發。這裡有一些更有趣的競爭者:QuarkusMicronautVert.xHelidon。最後,您必須自己選擇,但我們可以給您一些可能不完全標準的建議:除了 Spring Boot 之外,所有微服務框架通常都被宣傳為速度快得令人難以置信,幾乎可以瞬時啟動,低內存佔用,並且能夠擴展到無限。行銷資料通常具有令人印象深刻的圖形,展示了龐然大物 Spring Boot 旁邊或彼此之間的平台。從理論上講,這可以減輕支持遺留專案的開發人員的緊張,這些專案有時需要幾分鐘才能加載。或在雲端工作的開發人員希望在 50 毫秒內啟動/停止目前所需的盡可能多的微容器。 Java 微服務指南。 第 3 部分:一般問題 - 3然而,問題是這些(人為的)裸機啟動時間和重新部署時間幾乎對專案的整體成功沒有貢獻。至少它們的影響遠小於強大的框架基礎設施、強大的文件、社群和強大的開發人員技能。所以最好這樣看:如果到目前為止:
  • 您讓 ORM 大量運行,為簡單的工作流程產生數百個查詢。
  • 您需要無盡的千兆位元組來運行中等複雜的整體應用程式。
  • 您有如此多的程式碼並且複雜性如此之高(我們現在不是在談論像 Hibernate 這樣的潛在緩慢啟動器),以至於您的應用程式需要幾分鐘才能加載。
如果是這種情況,那麼新增額外的 mycoservice 問題(網路彈性、訊息傳遞、DevOps、基礎架構)將對您的專案產生比載入空的 Hello, world 更大的影響。對於開發期間的熱重新部署,您最終可能會得到JRebelDCEVM等解決方案。讓我們花點時間再次引用Simon Brown 的話:“如果人們無法構建(快速且高效)單體應用,那麼無論結構如何,他們都將很難構建(快速且高效)微服務。” 所以要明智地選擇你的框架。

哪些函式庫最適合同步 Java REST 呼叫?

在低階技術方面,您可能最終會得到以下 HTTP 用戶端程式庫之一:Java 的本機 HttpClient(自 Java 11 起)、Apache 的 HttpClientOkHttp。請注意,我在這裡說「可能」是因為還有其他選項,從老式的JAX-RS 客戶端到現代的WebSocket客戶端。無論如何,趨勢是產生一個 HTTP 客戶端,而不是自己擺弄 HTTP 呼叫。為此,您需要查看OpenFeign專案及其文檔,作為進一步閱讀的起點。

異步 Java 訊息傳遞的最佳代理是什麼?

您很可能會遇到流行的ActiveMQ(經典或 Artemis)RabbitMQKafka
  • ActiveMQ 和 RabbitMQ 是傳統的、成熟的訊息代理程式。它們涉及“聰明的經紀人”和“愚蠢的用戶”的互動。
  • 從歷史上看,ActiveMQ 具有易於內聯(用於測試)的優點,可以透過 RabbitMQ/Docker/TestContainer 設定來緩解。
  • Kafka 不能被稱為傳統的「智慧」經紀人。相反,它是一個相對「愚蠢」的訊息儲存(日誌檔案),需要聰明的消費者來處理。
為了更了解何時使用 RabbitMQ(或一般的其他傳統訊息代理程式)或 Kafka,請查看此 Pivotal 貼文作為起點。一般來說,在選擇訊息代理時,盡量忽略人為的效能原因。曾經有一段時間,團隊和線上社群會不斷爭論 RabbitMQ 有多快以及 ActiveMQ 有多慢。現在,對於 RabbitMQ 也提出了同樣的論點,他們說它運行緩慢,每秒處理 20-30,000 條訊息。Kafka 每秒記錄 10 萬則訊息。坦白說,這樣的比較就像溫暖與柔軟的比較。此外,在這兩種情況下,吞吐量值可能處於阿里巴巴集團的中低範圍內。然而,您在現實中不太可能遇到這種規模的項目(每分鐘數百萬條訊息)。它們肯定存在,並且會有問題。與其他 99% 的「常規」Java 商業專案不同。所以不要關注時尚和炒作。做出明智的選擇。

我可以使用哪些函式庫來測試微服務?

這取決於你的堆疊。如果您部署了 Spring 生態系統,那麼使用該框架的特定工具將是明智的選擇。如果 JavaEE 類似ArquillianDocker 和非常好的Testcontainers庫可能值得一看,它特別有助於輕鬆快速地設定 Oracle 資料庫以進行本地開發或整合測試。若要模擬測試整個 HTTP 伺服器,請查看Wiremock。若要測試非同步訊息傳遞,請嘗試實作 ActiveMQ 或 RabbitMQ,然後使用Awaitility DSL編寫測試。此外,還使用了所有常用工具 - JunitTestNG for AssertJMockito。請注意,這不是完整清單。如果您在這裡沒有找到您最喜歡的工具,請在評論部分中發布。

如何為所有 Java 微服務啟用日誌記錄?

微服務中的日誌記錄是一個有趣且相當複雜的主題。您現在擁有 n 個日誌文件,而不是可以使用 less 或 grep 命令操作的一個日誌文件,並且您希望它們不要太分散。本文(英文)很好地描述了日誌生態系的特徵。請務必閱讀它,並注意從微服務角度進行集中日誌記錄部分。在實踐中,您會遇到不同的方法:系統管理員編寫某些腳本,收集來自不同伺服器的日誌檔案並將其組合成一個日誌文件,並將它們放在 FTP 伺服器上以供下載。在並行 SSH 會話中執行 cat/grep/unig/sort 組合。這正是 Amazon AWS 所做的,您可以讓您的經理知道。使用GraylogELK Stack(Elasticsearch、Logstash、Kibana)等工具

我的微服務如何找到彼此?

到目前為止,我們假設我們的微服務彼此了解並且知道相應的 IPS。我們來談談靜態配置。因此,我們的銀行整體 [ip = 192.168.200.1] 知道它需要與風險伺服器 [ip = 192.168.200.2] 通信,該伺服器硬編碼在屬性檔案中。但是,您可以使事情變得更加動態:
  • 使用基於雲端的配置伺服器,所有微服務都可以從中提取其配置,而不是在其微服務上部署 application.properties 檔案。
  • 由於您的服務執行個體可以動態變更其位置,因此值得查看了解您的服務所在位置、它們的 IP 以及如何路由它們的服務。
  • 既然一切都是動態的,就會出現新的問題,例如自動選舉領導者:例如,誰是負責某些任務的主人,以免重複處理這些任務?當領導者失敗時誰來取代他?更換的依據是什麼?
通俗點講,這就是所謂的微服務編排,又是一個無底洞的話題。像EurekaZookeeper這樣的函式庫試圖透過顯示哪些服務可用來「解決」這些問題。另一方面,它們引入了額外的複雜性。詢問曾經安裝過 ZooKeeper 的人。

如何使用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 個額外呼叫(取決於分頁實作)。 Java 微服務指南。 第 3 部分:一般問題 - 4雖然這只是一個簡單的例子,雖然取自一個真實的(!)項目,但它也說明了以下問題:超市已經變得非常受歡迎。那是因為有了它們,你不必去 10 個不同的地方買蔬菜、檸檬水、冷凍披薩和衛生紙。相反,您可以前往一個地方。這樣更容易、更快捷。對於前端和微服務開發人員也是如此。

管理階層期望

管理層錯誤地認為,他們現在需要為一個(總體)專案僱用無限數量的開發人員,因為開發人員現在可以完全獨立地彼此工作,每個人都在自己的微服務上工作。最後(發布前不久)只需要進行少量整合工作。 Java 微服務指南。 第 3 部分:一般問題 - 5事實上,這種做法是有很大的問題。在下面的段落中,我們將嘗試解釋原因。

“較小的部分”並不等於“更好的部分”

如果認為分成 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 中的微服務?
  • 我們所有的微服務團隊不僅使用不同的程式語言,而且還處於不同的時區!我們如何正確協調這一切?
Java 微服務指南。 第 3 部分:一般問題 - 6主要的一點是,與 DevOps 一樣,在大型公司(甚至可能是國際公司)中成熟的微服務方法會帶來一系列額外的通訊問題。公司必須為此認真做好準備。

結論

讀完本文後,您可能會認為作者是微服務的狂熱反對者。這並不完全正確——我基本上是想強調在新技術的瘋狂競爭中很少有人關注的點。

微服務還是整體服務?

隨時隨地使用 Java 微服務是一種極端。另一個結果是像一個整體中的數百個良好的舊 Maven 模組。您的任務是找到適當的平衡。對於新項目尤其如此。沒有什麼可以阻止您採取更保守的「整體」方法並創建更少的優秀 Maven 模組,而不是從 20 個雲端就緒微服務開始。

微服務產生額外的複雜性

請記住,您擁有的微服務越多,而您擁有的DevOps 功能就越弱(不,運行幾個Ansible 腳本或部署到Heroku 不算!),您在此過程中遇到的問題就越多。即使只是通讀本指南專門討論有關 Java 微服務的一般問題的部分的結尾也是一項相當乏味的任務。認真思考如何針對所有這些基礎設施挑戰實施解決方案,您會突然意識到,這不再全是業務編程(您獲得報酬的事情),而是將更多技術鎖定在更多技術上。Shiva Prasad Reddy 在他的部落格中對此進行了完美的總結: 「當一個團隊花費70% 的時間與這種現代化基礎設施作鬥爭,而只剩下30% 的時間來處理實際的業務邏輯時,你不會知道這是多麼可怕的事情。 」 希瓦·普拉薩德·雷迪

值得創建 Java 微服務嗎?

為了回答這個問題,我想用一個非常厚臉皮、類似谷歌面試的預告片來結束這篇文章。如果您從經驗中知道這個問題的答案,即使它看起來與微服務沒有任何關係,您可能已經準備好採用微服務方法。

設想

想像一下,您有一個單獨運行在最小的Hetzner專用伺服器上的 Java 整體應用程式。這同樣適用於您的資料庫伺服器,它也運行在類似的Hetzner機器上。我們也假設您的 Java 整體架構可以處理工作流程(例如使用者註冊),並且您不會為每個工作流程建立數百個資料庫查詢,而是一個更合理的數量(<10)。

問題

您的 Java 整體應用程式(連接池)應在資料庫伺服器上開啟多少個資料庫連線?這是為什麼?您認為您的整體架構可以(大約)擴展多少同時活躍用戶?

回答

在評論部分留下您對這些問題的答案。我期待所有的答案。 Java 微服務指南。 第 3 部分:一般問題 - 8現在你下定決心吧。如果您讀到最後,我們非常感謝您!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION