Python 微服務開發--Python Microservices Development

用Python構建、測試、部署和擴展微服務

本文爲 Python Microservices Development 的選擇性翻譯,本書暫時無中文版,有能力請直接看原版node

什麼是微服務?

A microservice is a lightweight application, which provides a narrowed list of features with a well-defined contract. It's a component with a single responsibility, which can be developed and deployed independently.python

微服務是一個輕量級的應用程序,它經過定義良好的契約提供窄範圍的特性列表。 它是一個具備單一職責的組件,能夠獨立地進行開發和部署。react

Python Microservices Development
本書內容:
第一章 理解微服務,定義什麼是微服務,以及在如今web應用當中的角色.說明爲何Python很是適合構建微服務。
第二章 瞭解Flask,介紹Flask的主要特性。用Flask搭建一個簡單的web應用微服務。
第三章 編碼,文檔,和測試——良性循環,描述測試驅動和持續集成方法,Flask應用構建和打包實踐 第四章 設計Runnerly,經過應用的功能和用戶故事,解釋如何將他建成一個總體應用,而後把它分解成微服務,並解釋它們如何與數據進行交互。還將介紹Open API 2.0 規範(ex-Swagger),該規範可用於藐視HTTP APIs。
第五章 整合其餘服務,解釋一個服務如何與後端服務整合,怎樣處理網絡分割和其餘交互問題,以及如何在隔離狀態下測試服務。
第六章 服務安全,解釋如何保護你的微服務,以及如何處理用戶身份嚴重「」服務到服務身份驗證和用戶管理。還將介紹欺詐和濫用,以及如何將他們下降。 第七章 監控你的服務,解釋如何在你的代碼中添加日誌和指標(metrics),以及如何確保你對你的應用程序中正在發生的事情有一個清晰的全局理解,從而跟蹤問題並瞭解你的服務使用狀況。
第八章 整合起來,描述瞭如何設計和構建一個使用了微服務的JavaScript應用程序,給終端用戶界面。 第九章 打包運行Runnerly,描述如何打包、構建和運行整個Forrest應用程序。做爲一名開發人員,可以將構成應用程序的全部部分運行到單個開發包中是必不可少的。
第十章 集裝箱式服務,解釋什麼是虛擬化,如何使用Docker,以及如何將你的服務進行Dockerize。
第十一章 在AWS上部署,介紹現有的雲服務提供商,和AWS世界,展現瞭如何實例化服務器,對於運行微服務的應用程序很是有用的AWS服務。本文還介紹了CoreOS,一個專門爲在雲中部署Docker容器而建立的Linux發行版。
第十二章 下一步是什麼,在書的結尾處給出了一些關於如何獨立於特定的雲供應商和虛擬化技術構建微服務的建議,以免將全部雞蛋放在同一個籃子裏。它強調了你在第九章中學到的東西,打包和運行Runnerly。

單體應用的利弊:

  • 多是開始一個項目最簡單的方式
  • 集中式數據庫簡化了數據的設計和組織。
  • 部署一個應用程序很簡單。
  • 代碼中的任何更改均可能影響不相關的特性。當某個功能出現問題時,整個應用程序均可能出現問題。
  • 擴展應用程序的解決方案是有限的:你能夠部署多個實例,可是若是應用程序中的一個特定功能佔用了全部資源,那麼它就會影響到全部方面。
  • 隨着代碼庫的增加,很難保持它的整潔和受控。

一個酒店預訂網站的結構

1. 單體應用結構

單體應用結構

2. 微服務結構

微服務結構

  1. 在這個設計中,每一個組件都使用 HTTP 協議進行通訊,而且特性能夠經過 RESTful web 服務得到。
  2. 沒有集中的數據庫,每一個微服務在內部處理本身的數據結構,進出的數據使用語言無關的格式,如 JSON。

