如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

本文經過一個詳細的購物例子,展現瞭如何利用消息隊列,Spring Boot和Kubernetes進行微服務的開發,並闡述了針對微服務的伸縮,監控等方式,幫助用戶快速利用這些工具開發健壯的系統。前端

當你設計和構建大規模應用時,你將面臨兩個重大挑戰:可伸縮性和健壯性node

你應該這樣設計你的服務,即便它受到間歇性的重負載,它仍能可靠地運行。web

以Apple Store爲例。算法

每一年都有數百萬的Apple客戶預先註冊購買新的iPhone。spring

這是數百萬人同時購買物品。docker

若是你要將Apple商店的流量描述爲每秒的請求數量,那麼它多是下圖的樣子:數據庫

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

如今想象一下,你的任務是構建這樣的應用程序。後端

你正在創建一個商店,用戶能夠在那裏購買本身喜歡的商品。設計模式

你構建一個微服務來呈現網頁並提供靜態資產。 你還構建了一個後端REST API來處理傳入的請求。api

你但願將兩個組件分開,由於這樣可使用相同的REST API,爲網站和移動應用程序提供服務。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

今天是重要的一天,你的商店上線了。

你決定將應用程序擴展爲前端四個實例和後端四個實例,由於你預測網站比日常更繁忙。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

你開始接收愈來愈多的流量。

前端服務正在很好得處理流量。 可是你注意到鏈接到數據庫的後端正在努力跟上事務的數量。

不用擔憂,你能夠將後端的副本數量擴展到8。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

你收到的流量更多,後端沒法應對。

一些服務開始丟棄鏈接。 憤怒的客戶與你的客服取得聯繫。 而如今你被淹沒在大量流量中。

你的後端沒法應付它,它會失去不少鏈接。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

你剛丟了一大筆錢,你的顧客也不高興。

你的應用程序並無設計得健壯且高可用:

  • 前端和後端緊密耦合——實際上它不能在沒有後端的狀況下處理應用
  • 前端和後端必須一致擴展——若是沒有足夠的後端,你可能會淹沒在流量中
  • 若是後端不可用,則沒法處理傳入的事務。

失去事務意味着收入損失。

你能夠從新設計架構,以便將前端和後端用隊列分離。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

前端將消息發佈到隊列,然後端則一次處理一個待處理消息。

新架構有一些明顯的好處:

  • 若是後端不可用,則隊列充當緩衝區
  • 若是前端產生的消息多於後端能夠處理的消息,則這些消息將緩衝在隊列中
  • 你能夠獨立於前端擴展後端——即你能夠擁有數百個前端服務和後端的單個實例

如何構建這樣的應用程序?

你如何設計可處理數十萬個請求的服務? 你如何部署動態擴展的應用程序?

在深刻了解部署和擴展的細節以前,讓咱們關注應用程序。

編寫Spring應用程序

該服務有三個組件:前端,後端和消息代理。

前端是一個簡單的Spring Boot Web應用程序,帶有Thymeleaf模板引擎。

後端是一個消耗隊列消息的工做者。

因爲Spring Boot與JSM能出色得集成,所以你可使用它來發送和接收異步消息。

你能夠在learnk8s / spring-boot-k8s-hpa中找到一個鏈接到JSM的前端和後端應用程序的示例項目。

請注意,該應用程序是用Java 10編寫的,以利用改進的Docker容器集成能力。

只有一個代碼庫,你能夠將項目配置爲做爲前端或後端運行。

你應該知道該應用程序具備:

  • 一個購買物品的主頁
  • 管理面板,你能夠在其中檢查隊列中的消息數
  • 一個 /health 端點,用於在應用程序準備好接收流量時發出信號
  • 一個 /submit 端點,從表單接收提交併在隊列中建立消息
  • 一個 /metrics 端點,用於公開隊列中待處理消息的數量(稍後將詳細介紹)

該應用程序能夠在兩種模式下運行:

做爲前端,應用程序呈現人們能夠購買物品的網頁。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

做爲工做者,應用程序等待隊列中的消息並處理它們。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

請注意,在示例項目中,使用Thread.sleep(5000)等待五秒鐘來模擬處理。

你能夠經過更改application.yaml中的值來在任一模式下配置應用程序。

模擬應用程序的運行

默認狀況下,應用程序做爲前端和工做程序啓動。

你能夠運行該應用程序,只要你在本地運行ActiveMQ實例,你就應該可以購買物品並讓系統處理這些物品。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

