SpringBoot升级及Eureka&Apollo迁移至Nacos

背景:应部门要求微服务项目注册中心及配置中心需迁移至nacos,开发运维相分离,配置由运维组维护。

1 springBoot升级

1.1 版本变更

Nacos与SpringBoot、SpringCloud版本依赖关系(推荐使用),中间件部门提供的nacos版本为1.4 .2

版本依赖请参考:

https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

原springBoot版本:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.6.RELEASE</version>
</parent>

升级后springBoot版本:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.3</version>
</parent>

1.2 升级问题汇总

1)dom4j

问题描述:打包时提示找不到dom4j的jar包,之前没有写版本号。

问题原因:应该是被spring-boot-dependencies,2.6.3之前的某个版本移除了。

解决方案:增加版本号1.6.1

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>

2)tomcat

问题描述:在项目启动时tomcat一直报错,提示无法定位。

问题原因:因为父pom的tomcat版本死了为8.5.40。

解决方案:删除此行代码即可(springBoot会指定默认版本)。

<tomcat.version>8.5.40</tomcat.version>

3)jackson-mapper-asl

问题原因:此依赖已由SpringBoot默认引入。

解决方案:移除。

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
</dependency>

4)jackson-annotation

问题原因:该依赖未被使用。

解决方案:移除。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotation</artifactId>
</dependency>

5)配置文件环境激活

问题描述:springBoot 2.4版本之前,我们在yaml配置文件中,使用spring.profiles.active来定义不同环境的标识,比如下面这样

spring: 
  profiles:
    active:dev

而在本次2.4版本升级之后,SpringBoot为了提升对K8S的支持,我们需要将spring.profiles.active配置用spring.config.activate.on-profile替代(貌似不改也能生效,但是启动日志会有warn,所以最好改一下)。

spring:
  config:
    activate:
      on-profile: dev

6)Bean重复定义

问题描述:启动报错,微服务多个组件的openFeign都存在feign的重复定义问题,导致项目启动失败。

# 此注解就会生成一个Bean加载到IOC容器中,同名的Bean在springBoot2.1.0之前版本就会默认覆盖
@FeignClient(name = "xxxx")

问题原因:在springboot2.1.0之前,allowBeanDefinitionOverriding初始化为true,遇到重复的bean,会默认使用下一个bean的名字覆盖上一个(可以在wechatweb启动日志中查看)。springboot2.1.0以后的allowBeanDefinitionOverriding是没有初始化默认值的,所以是false,也就是不支持名称相同的bean被覆盖。

解决办法:在配置文件中将该值设置为true。

spring.main.allow-bean-definition-overriding = true 就是允许定义相同的bean对象去覆盖原有的

7)循环依赖

问题描述:多个组件启动时报错,控制台提示代码中有循环依赖。

@Service
public class xxxService implements xxxService {
​
    // @Value可以正常获取配置文件中的值
    @Value("${common.a}")
    private String a;
    @Value("${common.b}")
    private String b;
    @Value("${common.c}")
    private String c;
    @Value("${common.d}")
    private String d;
​
    // SpringBoot升级后该Bean的加载在@Value之前,所以获取到的值都是null
    @Bean
    private OssConfig ossConfig() {
        OssConfig ossConfig = new OssConfig();
        ossConfig.setEndPoint(a);
        ossConfig.setAccessKeyId(b);
        ossConfig.setAccessKeySecret(c);
        ossConfig.setBucketName(d);
        return ossConfig;
    }
​
    @Autowired
    OssConfig ossConfig;
}

下面为报错内容

*************************
​
Description:
​
The dependencies of some of the beans in the application context form a cycle:
​
  xxxController
┌─────┐
| xxxService (field com.xxx.common.model.OssConfig com.xxx.xxx.service.impl.xxx.xxxService.ossConfig)
└─────┘
​
Action:
​
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
​
不鼓励使用循环引用,默认情况下禁止使用循环引用。更新您的应用程序以删除bean之间的依赖循环。作为最后的手段,可以通过设置spring.main.allow-circular-references允许循环引用为true。

问题原因:实例化该Service时需要先实例化该类中的Ossconfig,而Ossconfig又需要先加载这个Service,导致循环依赖。在2.6.0之前,spring会自动处理循环依赖(单例)的问题,2.6.0 以后的版本默认禁止 Bean 之间的循环引用,如果存在循环引用就会启动失败报错。

