Spring Boot 微服务性能下降九成!使用 Arthas 定位根因

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
注册配置 MSE Nacos/ZooKeeper,118元/月
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 接收到公司业务部门的开发反馈,应用在升级公司内部框架后,UAT(预生产)环境接口性能压测不达标。

头图.png

作者 | 王瑞显  掌门教育基础架构部研发工程师
来源|阿里巴巴云原生公众号

背景

接收到公司业务部门的开发反馈,应用在升级公司内部框架后,UAT(预生产)环境接口性能压测不达标。
升级前压测报告:

1.png

升级后压测报告:

2.png

在机器配置(1C4G)相同的情况下,吞吐量从原来的 53.9/s 下降到了 6.4/s,且 CPU 负载较高。

并且开发反馈从公司全链路监控系统 SkyWalking 中查询到的链路信息可以得知大部分请求 Feign 调用的耗时不太正常(390ms),而实际被调用的下游服务响应速度很快(3ms)。

3.png

定位问题

在接收到反馈以后,我立即申请了相应机器的权限,并往相应机器上传了 Arthas(version 3.4.3)。

让业务方保持压测,开始问题定位。

1. 执行 profiler 命令对 CPU 进行性能分析

[arthas@17962]$ profiler start -d 30 -f /tmp/arthas/1.txt

等待 30s 后,打开 1.txt,查看 CPU 性能分析结果,开头部分示例如下:

--- 1630160766 ns (4.24%), 141 samples
  ......
  [14] org.springframework.boot.loader.LaunchedURLClassLoader.definePackageIfNecessary
  [15] org.springframework.boot.loader.LaunchedURLClassLoader.loadClass
  [16] java.lang.ClassLoader.loadClass
  [17] java.lang.Class.forName0
  [18] java.lang.Class.forName
  [19] org.springframework.util.ClassUtils.forName
  [20] org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.registerWellKnownModulesIfAvailable
  [21] org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.configure
  [22] org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.build
  [23] org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.addDefaultHttpMessageConverters
  [24] org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.getMessageConverters
  [25] org.springframework.boot.autoconfigure.http.HttpMessageConverters$1.defaultMessageConverters
  [26] org.springframework.boot.autoconfigure.http.HttpMessageConverters.getDefaultConverters
  [27] org.springframework.boot.autoconfigure.http.HttpMessageConverters.<init>
  [28] org.springframework.boot.autoconfigure.http.HttpMessageConverters.<init>
  [29] org.springframework.boot.autoconfigure.http.HttpMessageConverters.<init>
  [30] com.zhangmen.xxx.DefaultFeignConfig.lambda$feignDecoder$0
  [31] com.zhangmen.xxx.DefaultFeignConfig$$Lambda$704.256909008.getObject
  [32] org.springframework.cloud.openfeign.support.SpringDecoder.decode
  [33] org.springframework.cloud.openfeign.support.ResponseEntityDecoder.decode
 ......

2. 对可疑的方法执行 trace 命令输出方法路径上每个节点的耗时

分析上一步得到的 CPU 性能分析结果,可以发现最占用 CPU 的栈中,确实有 Feign 相关的栈帧。

并且发现 Feign 相关的栈帧周围出现了 com.zhangmen 相关的栈帧:com.zhangmen.xxx.DefaultFeignConfig$$Lambda$704.256909008.getObject 和 com.zhangmen.xxx.DefaultFeignConfig.lambda$feignDecoder$0。

在 1.txt 中搜索 com.zhangmen.xxx.DefaultFeignConfig,发现有 340 次命中,因此认为这是一个非常可疑的方法。

执行 trace 命令输出该方法路径上每个节点的耗时:

[arthas@17962]$ trace com.zhangmen.xxx.DefaultFeignConfig * '#cost>200' -n 3
`---[603.999323ms] com.zhangmen.xxx.DefaultFeignConfig:lambda$feignEncoder$1()
    `---[603.856565ms] org.springframework.boot.autoconfigure.http.HttpMessageConverters:<init>() #42

发现 org.springframework.boot.autoconfigure.http.HttpMessageConverters:() 比较耗时,并继续一层层 trace 追踪下去:

