本文經過一個詳細的購物例子,展現瞭如何利用消息隊列,Spring Boot和Kubernetes進行微服務的開發,並闡述了針對微服務的伸縮,監控等方式,幫助用戶快速利用這些工具開發健壯的系統。前端
當你設計和構建大規模應用時,你將面臨兩個重大挑戰:可伸縮性和健壯性。node
你應該這樣設計你的服務,即便它受到間歇性的重負載,它仍能可靠地運行。web
以Apple Store爲例。算法
每一年都有數百萬的Apple客戶預先註冊購買新的iPhone。spring
這是數百萬人同時購買物品。docker
若是你要將Apple商店的流量描述爲每秒的請求數量,那麼它多是下圖的樣子:數據庫
如今想象一下,你的任務是構建這樣的應用程序。後端
你正在創建一個商店,用戶能夠在那裏購買本身喜歡的商品。設計模式
你構建一個微服務來呈現網頁並提供靜態資產。 你還構建了一個後端REST API來處理傳入的請求。api
你但願將兩個組件分開,由於這樣可使用相同的REST API,爲網站和移動應用程序提供服務。
今天是重要的一天,你的商店上線了。
你決定將應用程序擴展爲前端四個實例和後端四個實例,由於你預測網站比日常更繁忙。
你開始接收愈來愈多的流量。
前端服務正在很好得處理流量。 可是你注意到鏈接到數據庫的後端正在努力跟上事務的數量。
不用擔憂,你能夠將後端的副本數量擴展到8。
你收到的流量更多,後端沒法應對。
一些服務開始丟棄鏈接。 憤怒的客戶與你的客服取得聯繫。 而如今你被淹沒在大量流量中。
你的後端沒法應付它,它會失去不少鏈接。
你剛丟了一大筆錢,你的顧客也不高興。
你的應用程序並無設計得健壯且高可用:
失去事務意味着收入損失。
你能夠從新設計架構,以便將前端和後端用隊列分離。
前端將消息發佈到隊列,然後端則一次處理一個待處理消息。
新架構有一些明顯的好處:
如何構建這樣的應用程序?
你如何設計可處理數十萬個請求的服務? 你如何部署動態擴展的應用程序?
在深刻了解部署和擴展的細節以前,讓咱們關注應用程序。
該服務有三個組件:前端,後端和消息代理。
前端是一個簡單的Spring Boot Web應用程序,帶有Thymeleaf模板引擎。
後端是一個消耗隊列消息的工做者。
因爲Spring Boot與JSM能出色得集成,所以你可使用它來發送和接收異步消息。
你能夠在learnk8s / spring-boot-k8s-hpa中找到一個鏈接到JSM的前端和後端應用程序的示例項目。
請注意,該應用程序是用Java 10編寫的,以利用改進的Docker容器集成能力。
只有一個代碼庫,你能夠將項目配置爲做爲前端或後端運行。
你應該知道該應用程序具備:
該應用程序能夠在兩種模式下運行:
做爲前端,應用程序呈現人們能夠購買物品的網頁。
做爲工做者,應用程序等待隊列中的消息並處理它們。
請注意,在示例項目中,使用Thread.sleep(5000)等待五秒鐘來模擬處理。
你能夠經過更改application.yaml中的值來在任一模式下配置應用程序。
默認狀況下,應用程序做爲前端和工做程序啓動。
你能夠運行該應用程序,只要你在本地運行ActiveMQ實例,你就應該可以購買物品並讓系統處理這些物品。
若是檢查日誌,則應該看到工做程序處理項目。
它確實工做了!編寫Spring Boot應用程序很容易。
一個更有趣的主題是學習如何將Spring Boot鏈接到消息代理。
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就不會有任何影響。
驗證安裝是否成功。 你應該看到以列表形式展現的一些資源。 集羣已經準備就緒,也許你應該當即開始部署?
還不行。
你必須先裝好你的東西。
部署到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部署應用程序。
你的應用程序有三個組件:
你應該分別部署這三個組件。
對於每一個組件你都應該建立:
部署中的每一個應用程序實例都稱爲Pod。
讓咱們從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
該模板冗長但直接易讀:
使用如下內容建立activemq-service.yaml文件:
apiVersion: v1kind: Servicemetadata:name: queuespec:ports:- port: 61616 targetPort: 61616selector:app: queue複製代碼
幸運的是,這個模板更短!
這個yaml表示:
你可使用如下命令建立資源:
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 看起來很像前一個。
可是有一些新的字段:
使用如下內容建立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有一個名爲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複製代碼
這個文件很神祕,因此讓我爲你翻譯一下:
你可使用如下命令建立資源:
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羣集添加更多節點。
羣集自動縮放器具備不一樣的形狀和大小。它也是特定於雲提供商的。
請注意,你將沒法使用minikube測試自動縮放器,由於它根據定義是單節點。
你能夠在Github上找到有關集羣自動調節器和雲提供程序實現的更多信息。
設計大規模應用程序須要仔細規劃和測試。
基於隊列的體系結構是一種出色的設計模式,能夠解耦你的微服務並確保它們能夠獨立擴展和部署。
雖然你能夠用腳原本擴展你的應用,但能夠更輕鬆地利用容器編排器(如Kubernetes)自動部署和擴展應用程序。
獲取更多Java高級架構最新視頻, 加入Java進階架構交流羣:142019080。直接點擊連接加羣。https://jq.qq.com/?_wv=1027&k=5lXBNZ7