從零開始學Python | 使用 gRPC 的 Python 微服務 II 從零開始學python | 使用 gRPC 的 Python 微服務 I
從零開始學python | 使用 gRPC 的 Python 微服務 I
生產就緒的 Python 微服務
此時,您的開發機器上運行了一個 Python 微服務架構,非常適合測試。在本節中,您將使其在云中運行。
碼頭工人
Docker是一項了不起的技術,它可以讓您將一組進程與同一臺機器上的其他進程隔離開來。您可以擁有兩組或更多組具有自己的文件系統、網絡端口等的進程。你可以把它想象成一個 Python 虛擬環境,而且對于整個系統來說更加安全。
Docker 非常適合部署 Python 微服務,因為您可以打包所有依賴項并在隔離的環境中運行微服務。當您將微服務部署到云時,它可以與其他微服務在同一臺機器上運行,而不會相互影響。這樣可以更好地利用資源。
本教程不會深入探討 Docker,因為它需要一整本書才能涵蓋。相反,您只需掌握將 Python 微服務部署到云所需的基礎知識。有關 Docker 的更多信息,您可以查看Python Docker 教程。
在開始之前,如果您想在您的機器上進行操作,請確保已安裝 Docker。您可以從官方網站下載。
您將創建兩個 Docker鏡像,一個用于 Marketplace 微服務,另一個用于 Recommendations 微服務。圖像基本上是一個文件系統加上一些元數據。本質上,您的每個微服務都將擁有一個自己的迷你 Linux 環境。它可以在不影響實際文件系統的情況下寫入文件,并在不與其他進程沖突的情況下打開端口。
要創建圖像,您需要定義一個Dockerfile.?您總是從一個包含一些基本內容的基本圖像開始。在這種情況下,您的基本映像將包含一個 Python 解釋器。然后將文件從開發機器復制到 Docker 映像中。您還可以在 Docker 映像中運行命令。這對于安裝依賴項很有用。
您將首先創建 Recommendations 微服務 Docker 映像。創建recommendations/Dockerfile并添加以下內容:
1FROM python 2 3RUN mkdir /service 4COPY protobufs/ /service/protobufs/ 5COPY recommendations/ /service/recommendations/ 6WORKDIR /service/recommendations 7RUN python -m pip install --upgrade pip 8RUN python -m pip install -r requirements.txt 9RUN python -m grpc_tools.protoc -I ../protobufs --python_out=. \ 10 --grpc_python_out=. ../protobufs/recommendations.proto 11 12EXPOSE 50051 13ENTRYPOINT [ "python", "recommendations.py" ]
這是逐行演練:
第 1 行使用基本的 Linux 環境和最新版本的 Python 初始化您的映像。此時,您的映像具有典型的 Linux 文件系統布局。如果你要進去看看,那就得/bin,/home以及所有你所期望的基本文件。
第 3 行創建了一個新目錄 at/service以包含您的微服務代碼。
第 4 行和第 5行將protobufs/和recommendations/目錄復制到/service.
第 6 行給了 Docker 一條WORKDIR /service/recommendations指令,這有點像cd在鏡像里面做一個。您提供給 Docker 的任何路徑都將相對于該位置,并且當您運行命令時,它將在該目錄中運行。
第 7 行更新pip以避免有關舊版本的警告。
第 8 行告訴 Dockerpip install -r requirements.txt在鏡像內運行。這會將所有grpcio-tools文件以及您可能添加的任何其他包添加到映像中。請注意,您沒有使用虛擬環境,因為它是不必要的。此映像中唯一運行的將是您的微服務,因此您無需進一步隔離其環境。
第 9行運行python -m grpc_tools.protoc命令從 protobuf 文件生成 Python 文件。您/service在圖像中的目錄現在看起來像這樣:
/service/ | ├── protobufs/ │ └── recommendations.proto | └── recommendations/ ├── recommendations.py ├── recommendations_pb2.py ├── recommendations_pb2_grpc.py └── requirements.txt
第 12 行告訴 Docker 你將在 port 上運行一個微服務50051,并且你想把它暴露在鏡像之外。
第 13 行告訴 Docker 如何運行你的微服務。
現在您可以從您的Dockerfile.?從包含所有代碼的目錄中運行以下命令 - 不是在recommendations/目錄中,而是在其上一級:
$ docker build . -f recommendations/Dockerfile -t recommendations
這將為 Recommendations 微服務構建 Docker 映像。當 Docker 構建映像時,您應該會看到一些輸出。現在你可以運行它:
$ docker run -p 127.0.0.1:50051:50051/tcp recommendations
您不會看到任何輸出,但您的 Recommendations 微服務現在正在 Docker 容器中運行。當你運行一個鏡像時,你會得到一個容器。您可以多次運行該映像以獲得多個容器,但仍然只有一個映像。
該-p 127.0.0.1:50051:50051/tcp選項告訴 Docker 將機器端口上的TCP 連接轉發50051到50051容器內的端口。這使您可以靈活地轉發機器上的不同端口。
例如,如果您運行的兩個容器都在 port 上運行 Python 微服務50051,那么您將需要在主機上使用兩個不同的端口。這是因為兩個進程不能同時打開同一個端口,除非它們在不同的容器中。
接下來,您將構建您的 Marketplace 映像。創建marketplace/Dockerfile并添加以下內容:
1FROM python 2 3RUN mkdir /service 4COPY protobufs/ /service/protobufs/ 5COPY marketplace/ /service/marketplace/ 6WORKDIR /service/marketplace 7RUN python -m pip install --upgrade pip 8RUN python -m pip install -r requirements.txt 9RUN python -m grpc_tools.protoc -I ../protobufs --python_out=. \ 10 --grpc_python_out=. ../protobufs/recommendations.proto 11 12EXPOSE 5000 13ENV FLASK_APP=marketplace.py 14ENTRYPOINT [ "flask", "run", "--host=0.0.0.0"]
這與 Recommendations 非常相似,但Dockerfile有一些不同:
第 13 行用于ENV FLASK_APP=marketplace.py設置FLASK_APP圖像內部的環境變量。Flask 需要這個來運行。
第 14 行添加--host=0.0.0.0到flask run命令中。如果你不添加這個,那么 Flask 將只接受來自 localhost 的連接。
但是等等,你不是還在運行所有東西localhost嗎?嗯,不是真的。當您運行 Docker 容器時,默認情況下它與您的主機是隔離的。localhost容器內部與localhost外部不同,即使在同一臺機器上。這就是為什么你需要告訴 Flask 接受來自任何地方的連接。
繼續并打開一個新終端。您可以使用以下命令構建您的 Marketplace 映像:
$ docker build . -f marketplace/Dockerfile -t marketplace
這將創建 Marketplace 圖像。您現在可以使用以下命令在容器中運行它:
$ docker run -p 127.0.0.1:5000:5000/tcp marketplace
您不會看到任何輸出,但您的 Marketplace 微服務正在運行。
聯網
不幸的是,即使您的 Recommendations 和 Marketplace 容器都在運行,如果您現在http://localhost:5000在瀏覽器中訪問,您會收到錯誤消息。您可以連接到 Marketplace 微服務,但它無法再連接到 Recommendations 微服務。容器是隔離的。
幸運的是,Docker 提供了一個解決方案。您可以創建一個虛擬網絡并將兩個容器添加到其中。您還可以為他們提供 DNS 名稱,以便他們可以找到彼此。
下面,您將創建一個名為的網絡microservices并在其上運行 Recommendations 微服務。您還將為其指定 DNS 名稱recommendations。首先,使用Ctrl+C停止當前正在運行的容器。然后運行以下命令:
$ docker network create microservices $ docker run -p 127.0.0.1:50051:50051/tcp --network microservices \ --name recommendations recommendations
該docker network create命令創建網絡。您只需要執行一次,然后就可以將多個容器連接到它。然后添加??network microservices到docker run命令以在此網絡上啟動容器。該??name recommendations選項為其提供了 DNS 名稱recommendations。
在重新啟動市場容器之前,您需要更改代碼。這是因為您localhost:50051在以下行中進行了硬編碼marketplace.py:
recommendations_channel = grpc.insecure_channel("localhost:50051")
現在您想要連接到recommendations:50051。但是,您可以從環境變量中加載它,而不是再次對其進行硬編碼。用以下兩行替換上面的行:
recommendations_host = os.getenv("RECOMMENDATIONS_HOST", "localhost") recommendations_channel = grpc.insecure_channel( f"{recommendations_host}:50051" )
這將在環境變量中加載 Recommendations 微服務的主機名RECOMMENDATIONS_HOST。如果未設置,則可以將其默認為localhost.?這允許您直接在您的機器上或在容器內運行相同的代碼。
由于您更改了代碼,因此您需要重建市場映像。然后嘗試在您的網絡上運行它:
$ docker build . -f marketplace/Dockerfile -t marketplace $ docker run -p 127.0.0.1:5000:5000/tcp --network microservices \ -e RECOMMENDATIONS_HOST=recommendations marketplace
這與您之前的運行方式類似,但有兩個不同之處:
您添加了??network microservices在與您的 Recommendations 微服務相同的網絡上運行它的選項。您沒有添加??name選項,因為與 Recommendations 微服務不同,不需要查找 Marketplace 微服務的 IP 地址。提供的端口轉發-p 127.0.0.1:5000:5000/tcp就足夠了,不需要DNS名稱。
您添加了-e RECOMMENDATIONS_HOST=recommendations,它在容器內設置環境變量。這就是將 Recommendations 微服務的主機名傳遞給代碼的方式。
此時,您可以再次localhost:5000在瀏覽器中嘗試,它應該可以正確加載。哈扎!
Docker 撰寫
使用 Docker 可以完成所有這些工作真是太神奇了,但它有點乏味。如果您可以運行一個命令來啟動所有容器,那就太好了。幸運的是有!它被稱為docker-compose,它是 Docker 項目的一部分。
您可以在 YAML 文件中聲明您的微服務,而不是運行一堆命令來構建圖像、創建網絡和運行容器:
1version: "3.8" 2services: 3 4 marketplace: 5 build: 6 context: . 7 dockerfile: marketplace/Dockerfile 8 environment: 9 RECOMMENDATIONS_HOST: recommendations 10 image: marketplace 11 networks: 12 - microservices 13 ports: 14 - 5000:5000 15 16 recommendations: 17 build: 18 context: . 19 dockerfile: recommendations/Dockerfile 20 image: recommendations 21 networks: 22 - microservices 23 24networks: 25 microservices:
通常,您將其放入名為docker-compose.yaml.?將其放在項目的根目錄中:
. ├── marketplace/ │ ├── marketplace.py │ ├── requirements.txt │ └── templates/ │ └── homepage.html | ├── protobufs/ │ └── recommendations.proto | ├── recommendations/ │ ├── recommendations.py │ ├── recommendations_pb2.py │ ├── recommendations_pb2_grpc.py │ └── requirements.txt │ └── docker-compose.yaml
本教程不會詳細介紹語法,因為它在其他地方有很好的文檔記錄。它實際上只是做與您已經手動完成的相同的事情。但是,現在您只需要運行一個命令來啟動您的網絡和容器:
$ docker-compose up
運行后,您應該可以再次localhost:5000在瀏覽器中打開,并且一切正常。
請注意,當容器與 Marketplace 微服務位于同一網絡中時,您不需要50051在recommendations容器中公開,因此您可以刪除該部分。
注意:使用 開發時docker-compose,如果您更改了任何文件,請運行docker-compose build以重建映像。如果您運行docker-compose up,它將使用舊圖像,這可能會令人困惑。
如果您想docker-compose在向上移動之前停下來進行一些編輯,請按Ctrl+C。
測試
要對您的 Python 微服務進行單元測試,您可以實例化您的微服務類并調用其方法。以下是您的RecommendationService實現的基本示例測試:
1# recommendations/recommendations_test.py 2from recommendations import RecommendationService 3 4from recommendations_pb2 import BookCategory, RecommendationRequest 5 6def test_recommendations(): 7 service = RecommendationService() 8 request = RecommendationRequest( 9 user_id=1, category=BookCategory.MYSTERY, max_results=1 10 ) 11 response = service.Recommend(request, None) 12 assert len(response.recommendations) == 1
這是一個細分:
第 6 行像其他任何類一樣實例化該類并在其上調用方法。
第 11 行傳遞None上下文,只要您不使用它,它就可以工作。如果要測試使用上下文的代碼路徑,則可以模擬它。
集成測試涉及使用多個未模擬的微服務運行自動化測試。所以這有點復雜,但不是太難。添加marketplace/marketplace_integration_test.py文件:
from urllib.request import urlopen def test_render_homepage(): homepage_html = urlopen("http://localhost:5000").read().decode("utf-8") assert "
這會向主頁 URL 發出 HTTP 請求,并檢查它是否返回了一些帶有標題和三個
那么你如何運行這種類型的測試呢?幸運的是,Docker 的好人也提供了一種方法來做到這一點。使用 運行 Python 微服務后docker-compose,您可以使用docker-compose exec.?因此,如果您想在marketplace容器內運行集成測試,可以運行以下命令:
$ docker-compose build $ docker-compose up $ docker-compose exec marketplace pytest marketplace_integration_test.py
這將pytest在marketplace容器內運行命令。由于您的集成測試連接到localhost,您需要在與微服務相同的容器中運行它。
部署到 Kubernetes
偉大的!您現在有幾個微服務在您的計算機上運行。您可以快速啟動它們并對它們運行集成測試。但是您需要讓它們進入生產環境。為此,您將使用Kubernetes。
本教程不會深入介紹 Kubernetes,因為這是一個很大的主題,并且其他地方提供了全面的文檔和教程。但是,在本節中,您將找到將 Python 微服務遷移到云中的 Kubernetes 集群的基礎知識。
注意:要將 Docker 映像部署到云提供商,您需要將 Docker 映像推送到Docker Hub 之類的映像注冊表。
以下示例使用本教程中的鏡像,這些鏡像已經推送到 Docker Hub。如果你想改變它們,或者如果你想創建自己的微服務,那么你需要在 Docker Hub 上創建一個帳戶,以便你可以推送圖像。如果您愿意,您也可以創建私有注冊中心,或者使用其他注冊中心,例如Amazon 的 ECR。
您可以從kubernetes.yaml.?完整的文件有點長,但它由四個不同的部分組成,因此您將一一查看它們:
1--- 2apiVersion: apps/v1 3kind: Deployment 4metadata: 5 name: marketplace 6 labels: 7 app: marketplace 8spec: 9 replicas: 3 10 selector: 11 matchLabels: 12 app: marketplace 13 template: 14 metadata: 15 labels: 16 app: marketplace 17 spec: 18 containers: 19 - name: marketplace 20 image: hidan/python-microservices-article-marketplace:0.1 21 env: 22 - name: RECOMMENDATIONS_HOST 23 value: recommendations
這定義了Marketplace 微服務的部署。一個部署告訴Kubernetes如何部署你的代碼。Kubernetes 需要四個主要信息:
要部署什么 Docker 鏡像
部署多少個實例
微服務需要哪些環境變量
如何識別您的微服務
您可以使用標簽告訴 Kubernetes 如何識別您的微服務。雖然這里沒有顯示,但您也可以告訴 Kubernetes 您的微服務需要哪些內存和 CPU 資源。您可以在Kubernetes 文檔 中找到許多其他選項。
這是代碼中發生的事情:
第 9 行告訴 Kubernetes 為您的微服務創建多少個 pod。一個Pod基本上是一個隔離的執行環境,就像一個輕量級的虛擬機,實現為一組容器。設置replicas: 3為每個微服務提供三個 pod。擁有多個允許冗余,在不停機的情況下實現滾動更新,根據您需要的更多機器進行擴展,并在一臺機器出現故障時進行故障轉移。
第 20 行是要部署的 Docker 映像。您必須在映像注冊表上使用 Docker 映像。要在那里獲取您的映像,您必須將其推送到映像注冊表。當您在 Docker Hub 上登錄您的帳戶時,有關于如何執行此操作的說明。
Recommendations 微服務的部署非常相似:
24--- 25apiVersion: apps/v1 26kind: Deployment 27metadata: 28 name: recommendations 29 labels: 30 app: recommendations 31spec: 32 replicas: 3 33 selector: 34 matchLabels: 35 app: recommendations 36 template: 37 metadata: 38 labels: 39 app: recommendations 40 spec: 41 containers: 42 - name: recommendations 43 image: hidan/python-microservices-article-recommendations:0.1
主要區別在于一種使用名稱marketplace,另一種使用recommendations.?您還在部署RECOMMENDATIONS_HOST上設置環境變量,marketplace但不在recommendations部署上設置。
接下來,您為 Recommendations 微服務定義一個服務。部署告訴 Kubernetes 如何部署您的代碼,而服務則告訴它如何將請求路由到它。為避免與通常用于談論微服務的術語服務混淆,您會在引用 Kubernetes 服務時看到大寫這個詞。
這是 的服務定義recommendations:
44--- 45apiVersion: v1 46kind: Service 47metadata: 48 name: recommendations 49spec: 50 selector: 51 app: recommendations 52 ports: 53 - protocol: TCP 54 port: 50051 55 targetPort: 50051
這是定義中發生的事情:
第 48 行:當您創建服務時,Kubernetes 本質上會name在集群內創建一個相同的 DNS 主機名。因此,您集群中的任何微服務都可以向recommendations.?Kubernetes 會將此請求轉發到您的 Deployment 中的一個 pod。
第 51行:此行將服務連接到部署。它告訴 Kubernetes 將請求轉發到Deploymentrecommendations中的 Pod 之一recommendations。這必須匹配labelsDeployment中的鍵值對之一。
該marketplace服務是類似的:
56--- 57apiVersion: v1 58kind: Service 59metadata: 60 name: marketplace 61spec: 62 type: LoadBalancer 63 selector: 64 app: marketplace 65 ports: 66 - protocol: TCP 67 port: 5000 68 targetPort: 5000
除了名稱和端口之外,只有一個區別。您會注意到它type: LoadBalancer僅出現在marketplace服務中。這是因為marketplace需要從 Kubernetes 集群外部訪問,而recommendations只需要在集群內部訪問。
注意:在具有許多微服務的大型集群中,使用IngressService 比使用Service更常見LoadBalancer。如果您正在企業環境中開發微服務,那么這可能是您要走的路。
查看 Sandeep Dinesh 的文章Kubernetes NodePort vs LoadBalancer vs Ingress?我什么時候應該使用什么?了解更多信息。
您可以通過展開下面的框來查看完整的文件:
完整kubernetes.yaml代碼顯示隱藏
現在您有了 Kubernetes 配置,下一步就是部署它!
您通常使用云提供商部署 Kubernetes。您可以選擇許多云提供商,包括Google Kubernetes Engine (GKE)、Amazon Elastic Kubernetes Service (EKS)和DigitalOcean。
如果您在公司部署微服務,那么您使用的云提供商很可能由您的基礎設施決定。對于此演示,您將在本地運行 Kubernetes。幾乎一切都與使用云提供商相同。
如果您在 Mac 或 Windows 上運行 Docker Desktop,那么它帶有本地 Kubernetes 集群,您可以在“首選項”菜單中啟用該集群。通過單擊系統托盤中的 Docker 圖標打開 Preferences,然后找到 Kubernetes 部分并啟用它:
如果您在 Linux 上運行,那么您可以安裝minikube。按照起始頁上的說明進行設置。
創建集群后,您可以使用以下命令部署微服務:
$ kubectl apply -f kubernetes.yaml
如果您想嘗試在云中部署到 Kubernetes,DigitalOcean 的設置最簡單,并且具有簡單的定價模型。您可以注冊一個帳戶,然后單擊幾下即可創建 Kubernetes 集群。如果您將默認值更改為僅使用一個節點和最便宜的選項,那么在撰寫本文時,成本僅為每小時 0.015 美元。
按照 DigitalOcean 提供的說明下載配置文件kubectl并運行上述命令。然后,您可以單擊DigitalOcean 中的Kubernetes按鈕以查看在那里運行的服務。DigitalOcean 將為您的LoadBalancer服務分配一個 IP 地址,因此您可以通過將該 IP 地址復制到您的瀏覽器來訪問您的 Marketplace 應用程序。
重要提示:完成后,請銷毀您的集群,這樣您就不會繼續為此付費。您還應該轉到 Networking 選項卡并銷毀 Load Balancer,它與集群分開但也會產生費用。
到 Kubernetes 的部署到此結束。接下來,您將學習如何監控 Python 微服務。
使用-進行 Python 微服務監控
一旦您在云中擁有一些微服務,您就希望了解它們的運行情況。您要監控的一些內容包括:
每個微服務收到多少請求
有多少請求導致錯誤,以及它們引發什么類型的錯誤
每個請求的延遲
異常日志,以便您可以稍后進行調試
您將在以下各節中了解執行此操作的幾種方法。
為什么不是裝飾器
一種可以做到這一點的方法,也是 Python 開發人員最自然的方法,就是為每個微服務端點添加一個裝飾器。但是,在這種情況下,使用裝飾器有幾個缺點:
新微服務的開發人員必須記住將它們添加到每個方法中。
如果你有很多監控,那么你最終可能會得到一堆裝飾器。
如果您有一堆裝飾器,那么開發人員可能會以錯誤的順序堆疊它們。
您可以將所有監控合并到一個裝飾器中,但這樣可能會變得混亂。
這堆裝飾器是你想要避免的:
1class RecommendationService(recommendations_pb2_grpc.RecommendationsServicer): 2 @catch_and_log_exceptions 3 @log_request_counts 4 @log_latency 5 def Recommend(self, request, context): 6 ...
在每個方法上都有這堆裝飾器是丑陋和重復的,它違反了DRY 編程原則:不要重復自己。裝飾器也是寫作的挑戰,尤其是當他們接受參數時。
-
您將在本教程中采用另一種使用裝飾器的方法:gRPC 有一個-概念,它提供類似于裝飾器的功能,但方式更簡潔。
將它添加到您的recommendations/requirements.txtwith 中pytest,您將很快使用它:
grpc-interceptor ~= 0.12.0 grpcio-tools ~= 1.30 pytest ~= 5.4
然后更新您的虛擬環境:
$ python -m pip install recommendations/requirements.txt
您現在可以使用以下代碼創建-。您不需要將此添加到您的項目中,因為它只是一個示例:
1from grpc_interceptor import ServerInterceptor 2 3class ErrorLogger(ServerInterceptor): 4 def intercept(self, method, request, context, method_name): 5 try: 6 return method(request, context) 7 except Exception as e: 8 self.log_error(e) 9 raise 10 11 def log_error(self, e: Exception) -> None: 12 # ...
log_error()每當調用微服務中未處理的異常時,它就會調用。例如,您可以通過將異常記錄到Sentry來實現這一點,以便在它們發生時獲得警報和調試信息。
要使用這個-,你可以grpc.server()像這樣傳遞它:
interceptors = [ErrorLogger()] server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), interceptors=interceptors)
使用此代碼,Python 微服務的每個請求和響應都將通過您的-,因此您可以計算它收到的請求和錯誤的數量。
grpc-interceptor還為每個 gRPC 狀態代碼和一個名為ExceptionToStatusInterceptor.?如果微服務引發異常之一,ExceptionToStatusInterceptor則將設置 gRPC 狀態代碼。這允許您通過將下面突出顯示的更改更改為以下內容來簡化您的微服務recommendations/recommendations.py:
1from grpc_interceptor import ExceptionToStatusInterceptor 2from grpc_interceptor.exceptions import NotFound 3 4# ... 5 6class RecommendationService(recommendations_pb2_grpc.RecommendationsServicer): 7 def Recommend(self, request, context): 8 if request.category not in books_by_category: 9 raise NotFound("Category not found") 10 11 books_for_category = books_by_category[request.category] 12 num_results = min(request.max_results, len(books_for_category)) 13 books_to_recommend = random.sample(books_for_category, num_results) 14 15 return RecommendationResponse(recommendations=books_to_recommend) 16 17def serve(): 18 interceptors = [ExceptionToStatusInterceptor()] 19 server = grpc.server( 20 futures.ThreadPoolExecutor(max_workers=10), 21 interceptors=interceptors 22 ) 23 # ...
這更具可讀性。您還可以從調用堆棧中的許多函數中引發異常,而不必傳遞context這樣您就可以調用context.abort().?您也不必自己在微服務中捕獲異常——-會為您捕獲它。
如果你想編寫自己的-,那么你應該測試它們。但是在測試-之類的東西時模擬太多是危險的。例如,您可以調用.intercept()測試并確保它返回您想要的內容,但這不會測試真實的輸入,甚至根本不會調用它們。
為了改進測試,您可以使用-運行 gRPC 微服務。該grpc-interceptor包提供了一個框架來做到這一點。下面,您將為ErrorLogger-編寫一個測試。這只是一個示例,因此您無需將其添加到您的項目中。如果您要添加它,那么您會將其添加到測試文件中。
以下是為-編寫測試的方法:
1from grpc_interceptor.testing import dummy_client, DummyRequest, raises 2 3class MockErrorLogger(ErrorLogger): 4 def __init__(self): 5 self.logged_exception = None 6 7 def log_error(self, e: Exception) -> None: 8 self.logged_exception = e 9 10def test_log_error(): 11 mock = MockErrorLogger() 12 ex = Exception() 13 special_cases = {"error": raises(ex)} 14 15 with dummy_client(special_cases=special_cases, interceptors=[mock]) as client: 16 # Test no exception 17 assert client.Execute(DummyRequest(input="foo")).output == "foo" 18 assert mock.logged_exception is None 19 20 # Test exception 21 with pytest.raises(grpc.RpcError) as e: 22 client.Execute(DummyRequest(input="error")) 23 assert mock.logged_exception is ex
這是一個演練:
ErrorLogger要模擬的第 3 到 8 行子類log_error()。您實際上并不希望發生日志記錄副作用。你只是想確保它被調用。
第 15 到 18 行使用dummy_client()上下文管理器創建連接到真實 gRPC 微服務的客戶端。你發送DummyRequest到微服務,它回復DummyResponse.?默認情況下,input的DummyRequest回顯到output的DummyResponse。但是,您可以傳遞dummy_client()特殊情況的字典,如果input匹配其中一個,則它將調用您提供的函數并返回結果。
第 21 到 23 行:您測試log_error()調用時會出現預期的異常。raises()返回另一個引發提供的異常的函數。您設置input為error以便微服務將引發異常。
有關測試的更多信息,您可以閱讀使用 Pytest 進行有效 Python 測試和了解 Python Mock 對象庫。
在某些情況下,-的替代方案是使用服務網格。它將通過代理發送所有微服務請求和響應,因此代理可以自動記錄請求量和錯誤計數等內容。為了獲得準確的錯誤日志,您的微服務仍然需要正確設置狀態代碼。因此,在某些情況下,您的-可以補充服務網格。一種流行的服務網格是Istio。
最佳實踐
現在你有一個有效的 Python 微服務設置。您可以創建微服務,一起測試它們,將它們部署到 Kubernetes,并使用-監控它們。此時您可以開始創建微服務。但是,您應該記住一些最佳實踐,因此您將在本節中學習一些。
Protobuf 組織
通常,您應該將 protobuf 定義與微服務實現分開。客戶端幾乎可以用任何語言編寫,如果您將 protobuf 文件捆綁到Python 輪或類似的東西中,那么如果有人想要 Ruby 或 Go 客戶端,他們將很難獲得 protobuf 文件。
即使你所有的代碼都是 Python,為什么有人需要為微服務安裝包來為它編寫客戶端?
一種解決方案是將您的 protobuf 文件放在與微服務代碼不同的 Git 存儲庫中。許多公司把所有的protobuf的文件全部微服務在一個單一的回購。這使得查找所有微服務、在它們之間共享通用 protobuf 結構以及創建有用的工具變得更加容易。
如果您選擇將 protobuf 文件存儲在單個 repo 中,則需要注意 repo 保持井井有條,并且絕對應該避免 Python 微服務之間的循環依賴。
Protobuf 版本控制
API 版本控制可能很困難。主要原因是如果你改變一個API并更新微服務,那么可能仍然有客戶端使用舊的API。當客戶端位于客戶的機器上時尤其如此,例如移動客戶端或桌面軟件。
你不能輕易強迫人們更新。即使可以,網絡延遲也會導致競爭條件,并且您的微服務很可能會使用舊 API 獲取請求。好的 API 應該向后兼容或版本化。
為了實現向后兼容性,使用 protobufs 版本 3 的 Python 微服務將接受缺少字段的請求。如果您想添加一個新字段,那沒關系。您可以先部署微服務,它仍然會接受來自舊 API 的請求,而沒有新字段。微服務只需要優雅地處理它。
如果您想進行更大幅度的更改,則需要對API進行版本控制。Protobufs 允許您將 API 放入包命名空間中,其中可以包含版本號。如果您需要徹底更改 API,則可以創建它的新版本。微服務也可以繼續接受舊版本。這允許您在逐步淘汰舊版本的同時推出新的 API 版本。
通過遵循這些約定,您可以避免進行重大更改。在公司內部,人們有時會覺得對 API 進行重大更改是可以接受的,因為他們控制著所有客戶端。這由您決定,但請注意,進行重大更改需要協調客戶端和微服務部署,并且會使回滾復雜化。
在微服務生命周期的早期,當沒有生產客戶端時,這可能沒問題。但是,一旦您的微服務對您公司的健康至關重要,養成只進行不間斷更改的習慣是很好的。
Protobuf Linting
確保不會對 protobuf 進行重大更改的一種方法是使用linter。一個流行的是buf。您可以將其設置為CI 系統的一部分,以便您可以檢查拉取請求中的重大更改。
類型檢查 Protobuf 生成的代碼
Mypy 是一個用于靜態類型檢查 Python 代碼的項目。如果您不熟悉 Python 中的靜態類型檢查,那么您可以閱讀Python 類型檢查以了解所有相關信息。
生成的代碼protoc有點粗糙,而且沒有類型注釋。如果您嘗試使用 Mypy 進行類型檢查,那么您將收到很多錯誤,并且它不會捕獲真正的錯誤,例如字段名稱拼寫錯誤。幸運的是,Dropbox 的好人為編譯器編寫了一個插件protoc來生成類型存根。這些不應與 gRPC 存根混淆。
為了使用它,您可以安裝mypy-protobuf包,然后更新命令以生成 protobuf 輸出。注意新??mypy_out選項:
$ python -m grpc_tools.protoc -I ../protobufs --python_out=. \ --grpc_python_out=. --mypy_out=. ../protobufs/recommendations.proto
大多數 Mypy 錯誤應該會消失。您可能仍會收到有關grpc包沒有類型信息的錯誤。您可以安裝非官方的gRPC 類型存根或將以下內容添加到您的 Mypy 配置中:
[mypy-grpc.*] ignore_missing_imports = True
您仍將獲得類型檢查的大部分好處,例如捕獲拼寫錯誤的字段。這對于在將錯誤投入生產之前捕獲錯誤非常有幫助。
從零開始學python | 使用 gRPC 的 Python 微服務 III
Docker Python 微服務
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。