若是檢查日誌,則應該看到工做程序處理項目。

它確實工做了!編寫Spring Boot應用程序很容易。

一個更有趣的主題是學習如何將Spring Boot鏈接到消息代理。

使用JMS發送和接收消息

Spring JMS(Java消息服務)是一種使用標準協議發送和接收消息的強大機制。

若是你之前使用過JDBC API,那麼你應該熟悉JMS API,由於它的工做方式很相似。

你能夠按JMS方式來使用的最流行的消息代理是ActiveMQ——一個開源消息服務器。

使用這兩個組件,你可使用熟悉的接口(JMS)將消息發佈到隊列(ActiveMQ),並使用相同的接口來接收消息。

更妙的是,Spring Boot與JMS的集成很是好,所以你能夠當即加快速度。

實際上,如下短類封裝了用於與隊列交互的邏輯:

@Componentpublic class QueueService implements MessageListener {private static final Logger LOGGER = LoggerFactory.getLogger(QueueService.class);@Autowiredprivate JmsTemplate jmsTemplate;public void send(String destination, String message) {LOGGER.info("sending message='{}' to destination='{}'", message, destination);jmsTemplate.convertAndSend(destination, message);}@Overridepublic void onMessage(Message message) {if (message instanceof ActiveMQTextMessage) { ActiveMQTextMessage textMessage = (ActiveMQTextMessage) message; try { LOGGER.info("Processing task " textMessage.getText()); Thread.sleep(5000); LOGGER.info("Completed task " textMessage.getText()); } catch (InterruptedException e) { e.printStackTrace(); } catch (JMSException e) { e.printStackTrace(); }} else { LOGGER.error("Message is not a text message " message.toString());}}} 複製代碼

你可使用send方法將消息發佈到命名隊列。

此外,Spring Boot將爲每一個傳入消息執行onMessage方法。

最後一個難題是指示Spring Boot使用該類。

你能夠經過在Spring Boot應用程序中註冊偵聽器來在後臺處理消息,以下所示:

@SpringBootApplication@EnableJmspublic class SpringBootApplication implements JmsListenerConfigurer {@Autowiredprivate QueueService queueService;public static void main(String[] args) {SpringApplication.run(SpringBootApplication.class, args);}@Overridepublic void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();endpoint.setId("myId");endpoint.setDestination("queueName");endpoint.setMessageListener(queueService);registrar.registerEndpoint(endpoint);}} 複製代碼

其中id是使用者的惟一標識符,destination是隊列的名稱。

你能夠從GitHub上的項目中完整地讀取Spring隊列服務的源代碼。

回顧一下你是如何在少於40行代碼中編寫可靠隊列的。

你必定很喜歡Spring Boot。

你在部署時節省的全部時間均可以專一於編碼

你驗證了應用程序的工做原理,如今是時候部署它了。

此時,你能夠啓動VPS,安裝Tomcat,並花些時間製做自定義腳原本測試,構建,打包和部署應用程序。

或者你能夠編寫你但願擁有的描述:一個消息代理和兩個使用負載均衡器部署的應用程序。

諸如Kubernetes之類的編排器能夠閱讀你的願望清單並提供正確的基礎設施。

因爲花在基礎架構上的時間減小意味着更多的時間編碼,此次你將把應用程序部署到Kubernetes。但在開始以前,你須要一個Kubernetes集羣。

你能夠註冊Google雲平臺或Azure,並使用Kubernetes提供的雲提供商服務。或者,你能夠在將應用程序移動到雲上以前在本地嘗試Kubernetes。

minikube是一個打包爲虛擬機的本地Kubernetes集羣。若是你使用的是Windows,Linux和Mac,那就太好了,由於建立羣集須要五分鐘。

你還應該安裝kubectl,即鏈接到你的羣集的客戶端。

你能夠從官方文檔中找到有關如何安裝minikube和kubectl的說明。

若是你在Windows上運行,則應查看有關如何安裝Kubernetes和Docker的詳細指南。

你應該啓動一個具備8GB RAM和一些額外配置的集羣:

minikube start --memory 8096 --extra-config=controller-manager.horizontal-pod-autoscaler-upscale-delay=1m --extra-config=controller-manager.horizontal-pod-autoscaler-downscale-delay=2m --extra-config=controller-manager.horizontal-pod-autoscaler-sync-period=10s複製代碼

請注意,若是你使用的是預先存在的minikube實例,則能夠經過銷燬VM來從新調整VM的大小。 只需添加--memory 8096就不會有任何影響。

驗證安裝是否成功。 你應該看到以列表形式展現的一些資源。 集羣已經準備就緒,也許你應該當即開始部署?

還不行。

你必須先裝好你的東西。

什麼比uber-jar更好?容器

部署到Kubernetes的應用程序必須打包爲容器。畢竟,Kubernetes是一個容器編排器,因此它自己沒法運行你的jar。

容器相似於fat jar:它們包含運行應用程序所需的全部依賴項。甚至JVM也是容器的一部分。因此他們在技術上是一個更胖的fat-jar。

將應用程序打包爲容器的流行技術是Docker。

雖然Docker是最受歡迎的,但它並非惟一可以運行容器的技術。其餘受歡迎的選項包括rkt和lxd。

若是你沒有安裝Docker,能夠按照Docker官方網站上的說明進行操做。

一般,你構建容器並將它們推送到倉庫。它相似於向Artifactory或Nexus推送jar包。但在這種特殊狀況下,你將在本地工做並跳過倉庫部分。實際上,你將直接在minikube中建立容器鏡像。

首先,按照此命令打印的說明將Docker客戶端鏈接到minikube:

minikube docker-env複製代碼

請注意,若是切換終端,則須要從新鏈接minikube內的Docker守護程序。 每次使用不一樣的終端時都應遵循相同的說明。

並從項目的根目錄構建容器鏡像:

docker build -t spring-k8s-hp0a .複製代碼

你能夠驗證鏡像是否已構建並準備好運行:

docker images |grep spring 複製代碼

很好。

羣集已準備好,你打包應用程序,也許你已準備好當即部署?

是的,你最終能夠要求Kubernetes部署應用程序。

將你的應用程序部署到Kubernetes

你的應用程序有三個組件:

  • 呈現前端的Spring Boot應用程序
  • ActiveMQ做爲消息代理
  • 處理事務的Spring Boot後端

你應該分別部署這三個組件。

對於每一個組件你都應該建立:

  • Deployment對象,描述部署的容器及其配置
  • 一個Service對象,充當Deployment部署建立的應用程序的全部實例的負載均衡器

部署中的每一個應用程序實例都稱爲Pod

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

部署ActiveMQ

讓咱們從ActiveMQ開始吧。

你應該建立一個activemq-deployment.yaml文件,其中包含如下內容:

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

name: queue

spec:

replicas: 1

template:

metadata:

labels:

app: queue

spec:

containers:

- name: web

image: webcenter/activemq:5.14.3

imagePullPolicy: IfNotPresent

ports:

- containerPort: 61616

resources:

limits:

memory: 512Mi

該模板冗長但直接易讀:

  • 你從名爲webcenter / activemq的官方倉庫中請求了一個activemq容器
  • 容器在端口61616上公開消息代理
  • 爲容器分配了512MB的內存
  • 你要求提供單個副本 - 你的應用程序的單個實例

使用如下內容建立activemq-service.yaml文件:

apiVersion: v1kind: Servicemetadata:name: queuespec:ports:- port: 61616 targetPort: 61616selector:app: queue複製代碼

幸運的是,這個模板更短!

這個yaml表示:

  • 你建立了一個公開端口61616的負載均衡器
  • 傳入流量分發到全部具備app:queue類型標籤的Pod(請參閱上面的部署)
  • targetPort是Pod暴露的端口

你可使用如下命令建立資源:

kubectl create -f activemq-deployment.yamlkubectl create -f activemq-service.yaml複製代碼

你可使用如下命令驗證數據庫的一個實例是否正在運行:

kubectl get pods -l=app=queue複製代碼

部署前端

使用如下內容建立fe-deployment.yaml文件:

apiVersion: extensions/v1beta1kind: Deploymentmetadata:name: frontendspec:replicas: 1template:metadata: labels: app: frontendspec: containers: - name: frontend image: spring-boot-hpa imagePullPolicy: IfNotPresent env: - name: ACTIVEMQ_BROKER_URL value: "tcp://queue:61616" - name: STORE_ENABLED value: "true" - name: WORKER_ENABLED value: "false" ports: - containerPort: 8080 livenessProbe: initialDelaySeconds: 5 periodSeconds: 5 httpGet: path: /health port: 8080 resources: limits: memory: 512Mi複製代碼

Deployment 看起來很像前一個。

可是有一些新的字段:

  • 有一個section能夠注入環境變量
  • 還有Liveness探針,能夠告訴你應用程序什麼時候能夠接受流量

