|
| 1 | +# 异步 Controller |
| 2 | + |
| 3 | +## 使用场景 |
| 4 | + |
| 5 | +大文件上传及上传后进行语音识别的应用场景 |
| 6 | + |
| 7 | +- 大文件上传: 企业员工上传完整的会议录音或录像文件 |
| 8 | +- 上传后进行语音识别: 对会议内容进行自动转写,生成会议纪要,便于后续查阅和分析讨论点 |
| 9 | + |
| 10 | +如果上传完成后不采用异步任务对文件进行语音识别,可能会出现以下问题: |
| 11 | + |
| 12 | +- 响应延迟: 语音识别是一个计算密集型的操作,特别是当处理大文件时。如果在同一请求中直接进行语音识别,服务器响应时间将大幅增加,导致用户体验变差。 |
| 13 | +- 超时错误: 许多 Web 服务器和 HTTP 客户端都有请求超时的限制。长时间的同步处理可能导致请求超时,从而使得语音识别任务失败。 |
| 14 | +- 资源占用: 大量的同步语音识别任务可能会占用大量服务器资源,包括 CPU 和内存,影响服务器处理其他请求的能力,降低整体服务的可用性和稳定性。 |
| 15 | +- 用户界面冻结: 对于基于 Web 的应用,如果在前端直接等待语音识别的结果,可能导致用户界面冻结,无法进行其他操作,影响用户体验。 |
| 16 | + |
| 17 | +目前比较通用的做法是采用异步任务 |
| 18 | + |
| 19 | +1. 文件上传 |
| 20 | + |
| 21 | +- 用户通过客户端(如 Web 页面、移动应用)上传文件。 |
| 22 | +- 服务器接收到文件后,存储在服务器上或云存储服务中,并立即返回一个响应告诉用户文件已成功上传。 |
| 23 | + |
| 24 | +2. 异步任务创建 |
| 25 | + |
| 26 | +- 在文件上传成功后,服务器不直接进行语音识别处理,而是创建一个异步任务。 |
| 27 | +- 这个任务被发送到消息队列中,这样可以在系统资源允许的情况下按顺序或并行处理。 |
| 28 | + |
| 29 | +3. 任务处理 |
| 30 | + |
| 31 | +- 后台工作进程监听消息队列。一旦发现新任务,就开始处理该任务。 |
| 32 | +- 在处理过程中,工作进程执行语音识别操作,将音频文件转写为文本。 |
| 33 | + |
| 34 | +4. 更新状态和通知 |
| 35 | + |
| 36 | +- 一旦异步任务完成,系统会更新任务的状态(例如,从“处理中”变为“已完成”)。 |
| 37 | +- 系统可以通过不同的方式通知用户任务已完成,比如电子邮件通知、短信、应用内通知等。 |
| 38 | +- 用户可以通过客户端查询任务状态,或者直接获取任务结果(如转写好的文本)。 |
| 39 | + |
| 40 | +## tio-boot 实现异步 |
| 41 | + |
| 42 | +- tio-boot 内置了线程池工具类 com.litongjava.tio.utils.thread.ThreadUtils |
| 43 | + |
| 44 | +配置线程池 |
| 45 | + |
| 46 | +``` |
| 47 | +package com.litongjava.apps.asrgpt.config; |
| 48 | +
|
| 49 | +import java.util.concurrent.ExecutorService; |
| 50 | +
|
| 51 | +import com.litongjava.jfinal.aop.annotation.AConfiguration; |
| 52 | +import com.litongjava.jfinal.aop.annotation.AInitialization; |
| 53 | +import com.litongjava.tio.boot.server.TioBootServer; |
| 54 | +import com.litongjava.tio.utils.thread.ThreadUtils; |
| 55 | +
|
| 56 | +@AConfiguration |
| 57 | +public class ExecutorServiceConfig { |
| 58 | +
|
| 59 | + @AInitialization |
| 60 | + public void config() { |
| 61 | + // 创建包含10个线程的线程池 |
| 62 | + ExecutorService executor = ThreadUtils.newFixedThreadPool(10); |
| 63 | +
|
| 64 | + // 项目关闭时,关闭线程池 |
| 65 | + TioBootServer.me().addDestroyMethod(() -> { |
| 66 | + if (executor != null && !executor.isShutdown()) { |
| 67 | + executor.shutdown(); |
| 68 | + } |
| 69 | + }); |
| 70 | +
|
| 71 | + } |
| 72 | +} |
| 73 | +
|
| 74 | +``` |
| 75 | + |
| 76 | +在 Controller 中使用线程池,开启异步任务 |
| 77 | + |
| 78 | +``` |
| 79 | +import com.litongjava.apps.asrgpt.services.AsrSubmitService; |
| 80 | +import com.litongjava.apps.asrgpt.validator.AsrSubmitValidator; |
| 81 | +import com.litongjava.jfinal.aop.Aop; |
| 82 | +import com.litongjava.tio.http.common.HttpRequest; |
| 83 | +import com.litongjava.tio.http.common.HttpResponse; |
| 84 | +import com.litongjava.tio.http.common.UploadFile; |
| 85 | +import com.litongjava.tio.http.server.util.Resps; |
| 86 | +import com.litongjava.tio.utils.resp.RespVo; |
| 87 | +import com.litongjava.tio.utils.thread.ThreadUtils; |
| 88 | +
|
| 89 | +import lombok.extern.slf4j.Slf4j; |
| 90 | +
|
| 91 | +@Slf4j |
| 92 | +public class AsrSubmitController { |
| 93 | +
|
| 94 | + public HttpResponse submit(HttpRequest request) { |
| 95 | + UploadFile uploadFile = request.getUploadFile("file"); |
| 96 | + String email = request.getParam("email"); |
| 97 | + log.info("upload file size:{}", uploadFile.getData().length); |
| 98 | + log.info("email:{}", email); |
| 99 | +
|
| 100 | + //validator |
| 101 | + RespVo respVo = Aop.get(AsrSubmitValidator.class).submit(email); |
| 102 | + if (respVo != null) { |
| 103 | + return Resps.json(request, respVo); |
| 104 | + } |
| 105 | +
|
| 106 | + // 使用ExecutorService异步执行任务 |
| 107 | + ThreadUtils.getFixedThreadPool().submit(() -> { |
| 108 | + try { |
| 109 | + RespVo result = Aop.get(AsrSubmitService.class).submit(uploadFile, email); |
| 110 | + // 在这里处理异步操作的结果,例如更新数据库或发送通知等 |
| 111 | + log.info("异步任务执行完成, 结果: {}", result); |
| 112 | + } catch (Exception e) { |
| 113 | + log.error("异步任务执行异常", e); |
| 114 | + } |
| 115 | + }); |
| 116 | +
|
| 117 | + // 立即响应客户端,表示文件上传请求已接收,正在处理中 |
| 118 | + RespVo ok = RespVo.ok(); |
| 119 | + ok.setMsg("文件上传成功,正在处理中..."); |
| 120 | + return Resps.json(request, ok); |
| 121 | + } |
| 122 | +} |
| 123 | +
|
| 124 | +``` |
0 commit comments