本文爲 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
關注點分離web
須要處理的是較小項目數據庫
更多的擴展和部署選項編程
微服務體系結構有助於解決應用程序開始增加時可能出現的許多問題。 然而,咱們須要意識到它帶來的一些新問題。json
這些都是很難回答的問題,有不少不一樣的方法能夠解決這些問題,咱們將在書中學到這一點。後端
兼容性問題安全
測試bash
WSGI 最大的問題在於它的同步性。你在前面的代碼中看到的應用程序函數對每一個傳入請求只調用一次,當函數返回時,它必須返回響應。 這意味着每次調用函數時,它都會阻塞,直到響應準備好爲止。
WSGI 服務器將容許你運行一個線程池來同時處理多個請求。 可是你不能運行成千上萬個這樣的服務,一旦這個池用完了,下一個請求就會阻止客戶的訪問,而你的微服務什麼也不作,只是空閒地等待後端服務的響應。
這就是爲何 Twisted 和 Tornado 這樣的非 wsgi 框架( 在JavaScript 領域中是node.js),很是成功的緣由——它徹底是異步的。
在編寫 Twisted 應用程序時,可使用回調來暫停和恢復生成響應的工做。 這意味着你能夠接受新的請求並開始處理它們。 這種模式顯著地減小了進程中的空閒時間。 它能夠處理成千上萬的併發請求。 固然,這並不意味着應用程序會更快地返回每一個響應。 它僅僅意味着一個進程能夠接受更多的併發請求,而且在數據準備發回時在這些請求之間進行切換。
WSGI 標準沒有簡單的方法來引入相似的東西,社區已經爭論了多年來達成共識---- 但失敗了。 可能的狀況是,社區最終會放棄 WSGI 標準。
與此同時,若是你考慮到WSGI標準,一個請求等於一個線程,那麼構建具備同步框架的微服務仍然是可能的而且徹底沒問題。
可是,有一個加強同步 web 應用程序的技巧—— Greenlet,將在下一節解釋。
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 兼容的項目被稱爲綠色項目。
若是你正在構建微服務,並且併發請求的數量很重要,那麼放棄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 微服務時遇到了如下幾個問題:
Tornado 基於相似的模型,但在某些領域作得更好。它有一個更輕的路由系統,並盡一切可能使代碼更接近普通的Python。 Tornado也使用回調模型,所以調試很困難。 依賴 Python 3中引入的新的異步特性。兩個框架都在努力彌合這一問題。
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 應用程序,很明顯,這不是一個二元世界,你不是必須在第一天就選擇一種方法並一直使用它。
你應該將微服務視爲一個單體應用程序的改進。 隨着項目的成熟,服務邏輯的一部分應該遷移到微服務中。 正如咱們在本章學到的,這是一個有用的方法,可是要當心謹慎,以避免落入一些常見的陷阱。
略
不一樣類型的測試
使用 WebTest
使用 pytest 和 Tox
開發者文檔
持續整合