其他分享
首页 > 其他分享> > Web应用cpu过高

Web应用cpu过高

作者:互联网

**

每一个技术难点的攻坚,都是你一次完美的蜕变

**

前言

整体项目架构基于lengleng开源pig项目做开发,现状表现为 auth服务单纯搜索列表,cpu维持在40左右,85条数据导入达到了更为夸张的三位数,其实其他操作cpu也会上升得不正常,阔怕~本文将针对数据导入以及导入后列表查询展开

在这里插入图片描述

SQL 层分析

	找到对应的底层sql后,客户端看执行时间,确实很慢,展开对应的执行计划进行分析,话不多说,上图,上干货~

在这里插入图片描述
这么多行的Result,sql 确实也很复杂,针对这段时间sql层优化做出一下总结:

关于SQL引起CPU升高的问题参考以往博客:RDS数据库cpu过高分析

JVM层分析

分享Alibaba一个开源的Java诊断工具----- Arthas,对代码侵入性小,附文地址:Arthas ,辅助性排查监控工具:JConsole && VisualVM,经过分析后JVM层总是有以下log频繁出现:

"C2 CompilerThread9" #48 daemon prio=9 os_prio=0 tid=0x00007f45f0b80000 nid=0x188 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

Think1—修改codeCache的默认大小(结论:没有效果)

关于CodeCache导致性能降低详情参考:CodeCache

Java代码在执行次数达到一个阈值会触发JIT编译,一旦代码块被编译成本地机器码,下次执行的时候会直接运行编译后的本地机器码。所以这本地机器码必须被缓存起来,而缓存这个本地机器码的内存区域就是Code
Cache,它并不属于Java堆的一部分,除了JIT编译的代码之外,Java所使用的本地方法代码(JNI)也会存在codeCache中。

当CodeCache空间使用完之后,JVM的JIT功能会被停止,将不会编译任何额外的代码;被编译过的代码仍然以编译方式执行,但是尚未编译的代码智能以解释的方式执行了。在这种情况下,如果应用中很多代码以解释方式执行,系统性能势必会大大降低,第一感觉貌似就是这个问题,于是开始修改JVM启动参数,验证方案是否可行。

Step1:通过jinfo命令查看当前默认的CodeCache大小,得到的结论与网上JDK版本对应的默认大小一致,为240M。具体命令如下,pid为Java进程ID。

jinfo -flag ReservedCodeCacheSize pid

Step2:查看当前CodeCache使用情况,这一步异常坎坷,由于线上堡垒机权限问题,网上的很多办法都没有成功。具体的方式大概有5种,分别如下:

Step3: 设置JVM启动参数,死马当活马医。设置CodeCache大小为300M,具体参数如下:

-XX:ReservedCodeCacheSize=300M

很遗憾,重启后运行一段时间发现,系统CPU负载依然会升高。后来通过公司的统一监控平台证明了我的CodeCache并没有使用满,由于CodeCache是属于堆外内存,然而我的系统堆外缓存使用一共还不到200M,所以也从一方面证明了思路一的方向是错误的。

如果计划尝试思路一的方法来解决问题,建议大家先想办法证明应用的CodeCache确实使用满了,正常来说JDK8 版本的默认值240M已经足够用了,如果其他版本的JRE环境可能确实会有问题。如果无法证明满了,也可以像我一样死马当活马医,试试就知道结果了,但是不要抱太大希望。

Think2:关闭JIT分层编译(结论:有效果)

编译器在编译过程中通常会考虑很多因素。比如:汇编指令的顺序。假设我们要将两个寄存器的值进行相加,执行这个操作一般只需要一个CPU周期;但是在相加之前需要将数据从内存读到寄存器中,这个操作是需要多个CPU周期的。编译器一般可以做到,先启动数据加载操作,然后执行其它指令,等数据加载完成后,再执行相加操作。由于解释器在解释执行的过程中,每次只能看到一行代码,所以很难生成上述这样的高效指令序列。而编译器可以事先看到所有代码,因此,一般来说,解释性代码比编译性代码要慢。

