调用HTTP接口

一般来说,推荐使用 deepfos.elements 中封装好的元素功能。

在以下情况,您可能需要直接调用HTTP接口。

  • 现有元素功能不能满足需要,但是有合适的接口

  • 元素功能引入了额外的接口调用,您希望优化性能

在阅读本节文档前,预设读者有基础的HTTP通信的相关知识,并且有一定的Python开发经验。 如果您认为您将要使用HTTP接口实现的功能有相当的通用性,欢迎您向deepfos提交PR。 流程可以参考这篇文档: 如何贡献

前言

deepfos对HTTP接口的封装是多层级的。 例如一个web服务提供以下接口:

GET controller-1/resource-a
GET controller-1/resource-b
GET controller-2/resource-c
GET controller-2/resource-d

那么deepfos中会对应封装出以下方法:

api.controller_1.get_resource_a
api.controller_1.get_resource_b
api.controller_2.get_resource_c
api.controller_2.get_resource_d

Package deepfos.api 中所有对外的api都以上述形式组织。

HTTP接口分为系统接口和组件接口两大类。仅实例化方法略有不同,使用逻辑是完全一致的。

调用系统接口

已封装的接口可以在文档: HTTP接口 中看到。

../_images/http_api_doc.png

所有可以实例化的接口都会在此处展示。

简单例子

以AppAPI为例,如果需要调用下图中的 get_server_names 方法

../_images/doc_appapi.png

则可以使用

from deepfos.api.app import AppAPI
from deepfos.api.models.app import QueryElementInfoByTypeDto

api = AppAPI()
api.element_info.get_server_names([
    QueryElementInfoByTypeDto(elementType='PY', elementName='test', path='/'),
    QueryElementInfoByTypeDto(elementType='SML', elementName='test', path='/'),
])

所有的接口函数都有完整的类型提示,绝大多数情况下,您只要通过查看函数签名,不需要查看接口的swagger文档就可以完成一个接口的调用。

使用原生python对象

有时候为了调用一个简单接口而特意组装一个python对象有些大费周章。您可能更熟悉python的原生数据结构,如 list, dict 等。 接口函数允许您不按照函数签名,而直接传入一个更接近 json 数据的参数。

同样以 get_server_names 方法为例,您还可以这样调用:

api.element_info.get_server_names([
    {'elementName': 'test', 'elementType': 'PY', 'path': '/'},
    {'elementName': 'test', 'elementType': 'SML', 'path': '/'},
])

尽管IDE可能会给您一些警告,但实际上这不会有任何问题,甚至比第一种调用方式性能更高!

隐藏的接口参数

除了签名所含有的参数外,接口函数还能够接收一些 “隐藏参数”

resp_model

接口的返回模型

retries

接口调用失败时的重试次数

resp_model 可以影响接口函数的返回。

按照签名, get_server_names 应该返回 List[ModuleServerNameVO] 对象,但假如希望拿到原始的 json 对象, 可以这样调用:

r = api.element_info.get_server_names([
    {'elementName': 'test', 'elementType': 'PY', 'path': '/'},
    {'elementName': 'test', 'elementType': 'SML', 'path': '/'},
], resp_model=None)

assert isinstance(r, list)
assert isinstance(r[0], dict)

此时函数将返回 List[Dict] 类型的对象。

您也可以传入其他有效的 Pydantic Model,如果能成功解析,返回的结果将会符合您定义的数据模型。

调用组件接口

调用组件接口与 调用系统接口 逻辑一致。但是由于组件可能存在多版本,实例化时必须指定组件版本。

api = MySQLAPI(version=1.0)
r = api.dml.run_sql('select 1')

异步调用接口

接口函数不仅能够同步调用,也可以异步调用:

import asyncio

async def run_sql():
    api = MySQLAPI(version=1.0, sync=False)
    r = await api.dml.run_sql('select 1')

asyncio.run(run_sql())

所有API在实例化时还接受一个可选参数 sync,其默认值为 True,即同步调用。 当传入 False 时,所有接口函数将变成异步函数。

异步编程常用于解决 I/O 并发问题,相较于线程,协程开销更小,切换更快,执行顺序明确, 而且只要内存允许,能够达到极高的并发量。

并发调用接口示例:

import asyncio

async def concurrently_run_sqls():
    api = MySQLAPI(version=1.0, sync=False)

    r = await asyncio.gather(
        api.dml.run_sql('select 1'),
        api.dml.run_sql('select 2'),
        api.dml.run_sql('select 3'),
        api.dml.run_sql('select 4'),
    )

asyncio.run(concurrently_run_sqls())

配置项

有些配置项能够全局影响接口的行为。

OPTION.api.verify_ssl

是否开启ssl校验

OPTION.api.timeout

默认超时时间

OPTION.api.dump_on_failure

是否在调用接口失败时输出curl命令

OPTION.api.dump_always

是否在所有接口调用时输出curl命令