You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 21:31:17