Spring boot admin 升级到2.3.1 遇到的问题总结

葛桐
2023-12-01

目录

问题1 :注册到Spring boot admin 服务器上的项目,在项目关闭或者重启的时候不会自动注销。

问题2 :如果spring boot admin server 已经是spring web security 项目了,那么它给spring boot admin client 发请求时,请求的connections的Header里面要加 username 和password的信息?

问题3 : 如果注册在spring boot admin server中的项目显示offline 但是这个项目的确运行着。

问题4 :注册到spring boot admin server的项目是加了 spring web security的。

问题5: Spring boot admin 可以利用Eureka server 或其它spring cloud server里注册的信息?不想每个项目都加spring boot admin client 依赖。

问题 6:Spring-boot-admin-server项目的CPU和Memory占用高,


问题1 :注册到Spring boot admin 服务器上的项目,在项目关闭或者重启的时候不会自动注销。

解决方法: 加下面参数,spring boot admin client 这个值默认是空,也就是false

spring.boot.admin.client.auto-deregistration=true

源码在这RegistrationApplicationListener个类onClosedContext 方法:

	@EventListener
	@Order(Ordered.LOWEST_PRECEDENCE)
	public void onClosedContext(ContextClosedEvent event) {
		if (event.getApplicationContext().getParent() == null
				|| "bootstrap".equals(event.getApplicationContext().getParent().getId())) {
			stopRegisterTask();

			if (autoDeregister) {
				registrator.deregister();
			}
		}
	}

实现这个项目重启或下线调用额外逻辑的是下面方法,Runtime.getRuntime().addShutdownHook(this.shutdownHook);类是AbstractApplicationContext

	@Override
	public void registerShutdownHook() {
		if (this.shutdownHook == null) {
			// No shutdown hook registered yet.
			this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
				@Override
				public void run() {
					synchronized (startupShutdownMonitor) {
						doClose();
					}
				}
			};
			Runtime.getRuntime().addShutdownHook(this.shutdownHook);
		}
	}

问题2 :如果spring boot admin server 已经是spring web security 项目了,那么它给spring boot admin client 发请求时,请求的connections的Header里面要加 username 和password的信息?

方法一: 直接spring的配置文件配置下面参数

spring.boot.admin.client.username=xxx
spring.boot.admin.client.password=xxx

方法二:在BlockingRegistrationClient实例里设置username或password。

@Configuration
@ConditionalOnClass({SpringBootAdminClientAutoConfiguration.class})
@ConditionalOnWebApplication
public class SpringBootAdminClientWebSecurityAutoConfiguration {
    
	private static final Log LOG = LogFactory.getLog(SpringBootAdminClientWebSecurityAutoConfiguration.class);
	
	@Bean
	@Conditional(SpringBootAdminClientEnabledCondition.class)
	public BlockingRegistrationClient registrationClient(ClientProperties client) {
		LOG.info("Instatiating BlockingRegistrationClient in SpringBootAdminClientWebSecurityAutoConfiguration");
		RestTemplateBuilder builder = new RestTemplateBuilder().setConnectTimeout(client.getConnectTimeout())
				.setReadTimeout(client.getReadTimeout());
		if (client.getUsername() != null && client.getPassword() != null) {
			builder = builder.basicAuthentication(client.getUsername(), client.getPassword());
			return new BlockingRegistrationClient(builder.build());
		}else {
			RestTemplate resetTemplate =  builder.build();
			resetTemplate.getInterceptors().add(new AspenBasicAuthorizationInterceptor());
			return new BlockingRegistrationClient(resetTemplate);
		}
	}
	
}

问题3 : 如果注册在spring boot admin server中的项目显示offline 但是这个项目的确运行着。

方法 一: 检查这个项目的Health的endpoint看看状态是不是up.

如果不是up, 那个是这个项目本身的Heath有问题. 解决了它就可以了。

方法 二: 如果检查这个项目的Health的endpoint看看状态是up 但是你发现它返回时间很长。

加大health的timeout时间。

spring.boot.admin.monitor.timeout.health=100000

问题4 :注册到spring boot admin server的项目是加了 spring web security的。

因为spring boot admin sever 也要在它注册的项目上发送请求并抓取相应的数据,所以要在connection的Header上面加username 和password.

方法 一

spring.security.user.name=client
spring.security.user.password=client
spring.boot.admin.client.instance.metadata.user.name=${spring.security.user.name}
spring.boot.admin.client.instance.metadata.user.password=${spring.security.user.password}

方法 二

创建一个类让它继承HttpHeadersProvider, 并且实现getHeaders方法

public class testBasicAuthHttpHeaderProvider implements HttpHeadersProvider{

	@Override
	public HttpHeaders getHeaders(Instance instance) {
		String username = WebSecurityInforHolder.getIntance().getSystemUser();//username
		String password =  WebSecurityInforHolder.getIntance().getPassword();// password

		HttpHeaders headers = new HttpHeaders();

		if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
			headers.set(HttpHeaders.AUTHORIZATION, encode(username, password));
		}

		return headers;
	}

	protected String encode(String username, String password) {
		String token = Base64Utils
				.encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
		return "Basic " + token;
	}
}

用spring实例化它。

    @Bean
    public HttpHeadersProvider httpHeadersProvider() {
        return new TestBasicAuthHttpHeaderProvider();
    }

问题5: Spring boot admin 可以利用Eureka server 或其它spring cloud server里注册的信息?不想每个项目都加spring boot admin client 依赖。

方法: Spring boot Admin 集成Eureka项目搭建。 说白了就是把 spring boot admin项目本身当成其中一个Eureka client 项目注册到Eureka server上,从而可以利用Eureka获取上面的注册信息。

