(1)cpu占用过高,使用JProfiler分析, 某方法占用20%的cpu时间,一会功夫就达到了120秒,注意是秒。
方法很简单,将yyyyMMdd格式的字符串转换为yyyy-MM-dd格式 使用的DateFormat,先parse为Date,再format为带横杠的格式。
<1>先改为字符串拼接,cpu有所下降,但还是很高
<2>写了个main方法,调用该方法10万次,用时900毫秒的样子, 调用3000万次,用时竟然是6秒。 主要是substring内部的复制占时间,尝试用char等方式优化,效果不理想。
<3> 分析业务方法,发现外部单次查询,for循环套for循环,该方法可能会被调用上万次。 每秒300并发的话,就是300万次,累计起来占时就很高了。
然后发现,该方法的入口参数,是最外部传进来的一个参数, 也就是说,最外部转换一次就行了,不需要搞TM的3000万次。
最外层转换,加入参数map中,里层直接用转换结果进行比较。
搞定,收工。
(2)另一个方法占用40%的cpu,是线程池的submit方法
// 触发查询逻辑 ExecutorService exec = Executors.newFixedThreadPool(1);
TimeoutCallable withTimeoutCallable = new TimeoutCallable(queryList, this);
Future<List<QueryRespData>> future = null; if (!exec.isShutdown()) { future = exec.submit(withTimeoutCallable); } exec.shutdown();
submit方法应该只是一个放队列的动作,占这么多cpu,不太可能。 所以觉得,可能是因为每次都做一个线程池的问题, 每秒300并发的话,做300个线程池,太浪费了
在方法顶部做了一个变量
/** * 线程池(最大2000,队列满了重新执行). */ private ExecutorService executor = new ThreadPoolExecutor(100, 2000, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy());
然后下面直接submit一下就完事。
修改后,上线部署测试OK,问题解决。
程序员当初为什么这么写?因为都是抄来的,一知半解的。
(3)经过两步优化,服务器的cpu由长期90%降到了稳定的50%左右。 剩下一些是gzip,fastjson,网络io的时间,总cpu时间不多。
还有个业务方法占40%,不过看不出具体哪句耗时, 方法太复杂,以后有时间再慢慢分析。
|