salt raet

salt raet

  1. 简介

    1
    salt raet 是继 Salt-Zeromq,Salt-Ssh 之后的第三套通信体系,全名为"Reliable Asynchronous Event Transport"(即基于事件的可靠异步传输协议)
  2. 为什么研发salt raet?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    现代大规模的分布式应用架构,其组件均是分布在互联网上的多个主机和多个CPU内核,往往是基于一个消息或事件总线,允许不同的分布式组件之间相互异步通信.
    通常情况下,消息总线是某种形式的消息队列服务,如: AMQP 或 ZeroMQ.
    消息总线支持通常被称为"发布/订阅"模式的信息交互方式

    一个具备完整功能的消息队列服务拥有很多的优点,然而,其存在的缺陷之一便是"大规模应用环境下的性能问题"

    一个消息队列服务完成两个独立而互补的功能:
    第一个是,在互联网上消息的异步传输
    第二个是,消息队列管理,这便是通过队列实现的诸如:消息识别,跟踪,存储,以及发布者和订阅者相互之间的消息分发

    对于众多应用程序而言,一个消息队列服务的优势之一便是通过API能很好的隐藏服务背后的细节,也就是其来自各个客户端的消息队列管理的复杂性.但是,消息队列服务存在的主要缺陷便是"大规模应用环境下的扩展性",其消息的容量,消息的定时以及内存,网络和CPU处理能力相关的需求往往成为关键,而客户端对于服务性能的调整往往显得无能为力.MQ服务在分布式的应用环境下通常会成为瓶颈所在,而更复杂的MQ服务,如 AMQP,则会在高负载下变得不可靠

    异步事件的网络传输 和 消息队列管理之间功能的分离,使得每个功能模块可以独立的调整各自大规模环境下的性能

    绝大部分的MQ服务是基于TCP/IP网络传输协议.TCP/IP对于网络通信有显著的"延迟效应",因而不太适合用于分布式事件驱动式应用程序交互的异步特性.这其中主要的原因便是在于"TCP/IP为了支持流传输针对'连接的建立'和'连接的关闭'以及'失败连接'的处理".从根本上来说,"TCP/IP是基于大规模的连续数据流作出了诸多优化,而不太适用于大量小规模异步事件或消息的传输".其在小规模应用系统下不成问题,而一旦达到一定规模,相关的通信特征的差异问题将会凸显.

    UDP/IP低延迟和无连接的特性注定它更适用于许多小规模异步消息的传输.UDP/IP本身的缺点在于它是不可靠的传输协议.这里所需要的便是一个适配的针对UDP/IP协议增添可靠性,而同时无损其低延迟及扩展性的传输协议.一个事务型协议,比流协议更适用于为异步事件提供可靠传输.进一步来说,由于大部分的MQ服务是基于TCP/IP协议,他们也更倾向于使用HTTP或者保证安全通信的TLS/SSL.虽然使用HTTP能够轻松的提供基于Web的集成系统,但是长远来说它也会成为高性能系统的瓶颈所在,TLS对于一个安全系统而言,其性能和漏洞两方面也同样存在问题.

    椭圆曲线加密,另一方面来说,在相对于其他实现方法更低的性能需求的前提下增强了系统的安全性.LibSodium提供了一个开源的椭圆曲线加密库,用于验证和加密支持.CurveCP协议基于LibSodium提供了一个引导安全网络信息交互的握手协议.最后,在分布式并发事件驱动的应用环境下管理和协调处理器资源(CPU,内存,网络)的一个最佳途径便是使用一种称为微线程的东西.一个微线程应该说是一个程序语言级的特性,它在不比函数调用消耗更多资源的情况下实现了代码逻辑上的并发.

    微线程使用协同工作的多任务处理来取代线程和/或进程,其避免了许多诸如资源竞争,上下文切换,和进程间通信的复杂性,同时提供了更高的性能.由于所有协同工作的微线程均运行于一个进程,也就造成一个简单的微线程应用的资源调用被限制在一个CPU核心.为了使得所有的CPU核心均得到充分的利用,应用程序需要能够为每个CPU核心运行至少一个进程.这就需要同一台主机的进程间通信.但是不同于传统的多进程处理方式,即一个进程完成一个逻辑并发的功能,一个基于微线程的多进程程序不再使用一个微线程处理一个逻辑并发功能的模式,而微线程的总数是取决于总的进程的最小数目的限制,并且其不超过CPU的核心数量.这将优化CPU的处理能力,同时最大限度地减少进程上下文切换的开销.

    一个使用这样的"微线程-多进程"架构平台的典型例子便是Erlang.事实上,Erlang模式的成功为RAET方案的可行性提供了强有力的支持.进一步来说,一个潜在的问题可能是:我们为什么不使用Erlang呢? 很不幸的是,Erlang生态系统跟Python相比较而言有些时候显得有些局限性,而它本身则使用了一种不太友好的语法结构. RAET的设计实现目标之一便是充分集成现有的Python生态系统丰富的类库和知识集,同时又能够方便的开发一个基于"微线程-多进程"架构模型的分布式应用程序.我们的终极目标便是想两全其美.

    RAET设计用于借助"微线程-多进程"应用框架提供在互联网上安全可靠的,可扩展的异步消息/事件传输,其使用UDP协议来完成主机间的通信,LibSodium来完成认证,加密,CurveCP握手协议来达成安全引导.相应的队列管理和微线程应用程序的支持由Ioflo提供.RAET可以算作是Ioflo的一个互补项目,它使得多个Ioflo程序可以通过网络结合在一起,作为一个分布式程序的一部分协同工作.

    导致开发RAET的一个主要驱动因素便是"使得Saltstack具备更好的扩展性的需求".Saltstack是一个使用Python编写的远程执行和配置管理平台.Saltstack使用Zeromq作为它的消息总线或者说消息队列服务.Zeromq是基于TCP/IP传输协议实现的,因而也存在上述相应的TCP/IP基础架构下的延迟及非同步性问题.此外,因为Zeromq是通过一个特殊的"套接字"将队列管理和传输集成在一起,其大规模应用环境下的队列的独立传输性能也成为问题所在,甚至于跟踪Bug也存在一定的困难.
  3. raet安装

    1
    pip install raet
  4. Raet支持两种通信方式

    1
    2
    3
    Raet支持两种通信方式:
    * 主机间通过UDP/IP协议套接字通信
    * 通过Unix域(UXD)套接字实现的同一主机进程间通信

