【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

Zato——基于 Python 的 ESB 和后端应用服务器

  • 2013-09-27
  • 本文字数:8023 字

    阅读完需:约 26 分钟

概述

Zato 是一个用 Python 编写的开源 ESB 和应用服务器。按照设计,它用于构建后端应用程序(即仅是 API)和在 SOA 中整合系统。

查看 Zato 的项目文档点击这里,查看其GitHub 页点击这里

Zato 的目标用户是使用 Python 或者 Ruby 和 PHP 等其它动态语言的开发人员,或者是那些考虑在工作中尝试动态语言的技术团队,后者或是因为看到动态语言在其它地方使用,或是因为愿意尝试用其中一种动态语言编写的非前端系统。

该平台是轻量级但完整的,它涵盖了架构师、程序员或者系统管理员的所有视角,对许多特性提供开箱即用的支持,包括 HTTP、SON、SOAP、SQL、AMQP、JMS WebSphere MQ、ZeroMQ、Redis NoSQL、FTP、基于浏览器的 GUI、CLI、API、安全、统计、作业调度、负载均衡和热部署。另外,它还提供了大量指南和参考样式方面的文档。

它的最初版本于 2013 年 5 月 18 日发布,最新的 1.1 版在 6 月初发布。

体系结构

Zato 环境是一个或者多个集群的集合。每个集群由多个共享同一个 SQL 和 Redis 数据库的服务器构成。这些服务器的前端是集群专属的 HA HTTP 负载均衡器。

所有的服务器始终处于活动状态,并且总是运行同一组服务。为了实现 active-standby 设定,负载均衡器可以根据需要把任意服务器离线。

负载均衡器是一个嵌入式的 HAProxy 实例,管理员通过命令行或者通过 GUI 调用 SSL XML-RPC 对其进行远程控制,该过程可以用也可以不用客户端证书。用户可以给服务器赋予权重,以及使用 HAProxy 自身提供的其它功能,如连接 ACL 或速率限制。

服务器基于 gunicorn / gevent 项目构建。该项目是一个联合体,它使用 libevent 来选择每个平台支持的最佳异步事件通知库,如 Linux 平台上的 epoll

为了充分利用单台计算机能够提供的所有 CPU,Zato 按照设定好的数值预先生成一定数量的工作进程,每个进程使用选定的异步网络连接库来处理连接,所有进程监听相同的套接字。负载均衡器用于在不同的计算机之间分配负载及提供 HA。

集群中的一台服务器担当启动 AMQP/JMS WebSphere MQ/ZeroMQ 资源调度器和连接器的角色。如果这台服务器意外宕机,那么始终处于活动状态的 ping 机制可以保证另外一台服务器接管这个角色。

应用程序可以使用多种协议进行集成,包括HTTP(对JSON/SOAP 和纯XML 有特殊支持)、FTP、AMQP、JMS WebSphere MQ(用于实现与现有的MQ Java 应用程序之间的无缝互通性)、Redis 和SQL。其中,HTTP 协议是同步调用Zato 服务的唯一方式。这种情况下,请求应用程序会进入阻塞状态等待响应。

程序员可以使用任何Python 库。如果Zato 自身尚未提供某个功能,也可以使用其它技术实现,如 XML-RPC 或者 SMTP ,该过程仅是一个导入 Python 内置包的问题而已。

集群管理使用基于浏览器的 GUI CLI 。前者主要用于管理处于运行状态的集群,后者则用于在操作系统中安装 Zato 组件,如服务器。

集群配置信息存储在 Redis 和 SQL 操作型数据库中。Redis 存储快速变化和频繁更新的数据,如统计信息或用户的运行时信息,而 SQL ODB 存储可以简单地映射成关系结构的数据。

虽然主要使用 GUI 进行环境配置,但也可以将集群的配置信息以 JSON 格式导出/ 导入,而且导出结果可以存储到一个外部的配置版本库,从而可以对其进行版本管理、标记或者版本间差异比较。

带有GUI 的内置调度器可以用于一次性作业或者循环作业(也可以用Cron 语法)。

