作者:微信小助手
发布时间:2022-09-30T11:37:47
Spring Boot 已经成为 Java 届的 No.1 框架,每天都在蹂躏着数百万的程序员们。当服务的压力上升,对 Spring Boot 服务的优化就会被提上议程。
本文将详细讲解 Spring Boot 服务优化的一般思路,并附上若干篇辅助文章作为开胃菜。
本文较长,最适合收藏之。
在开始对 Spring Boot 服务进行性能优化之前,我们需要做一些准备,把 Spring Boot 服务的一些数据暴露出来。
比如,你的服务用到了缓存,就需要把缓存命中率这些数据进行收集;用到了数据库连接池,就需要把连接池的参数给暴露出来。
我们这里采用的监控工具是 Prometheus,它是一个是时序数据库,能够存储我们的指标。Spring Boot 可以非常方便的接入到 Prometheus 中。
创建一个 Spring Boot 项目后,首先加入 Maven 依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
然后,我们需要在 application.properties 配置文件中,开放相关的监控接口。
management.endpoint.metrics.enabled=true
management.endpoints.web.exposure.include=*
management.endpoint.prometheus.enabled=true
management.metrics.export.prometheus.enabled=true
启动之后,我们就可以通过访问 http://localhost:8080/actuator/prometheus 来获取监控数据。
想要监控业务数据也是比较简单的。你只需要注入一个 MeterRegistry 实例即可。下面是一段示例代码:
@Autowired
MeterRegistry registry;
@GetMapping("/test")
@ResponseBody
public String test() {
registry.counter("test", "from", "127.0.0.1", "method", "test").increment();
return "ok";
}
从监控连接中,我们可以找到刚刚添加的监控信息。
test_total{from="127.0.0.1",method="test",} 5.0
这里简单介绍一下流行的 Prometheus 监控体系。Prometheus 使用拉的方式获取监控数据,这个暴露数据的过程可以交给功能更加齐全的 telegraf 组件。
如图,我们通常使用 Grafana 进行监控数据的展示,使用 AlertManager 组件进行提前预警。这一部分的搭建工作不是我们的重点,感兴趣的同学可自行研究。下图便是一张典型的监控图,可以看到 Redis 的缓存命中率等情况。
火焰图是用来分析程序运行瓶颈的工具。在纵向,表示的是调用栈的深度;横向表明的是消耗的时间。所以格子的宽度越大,越说明它可能是一个瓶颈。
火焰图也可以用来分析 Java 应用。可以从 GitHub 上下载 async-profiler 的压缩包 进行相关操作。
比如,我们把它解压到 /root 目录。然后以 javaagent 的方式来启动 Java 应用。命令行如下:
java -agentpath:/root/build/libasyncProfiler.so=start,svg,file=profile.svg -jar spring-petclinic-2.3.1.BUILD-SNAPSHOT.jar
运行一段时间后,停止进程,可以看到在当前目录下,生成了 profile.svg 文件,这个文件是可以用浏览器打开的,一层层向下浏览,即可找到需要优化的目标。
3 Skywalking
对于一个web服务来说,最缓慢的地方就在于数据库操作。所以,使用本地缓存和分布式缓存优化,能够获得最大的性能提升。
对于如何定位到复杂分布式环境中的问题,我这里想要分享另外一个工具:Skywalking。
Skywalking 是使用探针技术(Java Agent)来实现的。通过在 Java 的启动参数中,加入 javaagent 的 Jar 包,即可将性能数据和调用链数据封装、发送到 Skywalking 的服务器。
下载相应的安装包(如果使用 Elasticsearch 存储,需要下载专用的安装包),配置好存储之后,即可一键启动。
将 agent 的压缩包,解压到相应的目录。
tar xvf skywalking-agent.tar.gz -C /opt/
在业务启动参数中加入 agent 的包,比如原来的启动命令是:
java -jar /opt/test-service/spring-boot-demo.jar --spring.profiles.active=dev
改造后的启动命令是:
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=the-demo-name -jar /opt/test-service/spring-boot-demo.ja --spring.profiles.active=dev
访问一些服务的链接,打开 Skywalking 的 UI,即可看到下图的界面。我们可以从图中找到响应比较慢 QPS 又比较高的的接口,进行专项优化。
对一个普通的 Web 服务来说,我们来看一下,要访问到具体的数据,都要经历哪些主要的环节。
如下图,在浏览器中输入相应的域名,需要通过 DNS 解析到具体的 IP 地址上。为了保证高可用,我们的服务一般都会部署多份,然后使用 Nginx 做反向代理和负载均衡。
Nginx 根据资源的特性,会承担一部分动静分离的功能。其中,动态功能部分,会进入我们的 Spring Boot 服务。
Spring Boot 默认使用内嵌的 Tomcat 作为 Web 容器,使用典型的 MVC 模式,最终访问到我们的数据。
下面我们举例来看一下,哪些动作能够加快网页的获取。为了描述方便,我们仅讨论 HTTP1.1 协议的。
使用 CDN 加速文件获取
比较大的文件,尽量使用 CDN(Content Delivery Network)分发。甚至是一些常用的前端脚本、样式、图片等,都可以放到 CDN 上。CDN 通常能够加快这些文件的获取,网页加载也更加迅速。
合理设置 Cache-Control 值
浏览器会判断 HTTP 头 Cache-Control 的内容,用来决定是否使用浏览器缓存,这在管理一些静态文件的时候,非常有用。相同作用的头信息还有 Expires。Cache-Control 表示多久之后过期,Expires 则表示什么时候过期。
这个参数可以在 Nginx 的配置文件中进行设置。
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
# 缓存1年
add_header Cache-Control: no-cache, max-age=31536000;
}
减少单页面请求域名的数量