解决方案:

方案一

配置文件设置spring.main.allow-circular-references=true

采用方案一后启动不再报错,但是在测试中发现@Bean的方法无法获取到@Value中的值。经查,Springboot 中使用 @Configruation 和 @Bean 一起将 Bean 注册到 ioc 容器中,而 @Value 常用于将 yml 配置文件中的配置信息注入到类的成员变量中。当 @Configruation、@Bean 和 @Value 出现在同一个类中时,@Bean 会比 @Value 先执行,这会导致当 @Bean 注解的方法中用到 @Value 注解的成员变量时,无法注入(null)的情况。故采用方案二

方案二

以config形式单独抽取出来,同样可以实现IOC注入。

package com.xxx.xxx.config;
​
import com.xxx.common.model.OssConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
@Configuration
public class OssBeanConfig {
​
  @Value("${common.a}")
  private String a;
  @Value("${common.b}")
  private String b;
  @Value("${common.c}")
  private String c;
  @Value("${common.d}")
  private String d;
​
  @Bean
  public OssConfig ossConfig() {
    OssConfig ossConfig = new OssConfig();
    ossConfig.setEndPoint(a);
    ossConfig.setAccessKeyId(b);
    ossConfig.setAccessKeySecret(c);
    ossConfig.setBucketName(d);
    return ossConfig;
  }
}

8)引申HikariCP

HikariCP数据库连接池问题。

问题描述:因为要对数据库密码进行加密,所以重构了Hikari的配置,但是据库连接池使用的是HikariCP的默认值(池中最小最大都是10个连接数),而非配置文件中设定的值。

// 配置文件
spring:
 application:
 name: xxx
 datasource:
 url: ...
 driver-class-name: com.mysql.jdbc.Driver
 username: xxx
 password: xxx
 hikari:
  maximum-pool-size: 4 # 最大连接数
  minimum-idle: 1 # 最小连接数
  idle-timeout: 600000
  max-lifetime: 1800000
  connection-timeout: 500
  login-timeout: 500
  validation-timeout: 1000
  initialization-fail-timeout: 1000
// 原代码
@Configuration
public class DataSourceConfig {
  private static String KEY = "...";
  @Value("${spring.datasource.url}")
  private String url;
  @Value("${spring.datasource.driver-class-name}")
  private String driverClassName;
  @Value("${spring.datasource.username}")
  private String username;
  @Value("${spring.datasource.password}")
  private String password;
​
  @Bean
  public HikariDataSource dataSource(){
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setJdbcUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(String.valueOf(decode(password)));
    return dataSource;
  }
}

问题原因:HikariCP是SpringBoot自带的数据源 ,如果我们重写数据源则会覆盖它本身的数据源,但是我们在重写数据源时没有将Hikari的值注入。

解决方案:

方案一

// 实现InitializingBean接口,在Bean初始化时修改数据库密码
@Configuration
public class DataSourceConfig implements InitializingBean {
    @Value("${spring.datasource.password}")
    private String password;
​
    @Override
    public void afterPropertiesSet() throws Exception {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setPassword(String.valueOf(decode(password)));
    }
}

方案二

@Configuration
public class DataSourceConfig {
  private static String KEY = "jaas is the way";
  @Value("${spring.datasource.url}")
  private String url;
  @Value("${spring.datasource.driver-class-name}")
  private String driverClassName;
  @Value("${spring.datasource.username}")
  private String username;
  @Value("${spring.datasource.password}")
  private String password;
​
  @Bean
  // 通过注解@ConfigurationProperties("spring.datasource.hikari")获取配置文件中的值
  @ConfigurationProperties("spring.datasource.hikari")
  public HikariDataSource dataSource(){
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setJdbcUrl(url);
    dataSource.setUsername(username);
    log.info(String.valueOf(decode(password)));
    dataSource.setPassword(String.valueOf(decode(password)));
    return dataSource;
  }
}

注:连接池并不会在启动后立刻生成,只有在第一次与数据库建立连接时才会在连接池中生成配置的最小连接数。

9)数据库驱动升级

问题描述:在日志中发现warn提示mysql驱动问题