RaetMetaphor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
如上图:
Road,Estates,Main Estate:
* UPD通道便是一个"Road"
* 一个Road的成员便是"Estates"(就像现实中房屋前方的道路)
* 每个Estate拥有一个唯一的UDP主机端口地址"ha",一个唯一的字符串"name"和一个唯一的数字ID"eid"
* 一个Road上的Estate便称为"Main Estate"
* Main Estate允许其他Estates通过Join方法(key交换)加入到Road上并且允许(CurveCP)相互之间的信息交易
* Main Estate同样负责担任其他Estates的消息路由

Lane,Yards,Main Yard:
* 每个Estate都可以是一个"Lane",这便是一个UXD通道
* 一个Lane的成员便是"Yard"(简单来说便是一个Estate的细分点)
* 一个Lane的每个Yard成员都有一个唯一的UXD文件名称,主机地址"ha"和一个唯一的字符串"name".这个类通常也有一个Yard的数字ID"yid"用于生成Yard name但是它不是Yard实例的属性之一
* Lane name和Yard name结合起来便可以形成一个唯一的文件名,它是UXD的主机地址"ha"
* 一个Lane上的Yard便是一个Main Yard
* Main Yard负责形成Lane并且允许其他Yards加入到该lane;在此之前这还没有一个正式的处理过程.当前这会设置一个由Main yard维护的标志,其将会把任何事先未在Yards列表里的Yard发送过来的数据包Drop掉.另外,文件权限的设置可以用来阻止伪Yards与Main Yard交互
* Main Yard还负责其他Lane上的Yards之间的消息路由

运行IoFlo:
* 每个Estate UDP接口运行于一个在单个IoFlo House上下文中工作的RoadStack(UDP套接字),因此可以把运行UDP Stack的House看作是Estate组成的庄园别墅
* 每个Yard UXD接口运行于一个在单个IoFlo House上下文中工作的 LaneStack(Unix域套接字),因此可以把运行UXD Stack的House看作是Estate的附属Houses、Tents或Shacks
* "庄园"House的特殊之处在于其可以运行在针对Estate的UDP Stack和针对Main Yard的UXD Stack两套环境下运行
* Main Estate UDP Stack的House可以被认为是Mayor's House;
* 在上下文中,一个House便是一个数据存储.共享的数据存储可以被以一个点号分隔路径的唯一共享名称定位

路由
鉴于上面所描述的Ioflo运行架构,其依照如下方式完成路由:
* 为了定位一个特定的Estate,Estate name需要相应的指定
* 为了定位一个Estate下的特定Yard,Yard name需要相应的指定
* 为了定位一个House下的特定Queue,Share name需要相应的指定

UDP stack 将Estate name映射到UDP主机地址和Estate ID,而UXD stack 将Yard name映射到UXD主机地址,任意IoFlo行为的存储将Share name映射到共享引用.因此,路由是从: 在一个源Share、源Yard或是一个源Estate中的队列定义好的一个源节点到,一个源Share、源Yard或是一个源Estate中的队列定义好的目的节点

这里需要两个三选一的节点,一个是源节点,另一个则是目标节点:
* 源节点(Estate name,Yard name,Share name)
* 目标节点(Estate name,Yard name,Share name)
如果三合一中的任一元素均是None或者空,那么便会使用默认参数值