服务器和服务只通过Redis 和SQL ODB 进行状态共享。没有自定义的协议或者数据格式用于保持服务器状态的一致性。

Zato 使用超过 160 个自带的管理服务来进行自我管理,其中每一项都可以通过命令行或者在HTTP 上以JSON/SOAP 方式调用公共API 获得。GUI 和CLI 工具本身都是这些服务的客户端。

Zato 已经为 Python 应用程序创建了方便的客户端,所以用 Python 编写的应用程序在与 Zato 暴露的服务进行通信时还是只能使用 Python 对象。

服务

Zato 服务是实现了单个特定方法的 Python 类。它可以接收输入和产生输出,也可以只接收输入或者只产生输出。

服务可以从 GUI 或者命令行以静态的或者热部署的方式安装。安装过程会自动将服务编译成字节码。

服务可以使用任何数据格式,但是Zato 对JSON、SOAP 和普通XML 提供更多支持。如果使用了其中任何一种,序列化和反序列化都会在后台完成,开发人员只需通过点号就可以使用纯Python 对象,而不必基于诸如XSD 那样的模式创建Bean/ 模型/ 存根/ 类——尽管这意味着将没有代码完成。

同一服务可以在HTTP、AMQP、JMS WebSphere MQ、ZeroMQ 和调度器上暴露,而不需要修改任何代码,也不需要重启服务器。特别地,只有在HTTP 上暴露的服务可以进行同步调用。

SimpleIO(SIO)是一种声明式语法,用于表示简单的请求和响应。选用该语法,服务可以在不修改代码的情况下通过 JSON 或者 XML/SOAP 暴露。SIO 不能处理复杂文档,它不接受任意嵌套的结构。任何结构的任何文档都可以用于 Zato, 只是有时不能用于 SIO。

下面是一个基础服务的示例,该服务使用 Yahoo YQL/JSON Google’s XML API 获取一家公司的市场资本总额。

该服务接收一个股票代码(例如 GOOG 或 RHT),发出两个 HTTP 请求,然后清理响应并把它们组合成一个通用格式。根据请求格式的不同,组合结果以 JSON 或 XML 格式返回。

复制代码
# anyjson
from anyjson import loads
# bunch
from bunch import bunchify
# decimal
from decimal import Decimal
# lxml
from lxml.objectify import fromstring
# Zato
from zato.server.service import Service
class GetMarketCap(Service):
""" 根据公司股票代码返回其市场资本总额,单位是 10 亿 USD。
{1}
"""
class SimpleIO:
response_elem = 'market_cap'
input_required = ('symbol',)
output_required = ('provider', 'value')
def handle(self):
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Yahoo
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# 通过名称获取到 Y! 的连接
yahoo = self.outgoing.plain_http.get('Yahoo! YQL')
# 创建用于 YQL 查询的 URL 参数。
q = 'select * from yahoo.finance.quotes where symbol="{}"'.format(
self.request.input.symbol)
url_params = {'q':q, 'format':'json',
'env':'http://datatables.org/alltables.env'}
# 调用 Y!,并从 JSON 响应创建一个 bunch 实例,这样就可以用点号
# 引用这些元素。
yahoo_response = bunchify(loads(yahoo.conn.get(self.cid, url_params).text))
# 清理 Y! 的响应——如果有业务响应,就去掉最后一个字符。
# 假设响应总是以 10 亿为单位。
if yahoo_response.query.results.quote:
value1 = yahoo_response.query.results.quote.MarketCapitalization
value1 = Decimal(value1[:-1]) if value1 else 'n/a'
else:
value1 = 'n/a'
# 一个新的响应条目会附加到条目列表,根据服务调用方式的不同,
#Zato 会把它序列化为 JSON 或者 XML。
item1 = {'provider':'Yahoo!', 'value': str(value1)}
self.response.payload.append(item1)
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Google
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# 通过名称获取到 Google 的连接
google = self.outgoing.plain_http.get('Google Finance')
# 创建用于调用 Google 的 URL 参数
url_params = {'stock':self.request.input.symbol}
# 调用 Google 并从 XML 响应创建一个 Objectify 实例,这样就可以
# 用点号引用这些元素。
google_response = fromstring(google.conn.get(self.cid,
url_params).text)
# 清理 Google 的响应——如果有业务响应,就将百万转换成十亿。
if hasattr(google_response.finance, 'market_cap'):
value2 = Decimal(google_response.finance.market_cap.get('data')) / 1000
else:
value2 = 'n/a'
# 此外,将一个纯 Python 字典 (hashmap) 附加到响应对象,并由 Zato
# 完成序列化
item2 = {'provider':'Google', 'value': str(value2)}
self.response.payload.append(item2)

