springboot如何实现异步响应请求(前端请求超时的问题解决)
奋斗鱼 人气:0问题
实际场景中会遇到请求业务处理流程耗时较长,比如长查询,远程调用等,主线程会被一直占用会影响其他请求的响应,导致服务端性能下降。同时,前端向服务端发送请求后等待响应的超时时间比较短(一般20s或30s),而我们实际业务执行可能超过1分钟。所以以下需要解决此问题。
解决方案
解决的方案分为以下两步骤
1.服务端异步处理
你需要将请求接口进行一些简单的改进。将返回值类型变更为Callable<T>类型,其中T可以为任意类型比如String或你原有的自定义类型。以下的new Callable方法,会新建一个线程,用于执行业务逻辑并在得到结果后自动返回给前端。而主线程无需等待。
@RequestMapping(value = "/GetMonthCdateRpt", method = RequestMethod.POST) @ResponseBody public Callable<String> GetMonthCdateRpt(@RequestBody HashMap<String, Object> map) { logger.info("主线程开始"); Callable<String> result = new Callable<String>() { @Override public String call() throws Exception { logger.info("副线程开始"); /*这里沉睡1分钟,表示处理耗时业务执行*/ Thread.sleep(60000); logger.info("副线程返回"); return "success"; } }; logger.info("主线程返回"); return result; }
2.设置响应时间
改进完接口方法后,你需要新建一个WebMvcConfigurer的实现类,名字可以为WebAppConfigurer.java实现异步处理的支持,如果你已存在其他WebMvcConfigurer的类,则可以把代码追加进去。代码内容如下:
@Configuration public class WebAppConfigurer implements WebMvcConfigurer { //异步处理支持 @Override public void configureAsyncSupport(final AsyncSupportConfigurer configurer) { configurer.setDefaultTimeout(120 * 1000L);//设置响应时间 120s configurer.registerCallableInterceptors(timeoutInterceptor()); configurer.setTaskExecutor(threadPoolTaskExecutor()); } @Bean public TimeoutCallableProcessingInterceptor timeoutInterceptor() { return new TimeoutCallableProcessingInterceptor(); } @Bean public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor(); t.setCorePoolSize(10); t.setMaxPoolSize(100); t.setQueueCapacity(20); t.setThreadNamePrefix("WYF-Thread-"); return t; } }
执行完以上两步骤后,重新运行服务,调用接口可以异步返回结果,前端可以最大等待的响应时间为上面设置的120s。
问题已解决。
需要避免踩到的坑
如果你按上文操作后,还是会出现超时情况,有可能是你用到了以下几种软件,需要对应设置一下。
1.关于dubbo中的设置
如果你使用的了dubbo这样的Jave RPC框架,你除了以上设置后,还需要在application-*.yml配置消费端的默认的响应超时时长。
dubbo: application: name: myweb consumer: timeout: 120000 #默认超时时间120s
同时注意在dubbo的*consume-client.xml服务注册中,如果单个服务也设置了timeout会以各自服务配置的超时时长为准。如下的timeout是20秒,需要修改或直接删掉此timeout的设置。
<dubbo:reference id="reportUserInfoService" interface="com.zh.fee.service.UserInfoService" timeout="20000" retries="0" check="false"/>
2.关于tomcat的设置
上文中是springboot开发环境,使用了内置的tomcat。而在实际生产环境中一般用的是外置tomcat来部署(便于后续发布更新),需要在tomcat的配置文件server.xml中设置超时时间(默认20秒以下设置为120秒)。
<Connector port="8811" protocol="HTTP/1.1" connectionTimeout="120000" redirectPort="8443" />
3.关于Nginx的设置
如果服务端使用到Nginx做了反向代理转发请求,就需要在Nginx的配置文件nginx.conf中设置超时时间,否则会返回“java.io.IOException: 你的主机中的软件中止了一个已建立的连接”这样的异常提示。
未设置时Nginx响应时间默认60秒,这里我将http头部的keepalive_timeout 、client_header_timeout 、client_body_timeout 、send_timeout 、以及server代码块中的proxy_read_timeout 均配置为120秒。
http { include mime.types; default_type application/octet-stream; client_max_body_size 100m; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 120; #连接超时时间,服务器将会在这个时间后关闭连接 send_timeout 120; #发送超时时间 client_header_timeout 120; #请求头的超时时间 client_body_timeout 120; #请求体的读超时时间 #gzip on; ..... #业务系统的配置 server { listen 9092; server_name localhost; location / { proxy_pass http://127.0.0.1:8811/mywebsev/; proxy_read_timeout 120; # 等候后端服务器响应时间 秒 } } }
保存完记得重启Nginx。
以上完毕。
总结
加载全部内容