一、前言

我们的微服务响应生产环境出现一个功能响应时间过慢,对Prometheus监控 Http Request进行分析发下该功能调用的后端接口响应时间平均在30秒以上,分析源码接口有mysql查询、MongoDB查询操作,对慢sql进行分析首先排除了慢sql问题,但是MongoDB作为高性能数据库几乎不会查询缓慢问题,由于没有一个即时的分析工具,无法判断程序在那块耗时较长,只有采取笨办法,在程序各个关键节点输出时间戳日志,最终排查到是MongoDB问题,线上MongoDB因为某种原因导致查询商品的数据到达百万级,而程序开发者在设计时未考虑到大数据情况,因为未设置索引。

1.1 为什么选择Arthas

Arthas是Alibaba开源Java应用诊断工具,具有

  • 活跃成熟的社区,文档齐全
  • arthas的定位是诊断问题排查,不会长时间attach在进程上,也不会作用在集群,不需要太多考虑对性能的开销
  • 支持Docker镜像
  • Dashboard实时查看系统的运行状况
  • 查看入参/返回值/异常
  • 性能热点
  • 类冲突
  • WebConsole
    等特点和特性。更多细节参考 中文官网

二、使用Arthas

Arthas支持单独部署安装arthas-boot.jar和将Arthas安装到基础镜像里两种方式,我们的微服务是基于Dokcer镜像进行发布,因此我们选择将把Arthas安装到基础镜像里:

2.1 使用docker-maven-plugin + Dockerfile构建镜像

# 在pom.xml增加docker-maven-plugin配置
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<imageName>${project.artifactId}:${project.version}</imageName>
# dockerDirectory 参数表示使用Dockerfile构建镜像,${basedir}表示Dockerfile与pom.xml同级目录
<dockerDirectory>${basedir}</dockerDirectory>
<dockerHost>http://192.168.128.xxxx:2375</dockerHost>
<imageTags>
<imageTag>latest</imageTag>
</imageTags>
<forceTags>true</forceTags>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>

2.2 修改Dockerfile 添加安装arthas代码

FROM java:8
ADD /app.jar // # copy arthas
COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev","/app.jar"]

注意Docker构建应该使用完整的JDK基础包,否则会出现Docker应用中arthas无法正常运行的问题,因为Arthas依赖JAVA的命令行工具和类库

2.3 使用公开的JDK基础镜像

FROM openjdk:8-jdk
FROM openjdk:8-jdk-alpine
FROM java:8

三 诊断工具使用案例

3.1 在线实时分析接口响应缓慢问题

生产环境出现接口响应缓慢,常想到分析手段是通过日志埋点来分析接口在那块处理耗费时间长,而通过集成arthas,使用trace命令可以渲染和统计整个调用链路上的所有性能开销和追踪调用链路。

3.1.1 启动Docker 镜像进程中arthas

docker exec -it   c86629fdc6cb /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar"

启动结果:

[root@localhost ~]# docker exec -it   c86629fdc6cb /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar"
--2020-11-25 15:17:33-- https://arthas.aliyun.com/arthas-boot.jar
Resolving arthas.aliyun.com (arthas.aliyun.com)... 203.119.214.116
Connecting to arthas.aliyun.com (arthas.aliyun.com)|203.119.214.116|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 138711 (135K) [application/java-archive]
Saving to: ‘arthas-boot.jar’ arthas-boot.jar 100%[========================================================================================>] 135.46K 245KB/s in 0.6s 2020-11-25 15:17:34 (245 KB/s) - ‘arthas-boot.jar’ saved [138711/138711] [INFO] arthas-boot version: 3.4.4
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1 /app.jar

3.1.2 使用trace命令 侦听方法

 trace com.myyshop.saas.dsuserserver.controller.TdDsShopController createShop

访问接口,查看侦听结果

