如何在ServiceStack中获取异步长时API调用的进度反馈
ServiceStack异步长时API调用进度反馈方案
针对你不想拆分API、不用RabbitMQ,希望类似上传下载进度的需求,分享几个ServiceStack开发者常用的实现方式:
1. 流式响应分块推送进度
利用ServiceStack的流式响应能力,让服务端在每个阶段完成后,向客户端推送一个包含进度信息的JSON块,客户端逐块解析更新状态。
服务端实现
public class LongRunningTaskService : Service { public async Task<Stream> Any(LongRunningTaskRequest request) { var responseStream = new MemoryStream(); var writer = new StreamWriter(responseStream) { AutoFlush = true }; // 模拟5个阶段的任务 var totalStages = 5; for (var i = 1; i <= totalStages; i++) { // 执行当前阶段任务 await ExecuteStageTask(i); // 推送进度信息 var progress = new { Stage = i, Total = totalStages, Status = $"已完成阶段{i}" }; writer.WriteLine(JsonSerializer.SerializeToString(progress)); } responseStream.Position = 0; return responseStream; } private async Task ExecuteStageTask(int stage) { // 模拟长时任务 await Task.Delay(1000); } }
客户端实现
var client = new JsonClient(baseUrl); var stream = await client.GetStreamAsync(new LongRunningTaskRequest()); using var reader = new StreamReader(stream); string line; while ((line = await reader.ReadLineAsync()) != null) { var progress = JsonSerializer.DeserializeFromString<dynamic>(line); Console.WriteLine($"进度:{progress.Stage}/{progress.Total} - {progress.Status}"); // 更新UI进度 }
2. Server-Sent Events(SSE)实时推送
ServiceStack原生支持SSE,适合长时任务的实时进度推送,客户端无需轮询,保持连接接收事件。
服务端实现
public class TaskProgressSseService : Service { public async Task<IEnumerable<ServerSentEvent>> Get(TaskProgressSseRequest request) { var totalStages = 5; for (var i = 1; i <= totalStages; i++) { await ExecuteStageTask(i); yield return new ServerSentEvent { Event = "task-progress", Data = JsonSerializer.SerializeToString(new { Stage = i, Total = totalStages }) }; // 控制推送频率,避免频繁发送 await Task.Delay(100); } // 任务完成推送结束事件 yield return new ServerSentEvent { Event = "task-complete", Data = "任务已完成" }; } }
客户端实现
using var eventSource = new EventSource($"{baseUrl}/task-progress"); eventSource.OnMessage += (sender, e) => { if (e.Event == "task-progress") { var progress = JsonSerializer.DeserializeFromString<dynamic>(e.Data); Console.WriteLine($"当前进度:{progress.Stage}/{progress.Total}"); } else if (e.Event == "task-complete") { Console.WriteLine(e.Data); eventSource.Close(); } };
3. 复用上传下载的进度回调映射阶段进度
把阶段进度映射为虚拟的"字节进度",利用JsonClient自带的Progress回调来接收更新,模拟上传下载的x/y进度展示。
服务端实现
public async Task<object> Any(LongRunningTaskRequest request) { var totalStages = 5; var virtualTotalBytes = 100; // 虚拟总字节数 var bytesPerStage = virtualTotalBytes / totalStages; // 通过请求上下文的Response.OutputStream模拟进度写入 var response = Context.Response; response.ContentType = "application/octet-stream"; for (var i = 1; i <= totalStages; i++) { await ExecuteStageTask(i); // 写入对应阶段的虚拟字节数 var progressBytes = new byte[bytesPerStage]; await response.OutputStream.WriteAsync(progressBytes, 0, progressBytes.Length); await response.OutputStream.FlushAsync(); } return new LongRunningTaskResponse { Success = true }; }
客户端实现
var client = new JsonClient(baseUrl); var totalStages = 5; await client.SendAsync(new LongRunningTaskRequest(), progress: (sent, total) => { var completedStages = (int)Math.Round((double)sent / total * totalStages); Console.WriteLine($"进度:{completedStages}/{totalStages}"); });
内容的提问来源于stack exchange,提问作者Mark LFT
相关产品推荐
相关产品推荐