[arthas@17962]$ trace org.springframework.boot.autoconfigure.http.HttpMessageConverters <init> '#cost>200' -n 3
......
[arthas@17962]$ trace org.springframework.http.converter.json.Jackson2ObjectMapperBuilder registerWellKnownModulesIfAvailable '#cost>200' -n 3

4.png

最终发现 org.springframework.util.ClassUtils:forName() 比较耗时,并且抛出了异常。

使用 watch 命令查看具体的异常:

[arthas@17962]$ watch org.springframework.util.ClassUtils forName -e "throwExp" -n 

5.png

解决问题

将定位到的问题,反馈给相关业务开发,并建议引入 jackson-datatype-joda 依赖。

引入依赖后压测报告:

6.png

吞吐量从原来的 6.4/s 提升到了 69.3/s,比升级框架前的 53.9/s 还要高。

这时候相关业务开发反馈,这个问题是代码中自定义了 Feign 的编解码器(下图所示)导致的,并且这个编解码器在升级框架前也是一直存在的。

7.png

于是,对升级框架前的代码进行压测并在压测过程中使用 Arthas 执行以下命令:

8.png

发现同样有这个异常。引入 jackson-datatype-joda 依赖,再次进行压测,压测报告如下:

9.png

汇总前面的压测结果:

10.png

可以发现一个新的问题:为什么新旧版本同时不引入依赖,吞吐量相差近 8 倍,新旧版本同时引入依赖,吞吐量相差近 1 倍?

进一步定位问题

根据上一步中发现的新问题,接下来对 未升级框架并引入依赖的版本 和 升级框架并引入依赖的版本 分别进行压测,并在压测过程中使用 Arthas 的 profiler 命令采样 CPU 性能分析数据,得到样本 1 和样本 2 。并从样本 1 和样本 2 中找到相似栈进行对比:

11.png

通过对比可以发现,两个样本的相似栈的前 17 行有所不同。并对样本 2 中的可疑栈帧进行 trace 追踪:

