一 前言
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 架构(三驾马车)
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 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.2 Queue msg
+-----------------+
| register worker | 注册 worker
+-------+---------+
        |
+-------V---------+
|check_task_status| 检查任务的状态
+-------+---------+
        |
+-------V---------+
|queue.dequeue_any| 随机从多个队列中获取消息
+-----------------+
        |
+-------V-----------------+
|msg.set_status("started")| 设置消息状态
+-------+-----------------+
        |
+-------V---------+
|   pool.submit   | 提交到多线程队列中
+-----------------+2.3 cmd
$ python test_handler.pyLast updated