这是一个非常简单的集成示例,而并不是所有的场景都允许使用 SIO,但是不管服务多复杂,有一点需要强调,就是不应该回避使用 Python 进行编码。除了可执行外,从许多方面看来,它都非常像伪代码——这里更多使用示例可以说明这一点

事实上,这正是使用 Python 创建 Zato 的原因——作为一种十分高级的语言,Python 非常令人满意,它尽可能的减少麻烦和语言怪癖,使开发人员可以专注于集成。同时,它还是一种真正通用的编程语言,而不是一种有局限性、可能图形化和领域专属的第四代语言。

回到上面的例子,可以使用 GUI 创建需要的资源——本例中是“Yahoo!YQL”和“Google 财经”的传出连接。也可以从命令行使用 JSON 配置完成上述操作,但还是会显示图形界面。

这些独有的 API 不需要安全性保证,但是如果需要,则可以使用“HTTP 基础认证(HTTP Basic Auth)”、“WS-Security 用户名令牌(WS-Security Username Tokens)”或者技术账户(与 HTTP 基础认证类似,但是不需要 BASE64)。

现在,服务可以通过 GUI 或者命令行进行热部署了。此处使用后者:

复制代码
$ cp stockmarket.py /opt/server1/pickup-dir

确认信息会写到服务器日志:

复制代码
2013-06-20 19:25:16,115 - INFO - Uploaded package id:[53],
payload_name:[stockmarket.py]

接下来,可以使用 Zato 的 CLI 从命令行调用它,也可以从 GUI 调用它。这里采用前一种方法,并使用 JSON 和纯 HTML 两种格式:

复制代码
$ zato service invoke /opt/server1 stockmarket.get-market-cap \
--payload '{"symbol":"GOOG"}'
{u'market_cap': [
{u'value': u'298.8', u'provider': u'Yahoo!'},
{u'value': u'298.81505', u'provider': u'Google'}
]}
$
复制代码
$ zato service invoke /opt/server1/ stockmarket.get-market-cap --data-format
xml \
--transport soap --payload '\
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" \
xmlns:zato="https://zato.io/ns/20130518"> \
<soapenv:Body> \
<zato:request> \
<zato:symbol>IBM</zato:symbol> \
</zato:request> \
</soapenv:Body> \
</soapenv:Envelope>'
<market_cap>
<zato_env>
<cid>K255124128065587321859442392853212603320</cid>
<result>ZATO_OK</result>
</zato_env>
<item_list>
<item>
<provider>Yahoo!</provider>
<value>8.763</value>
</item>
<item>
<provider>Google</provider>
<value>8.76324</value>
</item>
</item_list>
</market_cap>
$

CLI 只是一种管理员或者开发人员快速访问服务的方式,而实际上,服务现在已经加载到一个安全的 HTTP 通道上,客户端应用程序可以通过该通道访问服务。服务器不需要重启。

Zato 1.1 其它特性速览

服务

服务使用 Python 编写,但如果需要,也可以使用 C 或 C++ 创建服务。

服务已从传输层解耦,因此,除非开发人员坚持深入研究底层细节,否则他们可以专注于数据校验、扩展、转换、路由或者调用其它服务。服务管理可以使用基于浏览器的 GUI、CLI 或者 JSON 配置。

Zato 还将服务与安全机制隔离——服务遵循这样的假设,如果它们被调用,那么前一层已经处理了授权 / 认证。