微服務優勢

  1. 關注點分離web

    • 首先,每一個微服務均可以由一個獨立的團隊獨立開發。 例如,構建一個預訂服務自己就是一個完整的項目。 負責團隊可使用任何編程語言和數據庫,只要它有一個良好文檔的 HTTP API。
    • 這也意味着相比單體應用,程序的進化更好地受到控制。例如,若是支付系統改變了與銀行的底層交互,那麼影響就侷限在該服務內部,而應用程序的其他部分保持穩定,可能不會受到影響。
    • 這種鬆散耦合大大提升了總體項目的速度,由於咱們在服務層面採用了相似於單一責任原則的理念
  2. 須要處理的是較小項目數據庫

    • 第二個好處是打破了項目的複雜性。 當嚮應用程序添加一個特性(如 PDF 報告)時,即便作得乾淨利落,也會使基本代碼變得更大、更復雜,有時還會變慢。
    • 在單獨的應用程序中構建這個特性能夠避免這個問題,而且可使用任何你想要的工具更容易地編寫它。 你能夠常常重構它,縮短髮布週期,可以更好的掌控。程序的增加仍在控制之下。
    • 在改進應用程序時,處理較小的項目也能夠下降風險: 若是團隊想要嘗試最新的編程語言或框架,他們能夠快速迭代一個原型,實現相同的微服務 API,嘗試它並決定是否仍然使用它
    • 一個真實的例子是 Firefox Sync 存儲微服務。 目前有一些試驗從當前的 Python + MySQL 實現切換到基於 go 的實現,該實現將用戶的數據存儲在獨立的 SQLite 數據庫中。 這個原型是高度試驗性的,可是由於咱們已經用一個定義良好的 HTTP API 將存儲特性分離到微服務中,因此很容易用一小部分用戶嘗試一下。(看來有時間仍是要學習一下Go)
  3. 更多的擴展和部署選項編程

    • 最後,將應用程序分割成組件,能夠更容易地根據約束進行擴展。 假設你有不少客戶天天預約酒店,而生成PDF消耗大量cpu。這時能夠將這個特定的微服務部署到擁有更大 cpu 的服務器上。
    • 另外一個典型的例子是 RAM 消耗型的微服務,好比那些與內存數據庫(如 Redis 或 Memcache)交互的服務。 您能夠調整部署,將其部署到具備更少 CPU 和更多 RAM 的服務器上。

所以,咱們能夠將微服務的好處歸納以下:

  • 一個團隊能夠獨立開發每一個微服務,使用任何能使用的技術棧。 他們能夠自定義一個發佈週期,只須要完成一個與語言無關的 HTTP API。
  • 開發人員將應用程序的複雜性分解爲邏輯組件。每一個微服務都專一於作好一件事情。
  • 因爲微服務是獨立的應用程序,所以對部署有更好的控制,這使得擴展更加容易。

微服務體系結構有助於解決應用程序開始增加時可能出現的許多問題。 然而,咱們須要意識到它帶來的一些新問題。json

微服務隱患

  1. 不合邏輯的分割
    • 微服務架構的第一個問題是如何設計它。一個團隊不可能在第一次就想出完美的微服務架構。 一些微服務(如 PDF 生成器)是顯而易見的用例。而只要是處理業務邏輯,你的代碼就有很大的可能,在你理解如何將應用分割成正確的微服務集合以前,四處移動。
    • 成熟的設計須要一些嘗試和失敗的循環。 添加和刪除微服務可能比重構單體應用程序更痛苦。
    • 若是分隔不明顯的話,能夠避免分割應用成微服務
    • 若是有任何懷疑分割有無心義,就保持在一塊兒。將一些代碼分割成一個新的微服務,比在合併回兩個微服務要容易得多
    • 例如,若是你老是必須將兩個微服務部署在一塊兒,或者一個微服務中的一個更改影響到另外一個微服務的數據模型,那麼您沒有正確地分割應用程序,而且這兩個服務應該從新組合。
  2. 更多的網絡交互
  3. 數據存儲和共享
    • 一個有效的微服務須要獨立於其餘微服務,理想狀況下不該該共享一個數據庫。 這對咱們的酒店預訂應用程序意味着什麼?
    • 一樣,這也引出了不少問題,好比:咱們是在全部數據庫中使用相同的用戶 id,仍是在每一個服務中使用獨立的id並將其做爲一個隱藏的實現細節?
    • 一旦用戶添加到系統中,咱們是經過數據抽取策略在其餘服務數據庫中複製一些它的信息,仍是這樣作有點過了?
    • 如何處理數據刪除?
    • 儘量避免數據重複,同時將微服務隔離開來,是設計基於微服務的應用程序的最大挑戰之一。

這些都是很難回答的問題,有不少不一樣的方法能夠解決這些問題,咱們將在書中學到這一點。後端

  1. 兼容性問題安全

    • 另外一個問題發生在功能更改影響多個微服務時。若是更改以向後不兼容的方式影響在服務之間傳輸的數據,那麼就會遇到麻煩。
    • 你部署的新服務是否能夠與其餘服務的舊版本一塊兒使用?仍是須要同時更改和部署多個服務?這是否意味着你發現了一些服務應該被合併回來?
  2. 測試bash

    • 最後,當你想要進行一些端到端測試並部署整個應用程序時,您如今必須處理許多應用。你須要一個健壯的、敏捷的部署流程來提升效率。你須要可以在開發整個應用程序時使用它。你不可能僅僅用幾個用例就徹底測試出來。
    • 介紹一些促進微服務的工具

WSGI標準

WSGI 最大的問題在於它的同步性。你在前面的代碼中看到的應用程序函數對每一個傳入請求只調用一次,當函數返回時,它必須返回響應。 這意味着每次調用函數時,它都會阻塞,直到響應準備好爲止。