使用如下內容建立fe-service.yaml文件:

apiVersion: v1kind: Servicemetadata:name: frontendspec:ports:- nodePort: 32000port: 80targetPort: 8080selector:app: frontendtype: NodePort複製代碼

你可使用如下命令建立資源:

kubectl create -f fe-deployment.yamlkubectl create -f fe-service.yaml複製代碼

你可使用如下命令驗證前端應用程序的一個實例是否正在運行:

kubectl get pods -l=app=frontend複製代碼

部署後端

使用如下內容建立backend-deployment.yaml文件:

apiVersion: extensions/v1beta1kind: Deploymentmetadata:name: backendspec:replicas: 1template:metadata: labels: app: backend annotations: prometheus.io/scrape: 'true'spec: containers: - name: backend image: spring-boot-hpa imagePullPolicy: IfNotPresent env: - name: ACTIVEMQ_BROKER_URL value: "tcp://queue:61616" - name: STORE_ENABLED value: "false" - name: WORKER_ENABLED value: "true" ports: - containerPort: 8080 livenessProbe: initialDelaySeconds: 5 periodSeconds: 5 httpGet: path: /health port: 8080 resources: limits: memory: 512Mi複製代碼

使用如下內容建立backend-service.yaml文件:

apiVersion: v1kind: Servicemetadata:name: backendspec:ports:- nodePort: 31000 port: 80 targetPort: 8080selector: app: backendtype: NodePort複製代碼

你可使用如下命令建立資源:

kubectl create -f backend-deployment.yamlkubectl create -f backend-service.yaml複製代碼

你能夠驗證後端的一個實例是否正在運行:

kubectl get pods -l=app=backend複製代碼

部署完成。

它真的有效嗎?

你可使用如下命令在瀏覽器中訪問該應用程序:

minikube service backend複製代碼

minikube service frontend複製代碼

若是它有效,你應該嘗試購買一些物品!

工做者在處理交易嗎?

是的,若是有足夠的時間,工做人員將處理全部待處理的消息。

恭喜!

你剛剛將應用程序部署到Kubernetes!

手動擴展以知足不斷增加的需求

單個工做程序可能沒法處理大量消息。 實際上,它當時只能處理一條消息。

若是你決定購買數千件物品,則須要數小時才能清除隊列。

此時你有兩個選擇:

  • 你能夠手動放大和縮小
  • 你能夠建立自動縮放規則以自動向上或向下擴展

讓咱們先從基礎知識開始。

你可使用如下方法將後端擴展爲三個實例:

kubectl scale --replicas=5 deployment/backend複製代碼

你能夠驗證Kubernetes是否建立了另外五個實例:

kubectl get pods複製代碼

而且應用程序能夠處理五倍以上的消息。

一旦工人排空隊列,你能夠縮小:

kubectl scale --replicas=1 deployment/backend複製代碼

若是你知道最多的流量什麼時候達到你的服務,手動擴大和縮小都很棒。

若是不這樣作,設置自動縮放器容許應用程序自動縮放而無需手動干預。

你只須要定義一些規則。

公開應用程序指標

Kubernetes如何知道什麼時候擴展你的申請?

很簡單,你必須告訴它。

自動調節器經過監控指標來工做。 只有這樣,它才能增長或減小應用程序的實例。

所以,你能夠將隊列長度公開爲度量標準,並要求autoscaler觀察該值。 隊列中的待處理消息越多,Kubernetes將建立的應用程序實例就越多。

那麼你如何公開這些指標呢?

應用程序具備 /metrics 端點以顯示隊列中的消息數。 若是你嘗試訪問該頁面,你會注意到如下內容:

# HELP messages Number of messages in the queue# TYPE messages gaugemessages 0複製代碼

應用程序不會將指標公開爲JSON格式。 格式爲純文本,是公開Prometheus指標的標準。 不要擔憂記憶格式。 大多數狀況下,你將使用其中一個Prometheus客戶端庫。

在Kubernetes中使用應用程序指標

你幾乎已準備好進行自動縮放——但你應首先安裝度量服務器。 實際上,默認狀況下,Kubernetes不會從你的應用程序中提取指標。 若是你願意,能夠啓用Custom Metrics API。

要安裝Custom Metrics API,你還須要Prometheus - 時間序列數據庫。 安裝Custom Metrics API所需的全部文件均可以方便地打包在learnk8s / spring-boot-k8s-hpa中。