`---ts=2020-11-25 07:32:47;thread_name=http-nio-7004-exec-5;id=4c;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@4ba534b0
`---[650.100619ms] com.myyshop.saas.dsuserserver.service.impl.TdDsShopServiceImpl$$EnhancerBySpringCGLIB$$3e545494:createShop()
`---[650.007439ms] org.springframework.cglib.proxy.MethodInterceptor:intercept()
`---[646.06381ms] com.myyshop.saas.dsuserserver.service.impl.TdDsShopServiceImpl:createShop()
+---[0.018917ms] com.myyshop.saas.dsuserserver.utils.LoginUtils:chkLogin() #110
+---[0.006053ms] com.dhgate.saas.common.core.model.LoginAppUser:getUserId() #111
+---[0.015575ms] com.baomidou.mybatisplus.core.conditions.query.QueryWrapper:<init>() #114
+---[0.027124ms] com.baomidou.mybatisplus.core.conditions.query.QueryWrapper:eq() #114
+---[5.757169ms] com.myyshop.saas.dsuserserver.mapper.TdDsShopMapper:selectCount() #114
+---[0.019784ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #121
+---[0.016377ms] com.dhgate.saas.common.core.utils.StringUtils:lowerCase() #121
+---[0.008651ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:setDomain() #121
+---[0.005473ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #124
+---[1.166932ms] com.myyshop.saas.dsuserserver.utils.DomainUtils:chkIsSensitiveWord() #124
+---[0.008751ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #131
+---[3.751548ms] com.myyshop.saas.dsuserserver.utils.DomainUtils:isRegisterDomains() #131
+---[0.008372ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #138
+---[0.012716ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getShopName() #138
+---[3.482077ms] com.myyshop.saas.dsuserserver.mapper.TdDsShopMapper:getcount() #138
+---[0.064746ms] com.myyshop.saas.dsuserserver.model.TdDsShop:<init>() #145
+---[0.007541ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #146
+---[0.012804ms] com.myyshop.saas.dsuserserver.model.TdDsShop:setDomain() #146
+---[0.009306ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getShopName() #148
+---[0.00715ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #148
+---[0.009197ms] com.myyshop.saas.dsuserserver.model.TdDsShop:setShopName() #148
+---[0.007298ms] com.myyshop.saas.dsuserserver.model.TdDsShop:setUserId() #149
+---[0.011004ms] com.myyshop.saas.dsuserserver.model.TdDsShop:setTemplateId() #150
+---[0.007945ms] com.myyshop.saas.dsuserserver.model.TdDsShop:setCreatedAt() #151
+---[0.007562ms] com.myyshop.saas.dsuserserver.model.TdDsShop:setUpdatedAt() #152
+---[4.256693ms] com.myyshop.saas.dsuserserver.mapper.TdDsShopMapper:insert() #153
+---[0.01248ms] com.myyshop.saas.dsuserserver.model.TdDsShop:getShopId() #155
+---[0.018589ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:<init>() #160
+---[0.009691ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getCurrency() #162
+---[0.009491ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:currency() #162
+---[0.007937ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getLanguage() #163
+---[0.008896ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:language() #163
+---[0.007881ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getRefundPolicyUrl() #164
+---[0.009011ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:refundPolicyUrl() #164
+---[0.008243ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getPrivacyPolicyUrl() #165
+---[0.008377ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:privacyPolicyUrl() #165
+---[0.007477ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getShippingTermsUrl() #166
+---[0.013257ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:shippingTermsUrl() #166
+---[0.025617ms] com.myyshop.saas.dsuserserver.model.TdDsShopSetting$Builder:build() #167
+---[10.906103ms] com.myyshop.saas.dsuserserver.service.TdDsShopSettingService:save() #169
+---[609.588604ms] com.myyshop.saas.dsuserserver.feign.MyyshopOrderClient:fistTimeCreateShop() #173
+---[0.024436ms] com.myyshop.saas.dsuserserver.model.dto.TdDsShopParam:getDomain() #179
+---[5.150318ms] com.myyshop.saas.dsuserserver.utils.DomainUtils:setRegisterDomains() #179
+---[0.154152ms] com.dhgate.saas.common.core.utils.CopyUtils:copyObject() #181
+---[0.013183ms] com.myyshop.saas.dsuserserver.model.vo.DsShopVo:setShopId() #182
`---[0.02153ms] com.dhgate.saas.common.core.model.Result:succeed() #184

通过调用链分析,可以看出接口处理大量时间花费到一个feign调用上,可以考虑对该接口进行优化。

 
 

Docker-maven-plugin + Dockerfile + Arthas实现应用诊断的更多相关文章

  1. 实战docker,编写Dockerfile定制tomcat8镜像,实现web应用在线部署

    最初在tomcat上部署web应用的方式,是通过maven的maven-compiler-plugin插件先打成war包,再将war包复制到tomcat的webapps目录下,后来用上了tomcat7 ...

  2. 使用Docker Maven 插件进行镜像的创建以及上传至私服

    1.在进行服务容器化部署的时候,需要将服务以及其运行的环境整个打包做成一个镜像,打包的过程有两种办法,第一种是首选通过maven打成jar包,然后再编写dockerfile,执行docker buil ...

  3. 如何在Docker容器中使用Arthas

    Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱. 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar ...

  4. 学习Maven之Cobertura Maven Plugin

    cobertura-maven-plugin是个什么鬼? cobertura-maven-plugin是一个校验单元测试用例覆盖率的工具,可以生成一个测试覆盖率报告,可以给单元测试用例编写提供参考. ...

  5. Maven实现Web应用集成測试自己主动化 -- 部署自己主动化(WebTest Maven Plugin)

    上篇:Maven实现Web应用集成測试自己主动化 -- 測试自己主动化(WebTest Maven Plugin) 之前介绍了怎样在maven中使用webtest插件实现web的集成測试,这里有个遗留 ...

  6. 解决Jetty Maven Plugin:Please initialize the log4j system properly(转)

    解决Jetty Maven Plugin:Please initialize the log4j system properly.Jetty Maven Plugin环境: <plugin> ...

  7. Spring Boot Maven Plugin(二):run目标

    简介 Spring Boot Maven Plugin插件提供spring boot在maven中的支持.允许你打包可运行的jar包或war包. 插件提供了几个maven目标和Spring Boot ...

  8. Spring Boot Maven Plugin(一):repackage目标

    简介 Spring Boot Maven Plugin插件提供spring boot在maven中的支持.允许你打包可运行的jar包或war包. 插件提供了几个maven目标和Spring Boot ...

  9. Docker(二):Dockerfile 使用介绍

    上一篇文章Docker(一):Docker入门教程介绍了 Docker 基本概念,其中镜像.容器和 Dockerfile .我们使用 Dockerfile 定义镜像,依赖镜像来运行容器,因此 Dock ...

随机推荐

  1. ElasticSearch详细笔记

    ElasticSearch详细笔记 什么是ElasticSearch Elasticsearch(简称ES)是一个基于Apache Lucene(TM)的开源搜索引擎,无论在开源还是专有领域,Luce ...

  2. 一篇文章搞定Git——Git代码管理及使用规范

    一篇文章搞定Git--Git代码管理及使用规范   https://blog.csdn.net/weixin_42092278/article/details/90448721

  3. java数据结构-07栈

    一.什么是栈 栈是一种线性结构,栈的特点就是先进后出(FILO):就像弹夹装子弹一样,最先压进去的在最底下,最后才被射出.  二.相关接口设计  三.栈的实现 栈可以用之前的数组.链表等设计,这里我使 ...

  4. 【总结】mybatis分页(实现 + 原理)

    1.mybatis pageHelper分页实现 (1)引入依赖 (2)配置pageHelper插件 <configuration> <!-- 引入 pageHelper插件 --& ...

  5. 基于 opencv 的图像处理入门教程

    前言 虽然计算机视觉领域目前基本是以深度学习算法为主,但实际上很多时候对图片的很多处理方法,并不需要采用深度学习的网络模型,采用目前成熟的图像处理库即可实现,比如 OpenCV 和 PIL ,对图片进 ...

  6. 正式班D20

    2020.11.02星期五 正式班D20 目录 11 软件包管理 11.1 软件包介绍 11.1.1 编程语言分类 11.1.2 三种安装包 11.2 rpm包管理 11.2.1 rpm包简介 11. ...

  7. c100k

    sysctl -w fs.file-max=10485760 #系统允许的文件描述符数量10msysctl -w net.ipv4.tcp_rmem=1024 #每个tcp连接的读取缓冲区1k,一个连 ...

  8. UML类图关系表示

    UML 之 C++类图关系全面剖析 分类: 软件设计与架构2008-10-16 08:52 5165人阅读 评论(3) 收藏 举报 umlc++borderclasscblog UML的类图关系分为: ...

  9. Runnable接口和Callable接口的区别

    Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已:Callable接口中的call()方法是有返回值的,是一个泛型,和Future.Fut ...

  10. 【kotlin】adapterPosition方法返回-1 无法获取位置

    在学习使用RecyclerView时 对Adapter的几个主要方法进行重写 通过使用书中的例子 在onCreateViewHolder中使用 viewHolder.itemView.setOnCli ...