Azure 函数
Azure Functions
Azure Functions 开发专家模式,包括隔离工作进程模型、Durable Functions 编排、冷启动优化及生产环境模式。涵盖 .NET、Python 和 Node.js 编程模型。
模式 (Patterns)
隔离工作进程模型 (.NET)
具有进程隔离功能的现代 .NET 执行模型
适用场景:构建新的 .NET Azure Functions 应用
模板
// Program.cs - 隔离工作进程模型
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
// 添加 Application Insights
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
// 添加 HttpClientFactory (防止套接字耗尽)
services.AddHttpClient();
// 添加自定义服务
services.AddSingleton<IMyService, MyService>();
})
.Build();
host.Run();
// HttpTriggerFunction.cs
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
public class HttpTriggerFunction
{
private readonly ILogger<HttpTriggerFunction> _logger;
private readonly IMyService _service;
public HttpTriggerFunction(
ILogger<HttpTriggerFunction> logger,
IMyService service)
{
_logger = logger;
_service = service;
}
[Function("HttpTrigger")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
_logger.LogInformation("Processing request");
try
{
var result = await _service.ProcessAsync(req);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(result);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing request");
var response = req.CreateResponse(HttpStatusCode.InternalServerError);
await response.WriteAsJsonAsync(new { error = "Internal server error" });
return response;
}
}
}
注意事项
- 进程内 (In-process) 模型将于 2026 年 11 月弃用
- 隔离工作进程支持 .NET 8, 9, 10 以及 .NET Framework
- 全面支持依赖注入 (DI)
- 支持自定义中间件
Node.js v4 编程模型
适用于 TypeScript/JavaScript 的现代以代码为中心的开发方式
适用场景:构建 Node.js Azure Functions
模板
// src/functions/httpTrigger.ts
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
export async function httpTrigger(
request: HttpRequest,
context: InvocationContext
): Promise<HttpResponseInit> {
context.log(Http function processed request for url "${request.url}");
try {
const name = request.query.get("name") || (await request.text()) || "world";
return {
status: 200,
jsonBody: { message: Hello, ${name}! }
};
} catch (error) {
context
.error("Error processing request:", error);
return {
status: 500,
jsonBody: { error: "Internal server error" }
};
}
}
// 使用 app 对象注册函数
app.http("httpTrigger", {
methods: ["GET", "POST"],
authLevel: "function",
handler: httpTrigger
});
// 定时触发器示例
app.timer("timerTrigger", {
schedule: "0 */5 * * * *", // 每 5 分钟执行一次
handler: async (myTimer, context) => {
context.log("Timer function executed at:", new Date().toISOString());
}
});
// Blob 触发器示例
app.storageBlob("blobTrigger", {
path: "samples-workitems/{name}",
connection: "AzureWebJobsStorage",
handler: async (blob, context) => {
context.log(Blob trigger processing: ${context.triggerMetadata.name});
context.log(Blob size: ${blob.length} bytes);
}
});
注意事项
- v4 模型以代码为中心,无需
function.json文件
- 使用类似于 Express.js 的
app对象
- 原生支持 TypeScript
- 所有触发器均在代码中注册
Python v2 编程模型
针对 Python 函数的基于装饰器(Decorator)的方法
适用场景:构建 Python Azure Functions
模板
function_app.py
import azure.functions as func import logging import jsonapp = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)
@app.route(route="hello", methods=["GET", "POST"])
async def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
logging.info("Python HTTP trigger function processed a request.")
try:
name = req.params.get("name")
if not name:
try:
req_body = req.get_json()
name = req_body.get("name")
except ValueError:
pass
if name:
return func.HttpResponse(
json.dumps({"message": f"Hello, {name}!"}),
mimetype="application/json"
)
else:
return func.HttpResponse(
json.dumps({"message": "Hello, World!"}),
mimetype="application/json"
)
except Exception as e:
logging.error(f"Error processing request: {str(e)}")
return func.HttpResponse(
json.dumps({"error": "Internal server error"}),
status_code=500,
mimetype="application/json"
)
@app.timer_trigger(schedule="0 */5 * * * *", arg_name="myTimer")
def timer_trigger(myTimer: func.TimerRequest) -> None:
logging.info("Timer trigger executed")
@app.blob_trigger(arg_name="myblob", path="samples-workitems/{name}",
connection="AzureWebJobsStorage")
def blob_trigger(myblob: func.InputStream):
logging.info(f"Blob trigger: {myblob.name}, Size: {myblob.length} bytes")
@app.queue_trigger(arg_name="msg", queue_name="myqueue",
connection="AzureWebJobsStorage")
def queue_trigger(msg: func.QueueMessage) -> None:
logging.info(f"Queue message: {msg.get_body().decode('utf-8')}")
注意事项
- v2 模型使用装饰器,无需
function.json文件
- Python 采用进程外运行(始终隔离)
- Python 需要基于 Linux 的托管环境
- 支持异步函数
Durable Functions - 函数链 (Function Chaining)
具有状态持久化的顺序执行
适用场景:需要具有自动重试机制的顺序工作流
模板
// C# 隔离工作进程 - 函数链
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
public class OrderWorkflow
{
[Function("OrderOrchestrator")]
public
static async Task<OrderResult> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var order = context.GetInput<Order>();
// 函数顺序执行,状态在每次执行之间持久化
var validated = await context.CallActivityAsync<ValidatedOrder>(
"ValidateOrder", order);
var payment = await context.CallActivityAsync<PaymentResult>(
"ProcessPayment", validated);
var shipped = await context.CallActivityAsync<ShippingResult>(
"ShipOrder", new ShipRequest { Order = validated, Payment = payment });
var notification = await context.CallActivityAsync<bool>(
"SendNotification", shipped);
return new OrderResult
{
OrderId = order.Id,
Status = "Completed",
TrackingNumber = shipped.TrackingNumber
};
}
[Function("ValidateOrder")]
public static async Task<ValidatedOrder> ValidateOrder(
[ActivityTrigger] Order order, FunctionContext context)
{
var logger = context.GetLogger<OrderWorkflow>();
logger.LogInformation("Validating order {OrderId}", order.Id);
// 验证逻辑...
return new ValidatedOrder { /* ... */ };
}
[Function("ProcessPayment")]
public static async Task<PaymentResult> ProcessPayment(
[ActivityTrigger] ValidatedOrder order, FunctionContext context)
{
// 带有内置重试机制的支付处理...
return new PaymentResult { /* ... */ };
}
[Function("OrderWorkflow_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext context)
{
var order = await req.ReadFromJsonAsync<Order>();
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"OrderOrchestrator", order);
return client.CreateCheckStatusResponse(req, instanceId);
}
}
笔记
- 活动(Activity)之间的状态自动持久化
- 针对瞬时故障自动重试
- 可在进程重启后恢复
- 内置用于监控的状态端点
Durable Functions - 扇出/扇入 (Fan-Out/Fan-In)
并行执行并聚合结果
适用场景:并行处理多个项目
模板
// C# 隔离工作进程 - 扇出/扇入
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
public class ParallelProcessing
{
[Function("ProcessImagesOrchestrator")]
public static async Task<ProcessingResult> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var images = context.GetInput<List<string>>();
// 扇出:并行启动所有任务
var tasks = images.Select(image =>
context.CallActivityAsync<ImageResult>("ProcessImage", image));
// 扇入:等待所有任务完成
var results = await Task.WhenAll(tasks);
// 聚合结果
var successful = results.Count(r => r.Success);
var failed = results.Count(r => !r.Success);
return new ProcessingResult
{
TotalProcessed = results.Length,
Successful = successful,
Failed = failed,
Results = results.ToList()
};
}
[Function("ProcessImage")]
public static async Task<ImageResult> ProcessImage(
[ActivityTrigger] string imageUrl, FunctionContext context)
{
var logger = context.GetLogger<ParallelProcessing>();
logger.LogInformation("Processing image: {Url}", imageUrl);
try
{
// 图像处理逻辑...
await Task.Delay(1000); // 模拟工作
return new ImageResult
{
Url = imageUrl,
Success = true,
ProcessedUrl = $"processed-{imageUrl}"
};
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to process {Url}", imageUrl);
return new ImageResult { Url = imageUrl, Success = false };
}
}
// Python 等效代码
// @app.orchestration_trigger(context_name="context")
// def process_images_orchestrator(context: df.DurableOrchestrationContext):
// images = context.get_input()
//
// # 扇出 (Fan-out):创建并行任务
// tasks = [context.call_activity("ProcessImage", img) for img in images]
//
// # 扇入 (Fan-in):等待所有任务完成
// results = yield context.task_all(tasks)
//
// return {"processed": len(results), "results": results}
}
注意事项
- 独立任务采用并行执行
- 所有任务完成后汇总结果
- 内存高效 —— 仅存储任务 ID
- 支持多达数千个并行活动
冷启动优化
降低生产环境中的冷启动延迟
适用场景:生产环境需要快速响应时间
模板
// 1. 使用带有预热实例的 Premium 计划
// host.json
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
},
"functionTimeout": "00:30:00"
}
// 2. 添加预热触发器 (Premium 计划)
[Function("Warmup")]
public static void Warmup(
[WarmupTrigger] object warmupContext,
FunctionContext context)
{
var logger = context.GetLogger("Warmup");
logger.LogInformation("Warmup trigger executed - initializing dependencies");
// 预初始化昂贵的资源
// 例如:数据库连接、HttpClients 等
}
// 3. 通过 DI 使用静态/单例客户端
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// HttpClientFactory 防止套接字耗尽
services.AddHttpClient<IMyApiClient, MyApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.Timeout = TimeSpan.FromSeconds(30);
});
// 为昂贵的初始化过程使用单例
services.AddSingleton<IExpensiveService>(sp =>
{
// 初始化一次,在多次调用中复用
return new ExpensiveService();
});
}
}
// 4. 减小包体积
// .csproj - 排除不必要的依赖
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
</PropertyGroup>
// 5. 使用包部署运行
// Azure CLI
// az functionapp deployment source config-zip \
// --resource-group myResourceGroup \
// --name myFunctionApp \
// --src myapp.zip \
// --build-remote true
注意事项
- 所有区域/语言的冷启动情况改善了约 53%
- Premium 计划提供预热实例
- 预热触发器在流量进入前进行初始化
- 包部署可减少冷启动时间
带有错误处理的队列触发器
通过死信队列 (Poison Queue) 实现可靠的消息处理
适用场景:处理来自 Azure Storage Queue 的消息
模板
// C# 独立工作进程 (Isolated Worker) - 队列触发器
using Microsoft.Azure.Functions.Worker;
public class QueueProcessor
{
private readonly ILogger<QueueProcessor> _logger;
private readonly IMyService _service;
public QueueProcessor(ILogger<QueueProcessor> logger, IMyService service)
{
_logger = logger;
_service = service;
}
[Function("ProcessQueueMessage")]
public async Task Run(
[QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]
QueueMessage message)
{
_logger.LogInformation("正在处理消息: {Id}", message.MessageId);
try
{
var payload = JsonSerializer.Deserialize<MyPayload>(message.Body);
await _service.ProcessAsync(payload);
_logger.LogInformation("消息处理成功: {Id}", message.MessageId);
}
catch (Exception ex)
{
_logger.LogError(ex, "处理消息时出错: {Id}", message.MessageId);
// 消息将重试最多 maxDequeueCount 次(默认 5 次)
// 然后被移至毒队列 (poison queue): myqueue-items-poison
throw;
}
}
// 可选:监控毒队列
[Function("ProcessPoisonQueue")]
public async Task ProcessPoison(
[QueueTrigger("myqueue-items-poison", Connection = "AzureWebJobsStorage")]
QueueMessage message)
{
_logger.LogWarning("正在处理毒队列消息: {Id}", message.MessageId);
// 记录到监控系统、发送警报或存储以供手动审查
await _service.HandlePoisonMessageAsync(message);
}
}
// host.json - 队列配置
// {
// "version": "2.0",
// "extensions": {
// "queues": {
// "maxPollingInterval": "00:00:02",
// "visibilityTimeout": "00:00:30",
// "batchSize": 16,
// "maxDequeueCount": 5,
// "newBatchThreshold": 8
// }
// }
// }
注意事项
- 消息最多重试
maxDequeueCount次
- 失败的消息将被移至毒队列 (poison queue)
- 通过
visibilityTimeout配置处理时间
batchSize控制并行处理数量
带有长运行模式的 HTTP 触发器
处理超过 230 秒 HTTP 限制的任务
适用场景:HTTP 请求触发耗时较长的任务
模板
// 异步 HTTP 模式 - 立即返回,随后轮询状态
[Function("StartLongRunning")]
public static async Task<HttpResponseData> StartLongRunning(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext context)
{
var input = await req.ReadFromJsonAsync<WorkRequest>();
// 启动编排 (立即返回)
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"LongRunningOrchestrator", input);
// 返回用于轮询状态的 URL
return client.CreateCheckStatusResponse(req, instanceId);
}
// 响应内容包含:
// {
// "id": "abc123",
// "statusQueryGetUri": "https://.../instances/abc123",
// "sendEventPostUri": "https://.../instances/abc123/raiseEvent/{eventName}",
// "terminatePostUri": "https://.../instances/abc123/terminate"
// }
// 替代方案:不使用 Durable Functions 的基于队列的模式
[Function("StartWork")]
[QueueOutput("work-queue")]
public static async Task<WorkItem> StartWork(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
FunctionContext context)
{
var input = await req.ReadFromJsonAsync<WorkRequest>();
var workId = Guid.NewGuid().ToString();
// 将工作入队,立即返回
var workItem = new WorkItem
{
Id = workId,
Request = input
};
// 返回 work ID 以便检查状态
var response = req.CreateResponse(HttpStatusCode.Accepted);
await response.WriteAsJsonAsync(new
{
workId = workId,
statusUrl = $"/api/status/{workId}"
});
return workItem;
}
[Function("ProcessWork")]
public static async Task ProcessWork(
[QueueTrigger("work-queue")] WorkItem work,
FunctionContext context)
{
// 此处执行耗时处理
// 在存储中更新状态以供轮询
}
注意事项
- 无论使用哪种方案,HTTP 超时时间均为 230 秒
- 异步模式建议使用 Durable Functions
- 立即返回并提供状态端点
- 客户端通过轮询确认完成情况
潜在陷阱 (Sharp Edges)
无论方案如何,HTTP 超时均为 230 秒
严重程度:高
场景:处理时间较长的 HTTP 触发函数
症状:
- 约 4 分钟后出现 504 Gateway Timeout。
- 请求在函数完成前终止。
- 即使函数在后台继续运行,客户端仍收到超时响应。
host.json中的超时设置对 HTTP 触发器无效。
原因:
Azure 负载均衡器对 HTTP 请求有硬编码的 230 秒空闲超时限制。无论你的函数应用超时设置如何,此限制均适用。
即使你在 host.json 中将 functionTimeout 设置为 30 分钟,从客户端的角度来看,HTTP 触发器在 230 秒后仍会超时。
函数在超时后可能会继续运行,但客户端无法收到响应。
推荐修复方案:
使用 Durable Functions 的异步模式
[Function("StartLongProcess")]
public static async Task<HttpResponseData> Start(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client)
{
var input = await req.ReadFromJsonAsync<WorkRequest>();
// 启动编排,立即返回
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"LongRunningOrchestrator", input);
// 返回用于轮询的状态 URL
return client.CreateCheckStatusResponse(req, instanceId);
}
// 客户端轮询 statusQueryGetUri 直至完成
使用基于队列的异步模式
[Function("StartWork")]
public static async Task<HttpResponseData> StartWork(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
[QueueOutput("work-queue")] out WorkItem workItem)
{
var workId = Guid.NewGuid().ToString();
workItem = new WorkItem { Id = workId, /* ... */ };
var response = req.CreateResponse(HttpStatusCode.Accepted);
await response.WriteAsJsonAsync(new {
id = workId,
statusUrl = $"/api/status/{workId}"
});
return response;
}
使用 Webhook 回调模式
// 客户端提供回调 URL
// 函数将工作入队,返回 202 Accepted
// 完成后,将结果 POST 到回调 URLHttpClient 实例化导致的套接字耗尽 (Socket Exhaustion)
严重程度:高
场景:在函数代码内部创建 HttpClient 实例
症状:
SocketException: "Unable to connect to remote server"
- "An attempt was made to access a socket in a way forbidden"
- 高负载下出现随机连接失败。
- 本地运行正常,但在生产环境中失败。
原因:
为每个请求创建新的 HttpClient 会创建新的套接字连接。套接字在关闭后会处于 TIME_WAIT 状态 240 秒。
在 Serverless 环境中,随着...
在高吞吐量场景下,你会迅速耗尽可用套接字(sockets)。这会影响所有网络客户端,而不仅仅是 HttpClient。
Azure Functions 在多个客户之间共享网络资源,这使得该问题更加关键。
建议修复方案:
使用 IHttpClientFactory(推荐)
// Program.cs
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddHttpClient<IMyApiClient, MyApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.Timeout = TimeSpan.FromSeconds(30);
});
})
.Build();
// MyApiClient.cs
public class MyApiClient : IMyApiClient
{
private readonly HttpClient _client;
public MyApiClient(HttpClient client)
{
_client = client; // 通过注入,由工厂管理
}
public async Task<string> GetDataAsync()
{
return await _client.GetStringAsync("/data");
}
}
使用静态客户端(替代方案)
public static class MyFunction
{
// 静态 HttpClient,在多次调用之间复用
private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
[Function("MyFunction")]
public static async Task Run(...)
{
var result = await _httpClient.GetAsync("...");
}
}
Azure SDK 客户端同样适用此模式
// 同样适用于:
// - BlobServiceClient
// - CosmosClient
// - ServiceBusClient
// 请使用 DI 或静态实例阻塞异步调用导致线程饥饿
严重程度:高
场景:在异步代码中使用 .Result、.Wait() 或 Thread.Sleep
症状:
- 高负载下出现死锁。
- 请求无限期挂起。
- 出现 "A task was canceled" 异常。
- 低并发时正常,高并发时失败。
原因:
Azure Functions 的线程池有限。阻塞调用(.Result、.Wait())在等待时会占用线程,导致其他工作无法执行。
Thread.Sleep 会阻塞本可以处理其他请求的线程。
在多个并发执行的情况下,线程会迅速耗尽,从而导致死锁和超时。
建议修复方案:
始终使用 async/await
// 错误 - 阻塞线程
var result = httpClient.GetAsync(url).Result;
someTask.Wait();
Thread.Sleep(5000);
// 正确 - 释放线程
var result = await httpClient.GetAsync(url);
await someTask;
await Task.Delay(5000);
修复同步方法调用
// 错误 - 在异步之上使用同步
public void ProcessData()
{
var data = GetDataAsync().Result; // 阻塞!
}
// 正确 - 全链路异步
public async Task ProcessDataAsync()
{
var data = await GetDataAsync();
}
在控制台/启动项中配置异步
// 如果必须在同步上下文中调用异步方法
public static void Main(string[] args)
{
// 仅在入口点使用 GetAwaiter().GetResult()
MainAsync(args).GetAwaiter().GetResult();
}
private static async Task MainAsync(string[] args)
{
// 此处编写异步代码
}
消费计划(Consumption Plan)10 分钟超时限制
严重程度:中
场景:在消费计划上运行长时间进程
症状:
- 函数在 10 分钟后终止。
- 日志中显示 "Function timed out"。
- 处理未完成且未捕获到错误。
- 开发环境(超时时间较长)正常,但生产环境失败。
原因:
消费计划的执行时间硬限制为 10 分钟。如果未配置,默认值为 5 分钟。
在消费计划中,该限制无法增加到 10 分钟以上。长时间运行的...
该工作需要 Premium 计划或不同的架构。
建议修复方案:
配置最大超时时间 (Consumption 计划)
// host.json
{
"version": "2.0",
"functionTimeout": "00:10:00" // Consumption 计划的最大值
}升级到 Premium 计划以获得更长的超时时间
// Premium 计划 - 默认 30 分钟,可设置为无限制
{
"version": "2.0",
"functionTimeout": "00:30:00" // 或删除此项以实现无限制
}对长工作流使用 Durable Functions
[Function("LongWorkflowOrchestrator")]
public static async Task<string> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
// 每个活动函数都有自己的超时时间
// 工作流可以运行数日
await context.CallActivityAsync("Step1", input);
await context.CallActivityAsync("Step2", input);
await context.CallActivityAsync("Step3", input);
return "Complete";
}将工作分解为更小的块
// 基于队列的分块处理
[Function("ProcessChunk")]
[QueueOutput("work-queue")]
public static IEnumerable<WorkChunk> ProcessChunk(
[QueueTrigger("work-queue")] WorkChunk chunk)
{
var results = Process(chunk);
// 如果还有工作,则将下一个块放入队列
if (chunk.HasMore)
{
yield return chunk.Next();
}
}
.NET In-Process 模型将于 2026 年 11 月弃用
严重程度:高
场景:创建新的 .NET 函数或维护现有函数
症状:
- 在新项目中使用 In-process 模型。
- 与宿主运行时产生依赖冲突。
- 无法使用最新的 .NET 版本。
- 未来的迁移负担。
失效原因:
In-process 模型将代码运行在与 Azure Functions 宿主相同的进程中。这会导致:
- 程序集版本冲突
- 仅限于 LTS .NET 版本
- 无法使用最新的 .NET 特性
- 与宿主运行时耦合过紧
支持将于 2026 年 11 月 10 日结束。在此日期之后,In-process 应用可能会停止工作或无法获得安全更新。
建议修复方案:
为新项目使用 Isolated Worker (隔离工作进程)
# 创建新的隔离工作进程项目
func init MyFunctionApp --worker-runtime dotnet-isolated
或使用 .NET 8
dotnet new func --name MyFunctionApp --framework net8.0将现有的 In-process 迁移至 Isolated
// 旧版 - In-process (使用 FunctionName 特性)
public class InProcessFunction
{
[FunctionName("MyFunction")]
public async Task<IActionResult> Run(
[HttpTrigger] HttpRequest req,
ILogger log)
{
log.LogInformation("Processing");
return new OkResult();
}
}
// 新版 - Isolated worker (使用 Function 特性)
public class IsolatedFunction
{
private readonly ILogger<IsolatedFunction> _logger;
public IsolatedFunction(ILogger<IsolatedFunction> logger)
{
_logger = logger;
}
[Function("MyFunction")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")]
HttpRequestData req)
{
_logger.LogInformation("Processing");
return req.CreateResponse(HttpStatusCode.OK);
}
}
关键迁移变更
FunctionName$\rightarrow$Function特性
HttpRequest$\rightarrow$HttpRequestData
IActionResult$\rightarrow$HttpResponseData
ILogger注入 $\rightarrow$ 构造函数注入
- 添加包含
HostBuilder的Program.cs
ILogger 未输出到控制台或 AppInsights
严重程度:中
场景:在 Isolated Worker 中使用依赖注入的 ILogger
症状:
- 日志未在本地控制台显示。
- 日志未在 Application Insights 中显示。
- 使用
context.GetLogger()时日志正常工作,但
未注入 ILogger。
必须在所有方法调用中传递 logger。
失效原因:
在隔离工作进程(isolated worker)模型中,通过依赖注入的 ILogger 可能无法正确连接到 Azure Functions 的日志管道。
本地开发受影响尤为严重——日志可能无法输出。
Application Insights 需要显式配置。
来自 FunctionContext 的 ILogger 与注入的 ILogger<T> 工作机制不同。
推荐修复方案:
正确配置 Application Insights
// Program.cs
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
// 添加 App Insights 遥测
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
})
.Build();配置日志级别
// host.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
},
"logLevel": {
"default": "Information",
"Host.Results": "Error",
"Function": "Information",
"Host.Aggregator": "Trace"
}
}
}使用 context.GetLogger 以确保可靠性
[Function("MyFunction")]
public async Task Run(
[HttpTrigger] HttpRequestData req,
FunctionContext context)
{
// 此 logger 始终有效
var logger = context.GetLogger<MyFunction>();
logger.LogInformation("Processing request");
}本地开发 - 检查 local.settings.json
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=..."
}
}缺少扩展包导致静默失败
严重程度:中
场景: 在未安装扩展的情况下使用触发器/绑定。
症状:
- 函数在事件触发时没有响应。
- 出现 "No job functions found" 警告。
- 尽管配置正确,但绑定失效。
- 添加扩展包后恢复正常。
失效原因:
Azure Functions v2+ 使用扩展包(extension bundles)来处理触发器和绑定。
如果扩展配置不正确或未安装相关包,函数宿主将无法识别绑定。
在隔离工作进程中,需要显式的 NuGet 包。
在进程内(in-process)模型中,需要 Microsoft.Azure.WebJobs.Extensions.*。
推荐修复方案:
检查扩展包(最常见情况)
// host.json - 扩展包可处理大多数情况
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}为隔离工作进程安装显式包
<!-- .csproj - 隔离工作进程包 -->
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.20.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.0" />
<!-- 存储触发器/绑定 -->
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="6.2.0" />
<!-- Service Bus -->
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.14.0" />
<!-- Cosmos DB -->
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.6.0" />
<!-- Durable Functions -->
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.1.0" />
验证函数注册
# 检查 re查找:
"Found the following functions:"
如果为空,请检查扩展和属性
### Premium 计划在新实例上仍存在冷启动
严重程度:中 (MEDIUM)
场景:使用 Premium 计划并期望零冷启动
症状:
尽管使用了 Premium 计划,但仍经历冷启动。
对新实例的首次请求响应缓慢。
在扩容事件期间出现延迟峰值。
预热实例未被使用。
原因分析:
Premium 计划提供预热实例,但:
- 默认仅提供一个预热实例
- 快速扩容仍会创建冷实例
- 预热实例仍需运行您的代码初始化
- 预热触发器虽运行,但您的代码可能依然缓慢
“预热”意味着运行时(Runtime)已就绪,而非您的应用程序已就绪。
建议修复方案:
添加预热触发器以初始化代码
// 初始化昂贵的资源
_cosmosClient.GetContainer("db", "container");
_httpClient.GetAsync("https://api.example.com/health").Wait();
}
## 配置预热实例数量增加预热实例数量(成本更高)
az functionapp config set \
--name <app-name> \
--resource-group <rg> \
--prewarmed-instance-count 3
## 优化应用程序初始化// 对重资源进行延迟初始化
private static readonly Lazy<ExpensiveClient> _client =
new Lazy<ExpensiveClient>(() => new ExpensiveClient());
// 连接池配置
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(connectionString, sql =>
sql.MinPoolSize(5)));
## 使用始终就绪实例(成本最高)实例始终运行,无冷启动
az functionapp config set \
--name <app-name> \
--resource-group <rg> \
--minimum-elastic-instance-count 2
```
验证检查项
硬编码连接字符串
严重程度:错误 (ERROR)
连接字符串绝不能硬编码
消息:硬编码连接字符串。请使用 Key Vault 或 App Settings。
代码中硬编码 API 密钥
严重程度:错误 (ERROR)
API 密钥应使用 Key Vault 或 App Settings
消息:硬编码 API 密钥。请使用 Key Vault 或环境变量。
生产环境中使用匿名授权级别
严重程度:警告 (WARNING)
匿名端点应通过其他手段进行保护
消息:匿名授权。请确保由 API Management 或其他认证方式保护。
阻塞的 .Result 调用
严重程度:错误 (ERROR)
使用 .Result 会阻塞线程并导致死锁
消息:阻塞的 .Result 调用。请改用 await。
阻塞的 .Wait() 调用
严重程度:错误 (ERROR)
使用 .Wait() 会阻塞线程
消息:阻塞的 .Wait() 调用。请改用 await。
使用 Thread.Sleep
严重程度:错误 (ERROR)
Thread.Sleep 会阻塞线程
消息:Thread.Sleep 阻塞线程。请改用 await Task.Delay()。
新建 HttpClient 实例
严重程度:警告 (WARNING)
每个请求创建 HttpClient 会导致套接字耗尽
消息:每个请求新建 HttpClient。请使用 IHttpClientFactory 或静态客户端。
HttpClient 位于 using 语句中
严重程度:警告 (WARNING)
释放 HttpClient 会导致套接字耗尽
消息:HttpClient 位于 using 语句中。请使用 IHttpClientFactory 以管理正确的生命周期。
进程内 (In-Process) FunctionName 属性
严重程度:信息 (INFO)
进程内模型将于 2026 年 11 月弃用
消息:进程内 FunctionName 属性。继续...
sider 正在迁移至隔离工作进程 (isolated worker)。
缺失 Function 特性
严重程度:警告 (WARNING)
隔离工作进程需要 [Function] 特性
消息:HttpTrigger 缺少 [Function] 特性(隔离工作进程必须包含此特性)。
协作
触发委派
- 用户需要 AWS serverless $\rightarrow$ aws-serverless (Lambda, API Gateway, SAM)
- 用户需要 GCP serverless $\rightarrow$ gcp-cloud-run (Cloud Run, Cloud Functions)
- 用户需要基于容器的部署 $\rightarrow$ gcp-cloud-run (Azure Container Apps 或 Cloud Run)
- 用户需要数据库设计 $\rightarrow$ postgres-wizard (Azure SQL, Cosmos DB 数据建模)
- 用户需要身份验证 $\rightarrow$ auth-specialist (Azure AD, Easy Auth, 托管身份)
- 用户需要复杂编排 $\rightarrow$ workflow-automation (Logic Apps, Power Automate)
使用场景
- 用户提及或暗示:azure function
- 用户提及或暗示:azure functions
- 用户提及或暗示:durable functions
- 用户提及或暗示:azure serverless
- 用户提及或暗示:function app
局限性
- 仅在任务明确符合上述范围时使用此技能。
- 不要将输出视为特定环境验证、测试或专家评审的替代方案。
- 如果缺少必要的输入、权限、安全边界或验收标准,请停止并请求澄清。