一个服务可以同时通过多种通道暴露,每一个通道使用不同的数据格式、安全定义或传输协议。

如果使用 JSON/XML/SOAP,服务会收到一个美观的 Python 对象作为输入,程序员可以通过常规的点号访问该对象(例如,request.customer.payment.date),而不需要手动序列化 / 反序列化。如果使用 SimpleIO(SIO),不管使用哪种数据格式,都会产生相同的对象,所以相同的服务可以通过多种通道暴露给客户端应用程序,并且每种通道都可以使用三种数据格式中的任意一种。

“钩子(Hook)”可以用来影响服务的生命周期,实现跨服务的代码共用(这是除 Python 类继承之外的另一种代码共用方式)。

任何同步调用都发生在同一操作系统级别的进程和线程,因此,操作系统可以捕获服务抛出的任何异常,并进行现场回溯(堆栈跟踪)。

异步调用需要通过 Redis 路由。消息首先发布到 Redis 上,另一个服务——可能在另一台服务器上——取得该请求。这也是调度器的工作原理,作业(服务)运行请求发布到 Redis 上,目标服务接收该请求作为输入。

有一点需要注意,所有围绕数据来源、格式和传输协议的抽象编程仅仅是为开发人员提供方便。例如,如果有一个需求,需要实现一个 Zato 自身没有提供的功能,那么开发人员总是可以直接访问原始的字节 / 字符串消息。

在活动集群上所做的几乎所有操作,如部署或重新配置,都不需要重启服务器和中断消息流。

GUI

GUI 是在 Django 中使用手写 HTML/CSS/jQuery 的方式编写而成。开发过程遵循诸如 Stephen Few 那样的信息设计和可用性专家所制定的指南。

它有如下主要特征:

  • 集群管理——包含指向关键组件的快速链接,具有向负载均衡器的配置中添加 / 删除服务器,及以服务器和负载均衡器的视角检查服务器是否运行正常的能力

  • 负载均衡器——包含用于更改负载均衡器基础数据的 GUI 和 HAProxy 的配置源代码视图,也可以通过它远程执行 HAProxy 命令及访问统计信息

  • 服务

    • 服务热部署
    • 服务以及请求频率、平均响应时间等基本统计信息列表(也有图表)
    • 查看特定服务的信息,包括服务的暴露通道、所在服务器和服务的基本统计信息
    • 可以从浏览器运行服务的服务调用程序
    • 具有语法高亮显示功能的源代码浏览器,可用于查看服务器上到底安装了什么(因为服务以源代码的形式部署,所以总是可以取得源代码,字节码是在部署过程中生成的)
    • 上传 / 下载 WSDL 文件(虽然 Zato 自身并不用它来验证请求),并使它可以通过公共的 URL 访问
    • 存储和访问请求 / 响应抽样信息(每 N 次请求进行一次抽样)
    • 存储和访问速度慢的请求 / 响应信息(超过设定阀值)
  • 安全——增加和删除安全定义,这些定义可以在通道和传出连接之间重用

  • 通道和传出连接——前者包括 AMQP JMS WebSphere MQ plain HTTP SOAP ZeroMQ ,后者包括 AMQP FTP JMS WebSphere MQ plain HTTP SOAP SQL ZeroMQ 。创建这些类的新对象或者更新已有对象,几乎从不需要重启和编码。

  • Redis NoSQL——一个用于远程执行 Redis 命令的 GUI。同时,它还具有指定数据字典及映射的能力。所谓映射是要表达这样一种对照关系,例如,在 ISO 4217 字符集中美元符号是 USD,但是它的数字码是 840。

  • 用于创建、更新或者手动执行作业的调度器。运行一个作业意味着执行一项服务,并且可以为该服务选择一个静态有效负载作为输入。

  • 在 GUI 中有用颜色标记集群的选项,例如,生产总是蓝色,测试总是绿色及开发总是灰色,这样可以避免“胖手指”综合征。

  • 可用于快速回答下面两个问题的统计信息——哪个服务最慢和哪个服务在给定时间范围内(小时 / 天 / 月 / 年或者任意一个时间段)使用最多。用户可以在浏览器中比较这些数据,也可以导出成 CVS 格式的文件(还可以通过 API 获取)。负载均衡器也会提供它自己的运行时统计信息。