你應下載該存儲庫的內容,並將當前目錄更改成該項目的monitoring文件夾。

cd spring-boot-k8s-hpa/monitoring複製代碼

從那裏,你能夠建立自定義指標API:

kubectl create -f ./metrics-serverkubectl create -f ./namespaces.yamlkubectl create -f ./prometheuskubectl create -f ./custom-metrics-api複製代碼

你應該等到如下命令返回自定義指標列表:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .複製代碼

任務完成!

你已準備好使用指標。

實際上,你應該已經找到了隊列中消息數量的自定義指標:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/messages" | jq .複製代碼

恭喜,你有一個公開指標的應用程序和使用它們的指標服務器。

你最終能夠啓用自動縮放器!

在Kubernetes中進行自動擴展部署

Kubernetes有一個名爲Horizontal Pod Autoscaler的對象,用於監視部署並上下調整Pod的數量。

你將須要其中一個來自動擴展實例。

你應該建立一個包含如下內容的hpa.yaml文件:

apiVersion: autoscaling/v2beta1kind: HorizontalPodAutoscalermetadata:name: spring-boot-hpaspec:scaleTargetRef:apiVersion: extensions/v1beta1kind: Deploymentname: backend minReplicas: 1maxReplicas: 10metrics:- type: Podspods: metricName: messages targetAverageValue: 10複製代碼

這個文件很神祕,因此讓我爲你翻譯一下:

  • Kubernetes監視scaleTargetRef中指定的部署。 在這種狀況下,它是工人。
  • 你正在使用messages指標來擴展你的Pod。 當隊列中有超過十條消息時,Kubernetes將觸發自動擴展。
  • 至少,部署應該有兩個Pod。 10個Pod是上限。

你可使用如下命令建立資源:

kubectl create -f hpa.yaml複製代碼

提交自動縮放器後,你應該注意到後端的副本數量是兩個。 這是有道理的,由於你要求自動縮放器始終至少運行兩個副本。

你能夠檢查觸發自動縮放器的條件以及由此產生的事件:

kubectl describe hpa複製代碼

自動定標器表示它可以將Pod擴展到2,而且它已準備好監視部署。

使人興奮的東西,但它有效嗎?

負載測試

只有一種方法能夠知道它是否有效:在隊列中建立大量消息。

轉到前端應用程序並開始添加大量消息。 在添加消息時,使用如下方法監視Horizontal Pod Autoscaler的狀態:

kubectl describe hpa複製代碼

Pod的數量從2上升到4,而後是8,最後是10。

該應用程序隨消息數量而變化!

你剛剛部署了一個徹底可伸縮的應用程序,可根據隊列中的待處理消息數進行擴展。

另外,縮放算法以下:

MAX(CURRENT_REPLICAS_LENGTH * 2, 4)複製代碼

在解釋算法時,文檔沒有多大幫助。 你能夠在代碼中找到詳細信息。

此外,每分鐘都會從新評估每一個放大,而每兩分鐘縮小一次。

以上全部都是能夠調整的設置。

可是你尚未完成。

什麼比自動縮放實例更好? 自動縮放羣集。

跨節點縮放Pod很是有效。 可是,若是羣集中沒有足夠的容量來擴展Pod,該怎麼辦?

若是達到峯值容量,Kubernetes將使Pods處於暫掛狀態並等待更多資源可用。

若是你可使用相似於Horizontal Pod Autoscaler的自動縮放器,但對於節點則會很棒。

好消息!

你能夠擁有一個羣集自動縮放器,能夠在你須要更多資源時爲Kubernetes羣集添加更多節點。

如何使用消息隊列,Spring Boot和Kubernetes擴展微服務

羣集自動縮放器具備不一樣的形狀和大小。它也是特定於雲提供商的。

請注意,你將沒法使用minikube測試自動縮放器,由於它根據定義是單節點。

你能夠在Github上找到有關集羣自動調節器和雲提供程序實現的更多信息。

概覽

設計大規模應用程序須要仔細規劃和測試。

基於隊列的體系結構是一種出色的設計模式,能夠解耦你的微服務並確保它們能夠獨立擴展和部署。

雖然你能夠用腳原本擴展你的應用,但能夠更輕鬆地利用容器編排器(如Kubernetes)自動部署和擴展應用程序。



獲取更多Java高級架構最新視頻, 加入Java進階架構交流羣:142019080。直接點擊連接加羣。https://jq.qq.com/?_wv=1027&k=5lXBNZ7

相關文章
相關標籤/搜索