Grails视图调用controller action时挂起超时问题的优化方案咨询
解决方案
方案1:轻量异步处理(无额外中间件,推荐)
这个方案适配你的需求,不需要引入消息队列,仅用Grails/Spring自带的异步能力即可实现,改动量极小。
步骤1:开启服务方法异步执行
给业务服务dbAction方法加Spring的@Async注解,让方法调用时不会阻塞主线程:
首先在项目启动类/配置类上加@EnableAsync注解开启异步支持,再修改服务代码:
import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service @Service class MyService { @Async def dbAction() { Sql sql = getSql() sql.executeUpdate(''' update mytable set mydata = calculate_data, updated_by = 'dbAction', updated_at = now() where id in (1,2,3) ''') // 可在这里新增执行完成后的状态标记,方便前端查询进度 } }
步骤2:修改控制器逻辑
控制器不需要等待业务方法执行完成,接收到请求后直接提交异步任务,立刻返回响应即可:
def dbAction() { // 你原有判断任务是否正在运行的逻辑直接复用即可 if (isTaskRunning()) { flash.message = "当前已有更新任务正在执行,请稍后再试" redirect(action: 'index') return } // 异步调用服务,不会阻塞当前请求 myservice.dbAction() flash.message = "数据库更新任务已提交,执行完成后自动生效" redirect(action: 'index') }
步骤3:优化前端交互
把原来的同步跳转链接改成AJAX异步触发,避免页面卡住无响应:
<li> <a href="javascript:void(0)" id="updateBtn">Run Update on DB</a> </li> <script> document.querySelector('#updateBtn').addEventListener('click', function() { if (!confirm('确认执行数据库更新?操作耗时较长请耐心等待')) return this.textContent = '提交中...' this.style.pointerEvents = 'none' fetch('${g.createLink(controller: "hello", action: "dbAction")}') .then(() => window.location.reload()) .catch(() => { alert('任务提交失败,请重试') this.textContent = 'Run Update on DB' this.style.pointerEvents = 'auto' }) }) </script>
方案2:极低改动临时方案
如果暂时不想改后端逻辑,仅修改前端也能快速解决超时问题:给a标签加target="_blank"属性,点击后会新开浏览器标签页执行任务,原页面不会卡住:
<li> <a class="" href="${g.createLink(controller: "hello", action: "dbAction")}" target="_blank">Run Update on DB </a> </li>
缺点是用户需要手动关闭新标签页,体验不如方案1。
可选优化:任务进度提示
如果需要让用户感知任务执行状态,可以新增简单的状态查询能力:
- 用缓存字段/数据库字段存储任务状态:未运行、运行中、已完成
- 前端提交任务后,每隔几秒轮询状态接口,任务执行完成后自动刷新页面或者弹窗提示即可
内容的提问来源于stack exchange,提问作者Mo Ziauddin
相关产品推荐
相关产品推荐

