从Docker简单部署到jvm高cpu问题演练

目录

    • 演练代码
    • 打jar包
    • docker部署
      • 编写Dockfile
      • 创建镜像
      • 生成容器并运行
    • jvm中cpu问题定位
    • 参考

演练代码

public class ThreadCpuTest {

    public static void main(String[] args) {
        Executor executor = Executors.newFixedThreadPool(5);
        executor.execute(() -> {

            while (true) {
                try {
                    TimeUnit.MICROSECONDS.sleep(200);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        executor.execute(() -> {
            while (true) {
                try {
                    TimeUnit.MICROSECONDS.sleep(200);
                    System.out.println("hello world" + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

    }

}

就是开两个线程在疯狂刷

打jar包

pom文件填加如下插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.5.5</version>
    <configuration>
        <archive>
        	//指定main方法入口
            <manifest>
                <mainClass>com.test.ThreadCpuTest</mainClass>
            </manifest>
        </archive>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

这个会在target下达成两个jar包,一个包含依赖,一个没有包含。用包含依赖那个
从Docker简单部署到jvm高cpu问题演练_第1张图片
使用normalWork-1.0-SNAPSHOT-jar-with-dependencies.jar,但在这里jar包名称改为:normalWork-1.0-SNAPSHOT.jar(这块改写了,原先的打包方式没有打进依赖)。

docker部署

编写Dockfile

FROM java:8
EXPOSE 8081
ADD normalWork-1.0-SNAPSHOT.jar work.jar
RUN sh -c ‘touch /work.jar’
ENV JAVA_OPTS="-Djava.rmi.server.hostname=$localName -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.rmi.port=1099 -Dcom.sun.management.jmxremote.ssl=false"
ENTRYPOINT [ “sh”, “-c”, “java $JAVA_OPTS -jar /work.jar” ]

JAVA_OPTS是用于开启JMX,进行jvm监控
ADD表示把文件拷入docker ,并改名成 work.jar

ENTRYPOINT 表示docker 启动执行第一个指令,这里是调用java 启动 jar

这里记录下RUN命令与CMD命令的区别:简单说,RUN命令在 image 文件的构建阶段执行,执行结果都会打包进入 image文件;CMD命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个RUN命令,但是只能有一个CMD命令。

创建镜像

创建镜像命令:

docker build -t normalwork .

normalwork是镜像名称,该命令在Dockfile所在的目录执行,并且jar包和Dockerfile在一个目录(注意Dockfile里面内容),下图:
从Docker简单部署到jvm高cpu问题演练_第2张图片
创建镜像成功如下:
从Docker简单部署到jvm高cpu问题演练_第3张图片
这个时候执行docker images,可以发现已经有了
从Docker简单部署到jvm高cpu问题演练_第4张图片

生成容器并运行

生成容器,容器生成后会自动运行java -jar work.jar,可以看出已经在打印输出了

docker run --env localName=192.168.0.106 -p 8081:8081 -p 1099:1099 -it --rm normalwork /bin/bash
(--env localName=192.168.0.106用于启动输入环境变量,这是是设置jmx的hostname)

从Docker简单部署到jvm高cpu问题演练_第5张图片

jvm中cpu问题定位

第一步:
首先进入docker应用的虚拟环境:

docker exec -it 4b46751c85da /bin/bash

4b46751c85da是容器的containerid,通过docker ps直接查出

第二步:
查看所有运行中进程,获取想要的进程pid,在这里为6。

top

从Docker简单部署到jvm高cpu问题演练_第6张图片
这里还有一种非常方便的方式获取jvm进程id,就是我们虚拟机自带的 jps 命令

jps 可以列出 正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称 以及这些进程的本地虚拟机唯一ID(Local Virtual Machine Identifier,LVMID)(进程ID)
在这里插入图片描述
从Docker简单部署到jvm高cpu问题演练_第7张图片
在这里的效果如下:
从Docker简单部署到jvm高cpu问题演练_第8张图片

第三步:
查看该进程下所有线程执行情况。

 top -H -p 6    (-H表示展示线程)

从Docker简单部署到jvm高cpu问题演练_第9张图片
可以发现pid为18、19的两个线程是cpu比较高得,也就是我们要找得。将18转为16进制:0x12,同理19:0x13。

第四步:
拉取jvm线程堆栈,6为上面的进程id。并cat 查看线程堆栈信息:
从Docker简单部署到jvm高cpu问题演练_第10张图片

jstack -l 6 > temp.txt
cat temp.txt

通过上面的线程ID:0x12和0x13,去找对应的线程信息:

找到我们熟悉的代码,然后去具体看业务代码干了啥。

参考

https://blog.csdn.net/xiao__gui/article/details/47341385
https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html
https://www.jianshu.com/p/2a32a4fc9852
https://blog.csdn.net/jiachengwin/article/details/73848952

你可能感兴趣的:(并发)