[WARN] --- [Thread-19][com.zaxxer.hikari.util.DriverDataSource-70] [] [] []: Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.

问题原因:springBoot默认指定的mysql-connector版本从5升到了8

//spring默认指定的mysql-connector版本
​
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

解决方案:

将spring.datasource.driver-class-name=com.mysql.jdbc.Driver
改成spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

10)javax.mail

javax.mail由springBoot统一管理,升级后改名为jakarta.mail

<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>jakarta.mail</artifactId>
</dependency>

2 springCloud升级

2.1 版本变更

原springCloud版本:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Finchley.SR2</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

升级后springCloud版本:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>2021.0.1</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

2.2升级问题汇总

1)Netflix-Ribbon被移除

问题描述:Ribbon被高版本OpenFeign及Nacos移除,同时微服务设置的ribbon超时也失效。

// 原ribbon配置文件
ribbon:
  ConnectTimeout:60000
  ReadTimeOut:60000

解决方案:引入最新的负载均衡组件spring-cloud-starter-loadbalancer,超时设置为openFeign超时,而不是负载均衡超时,可以达到同样的效果。

// loadbalancer依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
// openFeign超时设置
feign:
  client:
    config:
      default:
        connectTimeout: 60000
        readTimeout: 60000

2)bootstrap.yml不读取

问题原因:从Spring Boot 2.4版本开始,配置文件加载方式进行了重构。另外也有配置的默认值变化,原来默认启用 true 现在变更为 false 如下:

version:2.4之前

package org.springframework.cloud.bootstrap;
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if ((Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
        }
    }
}

version:2.4.2

package org.springframework.cloud.util;
public abstract class PropertyUtils {
    public static boolean bootstrapEnabled(Environment environment) {
        return (Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, false) || MARKER_CLASS_EXISTS;
    }
}

解决办法 官方说明:Spring Cloud Config

1、pom文件中引入如下配置

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

2、指定运行参数 spring.cloud.bootstrap.enabled 值为 true

1)在IDEA中:

spring.cloud.bootstrap.enabled=true

2)启动命令中:

java -jar -Dspring.cloud.bootstrap.enabled=true xxx.jar

我们选择了第一种方法,即添加dependecy方式,认为这种方法更加简单有效。

3.1 依赖变更

迁移原因:Spring Cloud 下的 Netflix Eureka 2.0组件项目宣布闭源,同时Spring也不再对Netflix下的部分组件进行维护。

解决方案:移除Eureka依赖以及Netflix的所有组件,引入alibaba相关依赖,修改配置文件。

// 在父pom引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2021.0.1.0</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
// 子模块引入注册中心
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
​
// 子模块引入配置中心
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
// 配置文件
spring:
  config:
    activate:
      on-profile: uat //激活环境
  cloud:
    nacos:
    username: //用户名
    password: //密码
      discovery: //注册
        server-addr: localhost:8847 //注册中心ip端口
        namespace:  //命名空间
        group: 
      config: //配置
        server-addr: localhost:8847 //配置中心ip端口
        file-extension: yaml //nacos配置文件后缀
        namespace: //命名空间
        group: 
        shared-configs: //共享配置
          - data-id:
            group:
          - data-id:
            group:

配置中心

注册中心

3.2 问题汇总

1)配置刷新

Nacos与Apollo不一样,它默认不支持配置的动态刷新,需要在使用@Value注解的方法或类上加上@RefreshScope注解。

2)配置类中@ConfigurationOnProperties与@RefreshScope冲突

如果在配置类中已经用了@ConfigurationOnProperties再用@RefreshScope会导致无法注册Bean,原因是@ConfigurationOnProperties已有动态刷新功能,不可与@RefreshScope一起用。

@Configuration
//@RefreshScope 
public class DataSourceConfig {
    private static String KEY = "jaas is the way";
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
​
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariDataSource dataSource() throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setJdbcUrl(url);
        dataSource.setUsername(username);
        log.info(String.valueOf(decode(password)));
        dataSource.setPassword(String.valueOf(decode(password)));
​
        return dataSource;
    }
}

3)@EnableEurekaClient

启动类中的@EnableEurekaClient需要改成@EnableDiscoveryClient。

//@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class xxxApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(xxxApplication.class, args);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值