CLI

命令行接口可以用于执行一系列管理操作,包括创建集群组件、对组件进行完整性检查、启动、停止或更新组件或管理加密资料。

下文会对两个命令进行说明。第一个针对正在运行的组件取得其操作系统级的信息。另一个用于创建完整的工作环境,该环境将包含两个服务器、Web 管理界面和一个负载均衡器,每个组件均使用随机产生的加密资料,所有的组件都已经完成配置和设置工作。

复制代码
$ zato info /opt/z1/load-balancer
+--------------------------------+--------------------------------+
| Key | Value |
+=========================+================+
| component_details | {"created_user_host": |
| | "dev1@box1", "version": |
| | "1.1", "component": |
| | "LOAD_BALANCER", "created_ts": |
| | "2013-06-19T14:55:42.027946"} |
+--------------------------------+--------------------------------+
| component_full_path | /opt/z1/load-balancer |
+--------------------------------+--------------------------------+
| component_host | box1/box1 |
+--------------------------------+--------------------------------+
| component_running | True |
+--------------------------------+--------------------------------+
| current_time | 2013-06-20T15:05:12.078273 |
+--------------------------------+--------------------------------+
| current_time_utc | 2013-06-20T13:05:12.078289 |
+--------------------------------+--------------------------------+
| master_proc_connections | [connection(fd=4, family=2, |
| | type=1, |
| | local_address=('127.0.0.1', |
| | 20151), remote_address=(), |
| | status='LISTEN')] |
+--------------------------------+--------------------------------+
| master_proc_create_time | 2013-06-20T13:04:15.440000 |
+--------------------------------+--------------------------------+
| master_proc_create_time_utc | 2013-06-20T11:04:15.440000+00: |
| | 00 |
+--------------------------------+--------------------------------+
| master_proc_name | python |
+--------------------------------+--------------------------------+
| master_proc_pid | 10793 |
+--------------------------------+--------------------------------+
| master_proc_username | dev1 |
+--------------------------------+--------------------------------+
| master_proc_workers_no | 0 |
+--------------------------------+--------------------------------+
| master_proc_workers_pids | [] |
+--------------------------------+--------------------------------+
$
{1}
复制代码
zato quickstart create /opt/qs-1 postgresql localhost 5432 zato1 zato1
localhost 6379
ODB database password (will not be echoed):
Enter the odb_password again (will not be echoed):
Key/value database password (will not be echoed):
Enter the kvdb_password again (will not be echoed):
[1/8] Certificate authority created
[2/8] ODB schema created
[3/8] ODB initial data created
[4/8] server1 created
[5/8] server2 created
[6/8] Load-balancer created
Superuser created successfully.
[7/8] Web admin created
[8/8] Management scripts created
Quickstart cluster quickstart-309837 created
Web admin user:[admin], password:[hita-yabe-yenb-ounm]
Start the cluster by issuing the /opt/qs-1/zato-qs-start.sh command
Visit https://zato.io/support for more information and support options
$

API

Zato 自带的管理服务有文档说明。客户端应用程序可以通过 JSON 或 SOAP 方式调用这些服务,为开发人员或管理员创建可供选择的工具或 GUI。实际上,Zato 提供的基于 Django 的 Web 管理界面就是这样的应用程序——所有的操作都是通过 API 执行,Web 控制台从不直接访问任何配置数据存储区。

总结

Zato 1.1 是个轻量级但完整的 ESB 应用服务器,已经可以用于很多任务。随着时间的推移,会加入更多的功能。具体而言,除了会在工具和 GUI 方面增加一组新内容外,接下来的几个版本将重点提供开箱即用的业务 API,用于连接具体的系统或应用程序。同时,还将为开发人员提供一个开发工具包,使他们可以创建自己的 API。在编写和支持其它集成模式方面,也会完成更多的工作。

关于作者

