4. 输入输出绑定实战
在 Azure Functions 中,触发器负责“什么时候执行”,而输入输出绑定负责“执行时能拿到什么、执行后把结果放到哪里”。绑定分为输入绑定和输出绑定:输入绑定把外部数据以参数形式注入函数,输出绑定把函数返回值或 out 参数写回目标服务。它的核心价值在于把 SDK 客户端创建、连接字符串读取、序列化、重试、关闭连接等胶水代码从业务逻辑中剥离,让函数体只保留“处理数据”这一件事。
绑定通过 function.json(C# 脚本、JavaScript、Python 等)或特性(C# 类库)声明。一个绑定声明通常包含四个关键字段:name 是函数签名中的参数名,type 是绑定类型,direction 是 in/out,connection 指向应用设置中的连接字符串键名。下面这张图展示了同一函数同时使用 Blob 输入、Queue 输出、Cosmos DB 输出和 Event Hubs 触发时的绑定关系与数据流向。
以 Python 为例,一个同时使用 Blob 输入和 Queue 输出的函数,其 function.json 可以这样声明:
{
"bindings": [
{
"name": "myBlob",
"type": "blobTrigger",
"direction": "in",
"path": "samples-workitems/{name}",
"connection": "AzureWebJobsStorage"
},
{
"name": "outputQueue",
"type": "queue",
"direction": "out",
"queueName": "processed-items",
"connection": "AzureWebJobsStorage"
}
]
}
对应的 Python 函数体只需要声明参数并返回值:
import logging
import azure.functions as func
def main(myBlob: func.InputStream, outputQueue: func.Out[str]):
content = myBlob.read().decode("utf-8")
logging.info(f"Blob 大小: {myBlob.length} 字节")
outputQueue.set(f"已处理: {myBlob.name}, 长度: {len(content)}")
这里没有 BlobServiceClient、没有 QueueClient、没有连接字符串解析、没有 close() 调用。运行时负责创建客户端、读取 Blob 内容、把字符串写入队列,并在函数结束后释放资源。这就是“减少胶水代码”的直接体现。
Cosmos DB 输出绑定同样简洁。假设要把处理结果写入 orders 数据库的 processed 集合,function.json 中增加:
{
"name": "outputDocument",
"type": "cosmosDB",
"direction": "out",
"databaseName": "orders",
"collectionName": "processed",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDBConnection"
}
函数中通过 outputDocument.set(json.dumps(result)) 写入文档。注意 createIfNotExists 在开发阶段很方便,但生产环境建议预先创建集合,避免权限或吞吐量配置被意外覆盖。
Event Hubs 作为触发器时,绑定声明需要指定 eventHubName、consumerGroup 和 connection。一个常见配置如下:
{
"name": "events",
"type": "eventHubTrigger",
"direction": "in",
"eventHubName": "telemetry",
"consumerGroup": "$Default",
"connection": "EventHubConnection",
"cardinality": "many"
}
cardinality 设为 many 时,函数接收的是事件数组,适合批量处理;设为 one 时每次只处理一条。批量处理能显著降低函数调用次数和写入压力,但要注意单次执行时间上限和批大小控制。
connection 字段都指向应用设置中的键名,而不是直接写连接字符串。本地开发时放在 local.settings.json 的 Values 节点;部署到 Azure 后放在 Function App 的“配置”中。这样同一份代码可以在不同环境切换目标资源,无需修改绑定声明。
绑定虽然减少了胶水代码,但并非所有场景都适合。当需要复杂查询、跨多个集合事务、精细控制重试策略或使用服务特有高级特性时,仍然应该使用 SDK。判断标准很简单:如果绑定能覆盖 80% 的常规读写,就用绑定;如果业务逻辑需要条件查询、分页、批量操作或自定义序列化,就注入 SDK 客户端。两者可以共存,例如用 Event Hubs 触发器接收事件,用 Cosmos DB SDK 执行复杂查询,再用 Queue 输出绑定发送通知。
最后给出一个可参考的绑定配置检查清单,用于在部署前快速核对:
| 检查项 | 说明 | 常见错误 |
|---|---|---|
| name 与函数签名一致 | 参数名必须与 function.json 中 name 完全相同 | 大小写不一致导致绑定失败 |
| direction 正确 | 输入用 in,输出用 out | 把输出写成 in,运行时无法写入 |
| connection 键存在 | 应用设置中必须有对应键名 | 本地能跑,部署后报连接字符串缺失 |
| path / queueName 有效 | 目标容器、队列或数据库已存在 | 路径拼写错误或资源未创建 |
| 输出类型匹配 | func.Out[str] 对应字符串,func.Out[bytes] 对应字节 | 类型不匹配导致序列化异常 |
掌握绑定的声明式配置后,函数代码会变得非常薄:输入参数直接可用,输出通过 set 或返回值写出,连接和生命周期交给运行时。这样既降低了出错概率,也让单元测试更容易——只需构造输入参数、调用函数、检查输出对象,无需模拟 SDK 客户端。