Azure Function上传至Storage Account速度慢、耗时非线性问题咨询
Azure Function 向 Storage Account 上传大文件耗时异常、触发超时
问题背景
- 业务需求:从Azure Function向关联Storage Account上传最大0.5GB的文件,当前上传耗时远超预期,超过10分钟触发Consumption计划超时限制
- 异常表现:文件大小与上传耗时呈非线性关系:160MB文件上传耗时1.5分钟,290MB文件上传耗时超过10分钟,查阅官方文档、社区公开资料均未定位根因
- 运行环境:Function App部署在Linux平台,Runtime版本4.0,采用Consumption托管计划(该计划最长执行时长限制为10分钟,已将host.json中超时参数配置为该计划允许的最大值)
- 瓶颈排查:通过运行日志验证,网站数据爬取、数据转换为CSV格式的环节耗时极短,不属于性能瓶颈,超时全部发生在文件上传阶段
现有配置
function.json
{ "scriptFile": "az_func.py", "bindings": [ { "name": "mytimer", "type": "timerTrigger", "direction": "in", "schedule": "0 0 0 * * 2" }, { "name": "$return", "type": "blob", "path": "cvmdailyreports/test_func_json.csv", "connection": "AzureWebJobsCVMDataStorageConn", "direction": "out" } ] }
az_func.py 业务代码
import datetime import logging import azure.functions as func from src.data.import_data import import_daily_reports from src.data.transform_data import transform_daily_reports def main(mytimer: func.TimerRequest): utc_timestamp = datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat() if mytimer.past_due: logging.info('The timer is past due!') logging.info('Python timer trigger function ran at %s', utc_timestamp) csv = transform_daily_reports(import_daily_reports(n=8)).to_csv(index=False) logging.info('The csv was created, sending to upload now.') return csv
根因说明
当前使用的Blob输出绑定默认走单块同步上传逻辑,各版本Python Worker对应的Blob SDK单块上传阈值在250MB-270MB区间。文件大小超过该阈值后,绑定层不会自动触发分块上传,反而会因为全量内容内存拷贝、上传失败全量重传的机制,导致耗时非线性暴涨。这个默认实现本身仅适配小文件上传场景,完全不适合100MB以上文件传输,耗时过长不属于正常现象。
优化方案
- 弃用默认的Blob输出绑定,直接在代码中调用Azure Blob Storage SDK v12+的高级上传接口,自动走分块并发上传逻辑。该接口会根据文件大小自动拆分上传块,支持块级并发传输、失败块单独重传,同区域部署场景下290MB文件上传耗时可控制在10秒内,0.5GB文件上传耗时不超过30秒。
- 先修改function.json,删除
$return对应的Blob输出绑定配置,避免绑定层重复执行上传逻辑。 - 替换原有代码中的返回逻辑,直接通过SDK上传,参考实现如下:
import os import datetime import logging import azure.functions as func from azure.storage.blob import BlobClient from src.data.import_data import import_daily_reports from src.data.transform_data import transform_daily_reports def main(mytimer: func.TimerRequest): utc_timestamp = datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat() if mytimer.past_due: logging.info('The timer is past due!') logging.info('Python timer trigger function ran at %s', utc_timestamp) csv_bytes = transform_daily_reports(import_daily_reports(n=8)).to_csv(index=False).encode("utf-8") logging.info('The csv was created, starting parallel upload.') # 复用现有存储连接字符串配置 blob_client = BlobClient.from_connection_string( conn_str=os.environ["AzureWebJobsCVMDataStorageConn"], container_name="cvmdailyreports", blob_name="test_func_json.csv" ) # 根据Consumption计划实例默认1Gbps网卡带宽,配置8并发、4MB单块大小即可打满带宽 blob_client.upload_blob( csv_bytes, overwrite=True, max_concurrency=8, block_size=4*1024*1024 ) logging.info('Upload completed.') - 先修改function.json,删除
- 若后续单文件大小超过1GB,可先将生成的CSV写入Function临时目录
/tmp(Consumption计划下该目录可用空间不低于1GB),再通过SDK上传文件流,进一步降低内存开销,避免大文件内存占用过高触发实例回收导致上传中断。 - 若后续存在更大文件、更稳定的上传需求,建议将Consumption计划替换为Premium计划:一是Premium计划最长执行时长可配置为60分钟,不会受10分钟超时限制;二是Premium计划实例与Storage Account之间走内网专用带宽,网络抖动更小,上传速度更稳定。
内容的提问来源于stack exchange,提问作者guidbem
相关产品推荐
相关产品推荐

