1.背景
之前负责过我们中台的SpringBoot和Cloud的升级,特次记录分享一下项目中可能出现的问题,方便后续的人快速定位问题。以及下述选择的解决方案都是基于让升级的服务影响和改动最小以及提供通用的解决方案的提前进行选择的。
1.1版本说明
升级前(大部分):
springboot版本:2.2.5.RELEASE
spring-cloud版本:Hoxton.SR3
升级后(2023年02月):
springboot版本:2.7.5;
spring-cloud版本:2021.0.4;
选择版本逻辑:
见官方映射逻辑
https://spring.io/projects/spring-cloud
同时选择最新中使用最多最稳定的版本。
2.升级带来可能不兼容问题
2.1ClassDefFoundError: kotlin/reflect/TypesJVMKt
升级kotlin版本<kotlin.version>1.6.21</kotlin.version>
2.2 NoClassDefFoundError: org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClient
问题原因:
io.opentracing.contrib.spring.cloud.feign.TraceFeignContext#addTracingClient 找不到对应的类
spring-cloud-netflix-ribbon 已从 SpringCloud 中删除
opentracing-spring-cloud-starter 作者没有时间更新对应新版的 Spring-cloud
见:
https://github.com/opentracing-contrib/java-spring-cloud/issues/312
https://github.com/opentracing-contrib/java-spring-cloud/pull/324
解决方案:
1.按需考虑是否替换为最新的OpenTelemetry,需结合自己公司的监控体系,影响较大
2.重写io.opentracing.contrib.spring.cloud.feign.TraceFeignContext#getInstances,影响小,关键代码如下:
package io.opentracing.contrib.spring.cloud.feign;
// 注意上面的package要同opentacing路径一致才能替换
public class CompatibleTraceFeignContext extends TraceFeignContext {
private final FeignContext delegate;
private final Tracer tracer;
private final List<FeignSpanDecorator> spanDecorators;
public CompatibleTraceFeignContext(Tracer tracer,
FeignContext delegate,
BeanFactory beanFactory,
List<FeignSpanDecorator> spanDecorators) {
super(tracer, delegate, beanFactory, spanDecorators);
this.delegate = delegate;
this.tracer = tracer;
this.spanDecorators = spanDecorators;
}
@Override
public <T> T getInstance(String name, Class<T> type) {
T object = this.delegate.getInstance(name, type);
return (T) this.compatibleAddTracingClient(object);
}
@Override
public <T> Map<String, T> getInstances(String name, Class<T> type) {
Map<String, T> tracedInstances = new HashMap<>();
Map<String, T> instances = this.delegate.getInstances(name, type);
if (instances == null) {
return tracedInstances;
}
for (Map.Entry<String, T> instanceEntry : instances.entrySet()) {
tracedInstances.put(instanceEntry.getKey(), (T) this.compatibleAddTracingClient(instanceEntry.getValue()));
}
return tracedInstances;
}
private Object compatibleAddTracingClient(Object bean) {
if (bean instanceof TracingClient) {
return bean;
}
if (bean instanceof Client) {
return compatibleBuildTracingClient((Client) bean, tracer);
}
return bean;
}
private TracingClient compatibleBuildTracingClient(Client delegate, Tracer tracer) {
return new TracingClientBuilder(delegate, tracer)
.withFeignSpanDecorators(spanDecorators)
.build();
}
}
2.3 升级SpringBoot 版本后,默认为Junit5,junit4相关类没有了
引入junit4或者修改换成Junit5写法(建议)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
junit4和junit5共存时,可能单测会执行不到junit4的方法,可以参考此链接 https://codeantenna.com/a/cMyp4oXyCU
2.4 org.springframework.dao.InvalidDataAccessApiUsageException: ConnectionCallback; isValid; nested exception is java.sql.SQLFeatureNotSupportedException: isValid
问题原因:
此问题是由于shrding版本过旧与springBoot-actuator不匹配,数据库健康检测失败。
具体原因见:https://github.com/apache/shardingsphere/issues/5882
原因的根源: shardingsphere-jdbc/shardingsphere-jdbc-core/src/main/java/org/apache/shardingsphere/driver/jdbc/unsupported/AbstractUnsupportedOperationConnection.java
@Override
public final boolean isValid(final int timeout) throws SQLException {
throw new SQLFeatureNotSupportedException("isValid");
}
此问题会导致健康检测地址返回{“status”:“DOWN”},从而注册不上consul
解决方案:
- 按需考虑是否要升级shardin版本,不过是alpha版本,见:https://github.com/apache/shardingsphere/pull/6002。
- 重写
2.5 高版本spring循环依赖校验变严
当前设置:不建议使用循环依赖,如果有这种情况, 先定位问题尝试修复
2.6@FeignClient 兼容问题 不支持@RequestMapping 写在Client接口上
原因:
Caused by: java.lang.IllegalArgumentException: @RequestMapping annotation not allowed on @FeignClient interfaces at org.springframework.cloud.openfeign.support.SpringMvcContract.processAnnotationOnClass(SpringMvcContract.java:182)
解决方案:
重写SpringMvcContract
2.7reactor-core版本问题
Caused by: java.lang.ClassNotFoundException: reactor.util.context.ContextView
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
... 58 common frames omitted
升级升级reactor-core到3.4.3
2.8 spring-boot-starter-data-redis 版本不兼容
org.springframework.boot.actuate.metrics.cache.RedisCacheMetrics.missCount(RedisCacheMetrics.java:56)
更新spring-boot-starter-data-redis 版本与SpringBoot一致
2.9升级版本之后的Spring Cloud Stream(SCS)消费kafka无法正常反序列化payload.
SCS消费业务系统发送的的消息时Message的payload的数据类型依然是byte[], 没有按照旧版本的行为序列化为String
触发条件:
- 使用SCS(@StreamListener)消费kafka消息,且没有为payload指定具体类型(入参Message没有指定泛型,指定泛型为Object也不会反序列化)
问题原因:
SCS是使用了AbstractMessageConverter来对payload进行反序列化的, AbstractMessageConverter有几个实现,序列化的过程是一个过滤器的模式,默认情况下,ApplicationJsonMessageMarshallingConverter是第一个处理器,当没有指定泛型时且payload是byte[]类型时,旧版本的处理逻辑如下:
新版逻辑:
无法命中条件,所以原样输出btye数组。
解决方案:指明泛型