WSGI 服務器將容許你運行一個線程池來同時處理多個請求。 可是你不能運行成千上萬個這樣的服務,一旦這個池用完了,下一個請求就會阻止客戶的訪問,而你的微服務什麼也不作,只是空閒地等待後端服務的響應。

這就是爲何 Twisted 和 Tornado 這樣的非 wsgi 框架( 在JavaScript 領域中是node.js),很是成功的緣由——它徹底是異步的。

在編寫 Twisted 應用程序時,可使用回調來暫停和恢復生成響應的工做。 這意味着你能夠接受新的請求並開始處理它們。 這種模式顯著地減小了進程中的空閒時間。 它能夠處理成千上萬的併發請求。 固然,這並不意味着應用程序會更快地返回每一個響應。 它僅僅意味着一個進程能夠接受更多的併發請求,而且在數據準備發回時在這些請求之間進行切換。

WSGI 標準沒有簡單的方法來引入相似的東西,社區已經爭論了多年來達成共識---- 但失敗了。 可能的狀況是,社區最終會放棄 WSGI 標準。
與此同時,若是你考慮到WSGI標準,一個請求等於一個線程,那麼構建具備同步框架的微服務仍然是可能的而且徹底沒問題。

可是,有一個加強同步 web 應用程序的技巧—— Greenlet,將在下一節解釋。

Gevent

Gevent提供了 socket 模塊的合做版本,該模塊使用 greenlets 來在socket中有數據可用時自動暫停和恢復執行。 甚至還有一個 monkey 補丁功能,能夠用 Gevent 的版本自動替換標準庫socket。 這使你的標準同步代碼在每次使用 socket時都神奇地異步——只需多加一行:

from gevent import monkey

monkey.patch_all()


def application(environ, start_response):
    headers = [('Content-type', 'application/json')]
    start_response('200 OK', headers)
    # ...do something with sockets here...
    return result
複製代碼

不過,這種隱式的魔力是有代價的。 爲了讓 Gevent 可以正常工做,全部的底層代碼都須要與 Gevent 所作的修補兼容。 一些來自社區的軟件包會由於這個緣由而繼續阻塞甚至產生意外的結果---- 特別是,若是他們使用 c 擴展,並繞過了標準庫 Gevent 補丁的一些特性。 但在大多數狀況下效果都很好。 與 Gevent 兼容的項目被稱爲綠色項目。

Twisted and Tornado

若是你正在構建微服務,並且併發請求的數量很重要,那麼放棄WSGI標準是很誘人的,你可使用異步框架Twisted 或 Tornado

import time
import json
from twisted.web import server, resource
from twisted.internet import reactor, endpoints


class Simple(resource.Resource):
    isLeaf = True

    def render_GET(self, request):
        request.responseHeaders.addRawHeader(b"content-type",
                                             b"application/json")
        return bytes(json.dumps({'time': time.time()}), 'utf8')

site = server.Site(Simple())
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8080)
endpoint.listen(site)
reactor.run()
複製代碼

雖然 Twisted 是一個很是健壯和高效的框架,可是它在構建 HTTP 微服務時遇到了如下幾個問題:

  • 您須要使用從Resource類派生的類來實現微服務中的每一個端點,並實現每一個受支持的方法。對於一些簡單的API,它添加了許多樣板代碼。
  • 因爲其異步性質,扭曲的代碼很難理解和調試。
  • 當你連接太多連續依次觸發的函數時,很容易陷入回調地獄 - 代碼可能變得混亂。
  • 正確測試Twisted應用程序很困難,您必須使用特定於Twisted的單元測試模型。

Tornado 基於相似的模型,但在某些領域作得更好。它有一個更輕的路由系統,並盡一切可能使代碼更接近普通的Python。 Tornado也使用回調模型,所以調試很困難。 依賴 Python 3中引入的新的異步特性。兩個框架都在努力彌合這一問題。

asyncio

from aiohttp import web
import time


async def handle(request):
    return web.json_response({'time': time.time()})


if __name__ == '__main__':
    app = web.Application()
    app.router.add_get('/', handle)
    web.run_app(app)

複製代碼

可是,基於Python 3的異步框架和庫仍然在不斷涌現,若是使用異步或aiohttp之類的框架,則須要針對每一個須要的特性堅持使用特定的異步實現。 若是須要在代碼中使用非異步的庫,則從異步代碼使用它意味着若是要防止阻塞事件循環,則須要執行一些額外的、具備挑戰性的工做。

若是你的微服務處理的資源數量有限,這多是可控的。 可是在寫這篇文章的時候,堅持使用已經存在了一段時間的同步框架而不是異步框架多是一個更安全的選擇。 讓咱們享受成熟軟件包的現有生態系統,並等待異步生態系統變得更加完善。

