一 前言

“概念完整性是系统设计中最重要的考虑因素”。

//概念完整性:反映一系列连贯的设计思路,像建筑一样,风格统一,易于整合 ------《人月神话》

1 特性

  • 快速开发

    • (1) 多地域配置:配置支持多地域,配置可以统一管理

    • (2) 无需配置路由:根据 handlers 目录自动加载路由(目前不支持动态路由)

    • (3) 显示参数管理:Handler 的参数与 HTTP 参数保持一致,含有参数检查

    • (4) 简易调试模式:简易方便的 DEBUG

    • (5) 引擎之状态机:具有可复用性的状态处理

    • (6) 引擎之工作流:长流程分步执行,可生成 dot 流程图

    • (7) 对象关系映射:自带 ORM

    • (8) 定时任务调度:支持定时执行某些方法

  • 方便运维

    • (1) 请求完整追溯:响应 Header 中包含请求的 reqid(会记录在日志中),便于 trace

    • (2) 自定义响应头:可自定义 HTTP header,如增加固定的接口版本号

    • (3) 代码耗时打点:通过代码打点可以准确获代码执行耗时,用于排查性能问题

    • (4) 线程堆栈打印:发送 kill -10 ${pid} 触发,用于排查程序夯住问题

    • (5) 变量内存打印:发送 kill -12 ${pid} 触发,用于排查内存泄露问题

  • 容易扩展

    • (1) 消息队列通信:开启百川配置即成为消费者,以拉模式消费由其他实例发布的消息

2 架构

2.1 三驾马车

三驾马车是中国古代特有的战车配置形式,其核心特征由一匹辕马与两匹梢(shao)马组成,辕马负责驾辕并保持车辆稳定,梢马负责拉长套以提升速度和爆发力

Butterfly 可以很方便将 Python 函数转换为 Http 服务,cmd,任务队列 task

    +---------------+  +-------------+  +-------------+
    |     cmd       |  |HTTP Request |  |  Queue Msg  |
    +------+--------+  +------+------+  +------^------+
           |                  |                |
  +-----------------------------------------------------+
  |        |                  |                |        |
  | +------V--------+  +------V------+  +------+------+ |
  | |test_handler.py|  |   Protocol  |  |   worker    | | Butterfly Framework
  | +------+--------+  +------+------+  +------+------+ |
  |        |                  |                |        |
  +-----------------------------------------------------+
           |                  |                |
  +--------V------------------V----------------+--------+
  |                         func                        | Application code
  +-----------------------------------------------------+


开发人员将 func code 按照 Butterfly handler 规范放在 <butterfly_project>/handlers/<app_name> package 下

要求:
    (1) 装饰器:添加 @funcattr.api 装饰器
    (2) 参数:第一个参数增加 req
    (3) 返回值:json_status, [content], [headers]
                json_status: (string) 必须有,实际返回给用户时,json_status 也会放到 json 串中,如 "OK", "ERR", "ERR_BAD_PARAMS"
                content    : (dict)非必须,API 返回结果,如 {"data": "test"}
                headers    : (list)非必须,API headers 如 [("demo", "1.0.1")]
调用:
    (1) cmd: python test_handler.py /<app_name>/<handler_name> <args>
    (2) HTTP: 启动 Butterfly 后,可以通过 curl "http://<ip>:<port>/<app_name>/<handler_name>?arg=value"
    (3) Queue: 开启百川后,自动监听对应 /<app_name>/<handler_name> 的消息

