本文为 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
开发者文档
持续整合