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. 持续整合

相关文章
相关标签/搜索