- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
最近在开发一个打卡接口,其实只需要做些判断,保存一下打卡结果即可,预计同时段1000多人在线打卡,但是第一次写完之后,压测效果非常糟糕,可以看到只有十几的并发,喝下的水都要喷出来了,那么简单的接口都能耗时那么久的吗,我预估100ms以内准可以的,那还有上百的并发才对。于是开始了我的优化之路.
看看主要代码,controller接收参数进来之后,执行一下服务方法就返回了,就是查询活动,判断活动时间,累计积分,保存结果,并发这么差我实属是想不到,让人摇头摇头郁闷,我应该没问题啊,部署的机器有问题?但是也都是还可以的服务器的.
@Override
public RelationActivityRecord clock(String userId, ClockDto clockDto) {
// 判断活动是否有效
RelationActivity relationActivity =
relationActivityService.getRelationActivity(clockDto.getActivityId(),
clockDto.getActivityType());
Date now = new Date();
if (now.before(relationActivity.getStartTime())) {
throw new BadRequestException(ErrorEnum.WRONG_ARGUMENTS, "活动还未开始,请耐心等待~");
}
if (now.after(relationActivity.getEndTime())) {
throw new BadRequestException(ErrorEnum.WRONG_ARGUMENTS, "活动已结束,可在主页查看个人作品哦~");
}
// 本次打卡获得积分
int score= 0;
// 当天的0点时间
Date toDay = DateUtil.beginOfDay(now);
if (Objects.equals(clockDto.getActivityType(), 1)
&& Objects.equals(clockDto.getClockType(), 1)) {
// 查询累计打卡次数
CurrentClockRecordVo clockRecordStat = this.currentRecord(userId,
clockDto.getActivityId(), clockDto.getActivityType(),
);
// 当天第一次的打卡,可以攒一个积分
if (clockRecordStat.getLastTime() != null && clockRecordStat.getLastTime().before(toDay)
&& clockRecordStat.getNum() < 100) {
score = 1;
}
} else if (Objects.equals(clockDto.getActivityType(), 1)) {
// 其他的活动之类...
throw new BadRequestException(ErrorEnum.WRONG_ARGUMENTS, "活动暂未开放");
} else {
throw new BadRequestException(ErrorEnum.WRONG_ARGUMENTS, "未知活动");
}
// 保存打卡记录
RelationActivityRecord newClockRecord = new RelationActivityRecord();
newClockRecord.setStudentId(userId);
newClockRecord.setActivityId(clockDto.getActivityId());
newClockRecord.setActivityType(clockDto.getActivityType());
newClockRecord.setClockType(clockDto.getClockType());
newClockRecord.setCreateTime(now);
newClockRecord.setContent(clockDto.getContent());
newClockRecord.setEnergy(energy);
newClockRecord.setVideoId(clockDto.getVideoId());
newClockRecord.setIntro(clockDto.getIntro());
this.save(newClockRecord);
// 有积分可以攒到学生账户上
if (score> 0) {
activityStudentService.addScore(clockDto.getActivityId(), userId, score);
}
return newClockRecord;
}
冷静冷静再次喝口水,遇到问题先找找外部原因,是不是springboot默认的tomcat最大连接数之类的默认值太小,项目里是没有重新配置的,但是一想前些天看过一个视频说,默认的都是8192最大连接数,那不改配置也是嘎嘎够用的。谨慎起见,写一个随便执行几条语句,不连接数据库的接口压测一下,一开始有1000多并发,然后积攒的排队请求多了之后,并发逐渐下降到600,正常合理,处理不过的请求会排队没办法,除非加大线程数的配置:
突然想起来,项目启动不是用的tomcat,而是更换成了undertow,这家伙有问题吗?但是求助搜索引擎,说它要比tomcat更彪悍,至少默认是有8000并发的,看来也排除了.
那会不会是JVM的堆栈设置的太小,处理卡顿,看了下gc数是有点多。其他项目拷贝过来的启动配置,才512M我去,马上改成4G试试。然而,实际效果并无变化。其实gc虽然很多,但实际耗费的时间加起来都没3s,可以排除是它的问题。(这里还遇到了一个问题,就是即使改大了之后,项目一次启动还是会有3次full-gc和几次的young-gc,一下子搞不懂为什么会这样,有经验的朋友可以给些排查方向) 。
为此,我判断大概率是接口耗时太久导致并发起不来.
代码除了一些判断语句,对象转换,查询插入更新数据库,就没有其他操作了。对象转换有问题吗,但是通篇都有对象转换,这都有问题可能吗。估计真的是数据库有问题了。为了提高查询速度,我只加了一个复合索引,这都那么慢是很不符合常理的.
那我就用skywalking探测一下! 。
这。。真的是接口耗时符非常久,虽然压测情况下,分配资源出来等了70ms,但接口执行的时候却花费了308ms,在skywalking怎么只能看到查询语句的耗时,可是查询语句只花了几毫秒而已.
那我加下p6spy看下,每条语句的执行耗时吧.
吃惊,吓人,这两插入更新语句居然需要两百毫秒.
咋回事,我想想,想不通,求助搜索引擎和大模型了:
留意到第2点,没有想到,我本来认为接口可以忍受不加事务带来数据不一致性,以提高并发。既然这么说了,那就加个事务试试。不加不知道,一加吓一跳,加了基本0耗时了.
再搜索一下这是为什么,有人说是加了事务之后,不会像之前一样插入一次写一次盘,然后在事务的干预下,攒够一批再写盘,这样效率就高了。那这里或者还能优化这一批能不能攒多点,也要衡量涉及到写盘的时候,当时的单个接口请求就会慢了.
然后有了另一个疑问,我事务是不是加得太大了,毕竟是先查询,再插入和更新数据,可以将它们分开的,那就分开试试,查询不加事务,插入更新提出一个方法加事务,但是,奇了怪了,结果并发效果更差了。看来这里面还有事。 同时发现这里一开始压测都压同一个用户,按理说真实情况是不同用户的,同一个用户可能竞争资源更加剧烈,所以对获取用户userId改了一下随机生成一个.
这个时候感觉到8.x版本的skywalking实在是很难提供到更全面的消耗信息,想到不久前看到skywalking9.x版本来了,那就下载一个试试吧。这里我一开始下载了最新的版本,结果死活启动不了一直闪退,什么端口占用啊都改了也不行,大坑。然后直接选9.0版本,就正常启动了。果然9.0版本就不一样了,界面布局舒服了,监控数据也活了,很赞.
而且,trace链路的耗时发布总算是全面了很多!!数据库连接都出来了。可以看到,看了下没有加事务之前,每次查询,写入,都要单独获取一次数据库链接!!!插入语句监控也能正常出来了! 。
查询不加事务,查询和更新用了事务,得获取三次数据库连接,第一次连接是为了查selelct 1判断数据库是否正常,可以去掉.
查询和插入更新在同一个大事务,只获取一次数据库连接,效率自然更高了.
这里基本也确定了,放一个事务里,效率要高,减少获取数据库连接.
此外,通过查看 skywalking链接追踪,发现是getConnection的时候特别耗时,获取一个连接要好几秒.
目前连接池用的是druid,参数是默认配置,查了下默认配置的最大连接数并不高,那问题估计就是这里了,加大试试。目前pg数据库设置是250个链接,如果是生产环境,数据库独享,yml和pg配置的最大连接数还能大些,目前先试试200,如果并发能提高说明这里也是能优化的点,当然实际配置还得看情况,不然会把数据库拉胯.
设置一秒200并发,持续30秒,效果还不错,平均700多ms一个请求,最后并发到250左右.
这里的undertow和数据库连接配置还需要逐步按实际情况不断调整,以达到单机最佳效果.
总结一下:
并发低,有配置的问题,也有代码的问题.
事务的添加需要留意一下添加之后的效果,有时候并非是自己想的那样,一开始觉得事务范围大不合适,结果它效果比不加事务还要好.
一个好工具太重要了,例如skywalking9.0.
多点分析.
好好总结这次经验,基本单机并发就这些问题比较多了.
最后此篇关于呕血回顾一次提高接口并发的经历,很实用的文章就讲到这里了,如果你想了解更多关于呕血回顾一次提高接口并发的经历,很实用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在比较工作簿中的工作表。该工作簿有两张名为 PRE 和 POST 的工作表,每张工作表都有相同的 19 列。行数每天都不同,但特定一天的两张表的行数相同。该宏将 PRE 工作表中的每一行与 POS
我有一个对象数组,我一次循环遍历该数组一个对象,然后进行几次检查以查看该数组中的每个对象是否满足特定条件,如果该对象满足此条件,则复制一个属性将此对象放入数组中(该属性还包含另一个对象)。 for(v
我正在编写一个必须非常快的应用程序。我使用 Qt 5.5 和 Qt Creator,Qt 的 64 位 MSVC2013 编译版本。 我使用非常困倦的 CS 来分析我的应用程序,我看到占用最多独占时间
我有以下 CountDownTimer 在我的 Android 应用程序中不断运行。 CountDownTimer timer_status; timer_status = new CountDown
有一个优化问题,我必须调用随机森林回归器的预测函数数千次。 from sklearn.ensemble import RandomForestRegressor rfr = RandomForestR
我正在努力提高现有 Asp.Net Web 应用程序的数据访问层的性能。场景是。 它是一个基于 Web 的 Asp.Net 应用程序。 数据访问层使用 NHibernate 1.2 构建并作为 WCF
我在我的 Intel Edison 上运行 Debian,并尝试使用 ffmpeg 通过 USB 网络摄像头捕获视频。我正在使用的命令是: ffmpeg -f video4linux2 -i /dev
我有一个 For循环遍历整数 1 到 9 并简单地找到与该整数对应的最底部的条目(即 1,1,1,2,3,4,5 将找到第三个“1”条目)并插入一个空白行。我将数字与仅对应于此代码的应用程序的字符串“
我有一个带有非规范化架构(1 个表)的 postgresql 数据库,其中包含大约 400 万个条目。现在我有这个查询: SELECT count(*) AS Total, (SELECT c
在 Ltac 中实现复杂的策略时,有一些 Ltac 命令或策略调用我预计会失败以及预期失败(例如终止 repeat 或导致回溯)。这些故障通常在故障级别 0 时引发。 更高级别引发的故障“逃避”周
我正在尝试提高 ansible playbook 的性能。我有一个测试剧本如下: --- - name: Test hosts: localhost connection: local g
我正在使用 axios从 Azure 存储 Blob 下载文件 (~100MB)。 axios({ method: 'get', url: uri, onDownloadProgress:
我有一个 ClojureScript 程序,主要对集合执行数学计算。它是在惯用的、独立于主机的 Clojure 中开发的,因此很容易对其进行基准测试。令我惊讶的是(与答案对 Which is fast
我有一个程序必须在硬件允许的情况下尽快发出数千个 http 请求。在现实世界中,这些连接中的每一个都将连接到一个离散的服务器,但我已经编写了一个测试程序来帮助我模拟负载(希望如此)。 我的程序使用 A
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在计算 Clojure 中 3d 点云的边界框。点云表示为 Java 原始浮点数组,点云中的每个点都使用 4 个浮点存储,其中最后一个浮点未使用。像这样: [x0 y0 z0 u0 x1 y1
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我正在尝试使用rayshader 包制作图像。我很高兴能够使用如下代码创建一个 png 文件: library(ggplot2) library(rayshader) example_plot <-
更新 显然,jQuery 模板可以被编译,并且它有助于显示带有 if 语句 的模板的性能 here . 但是如图here ,预编译的 jQuery 模板对我的情况没有多大作用,因为我的模板不包含逻辑
我是编程新手。我有一个启用分页的 ScrollView ,其中包含许多页面(最多十个),并且在每个页面上都有一个自定义按钮。每个自定义按钮都有一个自定义图像。我在 Interface Builder
我是一名优秀的程序员,十分优秀!