java 作为静态语言十分特殊,他需要编译,但并不是在执行之前就编译为本地机器码。Java的实现在解释性和编译性之间进行了折中,Java代码是编译性的,它会被编译成一个平台独立的字节码程序。JVM负责加载、解释、执行这些字节码程序,在这个过程中,还可能会将这些字节码实时编译成目标机器码,以便提升性能。

所以,在谈到 java的编译机制的时候,其实应该按时期,分为两个部分。一个是 javac指令 将java源码变为 java字节码的静态编译过程。 另一个是 java字节码编译为本地机器码的过程,并且因为这个过程是在程序运行时期完成的所以称之为即时编译(JIT:Just In Time)。

JIT编译类型:C1编译器、C2编译器、分层编译器

通常我们说即时编译器有两种类型,Client Compiler(C1编译器)和Server Compiler(C2编译器)。这两种编译器最大的区别就是,编译代码的时间点不一样。C1编译器会更早的对代码进行编译,因此在程序刚启动的时候,C1编译器比C2编译器执行的更快,所以C1编译器适用于一些GUI应用,可以缩短应用启动时间。C2编译器会收集更多的信息,然后才对代码进行编译优化,所以从长远角度考虑,C2编译器最终可以产生比C1编译器更优秀的代码,适用于长时间运行的后台接口服务。

可能大家都有一个困扰,JVM为什么要将编译器分为client和server,为什么不在程序启动时,使用client编译器,在程序运行一段时间后,自动切换为server编译器? 其实,这种技术是存在的,一般称之为 Tiered Compiler(分层编译器)。Java7 和Java 8可以使用选项-XX:+TieredCompilation来打开(-server选项也要打开)。在Java 8中,-XX:+TieredCompilation默认是打开的。

在一些特殊情况下,激进优化后的代码并不能有更高的性能。需要进行优化回退,将重新对代码进行解释执行。

衍生以下结论:

关闭分层编译,启用C2编译器:

-XX:-TieredCompilation -server

回归到系统验证,发现确实有一定作用,但是并不完全是这个原因引起的,才会引出以下第三点,逼逼叨半天这么多文字,一个体现在一个nohup语句,如下,后续更新各个参数作用以及如何设置对应size:

nohup java -Xms2048m -Xmx2048m -Xss256k -XX:NewSize=768M -XX:MaxNewSize=768M -XX:-TieredCompilation -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.port=18999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -XX:-TieredCompilation -server  -XX:CICompilerCount=4 -jar XXX.jar --spring.profiles.active=dev > eqics-auth.log 2>&1 &

系统架构(链路)层:

玩过这个pig项目的小伙伴都知道,所谓的分层架构,也将controller,service,dao分为不同的module,服务之间的调用通过openfeign,通过@Inner标识区分外网还是内网调用,进行对应的拦截与放行,虽然我们没有采用这种方式,但是也采取对应的放行措施了,日志中频繁打印以下log

@Override
    public void apply(RequestTemplate requestTemplate)
    {
        log.info("OAuth2FeignRequestInterceptor 1111111");
        SecurityContext securityContext = SecurityContextHolder.getContext();
        Authentication authentication = securityContext.getAuthentication();
        if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails)
        {

            log.info("authorization token:" + SecurityConstants.BEARER_TOKEN_TYPE);
            OAuth2AuthenticationDetails dateils = (OAuth2AuthenticationDetails) authentication.getDetails();
            log.info("token value:" + dateils.getTokenValue());

            requestTemplate.header(HttpHeaders.AUTHORIZATION,
                    String.format("%s %s", SecurityConstants.BEARER_TOKEN_TYPE, dateils.getTokenValue()));
        }
    }

作对应权限认证,也没有毛病,但是业务上的数据导入是在service层循环导入,所以打印了85次对应log,针对这块作修改,传入list后到dao处理,效率提升,cpu也恢复平稳✌️✌️✌️

==
如有问题,请留言指出,感谢您的阅读

聊技术侃篮球,欢迎扫描加博主微信,hiahiahia
在这里插入图片描述

标签:Web,代码,XX,CodeCache,过高,编译,编译器,C2,cpu
来源: https://blog.csdn.net/qq_39863690/article/details/116918600