2.1.1 HTTP 请求流

       +-------------------------------------------------------------+
       |                        WEB brower                           |
       +-----------------------------------^-------------------------+
     /                      |              |
    |  +--------------------V--------------|-------------------------+
    |  | +----------------HTTPServer(Threadpool&Queue)-------------+ |
    |  | |   +-------------------+ put +-----------------------+   | |
    |  | |   |ThreadPool(Queue) <------+ HTTPConnection        |   | |
    |  | |   |+---------------+  |     | +-------------------+ |   | |
    |  | |   ||WorkerThread   |  |     | |req=HTTPRequest()  | |   | |
    |  | |   ||+-+ +-+ +-+ +-+|  |     | |req.parse_request()| |   | |(把 socket 字节流,按 HTTP 协议解析)
    |  | |   ||+-+ +-+ +-+ +-+|  |     | |req.respond()#^!^  | |   | |(封装了 WSGIGateway.response)
    |  | |   |+-|---|---|---|-+  |     | +-------------------+ |   | |
    |  | |   +--|---|---|---|----+     +-----------------------+   | |
    |  | +------|---|---|---|-----------------------^--------------+ |
    |  |        |   |   |   |                       |                | WSGI server
    |  | +------V---V---V---V-WSGIGateway(response)-|--------------+ |
    |  | |                       +------------------+-------------+| |
    |  | |+----------------+     | +----------+   +-------------+ || |
    |  | ||   gen environ  |     | |header_set|   |response body| || |
    |  | |+-----+----------+     | +----------+   +-------------+ || |
    |  | |      |                +--^------^----------^-----------+| |
    |  | +------|-------------------|------|----------|------------+ |
    |  +--------|-------------------|------|----------|--------------+
    |  .........|...................|......|..........|......................
    |           |                   |      |          |
    |  +--------V--------+          |      |          |
    |  |       req       |          |      |          |        (1) 封装 environ 为 Request
    |  +-----------------+          |      |          |            生成 reqid
Butterfly       |                   |      |          |
    |           |                   |      |          |      \
    |  +--------V--------+          |      |          |       |(2) 路由
    |  |  apiname_getter |          |      |          |       |    在路由字典中匹配 environ['PATH_INFO']
    |  +-----------------+          |      |          |       |    {
    |           |                   |      |          |       |      '/apidemo/ping':
    |           |                   |      |          |       |        <xlib.protocol_json.Protocol object>,
    |  +--------V--------+ False +--+--+   |          |       |      '/{app}/{handler}':
    |  |is_protocol_exist|------>| 400 |   |          |       |        <xlib.protocol_json.Protocol object>
    |  +-----------------+       +-----+   |          |       |    }
    |           |                          |          |      /
    |  .........|..........................|..........|......................
    |           | (protocol_process)       |          |      \
    |           V                          |          |       |(3) 返回 Response
    |  +-----------------+                 |          |       |    参数:第一个参数为 Request 实例化对象 req
    |  | protocol        | Exception    +-----+       |       |          其他参数为 GET 请求参数名
    |  | +-------------+ |------------->| 500 |       |       |    例子:environ['QUERY_STRING']: 'age=16'
    |  | |/app1/handler| |              +-----+       |       |    +-------------handler demo---------------
    |  | |/app2/handler| |Normal+----------------------------+|    |@funcattr.api      # 标识 handler 类型
    |  | +--+-------+--+ |----->|httpstatus, headers, content||    |def demo(req, age):# req + HTTP 请求参数
    |  +----|-------|----+      +----------------------------+|    |   #(状态信息,数据信息,响应头列表)
    |       |       |                                         |    |   return "OK", {"data": age}, []
    |       |       |                                        /     +----------------------------------------
    |       |  +----V----------------------------------+
    |       |  |+---------+  +---------+  +-----------+|
    |       |  ||DiskCache|  |   FSM   |  |APScheduler||       基础公共库
    |       |  |+---------+  +---------+  +-----------+|
    |       |  +---------------------------------------+
    |  +----V------------------------------------------+
    |  |       (Redis ORM) / (MySQL ORM) / RAL         |       数据访问层
     \ +-----------------------------------------------+

2.1.2 Queue msg

+-----------------+
| register worker | 注册 worker
+-------+---------+
        |
+-------V---------+
|check_task_status| 检查任务的状态
+-------+---------+
        |
+-------V---------+
|queue.dequeue_any| 随机从多个队列中获取消息
+-----------------+
        |
+-------V-----------------+
|msg.set_status("started")| 设置消息状态
+-------+-----------------+
        |
+-------V---------+
|   pool.submit   | 提交到多线程队列中
+-----------------+

Last updated