1. 在 spring boot admin server的pom.xml里加 eureka client 的依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2. 在启动类上增加 @EnableDiscoveryClient 注解开启注册功能,代码如下所示。

@EnableDiscoveryClient
@EnableAdminServer
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

3. 配置 Eureka 注册信息:

eureka.client.serviceUrl.defaultZone=${EUREKA_SERVICE_URL:http://localhost:10001}/eureka/
eureka.client.registryFetchIntervalSeconds=5
eureka.instance.leaseRenewalIntervalInSeconds=10
#eureka.instance.health-check-url-path=/actuator/health

4. 暴露所有端点信息

management.endpoints.web.exposure.include=*

问题 6:Spring-boot-admin-server项目的CPU和Memory占用高,

相关代码

调用下面代码,加了一下逻辑。然后看web security项目里每个Request所用的密码和数据库里Hash码是否匹配。

public class TestBCryptEncoder implements PasswordEncoder {
	private BCryptPasswordEncoder passwordEncoder;
...
	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		String rpwd = rawPassword.toString();
        ...
		return passwordEncoder.matches(rpwd, encodedPassword);
	}
}

经过Heap dump后发现很多线程都停在了BCrypt.key这个方法上面。也试了加下面参数,但是没用。

-Djava.security.egd=file:/dev/urandom

参考资料

java - BCrypt performance deterioration - Stack Overflow

at org.springframework.security.crypto.bcrypt.BCrypt.key([BZI)V (BCrypt.java:438)                                                                                                                                                                                                            
at org.springframework.security.crypto.bcrypt.BCrypt.crypt_raw([B[BIZI)[B (BCrypt.java:546)                                                                                                                                                                                                  
at org.springframework.security.crypto.bcrypt.BCrypt.hashpw([BLjava/lang/String;)Ljava/lang/String; (BCrypt.java:636)                                                                                                                                                                        
at org.springframework.security.crypto.bcrypt.BCrypt.hashpw(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; (BCrypt.java:577)                                                                                                                                                        
at org.springframework.security.crypto.bcrypt.BCrypt.checkpw(Ljava/lang/String;Ljava/lang/String;)Z (BCrypt.java:743)                                                                                                                                                                        
at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.matches(Ljava/lang/CharSequence;Ljava/lang/String;)Z (BCryptPasswordEncoder.java:133)                                                                                                                                    
at ....TestBCryptEncoder.matches(Ljava/lang/CharSequence;Ljava/lang/String;)Z (AspenBCryptEncoder.java:50)                                                                                                                                                    |    |              |          
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(Lorg/springframework/security/core/userdetails/UserDetails;Lorg/springframework/security/authentication/UsernamePasswordAuthenticationToken;)V (DaoAuthenticationProvider.java:76
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(Lorg/springframework/security/core/Authentication;)Lorg/springframework/security/core/Authentication; (AbstractUserDetailsAuthenticationProvider.java:147)                         
at org.springframework.security.authentication.ProviderManager.authenticate(Lorg/springframework/security/core/Authentication;)Lorg/springframework/security/core/Authentication; (ProviderManager.java:182)                                                                                 
at org.springframework.security.authentication.ProviderManager.authenticate(Lorg/springframework/security/core/Authentication;)Lorg/springframework/security/core/Authentication; (ProviderManager.java:201)                                                                                 
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V (BasicAuthenticationFilter.java:156)                                          
at org.springframework.web.filter.OncePerRequestFilter.doFilter(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;Ljavax/servlet/FilterChain;)V (OncePerRequestFilter.java:117)                                                                                                   
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V (FilterChainProxy.java:336)                                                                                                                 
at org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter.doFilterInternal(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V (DefaultLogoutPageGeneratingFilter.java:58)                            
at org.springframework.web.filter.OncePerRequestFilter.doFilter(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;Ljavax/servlet/FilterChain;)V (OncePerRequestFilter.java:117)                                                                                                   
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V (FilterChainProxy.java:336)                                                                                                                 
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V (DefaultLoginPageGeneratingFilter.java:237)                                     

你可以通过看spring-boot-admin-server 里 Event Journal view就知道,那些错误的components一直不同的发注册信息进来. 注册信息如下:

{
    "registration": {
        "name": "xxxx",
        "managementUrl": "https://xxx/actuator",
        "healthUrl": "xxx/actuator/health",
        "serviceUrl": "xxx",
        "source": "http-api",
        "metadata": {
            "startup": "2022-02-21T02:20:47.662Z"
        }
    }
}

原因:

很多项目注册到spring admin server时报错。报错后,这些项目会隔一段时候在尝试注册一遍。因为项目太多,所以造成这样的请求很多,而且每个请求都会调用TestBCryptEncoder 做密码比对。但是因为BCrypt.key()这个方法很慢,所以很多线程就阻塞了。最终造成CPU usage 很高和Memory也很高。使得linux server都很慢。

为什么BCrypt.key()这个方法这慢,待调查。

解决方法

方法一:

1. spring boot server项目去掉spring web security。

方法二:

找到注册spring boot server 错误的原因。解决掉后这样的线程就少了。间接的解决了这个问题。

方法三:

由于strength数值越大,耗时越严重,所以就减少这个值或换加密算法,比如换成MD5.

记一次bcrypt加密引起的性能调优过程_xiaolaoban212的博客-CSDN博客

https://segmentfault.com/q/1010000003054250?bd_source_light=4746641

记一次使用BCryptPasswordEncoder,设置了不合理参数导致耗时严重的坑_mb607022e25a607的技术博客_51CTO博客

相关系列博客

Spring boot升级到2.3.2.Release和Spring framework升级到5.28.Release踩过的坑_keeppractice的博客-CSDN博客

Hibernate升级到5.4.18.final的过程踩过的坑_keeppractice的博客-CSDN博客

 类似资料: