深入探讨异步 API 的设计与实现

一、API 模式简介:同步与异步的对比

API 是客户端和服务器之间通信的桥梁。大多数 API 采用同步模式,执行的流程如下:

  1. 客户端发送请求。
  2. 服务器处理请求。
  3. 服务器返回响应。

同步模式对快速操作非常有效,比如数据查询或简单更新。但是,当遇到耗时较长的操作时,问题就显现出来了。例如:

  • 处理大文件(例如视频编码、音频分析)。
  • 大规模的数据分析与报告生成。
  • 图像处理(例如生成缩略图、优化图片)。

在这些场景中,客户端必须长时间等待,可能导致超时错误;而服务器也会因为单个长时间运行的请求而降低整体吞吐量。

二、同步模式的局限性

同步模式有几个核心问题:

  1. 客户端等待时间过长: 请求处理时间过长,用户体验不佳。
  2. 超时风险: 网络不稳定或数据量较大时,请求容易超时,导致失败。
  3. 服务器资源占用: 单个长时间请求会占用服务器资源,影响其他用户请求的响应时间。

这些问题促使我们寻找更高效的解决方案,这就是异步 API。

三、异步 API 的概念与实现

3.1 什么是异步 API?

异步 API 的核心理念是将请求的接收与处理分离。异步模式包括以下两个阶段:

  1. 接收请求并快速响应: 服务器立即返回一个状态或追踪 ID,表示请求已被接收。
  2. 后台异步处理: 请求的实际工作在后台完成,客户端可以通过状态查询接口获取处理进度。
3.2 异步 API 的关键优势
  1. 即时响应: 客户端能快速获得反馈,而不需要长时间等待。
  2. 非阻塞操作: 服务器可以并行处理多个请求,不会因为单个任务耗时过长而阻塞其他请求。
  3. 灵活的扩展性: 后台处理任务可以单独扩展,提高系统的吞吐量。
  4. 更好的错误处理: 如果任务失败,可以保存进度并重试,不会影响其他请求。
3.3 同步模式的具体问题示例

以下是一个常见的同步 API 示例,处理图片上传及优化:

[HttpPost]
public async Task<IActionResult> UploadImage(IFormFile file)
{if (file is null){return BadRequest();}// 保存原始图片var originalPath = await SaveOriginalAsync(file);// 生成缩略图var thumbnails = await GenerateThumbnailsAsync(originalPath);// 优化所有图片await OptimizeImagesAsync(originalPath, thumbnails);return Ok(new { originalPath, thumbnails });
}

此模式的问题在于:

  1. 客户端必须等待整个流程完成。
  2. 网络连接或图片较大时,请求容易超时。
  3. 如果图片处理失败,客户端需重新上传,浪费资源。