Dariusz Suchojad有 12 年的企业级架构和软件工程经验,其中 8 年在电信和银行领域从事 EAI/ESB/SOA/BPM/SSO 方面的工作。在剖析专用协议、系统集成开发和洽谈业务等三个方面,以及介于这三者之间的任何方面,他都同样出色。然而,每天不得不使用的低劣解决方案致使他花了太多的夜晚来灭火。于是,他辞掉工作,并在两年间用 16 个月的时间创建了 Zato ——一个基于 Python 的集成和后端服务器平台。

查看英文原文: Zato - Python-based ESB and Backend Application Server

2013-09-27 00:4111603
用户头像

发布了 256 篇内容, 共 81.6 次阅读, 收获喜欢 11 次。

关注

评论

发布
暂无评论
发现更多内容

如何快速上手AIRIOT?

AIRIOT

物联网 低代码平台

模块七作业: 王者荣耀商城异地多活架构设计

凯博无线

idea社区版和商业版有什么区别

源字节1号

软件开发

Oceanbase 读写分离方案探索与优化

OceanBase 数据库

分布式数据库 读写分离 oceanbase

OceanBase数据库荣获领先科技成果“新技术”奖

OceanBase 数据库

oceanbase 数博会

云原生训练营大作业

jjn0703

后端解构复习(一)

卢卡多多

技术栈 6月月更

当我们进行性能优化,我们在优化什么(LightHouse优化实操)

刘悦的技术博客

性能优化 前端 优化 性能优化手册

几经波折,InfluxDB的TSDB C位还能做多久?

CnosDB

IoT 时序数据库 开源社区 CnosDB infra

LabVIEW控制Arduino采集LM35温度传感器数值(基础篇—12)

不脱发的程序猿

单片机 LabVIEW Arduino LIAT 采集LM35温度传感器数值

Vue框架学习笔记 每天学习----四

恒山其若陋兮

6月月更

MPLS协议简述

穿过生命散发芬芳

6月月更 MPLS

InfoQ 极客传媒 15 周年庆征文 | 轻量级状态机COLA StateMachine保姆级入门教程

Zhendong

Java 架构设计 状态机 InfoQ极客传媒15周年庆

程序员们,快把这款AI“魔法”做到手机相机里,求求了

最新动态

内存数据库和磁盘数据库哪个更强?

OceanBase 数据库

内存数据库 oceanbase 磁盘数据库

LabVIEW控制Arduino采集光敏电阻数值(基础篇—14)

不脱发的程序猿

单片机 LabVIEW Arduino LIAT 采集光敏电阻数值

IteratorPattern-迭代器模式

梁歪歪 ♚

设计模式

ChainOfResponsibilityPattern-责任链模式

梁歪歪 ♚

设计模式

【动态规划入门篇】只需三步解决它

知心宝贝

算法 前端 动态规划 Leet Code 6月月更

CommandPattern-命令模式

梁歪歪 ♚

设计模式

OceanBase 成为信通院首批可信开源社区、可信开源项目

OceanBase 数据库

开源 oceanbase 信通院

基于 RDMA 的分布式系统研究进展

OceanBase 数据库

RDMA

高性能API网关Kong介绍

天翼云开发者社区

王者荣耀商城异地多活架构设计

大眼喵

「架构实战营」

LabVIEW控制Arduino采集热敏电阻温度数值(基础篇—13)

不脱发的程序猿

单片机 LabVIEW Arduino LIAT 采集热敏电阻温度数值

利用Python实现自动操作Excel文件

弑着去忘记う

架构训练 模块七

小马

#架构训练营

SPDK对接Ceph性能优化

天翼云开发者社区

开发工具

通用池化框架GenericObjectPool性能测试

FunTester

【Go实现】实践GoF的23种设计模式:原型模式

元闰子

Go 设计模式 原型模式

聊聊 Sharding-Jdbc 的原理-初篇

Nick

MySQL 分库分表 中间件 ShardingJDBC 6月月更

Zato——基于Python的ESB和后端应用服务器_Python_Dariusz Suchojad_InfoQ精选文章