Bug复盘:接口异步返回的重要性
前言
最近接收了一个老项目,突然甲方 QA 报了一个 bug,连续请求 60 次,成功 8 次,后面的 52 次全部失败,而且成功的 case 返回时间普遍较长。看了日志,并非业务上的异常。这让刚毕业没什么经验的我,顿时陷入了沉思。但回过神来考虑了一下,大胆才猜测,可能是网络问题或者是并发请求上的问题。
但其实业务异常相对容易排查,而网络或者并发的问题会相对难一些,刚好自己对于服务器服务器请求处理的流程也不太清楚,所以就花了时间看了下,最后基本断定是接口是实现方式出现了问题,重新改写成异步接口后问题基本解决。
所以今天就打算复盘一下,聊聊 tomcat 处理请求的流程,心里好有个数,以及对于某些场合下接口异步返回的重要性。
Tomcat 请求流程
项目是 Spring Boot 开发的,默认用 jar 包部署,实际上就是运行在一个内嵌的 tomcat 中,所以下面就简单理一下 tomcat 处理并发请求的基本流程。这里不具体涉及到相关的组件以及源码,仅仅是梳理过程。
基本的 HTTP 请求处理的过程如下图,其中 Connector 和 Engine 是 tomcat 内置的组件。

- Connector 会监听响应的端口,例如 80 或 443,新的 HTTP 请求过来后会做响应的处理。
- Connector 接受请求后,将其封装成 Request 对象,并创建线程来处理。tomcat 默认可以处理的并发数为 200 个(通过
maxThreads参数设置),实际的处理速度取决于我们自己实现的服务程序;超出maxThreads部分,tomcat 仍然不断接收,但最多不能超过maxConnector设置的数,默认 1w 个;超过maxConnector的部分,tomcat 仍然不断接收,但不做处理,放入 Connector 创建的一个队列中,但最多不能超过acceptCount,超过则拒绝(也就是我们所说的,服务器卡死、挂了)。 - 配置时 Connector 会与 Engine 进行绑定,新创建的线程会在 pipeline 中有序等待 Engine 进行处理,其中就包含了 servlet 和 Spring MVC 的处理流程。
- Engine 处理完成后会将结果返回给对应的 Connector,再做进一步封装后返回给 HTTP 请求的一方。
清楚了上述流程之后,基本上对于服务器如何处理并发请求有了一个基本的概念,当并发量大的时候,可以对上述参数进行改动,以适应自己的项目。
接口异步返回的重要性
在回到之前讲的项目上来,可以看到 tomcat 默认配置就已经具有不小的并发量了,并且在 Spring Boot 中 Controller 是单例的,且每个请求的处理互不相关,但是为什么接口返回的速度仍然不似预期呢?这其实和这个项目的业务时有关的。
这个接口是对算法的集成,发起请求后需要通过 HTTP 调用算法处理返回结果,请求调用的速度远大于接口处理的速度,再者算法依赖于独占的 GPU,也就意味着一个请求在处理时,其他请求必须等待。而之前实现的接口是同步的,且设置的算法接口返回的 timeout 为 15s,因此当请求积累到一定数量时,后续等待时间超过 15s,直接返回了异常的结果,导致后续请求全部失败。
显然,在处理速度低于请求速度的接口,并且依赖资源是独占或者很紧张的场景下,通过同步的形式返回接口是不可取的。由于接口占用的资源有限,可以理解成将此接口加上了一个 synchronized,后续请求过来都会无限制等待,或者设置了 timeout,无限制拒绝服务,这两种情况都不是我们想要的。
异步就是一种更优雅的形式,请求发送后,接口的调用者可以继续干别的事,请求处理完后会自动通知给调用者。并且在 Java 中的实现也是比较简单的,直接创建一个线程池来接收请求就可以了,线程池自带阻塞队列已经很好地帮我们处理排队这个场景,分布式场景则需要考虑用 redis 或者成熟的 mq 框架来进行调度了。调用者额外需要实现一个 callback 接口来接收处理完后的结果。这样再多的请求都能够有序的获取到处理的结果,无非是耗时的长短问题罢了。
@RequestMapping(value = "handleTask", method = RequestMethod.POST)
public RestResult handleTask(HttpServletRequest request,String callbackUrl) {
mServerPool.submit((Runnable) SpringUtil.getBean("imageTask", callbackUrl));
return new RestResult();
}
其实,用过支付宝支付 API 的开发者应该很熟悉这个套路,因为阿里也是这么在做的,发起支付后,用户有一段时间可以确认支付,因此这个过程并非实时返回的,所有会有一个 callback 接口,用于实现用户支付完成的后的业务逻辑,当用户完成支付后,支付宝服务器会回调到这个接口,完成最终的一个业务。
总结
以前一直觉得异步、并发很抽象,学习的时候也总是那么几个 demo(交叉输出、生产者消费者 etc.),但是真正遇到这么一个场景的时候,发现一切都是水到渠成的。只有在不断的实践中,才能调整对某一编程思想的认识,有新的体会。
Bug复盘:接口异步返回的重要性的更多相关文章
- .net 反射访问私有变量和私有方法 如何创建C# Closure ? C# 批量生成随机密码,必须包含数字和字母,并用加密算法加密 C#中的foreach和yield 数组为什么可以使用linq查询 C#中的 具名参数 和 可选参数 显示实现接口 异步CTP(Async CTP)为什么那样工作? C#多线程基础,适合新手了解 C#加快Bitmap的访问速度 C#实现对图片文件的压
以下为本次实践代码: using System; using System.Collections.Generic; using System.ComponentModel; using System ...
- 翻页bug 在接口文档中应规范参数的取值区间 接口规范
<?php$a=array("red","green","blue","yellow","brown&q ...
- ionic中将service中异步返回的数据赋值给controller的$scope
1.service中异步获取数据实例 angular.module('starter.services', []) .factory('Chats', function($http,$q) {//定义 ...
- PHP开发APP接口之返回数据
首先说明一下客户端APP通信的格式 1.xml:扩展标记语言(1.用来标记数据,定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言,xml格式统一,跨平台和语言,非常适合数据传输和通信,早已 ...
- EasyNVR无插件直播服务器软件接口调用返回“Unauthorized”最简单的处理方式
背景需求 对于EasyNVR的受众群体十分的广泛,不仅仅有将EasyNVR作为视频直播平台直接使用的,更多的是使用EasyNVR的对应功能集成到自身系统.对于前者,只需要将软件的使用功能搞清楚即可,对 ...
- 调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错
调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错 在需要解析的类型类上加上声明 eg:
- 链接服务器"(null)"的 OLE DB 访问接口 "SQLNCLI10" 返回了消息 "Cannot start more transactions on this session."
开发同事反馈一个SQL Server存储过程执行的时候,报"链接服务器"(null)"的 OLE DB 访问接口 "SQLNCLI10" 返回了消息 ...
- delphi xe7 多线程调用CMD,使用管道,临界区技术,实现指定用户名,多线程,异步返回CMD命令结果到memo
第一次发这个,发现格式很乱,不好看,可以用XE7的project--format project sources命令格式化一下代码. 后面我会上传此次修改函数用的源代码到云盘 链接: http://p ...
- ASP.NET WEB API微信支付通知接口,返回xml数据,微信服务器不识别问题
原文:ASP.NET WEB API微信支付通知接口,返回xml数据,微信服务器不识别问题 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/MrTra ...
随机推荐
- 11.13的C++##不想写结构,更不爱指针
//2019.11.13 卑微的Loving-Q瞎写的程序 报错请更改VS中的SDL检查// 我要去嗨了,在线卑微 1 #include<iostream> #include<std ...
- usaco training <1.2 Your Ride Is Here>
题面 Your Ride Is Here It is a well-known fact that behind every good comet is a UFO. These UFOs often ...
- OutOfMemoryError本地线程不足问题分析
java.lang.OutOfMemoryError本地线程不足问题 11月份中旬客户方的一个系统突然报内存异常,当时是早上上班的时候碰到该项目的项目经理,还跟该项目的项目经理开玩笑说你们系统上线将近 ...
- python数据分析三个重要方法之:numpy和pandas
关于数据分析的组件之一:numpy ndarray的属性 4个必记参数:ndim:维度shape:形状(各维度的长度)size:总长度dtype:元素类型 一:np.array()产生n维 ...
- JSONPath小试牛刀之Snack3
最近在网上看了些JSONPath的入门例子.打算用Snack3这个框架写写例子.json path对`JSON的处理绝对是神器. 1.准备JSON字符串 { "store": { ...
- Netty-主从Reactor多线程模式的源码实现
Netty--主从Reactor多线程模式的源码实现 总览 EventLoopGroup到底是什么? EventLoopGroup是一个存储EventLoop的容器,同时他应该具备线程池的功能. gr ...
- 20190908write from memory
JavaScript_Chapter1 文档名:first.js document.write("你好,今天天气凉爽"); 文档名:js_demo1.js /*1.定义js的变量语 ...
- 前端vue如何下载或者导出word文件和excel文件
前端用vue怎么接收并导出文件 window.location.href = "excel地址" 如果是 get 请求,那直接换成 window.open(url) 就行了 创建一 ...
- Ansible 介绍和安装
目录 Ansible 介绍 环境准备 Ansible安装 配置秘钥管理 配置Inventory文件 简单测试连通性 Ansible 介绍 运维工具分类: agent: puppet, func 这类都 ...
- Chapter 05—Advanced data management(Part 2)
二. 控制流 statement:一个单独的R语句或者是一个复合的R语句: cond:条件表达式,为TRUE或FALSE: expr:数字或字符表达式: seq:数字或字符串的顺序. 1.循环语句:f ...