這本書的第二版頗有可能使用異步框架。 可是對於這個版本,咱們將在整本書中使用 Flask 框架。

語言性能

固然,每一個人都知道Python比Java或GO慢,可是執行速度並不老是最優先考慮的。微服務一般是一層很薄的代碼,其生命週期的大部分時間都在等待來自其餘服務的一些網絡響應。與從 Postgres 服務器返回 SQL 查詢的速度相比,它的核心速度一般不那麼重要,由於後者佔用了響應的大部分時間。

可是想要一個儘量快的應用程序是合理的

Python 社區中關於加速語言的一個有爭議的話題是,GIL如何破壞性能,由於多線程應用程序不能使用多個進程。

GIL 有存在的充分理由。它保護CPython解釋器中非線程安全的部分,而且存在於其餘語言中,如 Ruby。到目前爲止,全部試圖刪除它的嘗試都未能生成更快的 CPython 實現。

對於微服務,除了防止在同一進程中使用多個內核以外,GIL 在高負載時會稍微下降性能,由於互斥鎖引入了系統調用開銷。

然而,圍繞 GIL 的全部審查都是有益的: 在過去幾年中已經完成了減小解釋器中 GIL 爭論的工做,而且在某些方面,Python 的性能有了很大的提升。

請記住,即便核心團隊刪除 GIL,Python 也是一種解釋語言和垃圾收集語言,而且會由於這些屬性而遭受性能損失。

在靜態編譯語言中編寫一個相似的函數將大大減小產生相同結果所需的操做數量。

不過,有一些方法能夠提升 Python 的執行速度。

一種方法是經過構建 c 擴展,或者使用語言的靜態擴展(如 Cython (http: / / Cython. org /) ,將部分代碼編寫到已編譯的代碼中,但這會使代碼更加複雜。

另外一個解決方案是最有但願的,那就是使用 PyPy 解釋器(http: / / PyPy. org /)簡單地運行應用程序。

Pypy 實現一個實時(JIT)編譯器。 這個編譯器在運行時直接用 CPU 能夠直接使用的機器代碼替換部分 Python 代碼。 對於 JIT 來講,整個技巧就是要在執行以前提早檢測到何時以及如何去作。

即便PyPy老是CPython以後的幾個Python版本,但它已經達到了能夠在生產中使用的程度,並且它的性能至關驚人。 咱們在 Mozilla 的一個項目須要快速執行,PyPy 版本幾乎和 Go 版本同樣快,因此咱們決定在那裏使用 Python。 ... 不管如何,對於大多數項目來講,Python 及其生態系統的好處大大超過了本節描述的性能問題,由於微服務的開銷不多成爲問題。 若是性能有問題,微服務方法容許你重寫性能關鍵組件,而不會影響系統的其他部分。

在本章中,咱們比較了單體應用和微服務的方法來構建 web 應用程序,很明顯,這不是一個二元世界,你不是必須在第一天就選擇一種方法並一直使用它。

你應該將微服務視爲一個單體應用程序的改進。 隨着項目的成熟,服務邏輯的一部分應該遷移到微服務中。 正如咱們在本章學到的,這是一個有用的方法,可是要當心謹慎,以避免落入一些常見的陷阱。

Flask

  1. 不一樣類型的測試

    • 單元測試: 確保一個類或一個函數獨立地工做
    • 功能測試: 從使用者的角度驗證微服務是否言行一致,即便對於錯誤請求,微服務也能正確運行
    • 集成測試: 驗證微服務如何與其全部網絡依賴項集成
    • 負載測試: 測量微服務性能
      • 當個人服務承受壓力時,它是 RAM 仍是主要受 cpu 限制?
      • 是否能夠添加相同服務的其餘實例並橫向擴展?
      • 若是個人微服務調用其餘服務,可使用鏈接池,仍是必須經過一個鏈接序列化全部的交互?
      • 服務能一次運行多天而不降級嗎?
      • 服務在使用高峯期以後是否正常工做?
    • 端到端測試: 驗證整個系統是否與端到端測試一塊兒工做
    • 總結:
      • 功能測試是要編寫的最重要的測試,而且經過在測試中實例化應用程序並與之交互,很容易在 Flask 中完成這項工做
      • 單元測試是一個很好的補充,可是不要濫用模擬
      • 集成測試相似於功能測試,可是與真正的部署相對立
      • 負載測試對於瞭解微服務瓶頸和規劃下一步很是有用
      • 端到端測試須要使用客戶端一般使用的相同 UI
  2. 使用 WebTest

  3. 使用 pytest 和 Tox

    • pytest自動發現和運行項目中的全部測試
    • Tox python多版本測試
  4. 開發者文檔

  5. 持續整合

相關文章
相關標籤/搜索