3.4 异步 API 的解决方案
  1. 新的图片上传接口设计
    异步模式将操作拆分为两部分:快速接收请求与后台处理。以下是改进后的接口:

    [HttpPost]
    public async Task<IActionResult> UploadImage(IFormFile? file)
    {if (file is null){return BadRequest("未上传文件。");}if (!imageService.IsValidImage(file)){return BadRequest("无效的图片文件。");}// 阶段 1:接收请求var id = Guid.NewGuid().ToString();var folderPath = Path.Combine(_uploadDirectory, "images", id);var fileName = $"{id}{Path.GetExtension(file.FileName)}";var originalPath = await imageService.SaveOriginalImageAsync(file,folderPath,fileName);// 将阶段 2 的任务加入后台队列var job = new ImageProcessingJob(id, originalPath, folderPath);await jobQueue.EnqueueAsync(job);// 返回状态 URLvar statusUrl = GetStatusUrl(id);return Accepted(statusUrl, new { id, status = "queued" });
    }
    
  2. 后台任务处理
    实际的图片处理任务移交到后台执行,利用独立的线程或服务完成繁重的操作:

    public class ImageProcessor : BackgroundService
    {protected override async Task ExecuteAsync(CancellationToken ct){await foreach (var job in jobQueue.DequeueAsync(ct)){try{await statusTracker.SetStatusAsync(job.Id, "processing");// 生成缩略图await GenerateThumbnailsAsync(job.OriginalPath, job.OutputPath);// 优化图片await OptimizeImagesAsync(job.OriginalPath, job.OutputPath);await statusTracker.SetStatusAsync(job.Id, "completed");}catch (Exception ex){await statusTracker.SetStatusAsync(job.Id, "failed");logger.LogError(ex, "图片处理失败 {Id}", job.Id);}}}
    }
    

四、实时状态更新的实现

4.1 状态查询接口

客户端可以通过以下接口查询任务的状态:

[HttpGet("{id}/status")]
public IActionResult GetStatus(string id)
{if (!statusTracker.TryGetStatus(id, out var status)){return NotFound();}var response = new{id,status,links = new Dictionary<string, string>()};if (status == "completed"){response.links = new Dictionary<string, string>{["original"] = GetImageUrl(id),["thumbnail"] = GetThumbnailUrl(id, width: 200),["preview"] = GetThumbnailUrl(id, width: 800)};}return Ok(response);
}
4.2 实时通知:减少轮询

虽然状态查询接口是一个解决方案,但它增加了客户端和服务器的负担。特别是客户端需要频繁轮询状态,导致大量无效的请求。
使用 SignalRWebSocket 可以实现实时通知。状态变化时,服务器主动向客户端推送更新,减少网络流量并提高响应速度。例如:

  • 当图片处理完成时,服务器通过 WebSocket 通知客户端。
  • 用户可以立即获取完成的结果,而无需多次刷新页面。
4.3 其他通知方式
  • 电子邮件通知: 适用于长时间运行的任务,用户可在任务完成时收到通知,而无需保持浏览器页面开启。
  • Webhook 回调: 适用于系统间通信,任务完成时服务器主动通知其他系统,实现自动化工作流。

五、性能优化与扩展性

5.1 任务队列设计

对于单服务器应用,可以使用 .NET 的 Channel 管理内存中的任务队列:

public class JobQueue
{private readonly Channel<ImageProcessingJob> _channel;public JobQueue(){var options = new BoundedChannelOptions(1000){FullMode = BoundedChannelFullMode.Wait};_channel = Channel.CreateBounded<ImageProcessingJob>(options);}public async ValueTask EnqueueAsync(ImageProcessingJob job,CancellationToken ct = default){await _channel.Writer.WriteAsync(job, ct);}public IAsyncEnumerable<ImageProcessingJob> DequeueAsync(CancellationToken ct = default){return _channel.Reader.ReadAllAsync(ct);}
}

对于分布式系统,可以使用 RabbitMQRedis 实现分布式任务队列,提高扩展性。

5.2 错误处理与重试机制

利用 Polly 等库实现重试策略。例如:

  • 图片优化失败时,系统可以自动重试多次,避免用户操作中断。
  • 如果某个任务失败,可以记录进度并重新排队,确保系统稳定性。
5.3 动态扩展

异步 API 的设计使得后台任务处理可以独立扩展。例如,增加更多的服务器专门处理任务,而主服务器则专注于接收请求。这种分离提高了整个系统的吞吐量和可靠性。

六、总结

同步 API 简单高效,适用于快速操作,但在处理耗时任务时暴露出等待时间长、超时风险高、资源占用严重等局限性。为解决这些问题,异步 API 应运而生,其核心是将请求接收与处理分离。通过即时响应和后台异步处理,异步 API 提供了更好的用户体验和系统性能。结合任务队列、实时状态更新(如 SignalR)以及动态扩展等技术,异步 API 实现了高并发、低延迟和灵活扩展,适合处理复杂任务场景。同时,配套的错误重试和通知机制提升了系统的可靠性和用户满意度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/62036.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黄仁勋:人形机器人在内,仅有三种机器人有望实现大规模生产

11月23日&#xff0c;芯片巨头、AI时代“卖铲人”和最大受益者、全球市值最高【英伟达】创始人兼CEO黄仁勋在香港科技大学被授予工程学荣誉博士学位&#xff1b;并与香港科技大学校董会主席沈向洋展开深刻对话&#xff0c;涉及人工智能&#xff08;AI&#xff09;、计算力、领导…

【Linux学习】【Ubuntu入门】2-3 make工具和makefile引入

1.使用命令新建三个.c文件vi main.c&#xff0c;vi input.c&#xff0c;vi caclcu.c&#xff0c;两个.h文件vi input.h&#xff0c;vi caclcu.h 2.vi Makefile&#xff1a;新建Makefile文件&#xff0c;输入一下内容 注意&#xff1a;命令列表中每条命令前用TAB键&#xff0c;不…

wsl2的Ubuntu18.04安装ros和anaconda

参考&#xff1a;超详细 WSL2 安装 ros 和 anaconda_wsl2安装anaconda-CSDN博客 一.安装ros 1. 更换系统源 输入 wget http://fishros.com/install -O fishros && . fishros 和上面的链接一样&#xff0c;依次输入5-2-1 2. 安装ros 输入 wget http://fishros.c…

1-golang_org_x_crypto_bcrypt测试 --go开源库测试

1.实例测试 package mainimport ("fmt""golang.org/x/crypto/bcrypt" )func main() {password : []byte("mysecretpassword")hashedPassword, err : bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)if err ! nil {fmt.Println(err)…

【FPGA】Verilog:利用 4 个串行输入- 串行输出的 D 触发器实现 Shift_register

0x00 什么是寄存器 寄存器(Register)是顺序逻辑电路中使用的基本组成部分之一。寄存器用于在数字系统中存储和处理数据。寄存器通常由位(bit)构成,每个位可以存储一个0或1的值。通过寄存器,可以设计出计数器、加法器等各种数据处理电路。 0x01 寄存器的种类 基于 D 触发…

CentOS 7安装SSHFS 实现远程主机目录 挂载为本地目录

安装sshfs 官方下载地址 https://github.com/libfuse/sshfs/releases 首先&#xff0c;我们需要安装sshfs软件。sshfs是一个基于SSH文件传输协议的文件系统客户端&#xff0c;它的官方网页是&#xff1a;http://fuse.sourceforge.net/sshfs.html 。在CentOS下&#xff0c;我们…

MySQL:IF()函数根据指定条件返回不同的值

语法如下&#xff1a; IF(condition, value_if_true, value_if_false) 其中&#xff0c;condition表示要判断的条件&#xff0c;如果条件成立&#xff0c;则返回value_if_true&#xff1b;如果条件不成立&#xff0c;则返回value_if_false。 案例 SELECT IF(3 > 2, True…

算法 差分修改 极简

N个气球排成一排&#xff0c;从左到右依次编号为1,2,3....N.每次给定2个整数a b(a < b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了&#xff0c;你能帮他算出每个气球被涂过…

用 Python 从零开始创建神经网络(十):优化器(Optimizers)(持续更新中...)

优化器&#xff08;Optimizers&#xff09; 引言1. 随机梯度下降/Stochastic Gradient Descent (SGD)2. 学习率&#xff08;Learning Rate&#xff09;3. 学习率衰减&#xff08;Learning Rate Decay&#xff09;4. 带动量的随机梯度下降法&#xff08;Stochastic Gradient Des…

ubity3D基础

Unity是一个流行的游戏开发引擎&#xff0c;它使用C#作为其主要的编程语言。以下是一些Unity中C#编程的基础概念&#xff1a; • Unity编辑器&#xff1a; • Unity编辑器是Unity游戏引擎的核心&#xff0c;提供了一个可视化界面&#xff0c;用于创建和管理游戏项目。 • C#脚本…

利用c语言详细介绍下栈的实现

数据结构中&#xff0c;栈是一种线性结构&#xff0c;数据元素遵循后进先出的原则。栈的一端为栈顶&#xff0c;一端为栈底或栈尾&#xff0c;数据只在栈顶端进行操作。新插入数据称为入栈或者压栈&#xff0c;删除数据叫做出栈或者退栈。 一、图文介绍 我们通过建立一个stack…

元组部分介绍

元组部分 元组的基本格式与特点 #1.元组 #基本格式&#xff1a; 元组名&#xff08;元素1&#xff0c;元素2&#xff0c;元素3&#xff09; #注意&#xff1a;所有元素包含在小括号内&#xff0c;元素与元素之间用逗号隔开&#xff0c;可以是不同的元素类型 #注意&#xff1a…

Jackson、Gson、FastJSON三款JSON利器比拼

在Java领域&#xff0c;有多种JSON工具包&#xff0c;比如Jackson、Gson、FastJSON&#xff0c;每家都各有所长&#xff0c;下面我们从性能、特性、生态、易用 性等几个方面来展开下&#xff1a; 一、Jackson 性能 Jackson是一款高性能的JSON处理库。它在序列化和反序列化操作…

使用 OpenCV 进行视频中的行人检测

在计算机视觉领域&#xff0c;行人检测是一个重要的研究方向&#xff0c;它在视频监控、自动驾驶、人机交互等领域都有着广泛的应用。本文将介绍如何使用 OpenCV 库来实现视频中的行人检测。 环境准备 首先&#xff0c;我们需要安装 OpenCV 库。可以通过以下命令来安装&#…

pytest日志总结

pytest日志分为两类&#xff1a; 一、终端&#xff08;控制台&#xff09;打印的日志 1、指定-s&#xff0c;脚本中print打印出的信息会显示在终端&#xff1b; 2、pytest打印的summary信息&#xff0c;这部分是pytest 的默认输出&#xff08;例如测试结果PASSED, FAILED, S…

数据治理:在企业数据管理中的关键角色与实现路径——《DAMA 数据管理知识体系指南》读书笔记- 第 3 章

文章目录 1. 数据治理的核心内涵与战略价值2. 数据治理的驱动因素&#xff1a;不仅仅是合规3. 数据治理的组织模型&#xff1a;选择适合企业结构的运营模式4. 实施数据治理的关键步骤&#xff1a;战略、制度和文化5. 数据治理工具的选择&#xff1a;支持业务与流程的高效管理6.…

candence: 常用的一些命令: Move / Mirror / Rotate / Spain / Fix / unFix / Flipdesign

常用的一些命令 一、 Move 移动 一个可移动一个&#xff0c;也可多个 移动器件 二、 Mirror 镜像 Mirror 就是top 和 bottom 层的器件进行相互转换 三、 Rotate 旋转 移动过程中旋转 四、旋转 Spain 不能在移动中旋转 可以一次旋转一个&#xff0c;也可多个 一次旋转…

【测试工具JMeter篇】JMeter性能测试入门级教程(二)出炉,测试君请各位收藏了!!!

上篇文章&#xff1a;CSDN 我们介绍了JMeter的一些原理介绍&#xff0c;以及安装配置和启动流程&#xff0c;本文我们就来讲讲JMeter如何使用。 一、JMeter目录结构组成 1. 根目录 Jmeter安装包解压后的根目录如下图&#xff1a; 1.1 backups目录&#xff1a;脚本备份目录&am…

【拥抱AI】如何调整Milvus的存储路径?

调整Milvus的存储路径是一个重要的配置步骤&#xff0c;可以确保你的数据存储在合适的磁盘位置&#xff0c;避免磁盘空间不足或性能瓶颈。以下是如何调整Milvus存储路径的详细步骤&#xff1a; 1. 修改配置文件 Milvus的存储路径配置主要在server_config.yaml文件中。你需要编…

VSCode 如何选中包含某个字母的所有行

文章目录 写在前面一、需求描述二、解决方法参考链接 写在前面 自己的测试环境&#xff1a;VSCode 一、需求描述 由于需要处理文件&#xff0c;需求是删除文件中包含某个字母的所有行。 二、解决方法 在 Visual Studio Code (VSCode) 中&#xff0c;如果你想选中所有包含某…