记一次线程池调优经历

简介: 背景: 最近的一个项目需要用到招标,临时加了给我们的系统增加了一个性能需求,多少呢? 一秒钟300次NTP(不知道ntp的同学可以百度一下),平均3ms一次啊,没测试过,心里没有底。(⊙o⊙)…                                                             情境介绍: 系统是一个时间服务器系统,客户端就是window系统,或者其他的一些服务器,来向时间服务器同步时间。

背景:

最近的一个项目需要用到招标,临时加了给我们的系统增加了一个性能需求,多少呢?

一秒钟300次NTP(不知道ntp的同学可以百度一下),平均3ms一次啊,没测试过,心里没有底。(⊙o⊙)…

                                                           

情境介绍:

系统是一个时间服务器系统,客户端就是window系统,或者其他的一些服务器,来向时间服务器同步时间。

默认的window会向这个time.winodows.com进行时间同步,当然你也可以换成其他时间同步服务器。

划重点了:服务端NTP接口采用的是netty框架写的一个接口,netty想必大家都了解的吧,nio通信,性能超好的。

测试代码是使用Executors.newFixedThreadPool写的客户端,10个线程数发送ntp包

 

第一次测试

数据库连连接池最大设置为40个,测试结果俩一秒钟28次,是的你没有看错,连十分之一都没有,怎么这么差劲啊

达不到预期啊,不行啊,这不就达不到要求的了吗,得改啊,哪里改啊,怎么改啊?

回到代码中去,顺藤摸瓜找到具体业务类,就是继承SimpleChannelInboundHandler的类,从头到尾打量了一下业务代码,发现业务主要是构造返回消息,记录日志。

构造返回就是Java里的构造对象什么的,根本不耗时的。

                  

那就想不是有记录日志吗,不往数据库里面写东西了,把它注释掉,跑一遍试试看。

 

第二遍测试

哎呦妈呀,起飞了老铁,直接飙到了每秒钟2万次。

看到这个令人惊讶的数据,这远远超过要求啊,哈哈哈,妥妥的。

突然一想,不对啊,这好像和产品设计不符合啊,设计里是要求记录日志的啊,这个是有点滥竽充数啊,不行不行,这个得改。

仔细分析一下,日志得写到数据库,读了《java高并发程序设计》,心想是不是可以用异步的方式记录日志呢,弄一个线程池吧。

阿里巴巴JAVA开发手册里是不推荐使用Executors中的现成的线程池的(具体原因我就不说了,可以看一下),那就自己写一个吧。

 

第三次测试

考虑到任务提交速度快的原因,第一次构造线程池采用了直接提交的队列,这样任务处理的快一些

private static ExecutorService saveThreadPool = new ThreadPoolExecutor(2, 100, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());

 

测试代码再跑一遍,哎呦妈呀,又起飞了老铁!

心想我这么牛逼的吗,这随便写个线程池就OK了。

在服务器执行了top一看,不得了:

这个cpu直接占满了,系统里还有其他服务的,不能把资源全都给它啊。

这次留了个心眼看了下数据库日志,不对啊,没有全部记录啊,尼玛发了一百多万结果只记录了几万条,这个差太多了啊。

为什么漏掉了呢,回过头来继续看线程池的构造。

终于发现了纰漏,拒绝策略是用了new ThreadPoolExecutor.DiscardPolicy(),这个可出事了啊,不行不行,这直接丢弃了,记录日志的任务不能直接丢弃。

 

第四次测试

这次又把书拿出来看了看,看了一些大牛写的线程池构造,最终敲定了这样的构造方式。

private static ExecutorService saveThreadPool = new ThreadPoolExecutor(2, 40, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(50000), Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());

采用LinkedBlockingQueue,最多可有50000个任务在阻塞队列中,线程池最大值设置40个,核心池大小设置2个,多出来的38个线程最多活跃60秒就会被回收。

拒绝采用CallerRunsPolicy,不会丢弃任务,只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。

 

在代码测试中,执行了top,发现cpu的利用率稳定在24%左右,这个可以接受

 

测试结果:

每秒钟1千2,这个也远远超过了性能指标,而且日志也全都记录到数据中,不过这个不是及时性的,它会在测试程序结束1分钟后,才会完成数据如插入,这个和队列任务有关。

 

 总结:

这个线程池我是没有关闭的,因为每次任务提交后队列中还有很多任务,如果关闭的话,每次在开启一个线程池会降低速度,所以这个就不关闭了吧

如果有大神看出什么端倪的话,欢迎批评斧正,继续优化,个人感觉还有提升空间。

 

个人博客网站 http://www.janti.cn
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
24天前
|
缓存 算法 Java
Java内存管理与调优:释放应用潜能的关键
【4月更文挑战第2天】Java内存管理关乎性能与稳定性。理解JVM内存结构,如堆和栈,是优化基础。内存泄漏是常见问题,需谨慎管理对象生命周期,并使用工具如VisualVM检测。有效字符串处理、选择合适数据结构和算法能提升效率。垃圾回收自动回收内存,但策略调整影响性能,如选择不同类型的垃圾回收器。其他优化包括调整堆大小、使用对象池和缓存。掌握这些技巧,开发者能优化应用,提升系统性能。
|
4月前
|
存储 固态存储 Java
java性能调优——技客时间
java性能调优——技客时间
29 2
|
6月前
|
设计模式 缓存 Java
Java多线程线程池:提升应用性能的终极利器
Java多线程线程池:提升应用性能的终极利器
569 0
|
6月前
|
监控 Java
【JVM线上调优】
【JVM线上调优】
|
20天前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
【4月更文挑战第6天】Java并发编程中,线程池通过重用线程降低性能开销,控制并发级别。关键在于理解线程池工作原理:核心线程数、最大线程数、队列和拒绝策略。优化技巧包括合理设置线程池大小、选择合适队列、避免过度使用、自定义拒绝策略和正确关闭线程池。I/O密集型应用案例:大核心线程数、使用 `CachedThreadPool`、`LinkedBlockingQueue` 和定制拒绝策略。正确配置和管理线程池对提升应用性能至关重要。
|
3月前
|
监控 安全 算法
【面试问题】如果让你设计一个线程池如何设计?
【1月更文挑战第27天】【面试问题】如果让你设计一个线程池如何设计?
|
8月前
|
存储 算法 Oracle
一次性聊透JVM架构设计,就算八股文也得会
有位小伙伴在我的粉丝群里面问我一个面试题,说面试被问对JVM的理解,不知道怎么回答,今天咱们来聊透,就算是八股文你也得会。另外,往期面试题解析中配套的文档我已经准备好,想获得的可以在我的煮叶简介中找到。
50 0
|
存储 监控 Java
一文全貌了解线程池的正确使用姿势
一文全貌了解线程池的正确使用姿势
121 0
一文全貌了解线程池的正确使用姿势
使用线程池多线程优化大数据量项目 ✨ 每日积累
使用线程池多线程优化大数据量项目 ✨ 每日积累
使用线程池多线程优化大数据量项目 ✨ 每日积累
|
存储 缓存 监控
老生常谈的线程池希望你已经都会了
老生常谈的线程池希望你已经都会了
351 0
老生常谈的线程池希望你已经都会了