当前位置:网站首页>线上接口流量突增,快要扛不住了
线上接口流量突增,快要扛不住了
2022-08-08 06:20:00 【java熬夜党】
优化手段
排查连接池大小
其实大部分的请求都是会访问数据库,而数据库严重依赖连接池数量,如果一个项目连接池数量设置过小,那势必会导致性能下降。
Hikarip连接池配置说明如下:
#最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
spring.datasource.hikari.minimum-idle=5
#最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值
spring.datasource.hikari.maximum-pool-size=100
#自动提交从池中返回的连接,默认值为true
spring.datasource.hikari.auto-commit=true
#空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
#只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
spring.datasource.hikari.idle-timeout=30000
#连接池名称,默认HikariPool-1
spring.datasource.hikari.pool-name=Hikari
#连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短;单位ms
spring.datasource.hikari.max-lifetime=55000
#连接超时时间:毫秒,小于250毫秒,会被重置为默认值30秒
后来发现项目中设置的最大连接数maximum-pool-size是50,有点小,后面我改成了500。
tomcat性能优化
我们项目采用的spingboot项目,内置的tomcat容器,影响tomcat容器性能的重要参数如下:

maxThreads
我们知道 maxThreads 指的是请求处理线程的最大数量,在 Tomcat7 和 Tomcat8 中都是默认 200 个。
对于这个参数的设置,需要根据任务的执行内容去调整,一般来说计算公式为:最大线程数 = ((IO时间 + CPU时间)/CPU时间) * CPU 核数。这个公式的思路其实很简单,就是最大化利用 CPU 的资源。一个任务的耗时分为 IO 耗时和 CPU 耗时,基本上 IO 耗时是最多的,这时候 CPU 是没事干的。
maxConnections
maxConnections 指的是当线程池的线程达到最大值,并且都在忙的时候,Connector 中的队列最多能容纳多少个连接。一般来说,我们都要设置一个合理的数值,不能让其无限制堆积。因为 Tomcat 的处理能力肯定是有限的,到达一定程度肯定就处理不过来了,因此你堆积太多了也没啥用,反而会造成内存堆积,最终导致内存溢出 OOM 的发生。一般来说,一个经验值是可以设置成为 maxThreads 同样的大小。
acceptCount
acceptCount 指的是当 Container 线程池达到最大数量且没有空闲线程,同时 Connector 队列达到最大数量时,操作系统最多能接受的连接数。 当队列中的个数达到最大值后,进来的请求一律被拒绝,默认值是 100。这可以理解成是操作系统的一种自我保护机制吧,堆积太多无法处理,那就直接拒绝掉,保护自身资源。
在项目中,由于时间有限,我没有用jemeter等进行性能压测,直接预估了下, 设置了maxThreads、maxConnections、acceptCount都为800,如果时间允许的情况下,建议还是通过jemter压测出一个以最优值。
接口代码优化
完成上面的系统级别的优化后,就要针对具体的代码进行分析优化了,首先推荐一个神器arthas, 可以查看接口中的方法耗时情况,执行trace命令,可以看到如下例图:
优化方法无非是如下几种情况:
- 尽量避免for循环中查询数据库或者访问外部接口
- sql调优
- 缓存
- ....
排查了项目的情况,发现是在调用远程服务时,返回延迟比较大,考虑到该远程服务变化可能很小,于是做了一个本地缓存处理,同时定时同步处理,大致代码如下:
- 缓存接口定义
public interface LocalCache<T> {
/**
* 根据key获取缓存信息
* @param key 缓存key
* @return 缓存对象
*/
T get(String key);
/**
* 保存缓存信息, 存在了会覆盖
* @param key 缓存key
* @param cacheItem 缓存对象
*/
void save(String key, T cacheItem);
/**
* 根据缓存key删除缓存信息
*
* @param key 缓存对象
*/
void delete(String key);
}
- 定义抽象父类
public abstract class AbstractGuavaCache<T> implements LocalCache<T>, InitializingBean {
private static final ScheduledExecutorService SCHEDULED_CACHE =
new ScheduledThreadPoolExecutor(2, new ThreadFactoryBuilder().setNameFormat("guava cache-%d").build());
protected long expireSeconds;
protected long maximumSize;
protected long initDelay;
protected long delay;
protected Function<String, T> loadFunction;
protected boolean cacheTaskSwitch;
/**
* 定义缓存对象
*/
private LoadingCache<String, T> guavaCache;
public AbstractGuavaCache() {}
@Override
public void afterPropertiesSet() {
// 初始化guavaCache对象
this.guavaCache = CacheBuilder.newBuilder().expireAfterWrite(expireSeconds, TimeUnit.SECONDS)
.maximumSize(maximumSize).build(new CacheLoader<String, T>() {
@Override
public T load(String key) {
return loadFunction != null ? loadFunction.apply(key) : null;
}
});
// 定时任务
if(cacheTaskSwitch) {
SCHEDULED_CACHE.scheduleWithFixedDelay(() -> {
try {
this.reloadAllToCache();
} catch (Exception e) {
log.error("cache error", e);
}
}, initDelay, delay, TimeUnit.SECONDS);
}
}
@SneakyThrows
@Override
public T get(String key) {
return guavaCache.get(key);
}
@Override
public void save(String key, T cacheItem) {
guavaCache.put(key, cacheItem);
}
@Override
public void delete(String key) {
guavaCache.invalidate(key);
}
/**
* 更新缓存操作
*/
protected void reloadAllToCache() {
}
}
- 定义具体实现
@Component("orgCache")
@Slf4j
public class SysOrgCacheManager extends AbstractGuavaCache<SysOrg> implements OrgCacheService {
public SysOrgCacheManager(EventCacheProperties eventCacheProperties) {
this.expireSeconds = eventCacheProperties.getExpireSeconds();
this.maximumSize = eventCacheProperties.getMaximumSize();
this.cacheTaskSwitch = eventCacheProperties.isTaskEnabled();
this.initDelay = eventCacheProperties.getInitDelay();
this.delay = eventCacheProperties.getDelay();
this.loadFunction = key -> loadByKey(key);
}
private SysOrg loadByKey(String orgId) {
log.warn("缓存未命中,直接查询数据库, orgId: [{}]", orgId);
SysOrg org = OrgApi.getOrgById(orgId);
SysOrg cacheOrg = transformCachedOrg(org);
log.warn("缓存未命中,加载后的数据, data: [{}]", JSON.toJSONString(cacheOrg));
return cacheOrg;
}
private SysOrg transformCachedOrg(SysOrg org) {
if(org == null || StrUtil.isEmpty(org.getId())) {
return null;
}
return new SysOrg().setId(org.getId()).setName(org.getName()).setFullPath(org.getFullPath());
}
@Override
protected void reloadAllToCache() {
log.info("-------cache org 【缓存机构】 开始 -------");
TimeInterval timeInterval = new TimeInterval();
// 查询全量的机构数据
List<SysOrg> remoteOrgs = OrgApi.selectAll();
remoteOrgs.forEach(org -> {
SysOrg cacheOrg = this.transformCachedOrg(org);
this.save(org.getId(), cacheOrg);
});
log.info("-------cache org【缓存机构】 结束, cost: [{}] -------", timeInterval.intervalSecond());
}
@Override
public List<SysOrg> findOrgsByIds(Collection<String> orgIds) {
if(CollUtil.isEmpty(orgIds)) {
return Lists.newArrayListWithExpectedSize(16);
}
List<SysOrg> orgs = orgIds.stream().map(orgId -> this.get(orgId)).filter(Objects::nonNull).collect(Collectors.toList());
return orgs;
}
}
这里是通过guava cache实现的,通过配置可以修改缓存全量刷新的时间、缓存的失效时间、缓存最多存储的数据量。
总结
后面复盘分析了下,导致该问题主要的原因如下:
- 公司管理松散混乱,没有流程,包括本次部署架构和方案都没有,实施水平参差不齐,员工流动性大,招进来就用,完全没有什么培训机制。
- 公司标准研发流程存在问题,蒙眼狂奔,一大堆新需求,砍工时,完全没有排非功能性测试、性能测试的时间。
- 开发人员也要不断提高自己
目前虽然基本达到客户需求,但是感觉还是有很多不足,大家有没有一些其他的优化思路和方案呢?
边栏推荐
- postgis 数据表 迁移时错误解决方法
- 代码自动初始化
- TCP/IP基本实现
- 独立成分分析ICA/FastICA
- Neural network to solve what problem, neural network results is not stable
- Tensorboard的使用 ---- SummaryWriter类(pytorch版)
- Completed - desktop interactive wizard design based on facial expressions (share the results, attach the data set of facial expressions and the yolov5 model trained by yourself and the interactive int
- Threads, control, communications
- Leetcode sword 】 refers to the Offer (special commando) summary
- 分类任务说明
猜你喜欢

The tests that need to be done in the development of medical device products

李沐老师 PyTorch版——线性回归 + softmax回归的简洁实现(3)

Why do big Internet companies keep hiring while frantically laying off staff?

缓存存在的问题:缓存穿透、缓存击穿、缓存雪崩

automation tool

卷积神经网络 图像识别,卷积神经网络 图像处理

不知道取什么名字

Web 攻击的日志分析:初学者指南

KDD'22 Recommendation System Papers (24 Research & 36 Application Papers)

Query and track multiple express tracking numbers, and filter the tracking numbers shipped at a certain time
随机推荐
人体神经元细胞分布图片,神经元人体分布大图
为什么互联网大厂一边疯狂裁员,一边不停招聘?
Web attack log analysis: a guide for beginners
flex布局缺点
The CAP theorem instance analysis
神经网络一般训练多少次,神经网络训练时间过长
日常bug小结:
仿记事本;QFile
“忙碌”的 Polkadot最新努力,对DOT投资者意味着什么?
The tests that need to be done in the development of medical device products
仿QQ好友列表,QListWidget!
uvm简介
Shorthand for flex layout properties
How many times the neural network is generally trained, the neural network training time is too long
APISIX Ingress v1.5-rc1 released
webstorage
Web 攻击的日志分析:初学者指南
云计算和云服务,云计算
图解LeetCode——636. 函数的独占时间(难度:中等)
分类任务说明