[arthas@10561]$ trace org.apache.catalina.loader.WebappClassLoaderBase$CombinedEnumeration * '#cost>100' -n 3
`---[171.744137ms] org.apache.catalina.loader.WebappClassLoaderBase$CombinedEnumeration:hasMoreElements()
    `---[171.736943ms] org.apache.catalina.loader.WebappClassLoaderBase$CombinedEnumeration:inc() #2685
        `---[171.724546ms] org.apache.catalina.loader.WebappClassLoaderBase$CombinedEnumeration:inc()

发现升级框架后,在类加载器这部分存在比较耗时的情况。

而对样本 1 中这部分进行 trace 追踪没有出现耗时大于 100ms 的情况。

又进一步使用 profiler 命令,分别生成两个版本在压测场景下的火焰图,并找到相似栈进行对比:

[arthas@10561]$ profiler start -d 30 -f /tmp/arthas/1.svg

12.png

发现 升级框架并引入依赖的版本 还多出了一些 org/springframework/boot/loader/ 相关的栈。

进一步解决问题

将新的发现反馈给相关业务开发。

他们反映此次除了框架升级外,还有 Spring Boot war to jar 部署的调整。从使用独立的 Tomcat war 部署,改造成用 Spring Boot 内嵌 Tomcat java -jar 部署。故怀疑两种部署方式在类加载器上存在性能差异。
相关业务开发在我上一步定位问题期间,根据我最初定位到的问题,在 Google 搜索 feign com.fasterxml.jackson.datatype.joda.JodaModule,找到了一篇相关的文章《loadClass 导致线上服务卡顿分析》

文章中的作者,遇到的问题与我们基本相似。

看了这篇文章后,我又 Debug 了部分源码,最后了解到问题产生的根本原因是:SpringEncoder / SpringDecoder 在每次编码 / 解码时都会调用 ObjectFactory.getObject()).getConverters() 获取 HttpMessageConverters。而我们自定义的 DefaultFeignConfig 中配置的ObjectFactory 的实现是每次都 new 一个新的 HttpMessageConverters 对象。

HttpMessageConverters 的构造方法又默认会执行 getDefaultConverters 方法获取默认的 HttpMessageConverter 集合,并初始化这些默认的 HttpMessageConverter。其中的 MappingJackson2HttpMessageConverter(有两个,见下图) 每次初始化时都会加载不在 classpath 中的 com.fasterxml.jackson.datatype.joda.JodaModule和 com.fasterxml.jackson.datatype.joda$JodaModule(org.springframework.util.ClassUtils 加载不到类时,会尝试再加载一下内部类),并抛出 ClassNotFoundException,且该异常最后被生吞。

而部分和 XML 相关的默认的 HttpMessageConverter,SourceHttpMessageConverter 和 Jaxb2RootElementHttpMessageConverter(各有两个,见下图)每次初始化时,会执行 TransformerFactory.newInstance(),执行过程中会使用 SPI 扫描 classpath 下的 META-INF / services 目录获取具体的实现,并且每次扫描完也没有获取到具体的实现,最终使用默认指定的 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl作为实现。

最终导致每一次 Feign 调用(包含编码和解码),都会加载 4 次不在 classpath 中的 com.fasterxml.jackson.datatype.joda.JodaModule 和 com.fasterxml.jackson.datatype.joda$JodaModule(共8次),且 8 次使用 SPI 扫描 classpath 下的 META-INF / services 目录获取查找不到的实现,而 war to jar 后,类加载器在频繁查找和加载资源上的性能有所下降,最终导致严重影响接口性能。

默认的 HttpMessageConverter 集合:

13.png

部分关键代码如下。

org/springframework/boot/autoconfigure/http/HttpMessageConverters.:

14.png

org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.registerWellKnownModulesIfAvailable:

15.png

org/springframework/util.ClassUtils.forName:

16.png

org/springframework/http/converter/xml/SourceHttpMessageConverter:

17.png

javax/xml/transform/FactoryFinder.find:

18.png

文章中对于这个问题还提供了两种解决方法:

第一种方法,就是我最初建议的引入 jackson-datatype-joda 依赖,避免每次初始化默认的 MappingJackson2HttpMessageConverter 时 ClassLoader 反复加载不在 classpath 中的 com.fasterxml.jackson.datatype.joda.JodaModule 和 com.fasterxml.jackson.datatype.joda$JodaModule。

第二种方法,不初始化默认的 HttpMessageConverter。由于我们此处只需要使用自定义的 FastJsonHttpMessageConverter 来执行编解码,完全可以避免执行 getDefaultConverters 方法,重复初始化许多用不到的默认的 HttpMessageConverter。因此在 new HttpMessageConverters 对象时 ,可以将 addDefaultConverters 参数设置为 false。

ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(false, new HttpMessageConverter[] { (HttpMessageConverter)fastJsonHttpMessageConverter });

实际上,我们还可以修改 DefaultFeignConfig 中 ObjectFactory 的实现,避免每次都 new 一个新的 HttpMessageConverters 对象(重复初始化 HttpMessageConverters),实现进一步优化。
故建议相关业务开发,将 DefaultFeignConfig 修改成如下代码:

19.png

在相关业务开发将新旧版本代码中的 DefaultFeignConfig 都进行改进并部署到 FAT(测试)环境以后,我在自己本机用 JMeter 对 FAT 环境进行了模拟压测。

旧版改进后压测结果:

20.png

新版改进后压测结果:

21.png

发现此时,两个版本的接口性能已经基本相同。

最终测试人员在 UAT 环境,对升级框架并改进 DefaultFeignConfig 后的代码再次进行压测,压测结果如下:

22.png

吞吐量从最初不达标的 6.4/s,提升到了160.4/s。

那为什么 war to jar 部署的调整,会导致类加载器在频繁查找和加载资源时性能有所下降呢?

在对 SpringBoot 可执行 jar 的原理做了一番了解以后。怀疑和 Spring Boot 为了做到能够以一个 fat jar 来启动,从而扩展了 JDK 的 JarFile URL 协议,并定制了自己的 ClassLoader 和 jar 文件协议的 Hander,实现了 jar in jar、jar in directory 的加载方式有关。

对 SpringBoot 可执行 jar 原理感兴趣的朋友可以参考:《可执行 jar 包》

War2Jar 类加载器性能下降根因探究

为了验证自己的猜测,我在自己本机搭建了一个简单的 Demo。

Demo 中有两个服务,A 和 B。将 A 和 B 都注册到 Eureka 注册中心,并且 A 通过 Feign 调用 B。

接下来使用 Jmeter 在相同的配置下对各种场景进行压测,并在压测过程中使用 Arthas 的 profiler 命令生成各种场景下的火焰图。

压测结果如下(-Xms512m -Xmx512m):

23.png

通过对比表格三和表格四可以得知,代码优化后,是否引入依赖,对吞吐量几乎没有影响。

通过表格三和表格四可以得知,代码优化后,在不会频繁查找和加载不存在的资源时,三种部署方式的吞吐量基本一致。

通过表格二可以得知,在频繁使用 SPI 获取 classpath 下查找不到的实现时,Tomcat war 部署性能更好。

通过表格一可以得知,在频繁加载不存在的类时,将 jar 包解压后通过 JarLauncher 启动性能更好。

对比表格一中 ③ 和 ② 相似栈的火焰图:

24.png

可以发现两者在 org/springframework/boot/loader/LaunchedURLClassLoader.loadClass 加载类时,存在差异。

② 不仅会执行 java/lang/ClassLoader.loadClass,还会执行 org/springframework/boot/loader/LaunchedURLClassLoader.definePackageIfNecessary。

查看 org/springframework/boot/loader/LaunchedURLClassLoader.loadClass 的源码:

25.png

发现存在一个条件分支。

查看 org/springframework/boot/loader/Launcher.createArchive 的源码:

26.png

发现这个条件的值与应用是 可执行 jar 文件 还是 文件目录 有关。

对 ② 再次进行压测,并 trace org/springframework/boot/loader/LaunchedURLClassLoader.definePackageIfNecessary:

`---[165.770773ms] org.springframework.boot.loader.LaunchedURLClassLoader:definePackageIfNecessary()
    +---[0.00347ms] org.springframework.boot.loader.LaunchedURLClassLoader:getPackage() #197
    `---[165.761244ms] org.springframework.boot.loader.LaunchedURLClassLoader:definePackage() #199

发现这个地方确实存在比较耗时的情况。

阅读该部分源码,从注释中即可得知,definePackageIfNecessary 主要是为了在调用 findClass 之前尝试根据类名去定义类所在的 package,确保 jar 文件嵌套 jar 包里的 manifest 能够和 package 关联起来。

27.png

Debug definePackageIfNecessary 这部分代码,发现在 definePackage 时,会遍历 BOOT-INF/lib/ 下所有的 jar 包 以及 BOOT-INF/classes/。如果发现这些资源中存在指定的类,就继续调用 definePackage 方法,否则遍历完直接返回 null。

28.png

29.png

前面说过,每一次 Feign 调用都会加载 4 次不在 classpath 中的 com.fasterxml.jackson.datatype.joda.JodaModule 和 com.fasterxml.jackson.datatype.joda$JodaModule (共8次)。而我这个简单的 Demo 应用依赖的 jar 有 117 个(实际企业级项目将会更多)。那么每一次 Feign 调用,就会执行 8 * (117 + 1),总计 944 次循环里的逻辑。而逻辑中的 org.springframework.boot.loader.jar.Handler.openConnection 方法在执行过程中又会涉及到比较耗时的 IO 操作,最终导致严重影响接口性能。从生成的火焰图中,也可以看到这部分处理逻辑。

30.png

至此,已经可以确认 war to jar 部署的调整,导致类加载器在频繁查找和加载资源时性能有所下降的根因就是:Spring Boot 为了做到能够以一个 fat jar 来启动,增加了一些定制的处理逻辑,而这部分定制的处理逻辑在频繁执行时,会对程序性能产生较大影响。

至于 [为什么在频繁加载不存在的类时,将 jar 包解压后通过 JarLauncher 启动比 Tomcat war 部署性能更好?] 、[在频繁使用 SPI 获取 classpath 下查找不到的实现时,Tomcat war 部署又比将 jar 包解压后通过 JarLauncher 启动性能更好?] ,受限于篇幅,就不在本文中继续展开了。感兴趣的朋友可以按照本文中介绍的方法,再结合相关源码进行进一步探究。

总结

大家在自定义 Feign 的编解码器时,如果用到了 SpringEncoder / SpringDecoder,应避免 HttpMessageConverters 的重复初始化。如果不需要使用那些默认的 HttpMessageConverter,可以在初始化 HttpMessageConverters 时将第一个入参设置为 false,从而不初始化那些默认的 HttpMessageConverter。

另外,应该了解不同的部署方式在类加载器频繁查找和加载资源时是存在性能差异的。

我们在写代码时,也应该要避免重复初始化,以及反复查找和加载不存在的资源。

最后,善用SkyWalking Arthas 可以帮助我们更加高效地排查程序错误和性能瓶颈。

彩蛋

如果应用使用了 SkyWalking Agent,再使用 Arthas,可能会遇到 Arthas 部分命令(trace、watch 等会对类进行增强的命令)不能正常工作的问题。

解决方案:https://github.com/apache/skywalking/blob/master/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md

而当 Arthas 可以正常工作以后,我们对于 SkyWalking Agent 已经增强过的类的方法执行 trace 等命令时,最好在方法名后面加上一个 * 符号进行模糊匹配。Arthas 最终会将所有匹配方法的 trace 追踪结果进行汇总展示。

方法名不加 * 进行 trace:

31.png

方法名加上 * 进行trace:

32.png

可以看到方法名加上 * 以后,trace 得到的结果才是我们理想的结果。

这是由于 SkyWalking Agent 使用了 ByteBuddy 做字节码增强。而 ByteBuddy 每增强一个方法,都会为该方法生成一个辅助内部类(HelloController$auxiliary$jiu2bTqU),并且会对当前类(HelloController)中的原方法(test1)进行重命名(test1$original$lyu0XDob),并生成一个与原方法同名的方法(test1)和一个不同名但仅供辅助内部类调用的方法(test1$original$lyu0XDob$accessor$8F82ImAF)。

使用同事开发的 Java 反编译工具可以直观地看到相关代码:

33.png

34.png

另外,在使用 Arthas 的时候,建议选择最新的版本。比如,3.4.2 以前的版本 trace 追踪大方法时就可能会导致 JVM Metaspace OOM。详情见:《记一次由 Arthas 引起的 Metaspace OOM 问题》

如果你想要基于 Arthas 打造企业级在线诊断平台,可以参考《工商银行打造在线诊断平台的探索与实践》

作者简介

王瑞显,开源爱好者,现任掌门教育基础架构部研发工程师,主要负责公司全链路监控系统和应用诊断平台的研发。

欢迎登陆 start.aliyun.com 知行动手实验室体验 Arthas 57 个动手实验:https://start.aliyun.com/handson-lab/#!category=arthas

35.gif

Arthas 实验预览

为了让更多开发者开始用上 Arthas 这个 Java 诊断神器,Arthas 社区联合 JetBrains 推出 Arthas 有奖征文活动聊聊这些年你和 Arthas 之间的那些事儿。活动仍在火热进行中,点击即可参与,欢迎大家踊跃投稿,参与即有可能获奖!

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
24天前
|
消息中间件 Java 开发者
【颠覆想象】Spring Boot重构未来:解耦与隔离,打造坚不可摧的微服务帝国!
【8月更文挑战第29天】本文通过构建电子商务平台的具体案例,深入探讨了如何利用 Spring Boot 实现服务间的解耦与隔离。文章详细介绍了依赖注入、模块化设计及异步通信等关键技术,并提供了具体代码示例。通过依赖注入,对象间耦合得以降低;模块化设计使各功能域独立,降低系统复杂度;异步通信则利用消息队列提升系统吞吐量与响应速度。这些方法不仅优化了系统架构,还加快了开发进程。
41 0
|
22天前
|
Kubernetes Cloud Native Java
云原生之旅:从容器到微服务的演进之路Java 内存管理:垃圾收集器与性能调优
【8月更文挑战第30天】在数字化时代的浪潮中,企业如何乘风破浪?云原生技术提供了一个强有力的桨。本文将带你从容器技术的基石出发,探索微服务架构的奥秘,最终实现在云端自由翱翔的梦想。我们将一起见证代码如何转化为业务的翅膀,让你的应用在云海中高飞。
|
23天前
|
Java UED Sentinel
微服务守护神:Spring Cloud Sentinel,让你的系统在流量洪峰中稳如磐石!
【8月更文挑战第29天】Spring Cloud Sentinel结合了阿里巴巴Sentinel的流控、降级、熔断和热点规则等特性,为微服务架构下的应用提供了一套完整的流量控制解决方案。它能够有效应对突发流量,保护服务稳定性,避免雪崩效应,确保系统在高并发下健康运行。通过简单的配置和注解即可实现高效流量控制,适用于高并发场景、依赖服务不稳定及资源保护等多种情况,显著提升系统健壮性和用户体验。
47 1
|
23天前
|
Cloud Native Java Nacos
微服务时代的新宠儿!Spring Cloud Nacos实战指南,带你玩转服务发现与配置管理,拥抱云原生潮流!
【8月更文挑战第29天】Spring Cloud Nacos作为微服务架构中的新兴之星,凭借其轻量、高效的特点,迅速成为服务发现、配置管理和治理的首选方案。Nacos(命名和配置服务)由阿里巴巴开源,为云原生应用提供了动态服务发现及配置管理等功能,简化了服务间的调用与依赖管理。本文将指导你通过五个步骤在Spring Boot项目中集成Nacos,实现服务注册、发现及配置动态管理,从而轻松搭建出高效的微服务环境。
94 0
|
23天前
|
Dubbo Java 应用服务中间件
💥Spring Cloud Dubbo火爆来袭!微服务通信的终极利器,你知道它有多强大吗?🔥
【8月更文挑战第29天】随着信息技术的发展,微服务架构成为企业应用开发的主流模式,而高效的微服务通信至关重要。Spring Cloud Dubbo通过整合Dubbo与Spring Cloud的优势,提供高性能RPC通信及丰富的生态支持,包括服务注册与发现、负载均衡和容错机制等,简化了服务调用管理并支持多种通信协议,提升了系统的可伸缩性和稳定性,成为微服务通信领域的优选方案。开发者仅需关注业务逻辑,而无需过多关心底层通信细节,使得Spring Cloud Dubbo在未来微服务开发中将更加受到青睐。
52 0
|
13天前
|
XML Java 数据库
在微服务架构中,请求常跨越多个服务,涉及多组件交互,问题定位因此变得复杂
【9月更文挑战第8天】在微服务架构中,请求常跨越多个服务,涉及多组件交互,问题定位因此变得复杂。日志作为系统行为的第一手资料,传统记录方式因缺乏全局视角而难以满足跨服务追踪需求。本文通过一个电商系统的案例,介绍如何在Spring Boot应用中手动实现日志链路追踪,提升调试效率。我们生成并传递唯一追踪ID,确保日志记录包含该ID,即使日志分散也能串联。示例代码展示了使用过滤器设置追踪ID,并在日志记录及配置中自动包含该ID。这种方法不仅简化了问题定位,还具有良好的扩展性,适用于各种基于Spring Boot的微服务架构。
29 3
|
16天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
45 2
|
16天前
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
65 2
|
20天前
|
缓存 安全 Java
如何利用Go语言提升微服务架构的性能
在当今的软件开发中,微服务架构逐渐成为主流选择,它通过将应用程序拆分为多个小服务来提升灵活性和可维护性。然而,如何确保这些微服务高效且稳定地运行是一个关键问题。Go语言,以其高效的并发处理能力和简洁的语法,成为解决这一问题的理想工具。本文将探讨如何通过Go语言优化微服务架构的性能,包括高效的并发编程、内存管理技巧以及如何利用Go生态系统中的工具来提升服务的响应速度和资源利用率。
|
24天前
|
Prometheus 监控 Cloud Native
Spring Boot 性能护航!Prometheus、Grafana、ELK 组合拳,点燃数字化时代应用稳定之火
【8月更文挑战第29天】在现代软件开发中,保证应用性能与稳定至关重要。Spring Boot 作为流行的 Java 框架,结合 Prometheus、Grafana 和 ELK 可显著提升监控与分析能力。Prometheus 负责收集时间序列数据,Grafana 将数据可视化,而 ELK (Elasticsearch、Logstash、Kibana)则管理并分析应用日志。通过具体实例演示了如何在 Spring Boot 应用中集成这些工具:配置 Prometheus 获取度量信息、Grafana 显示结果及 ELK 分析日志,从而帮助开发者快速定位问题,确保应用稳定高效运行。
38 1