使用 Quarkus 和 MicroProfile 實現微服務特性

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quarkus 的文章之前寫過三篇了,講過了 Quarkus 的小而快。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/YcEqFm3oxlsEvJ3ckRbQyA","title":"","type":null},"content":[{"type":"text","text":"Hello, Quarkus","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/Dq3hQrXE4XWH-MyjBAGMEw","title":"","type":null},"content":[{"type":"text","text":"應\"雲\"而生的 Java 框架 Quarkus:構建本機可執行文件","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/1eqjYC36O3qm1XDw84aAPA","title":"","type":null},"content":[{"type":"text","text":"誰說 Java 不能用來跑 Serverless?","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一直在醞釀寫一篇 Quarkus 生態相關的,因爲最近一直在忙 Meetup 的事情而擱淺。正好看到了這篇文章,就拿來翻譯一下,補全雲原生中的“微服務”這一塊。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文譯自","attrs":{}},{"type":"link","attrs":{"href":"https://www.infoq.com/articles/microservicilities-quarkus","title":"","type":null},"content":[{"type":"text","text":"《Implementing Microservicilities with Quarkus and MicroProfile》","attrs":{}}]},{"type":"text","text":" 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"爲什麼要使用微服務特性?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服務架構中,一個應用程序是由幾個相互連接的服務組成的,這些服務一起工作來實現所需的業務功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,典型的企業微服務架構如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/36e427782bc59adb1524d1a2178fd31d.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"剛開始,使用微服務架構實現應用程序看起來很容易。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,因爲有了單體架構沒有一些新的挑戰,因此做起來並不容器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉幾個例子,比如容錯、服務發現、擴展性、日誌記錄和跟蹤。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決這些挑戰,每個微服務都應實現我們在 Red Hat 所說的“微服務特性”。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[ ] 該術語是指除業務邏輯以外,服務還必須實現來解決的跨領域關注點清單,如下圖所示:","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d3/d34a02448308ac1b5597362b1c6e2e6f.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以用任何語言(Java、Go、JavaScript)或任何框架(Spring Boot、Quarkus)實現業務邏輯,但是圍繞業務邏輯,應實現以下關注點:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"API","attrs":{}},{"type":"text","text":":可通過一組定義的 API 操作來訪問該服務。例如,對於 RESTful Web API,HTTP 用作協議。此外,可以使用諸如 Swagger 之類的工具來記錄 API 。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"服務發現(Discovery)","attrs":{}},{"type":"text","text":":服務需要發現其他服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"調用服務(Invocation)","attrs":{}},{"type":"text","text":":發現服務後,需要使用一組參數對其進行調用,並選擇性地返回響應。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"彈性(Elasticity)","attrs":{}},{"type":"text","text":":微服務架構的重要特徵之一是每個服務都是彈性的,這意味着可以根據系統的關鍵程度或當前的工作量等參數獨立地進行縮放。(譯者注:這裏的彈性只是資源的彈性)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"彈性(Resiliency)","attrs":{}},{"type":"text","text":":在微服務架構中,我們在開發時應牢記失敗,尤其是在與其他服務進行通信時。在單體應用中,整個應用程序處於啓動或關閉狀態。但是,當此應用程序分解爲微服務體系結構時,該應用程序由多個服務組成,並且所有這些服務都通過網絡互連,這意味着該應用程序的某些部分可能正在運行,而其他部分可能會失敗。遏制故障對避免通過其他服務傳播錯誤很重要。彈性(或應用程序彈性)是應用程序/服務對問題做出反應並仍然提供最佳結果的能力。(譯者注:這裏的彈性與容錯相關,對失敗處理的彈性)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"管道(Pipeline)","attrs":{}},{"type":"text","text":":服務應獨立部署,而無需進行任何形式的編排。因此,每個服務應具有自己的部署管道。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"身份驗證(Authentication)","attrs":{}},{"type":"text","text":":關於微服務體系結構中的安全性的關鍵方面之一是如何對內部服務之間的調用進行身份驗證/授權。Web 令牌(通常是令牌)是在內部服務中安全地表示聲明的首選方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"日誌記錄(Logging)","attrs":{}},{"type":"text","text":":在單體應用程序中,日誌記錄很簡單,因爲該應用程序的所有組件都在同一節點上運行。然後現在組件以服務的形式分佈在多個節點上,因此,要擁有完整的日誌記錄視圖,需要一個統一的日誌記錄系統/數據收集器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"監控(Monitoring)","attrs":{}},{"type":"text","text":":衡量系統的性能、瞭解應用程序的整體運行狀況,以及在出現問題時發出警報是保持基於微服務的應用程序正確運行的關鍵方面。監控是控制應用程序的關鍵方面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"跟蹤(Tracing)","attrs":{}},{"type":"text","text":":跟蹤用於可視化程序的流程和數據進度。作爲開發人員/運維人員,當我們需要檢查用戶在整個應用程序中的行程時,這特別有用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes正在成爲部署微服務的實際工具。這是一個用於自動化、編排、擴展和管理容器的開源系統。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 Kubernetes 時,十個微服務特性中只有三個被涵蓋。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/70/7076fe1eb755044a0387e8bc02f1bbad.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**服務發現 **是通過 ","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Kubernetes 服務","attrs":{}},{"type":"text","text":"的概念實現的。它提供了一種使用穩定的虛擬 IP 和 DNS 名稱將 Kubernetes Pod 分組(作爲一個整體)的方法。發現服務只是使用 Kubernetes 的服務名作爲 hostname 進行請求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 Kubernetes 可以很容易地","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"調用服務","attrs":{}},{"type":"text","text":",因爲平臺本身提供了調用任何服務所需的網絡。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從一開始,Kubernetes 就一直在考慮","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"彈性","attrs":{}},{"type":"text","text":"(或可伸縮性),例如運行時","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kubectl scale deployment myservice --replicas=5 command","attrs":{}}],"attrs":{}},{"type":"text","text":",myservice deployment 可伸縮至五個副本或實例。Kubernetes 平臺負責尋找合適的節點,部署服務並始終保持所需數量的副本並正常運行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是其餘的微服務特性又如何呢?Kubernetes 僅涵蓋其中的三個,那麼我們如何實現剩下的呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據所使用的語言或框架,可以遵循的策略很多。但是在本文中,我們將瞭解如何使用 ","attrs":{}},{"type":"link","attrs":{"href":"https://quarkus.io/","title":"","type":null},"content":[{"type":"text","text":"Quarkus","attrs":{}}]},{"type":"text","text":" 實現其中的一些","attrs":{}},{"type":"link","attrs":{"href":"https://quarkus.io/","title":"","type":null},"content":[{"type":"text","text":"策略","attrs":{}}]},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是 Quarkus?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://quarkus.io/","title":"","type":null},"content":[{"type":"text","text":"Quarkus","attrs":{}}]},{"type":"text","text":" 是針對 Java 虛擬機(JVM)和本機編譯的全棧 Kubernetes 本地 Java 框架,專門針對容器優化 Java,使其成爲無服務器(Serverless)、雲和 Kubernetes 環境的高效平臺。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Instead of reinventing the wheel, Quarkus uses well-known enterprise-grade frameworks backed by standards/specifications and makes them compilable to a binary using ","attrs":{}},{"type":"link","attrs":{"href":"https://www.graalvm.org/","title":"","type":null},"content":[{"type":"text","text":"GraalVM","attrs":{}}]},{"type":"text","text":".Quarkus不用重新發明輪子,而是使用以標準/規範爲後盾的知名企業級框架,並使用 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.graalvm.org/","title":"","type":null},"content":[{"type":"text","text":"GraalVM","attrs":{}}]},{"type":"text","text":" 將其編譯爲二進制","attrs":{}},{"type":"link","attrs":{"href":"https://www.graalvm.org/","title":"","type":null},"content":[{"type":"text","text":"文件","attrs":{}}]},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是 MicroProfile?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quarkus 與 ","attrs":{}},{"type":"link","attrs":{"href":"https://microprofile.io/","title":"","type":null},"content":[{"type":"text","text":"MicroProfile","attrs":{}}]},{"type":"text","text":" 規範集成,從而將企業 Java 生態系統遷移到微服務體系結構中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下圖中,我們看到了構成 MicroProfile 規範的所有 API。某些 API(例如 CDI、JSON-P 和 JAX-RS)基於 ","attrs":{}},{"type":"link","attrs":{"href":"https://jakarta.ee/","title":"","type":null},"content":[{"type":"text","text":"Jakarta EE","attrs":{}}]},{"type":"text","text":"(以前的 Java EE)規範。其餘的由 Java 社區開發。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/5449c4edbec80cb3f915ac215b1e950c.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Let’s implement API, invocation, resilience, authentication, logging, monitoring, and tracing microservicilities using Quarkus.讓我們使用Quarkus實現API、調用、彈性、身份驗證、日誌記錄、監視和跟蹤微服務特性。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"如何使用 Quarkus 實現微服務特性","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"入門","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開始使用 Quarkus 的最快方法是通過在","attrs":{}},{"type":"link","attrs":{"href":"https://code.quarkus.io/","title":"","type":null},"content":[{"type":"text","text":"開始頁面","attrs":{}}]},{"type":"text","text":"中選擇所需的依賴。對於當前示例,選擇如下依賴關係以滿足微服務需求:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"API:RESTEasy JAX-RS、RESTEasy JSON-B、OpenAPI調用:REST Client JSON-B彈性:Fault Tolerance認證:JWT記錄:GELF監控:Micrometer metrics跟蹤:OpenTracing","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/61/61f2a5b570da38ee3ccb0562e4137a9e.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以手動選擇各自的依賴關係,或瀏覽以下鏈接 ","attrs":{}},{"type":"link","attrs":{"href":"https://code.quarkus.io/?a=microservicilities-quarkus&e=resteasy&e=resteasy-jsonb&e=rest-client-jsonb&e=smallrye-jwt&e=smallrye-openapi&e=logging-gelf&e=smallrye-fault-tolerance&e=micrometer&e=smallrye-opentracing","title":"","type":null},"content":[{"type":"text","text":"Quarkus 微服務特性生成器","attrs":{}}]},{"type":"text","text":",所有這些都會被選中。然後按“生成應用程序”按鈕以下載包含支架應用程序的zip文件。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"服務","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於當前示例,僅使用兩個服務生成了一個非常簡單的應用程序。一個名爲“","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務 rating service","attrs":{}},{"type":"text","text":"”的服務返回給定書籍的評級,而另一個名爲“","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"書籍服務 book service","attrs":{}},{"type":"text","text":"”的服務則返回一本書的信息及其評級。服務之間的所有調用都必須經過身份驗證。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下圖中,我們看到了整個系統的概述:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af891e45ea0c95d3f91641433b1b2230.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務","attrs":{}},{"type":"text","text":"已經開發並作爲 Linux 容器提供。通過運行以下命令,在端口 9090 上啓動服務:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"docker run --rm -ti -p 9090:8080 \nquay.io/lordofthejars/rating-service:1.0.0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要驗證服務,請向 http://localhost:9090/rate/1 發出請求","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"curl localhost:8080/rate/1 -vv\n\n> GET /rate/1 HTTP/1.1\n> Host: localhost:8080\n> User-Agent: curl/7.64.1\n> Accept: */*\n>\n< HTTP/1.1 401 Unauthorized\n< www-authenticate: Bearer {token}\n< Content-Length: 0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回的狀態碼是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"401 Unauthorized","attrs":{}}],"attrs":{}},{"type":"text","text":" 因爲沒有在請求中攜帶令牌(JWT)提供授權信息。只有帶有 ","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"group ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Echoer","attrs":{}}],"attrs":{}},{"type":"text","text":" 有效令牌才能訪問評級服務。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"API","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quarkus 使用衆所周知的 JAX-RS 規範來定義 RESTful Web API。在幕後,Quarkus 使用 RESTEasy 實現直接與 Vert.X 框架一起使用,而無需使用 Servlet 技術。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們爲實現最常見操作的圖書服務定義一個 API:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import javax.ws.rs.Consumes;\nimport javax.ws.rs.DELETE;\nimport javax.ws.rs.GET;\nimport javax.ws.rs.POST;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.PathParam;\nimport javax.ws.rs.Produces;\nimport javax.ws.rs.QueryParam;\nimport javax.ws.rs.core.MediaType;\nimport javax.ws.rs.core.Response;\nimport javax.ws.rs.core.UriBuilder;\n\n@Path(\"/book\")\npublic class BookResource {\n\n @GET\n @Path(\"/{bookId}\")\n @Produces(MediaType.APPLICATION_JSON)\n public Book book(@PathParam(\"bookId\") Long bookId) {\n // logic\n }\n\n @POST\n @Consumes(MediaType.APPLICATION_JSON)\n public Response getBook(Book book) {\n // logic\n\n return Response.created(\n UriBuilder.fromResource(BookResource.class)\n .path(Long.toString(book.bookId))\n .build())\n .build();\n }\n\n @DELETE\n @Path(\"/{bookId}\")\n public Response delete(@PathParam(\"bookId\") Long bookId) {\n // logic\n\n return Response.noContent().build();\n }\n\n @GET\n @Produces(MediaType.APPLICATION_JSON)\n @Path(\"search\")\n public Response searchBook(@QueryParam(\"description\") String description) { \n // logic\n\n return Response.ok(books).build();\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先要注意的是,定義了四個不同的端點:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"GET /book/{bookId}","attrs":{}}],"attrs":{}},{"type":"text","text":" 使用 GET HTTP 方法返回帶有其評級的圖書信息。return 元素會自動解編爲 JSON。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"POST /book","attrs":{}}],"attrs":{}},{"type":"text","text":" 使用 POST HTTP 方法插入一本書作爲正文內容。正文內容會自動從 JSON 編組到 Java 對象。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"DELETE /book/{bookId}","attrs":{}}],"attrs":{}},{"type":"text","text":" 使用 DELETE HTTP 方法通過書的 ID 刪除書。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"GET /book/search?description={description}","attrs":{}}],"attrs":{}},{"type":"text","text":" 按書名搜索書籍。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意的第二件事是返回類型,有時是 Java 對象,有時是 Java 實例 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"javax.ws.rs.core.Response","attrs":{}}],"attrs":{}},{"type":"text","text":"。使用 Java 對象時,會將其從 Java 對象序列化爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@Produces","attrs":{}}],"attrs":{}},{"type":"text","text":" 註解中設置的媒體類型。在此特定服務中,輸出爲 JSON 文檔。通過該 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Response","attrs":{}}],"attrs":{}},{"type":"text","text":" 對象,我們可以對返回給調用方的內容進行細粒度的控制。可以設置 HTTP 狀態代碼、標頭或返回給調用方的內容。取決於使用場景,是偏愛一種方法而不是另一種方法。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"調用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在定義了用於訪問","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖書服務","attrs":{}},{"type":"text","text":"的 API 之後,是時候開發一段代碼來調用","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務","attrs":{}},{"type":"text","text":"以檢索圖書的評級了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quarkus 使用 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/eclipse/microprofile-rest-client","title":"","type":null},"content":[{"type":"text","text":"MicroProfile Rest Client","attrs":{}}]},{"type":"text","text":" 規範來訪問外部(HTTP)服務。它提供了一種類型安全的方法,以通過某些 JAX-RS 2.0 API 通過 HTTP 調用 RESTful 服務,以實現一致性和更易於重用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要創建的第一個元素是一個使用 JAX-RS 批註表示遠程服務的接口。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import javax.ws.rs.GET;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.PathParam;\nimport javax.ws.rs.Produces;\nimport javax.ws.rs.core.MediaType;\n\nimport org.eclipse.microprofile.rest.client.inject.RegisterRestClient;\n\n@Path(\"/rate\")\n@RegisterRestClient\npublic interface RatingService {\n \n @GET\n @Path(\"/{bookId}\")\n @Produces(MediaType.APPLICATION_JSON)\n Rate getRate(@PathParam(\"bookId\") Long bookId);\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"When the getRate() method is called, a remote HTTP call is invoked at /rate/{bookId} replacing the bookId with the value set in the method parameter. It is important to annotate the interface with the @RegisterRestClient annotation.Then the RatingService interface needs to be injected into BookResource to execute the remote calls.當 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getRate() ","attrs":{}}],"attrs":{}},{"type":"text","text":"方法被調用時,遠程 HTTP 請求在調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"/rate/{bookId}","attrs":{}}],"attrs":{}},{"type":"text","text":" 替換 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"bookId","attrs":{}}],"attrs":{}},{"type":"text","text":" 用在該方法中的參數值集合。用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@RegisterRestClient","attrs":{}}],"attrs":{}},{"type":"text","text":" 註解對接口進行註解很重要。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"RatingService","attrs":{}}],"attrs":{}},{"type":"text","text":" 需要將接口注入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"BookResource","attrs":{}}],"attrs":{}},{"type":"text","text":" 以執行遠程調用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.eclipse.microprofile.rest.client.inject.RestClient;\n\n@RestClient\nRatingService ratingService;\n\n@GET\n@Path(\"/{bookId}\")\n@Produces(MediaType.APPLICATION_JSON)\npublic Book book(@PathParam(\"bookId\") Long bookId) {\n final Rate rate = ratingService.getRate(bookId);\n\n Book book = findBook(bookId);\n return book;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"The @RestClient annotation injects a proxied instance of the interface, providing the implementation of the client.The last thing is to configure the service location (the hostname part). In Quarkus, the configuration properties are set in src/main/resources/application.properties file. To configure the location of the service, we need to use the fully qualified name of the Rest Client interface with URL as key, and the location as a value:該 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@RestClient","attrs":{}}],"attrs":{}},{"type":"text","text":" 註解注入界面的代理實例,提供客戶端的實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後一件事是配置服務位置(","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"hostname","attrs":{}},{"type":"text","text":" 部分)。在 Quarkus 中,配置屬性在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"src/main/resources/application.properties","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件中設置。要配置服務的位置,我們需要使用 Rest Client 接口的標準名稱,其中 URL 作爲鍵,而 location 作爲值:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"org.acme.RatingService/mp-rest/url=http://localhost:9090\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在正確訪問","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評估服務","attrs":{}},{"type":"text","text":"而沒有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"401 Unauthorized","attrs":{}}],"attrs":{}},{"type":"text","text":" 問題之前,必須解決相互認證問題。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"身份驗證","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於令牌的身份驗證機制允許系統基於安全令牌對身份進行身份驗證、授權和驗證。Quarkus 與 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/eclipse/microprofile-jwt-auth","title":"","type":null},"content":[{"type":"text","text":"MicroProfile JWT RBAC 安全","attrs":{}}]},{"type":"text","text":"規範集成在一起,以使用 JWT 令牌保護服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要使用 MicroProfile JWT RBAC 安全性保護端點,我們只需要使用批註對方法進行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@RolesAllowed","attrs":{}}],"attrs":{}},{"type":"text","text":" 註解。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@GET\n@Path(\"/{bookId}\")\n@RolesAllowed(\"Echoer\")\n@Produces(MediaType.APPLICATION_JSON)\npublic Book book(@PathParam(\"bookId\") Long bookId)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,我們配置令牌的發行方和公鑰的位置,以驗證令牌在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"application.properties","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件中的簽名:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"mp.jwt.verify.publickey.location=https://raw.githubusercontent.com/redhat-developer-demos/quarkus-tutorial/master/jwt-token/quarkus.jwt.pub\nmp.jwt.verify.issuer=https://quarkus.io/using-jwt-rbac\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此擴展名自動驗證:令牌有效;發行方是正確的;令牌尚未修改;簽名有效;沒有過期。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這兩種","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖書服務","attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務","attrs":{}},{"type":"text","text":"現在是由同一 JWT 發行方和密鑰保護,因此服務之間的通信要求驗證提供在令牌的有效承載用戶 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Authentication","attrs":{}}],"attrs":{}},{"type":"text","text":" 頭部。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務","attrs":{}},{"type":"text","text":"啓動和運行,讓我們開始用下面的命令","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖書服務","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"./mvnw compile quarkus:dev\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Finally, we can make a request to get book information providing a valid JSON Web Token as a bearer token.The generation of the token is out of the scope of this article, and a token has been already generated:最後,我們可以請求獲取提供有效 JSON Web 令牌作爲承載令牌的圖書信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"令牌的生成不在本文的討論範圍之內,並且已經生成了令牌:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5e/5ea1ab40ab4d63e92e4529d9efdc083d.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl -H \"Authorization: Bearer eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJqZG9lLXVzaW5nLWp3dC1yYmFjIiwiYXVkIjoidXNpbmctand0LXJiYWMiLCJ1cG4iOiJqZG9lQHF1YXJrdXMuaW8iLCJiaXJ0aGRhdGUiOiIyMDAxLTA3LTEzIiwiYXV0aF90aW1lIjoxNTcwMDk0MTcxLCJpc3MiOiJodHRwczpcL1wvcXVhcmt1cy5pb1wvdXNpbmctand0LXJiYWMiLCJyb2xlTWFwcGluZ3MiOnsiZ3JvdXAyIjoiR3JvdXAyTWFwcGVkUm9sZSIsImdyb3VwMSI6Ikdyb3VwMU1hcHBlZFJvbGUifSwiZ3JvdXBzIjpbIkVjaG9lciIsIlRlc3RlciIsIlN1YnNjcmliZXIiLCJncm91cDIiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamRvZSIsImV4cCI6MjIwMDgxNDE3MSwiaWF0IjoxNTcwMDk0MTcxLCJqdGkiOiJhLTEyMyJ9.Hzr41h3_uewy-g2B-sonOiBObtcpkgzqmF4bT3cO58v45AIOiegl7HIx7QgEZHRO4PdUtR34x9W23VJY7NJ545ucpCuKnEV1uRlspJyQevfI-mSRg1bHlMmdDt661-V3KmQES8WX2B2uqirykO5fCeCp3womboilzCq4VtxbmM2qgf6ag8rUNnTCLuCgEoulGwTn0F5lCrom-7dJOTryW1KI0qUWHMMwl4TX5cLmqJLgBzJapzc5_yEfgQZ9qXzvsT8zeOWSKKPLm7LFVt2YihkXa80lWcjewwt61rfQkpmqSzAHL0QIs7CsM9GfnoYc0j9po83-P3GJiBMMFmn-vg\" localhost:8080/book/1 -v\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"響應又是 forbidden 錯誤:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"< HTTP/1.1 401 Unauthorized\n< Content-Length: 0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能想知道爲什麼在提供有效令牌後仍然出現此錯誤。如果我們檢查圖書服務的控制檯,就會發現拋出了以下異常:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"log"},"content":[{"type":"text","text":"org.jboss.resteasy.client.exception.ResteasyWebApplicationException: Unknown error, status code 401\n at org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper.wrap(WebApplicationExceptionWrapper.java:107)\n at org.jboss.resteasy.microprofile.client.DefaultResponseExceptionMapper.toThrowable(DefaultResponseExceptionMapper.java:21)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發生此異常的原因是,我們已獲得身份驗證並有權訪問","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖書服務","attrs":{}},{"type":"text","text":",但承載令牌尚未傳播到","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/32/32f9071eb0aab8e81c64cf6a64a321e3.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了自動將 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Authorization","attrs":{}}],"attrs":{}},{"type":"text","text":" 標頭從傳入請求傳播到其餘客戶端請求,需要進行兩次修改。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一個修改是修改 Rest Client 界面,並使用對其進行註解 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"org.eclipse.microprofile.rest.client.inject.RegisterClientHeaders","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Path(\"/rate\")\n@RegisterRestClient\n@RegisterClientHeaders\npublic interface RatingService {}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二個修改是配置在請求之間傳播哪些標頭。這是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"application.properties","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件中設置的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"properties"},"content":[{"type":"text","text":" org.eclipse.microprofile.rest.client.propagateHeaders=Authorization\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"執行與之前相同的 curl 命令,我們將獲得正確的輸出:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"< HTTP/1.1 200 OK\n< Content-Length: 39\n< Content-Type: application/json\n {\n\n @Override\n public Rate handle(ExecutionContext context) {\n Rate rate = new Rate();\n rate.rate = 0;\n return rate;\n }\n \n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後要做的是用註解對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getRating()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法進行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"@org.eclipse.microprofile.faulttolerance.Fallback","attrs":{}}],"attrs":{}},{"type":"text","text":" 註解,以配置無法恢復時要執行的回退類。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Retry(maxRetries = 3, delay = 1000)\n@Fallback(RatingServiceFallback.class)\nRate getRate(@PathParam(\"bookId\") Long bookId);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果重複與以前相同的請求,則不會引發任何異常,但是有效值的輸出將 rating 字段設置爲 0。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"* Connection #0 to host localhost left intact\n{\"bookId\":2,\"name\":\"Book 2\",\"rating\":0}* Closing connection 0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"規範提供的任何其他策略都可以使用相同的方法。例如,對於斷路器模式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@CircuitBreaker(requestVolumeThreshold = 4,\n failureRatio=0.75,\n delay = 1000)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在四個連續調用的滾動窗口中發生了三個(","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"4 x 0.75","attrs":{}},{"type":"text","text":")故障,則電路將斷開 1000 ms,然後恢復到半斷開狀態。如果在半開時調用成功,則將其再次關閉。否則,它將保持打開狀態","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"日誌記錄","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服務架構中,建議將所有服務的日誌收集在一個統一的日誌中,以更有效地使用和理解。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一種解決方案是使用 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.fluentd.org/","title":"","type":null},"content":[{"type":"text","text":"Fluentd","attrs":{}}]},{"type":"text","text":",它是 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.fluentd.org/","title":"","type":null},"content":[{"type":"text","text":"Kubernetes","attrs":{}}]},{"type":"text","text":" 中用於統一日誌記錄層的開源數據收集器。Quarkus 使用 Graylog 擴展日誌格式(GELF)與 Fluentd 集成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"集成真的很簡單。首先,與其他任何 Quarkus 應用程序一樣使用日誌邏輯:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.jboss.logging.Logger;\n\nprivate static final Logger LOG = Logger.getLogger(BookResource.class);\n\n@GET\n@Path(\"/{bookId}\")\n@RolesAllowed(\"Echoer\")\n@Produces(MediaType.APPLICATION_JSON)\npublic Book book(@PathParam(\"bookId\") Long bookId) {\n LOG.info(\"Get Book\");\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,啓用 GELF 格式並設置 Fluentd 服務器位置:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"properties"},"content":[{"type":"text","text":"quarkus.log.handler.gelf.enabled=true\nquarkus.log.handler.gelf.host=localhost\nquarkus.log.handler.gelf.port=12201\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,我們可以向記錄的端點發出請求:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"curl -H \"Authorization: Bearer ...\" localhost:8080/book/1\n\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸出方面沒有任何變化,但是日誌行已傳輸到 Fluentd。如果使用 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.elastic.co/kibana","title":"","type":null},"content":[{"type":"text","text":"Kibana","attrs":{}}]},{"type":"text","text":" 可視化數據,我們將看到存儲的日誌行:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/20/201771c6a9bac489727daefab6aba653.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"監控","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Monitoring is another \"microservicilitie\" that needs to be implemented in our microservice architecture. Quarkus integrates with ","attrs":{}},{"type":"link","attrs":{"href":"https://micrometer.io/","title":"","type":null},"content":[{"type":"text","text":"Micrometer","attrs":{}}]},{"type":"text","text":" for application monitoring. Micrometer provides a single entry point to the most popular monitoring systems, allowing you to instrument your JVM-based application code without vendor lock-in.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"For this example, ","attrs":{}},{"type":"link","attrs":{"href":"https://prometheus.io/","title":"","type":null},"content":[{"type":"text","text":"Prometheus","attrs":{}}]},{"type":"text","text":" format is used as monitoring output but Micrometer (and Quarkus) also supports other formats like Azure Monitor, Stackdriver, SignalFx, StatsD, and DataDog.","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"You can register the following Maven dependency to provide Prometheus output:監控是另一個需要在我們的微服務架構中實現的 “微服務特性”。Quarkus 與 ","attrs":{}},{"type":"link","attrs":{"href":"https://micrometer.io/","title":"","type":null},"content":[{"type":"text","text":"Micrometer","attrs":{}}]},{"type":"text","text":" 集成在一起以進行應用程序監控。Micrometer 提供了最流行的監控系統的單個入口點,使你無需供應商鎖定即可檢測基於 JVM 的應用程序代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於此示例,監控輸出採用 ","attrs":{}},{"type":"link","attrs":{"href":"https://prometheus.io/","title":"","type":null},"content":[{"type":"text","text":"Prometheus","attrs":{}}]},{"type":"text","text":" 格式,但 Micrometer(和 Quarkus)還支持其他格式,例如 Azure Monitor、Stackdriver、SignalFx、StatsD 和 DataDog。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以註冊以下 Maven 依賴項以提供 Prometheus 輸出:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"xml"},"content":[{"type":"text","text":"\n io.quarkus\n quarkus-micrometer-registry-prometheus\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"默認情況下,Micrometer 擴展註冊了一些與系統,JVM 或 HTTP 相關的度量。收集的指標的一個子集在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"/q/metrics","attrs":{}}],"attrs":{}},{"type":"text","text":" 端點處可用,如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"curl localhost:8080/q/metrics\n\njvm_threads_states_threads{state=\"runnable\",} 22.0\njvm_threads_states_threads{state=\"blocked\",} 0.0\njvm_threads_states_threads{state=\"waiting\",} 10.0\nhttp_server_bytes_read_count 1.0\nhttp_server_bytes_read_sum 0.0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,也可以使用 Micrometer API 來實現特定於應用程序的指標。讓我們實現一個自定義指標,該指標用於衡量評價最高的圖書。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"io.micrometer.core.instrument.MeterRegistry","attrs":{}}],"attrs":{}},{"type":"text","text":" 該類可以完成指標(在這種情況下爲量規)的註冊。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private final MeterRegistry registry;\nprivate final LongAccumulator highestRating = new LongAccumulator(Long::max, 0);\n \npublic BookResource(MeterRegistry registry) {\n this.registry = registry;\n registry.gauge(\"book.rating.max\", this,\n BookResource::highestRatingBook);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求一下,並驗證量規是否正確更新。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"curl -H \"Authorization: Bearer ...\" localhost:8080/book/1\n\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n\ncurl localhost:8080/q/metrics\n\n# HELP book_rating_max\n# TYPE book_rating_max gauge\nbook_rating_max 3.0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們還可以設置一個計時器來記錄從","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"評級服務","attrs":{}},{"type":"text","text":"獲取評級信息所花費的時間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Supplier rateSupplier = () -> {\n return ratingService.getRate(bookId);\n};\n \nfinal Rate rate = registry.timer(\"book.rating.test\").wrap(rateSupplier).get();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求一下,並驗證收集評價​​所花費的時間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"# HELP book_rating_test_seconds\n# TYPE book_rating_test_seconds summary\nbook_rating_test_seconds_count 4.0\nbook_rating_test_seconds_sum 1.05489108\n# HELP book_rating_test_seconds_max\n# TYPE book_rating_test_seconds_max gauge\nbook_rating_test_seconds_max 1.018622001\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Micrometer 使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MeterFilter","attrs":{}}],"attrs":{}},{"type":"text","text":" 實例來自定義 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MeterRegistry","attrs":{}}],"attrs":{}},{"type":"text","text":" 實例發出的度量。Micrometer 擴展將檢測 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MeterFilter","attrs":{}}],"attrs":{}},{"type":"text","text":" CDI bean,並在初始化 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MeterRegistry","attrs":{}}],"attrs":{}},{"type":"text","text":" 實例時使用它們。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,我們可以定義一個通用標籤來設置運行應用程序的環境(產品、測試、預發佈等)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Singleton\npublic class MicrometerCustomConfiguration {\n \n @Produces\n @Singleton\n public MeterFilter configureAllRegistries() {\n return MeterFilter.commonTags(Arrays.asList(\n Tag.of(\"env\", \"prod\")));\n }\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送新請求並驗證指標是否已標記。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"http_client_requests_seconds_max{clientName=\"localhost\",env=\"prod\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/rate/2\",} 0.0\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請注意 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"env","attrs":{}}],"attrs":{}},{"type":"text","text":" 包含值爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"prod","attrs":{}}],"attrs":{}},{"type":"text","text":" 的標籤。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"跟蹤","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quarkus 應用程序利用 ","attrs":{}},{"type":"link","attrs":{"href":"https://opentracing.io/","title":"","type":null},"content":[{"type":"text","text":"OpenTracing","attrs":{}}]},{"type":"text","text":" 規範爲交互式 Web 應用程序提供分佈式跟蹤。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們配置 OpenTracing 以連接到 Jaeger 服務器,將 book-service 設置爲服務名稱以標識跟蹤:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"properties"},"content":[{"type":"text","text":"quarkus.jaeger.enabled=true\nquarkus.jaeger.endpoint=http://localhost:14268/api/traces\nquarkus.jaeger.service-name=book-service\nquarkus.jaeger.sampler-type=const\nquarkus.jaeger.sampler-param=1\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在發一個請求:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"curl -H \"Authorization: Bearer ...\" localhost:8080/book/1 {\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"訪問Jaeger UI以驗證是否跟蹤了該調用:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f1/f1872bf629e6a3c228182f259b753fc1.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與開發整體應用程序相比,開發和實現微服務體系結構更具挑戰性。我們認爲,微服務可以驅動你根據應用程序基礎結構正確地開發服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此處介紹的大多數微服務(API 和管道除外)是新的,或者在整體應用中實現方式有所不同。原因是現在應用程序被分解成幾部分,所有部分都在網絡中互連。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你May 26, 2021打算開發微服務並將其部署到 Kubernetes,那麼 Quarkus 是一個很好的解決方案,因爲它可以與 Kubernetes 順利集成。實施大多數微服務很簡單,只需要幾行代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文演示的源代碼可以在 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/lordofthejars/microservicilities-quarkus","title":"","type":null},"content":[{"type":"text","text":"github","attrs":{}}]},{"type":"text","text":" 上找到。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"關於作者","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a00b9680fbbc5fec8484897b58bce899.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Alex Soto 是 Red Hat 開發人員經驗總監。他對 Java 世界,軟件自動化充滿熱情,並且他相信開源軟件模型。Soto 是 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.manning.com/books/testing-java-microservices","title":"","type":null},"content":[{"type":"text","text":"Manning","attrs":{}}]},{"type":"text","text":" 的合著者 | ","attrs":{}},{"type":"link","attrs":{"href":"https://www.manning.com/books/testing-java-microservices","title":"","type":null},"content":[{"type":"text","text":"測試 Java 微服務","attrs":{}}]},{"type":"text","text":"和 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.oreilly.com/library/view/quarkus-cookbook/9781492062646/","title":"","type":null},"content":[{"type":"text","text":"O'Reilly Quarkus Cookbook","attrs":{}}]},{"type":"text","text":" 和幾個開源項目的貢獻者。自 2017 年以來一直是 Java 冠軍,他還是 Salle URL University 的國際演講者和老師。你可以在 Twitter (Alex Soto)上關注他,以隨時瞭解 Kubernetes 和 Java 世界中正在發生的事情。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章