服务观测监控SpringBootAdmin

概述

架构设计里面很重要的一点是程序的可观测性,那么对微服务集群的所有微服务的进程消息进行监控,动态配置就很重要了。例如虚拟机的堆栈、线程等信息,特别是希望能够实时的查看到服务启动后的配置信息,希望每个开发人员都能够通过统一入口去观测微服务,去动态调整一些服务数据。需要成本低,所以排除了自研的可能性。最后我选择了SpringBoot Admin来实现这一要求。

依赖的框架以及版本

工具 版本
spring-boot-admin-starter-client 2.3.0
spring-boot-starter-web 2.3.1.RELEASE
spring-boot-admin-starter-server 2.3.0
spring-boot-starter-security 2.3.1.RELEASE

实现步骤

1,搭建服务器端

  • 引入对应的依赖

        
          de.codecentric
          spring-boot-admin-starter-server
          2.3.0
          
            
              io.projectreactor.netty
              reactor-netty
            
          
        
        
          io.projectreactor.netty
          reactor-netty
          0.9.9.RELEASE
        
        
          org.springframework.boot
          spring-boot-starter-web
          2.3.1.RELEASE
        
        
          org.springframework.boot
          spring-boot-starter-security
          2.3.1.RELEASE
        
    
  • 配置security安全类

    package com.lqd.configuration;
    
    import de.codecentric.boot.admin.server.config.AdminServerProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
    import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
    
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        private final String adminContextPath;
    
        public SecurityConfig(AdminServerProperties adminServerProperties) {
            this.adminContextPath = adminServerProperties.getContextPath();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            SavedRequestAwareAuthenticationSuccessHandler successHandler
                    = new SavedRequestAwareAuthenticationSuccessHandler();
            successHandler.setTargetUrlParameter("redirectTo");
            successHandler.setDefaultTargetUrl("/");
    
            http.authorizeRequests()
                    .antMatchers("/assets/**").permitAll()
                    .antMatchers("/login").permitAll()
                    .anyRequest().authenticated().and()
                    .formLogin().loginPage("/login")
                    .successHandler(successHandler).and()
                    .logout().logoutUrl("/logout").and()
                    .httpBasic()
                    .and()
                    .csrf()
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                    .ignoringAntMatchers(
                            "/instances",
                            "/actuator/**"
                    );
            // @formatter:on
        }
    }
    
  • 配置文件添加相应配置

    server.port=38999
    server.servlet.context-path=/
    spring.application.name=monitor-server
    spring.profiles.active=dev
    spring.security.user.name=admin
    spring.security.user.password=666666
    spring.boot.admin.ui.brand=Monitor
    spring.boot.admin.ui.title=Monitor
    

2,搭建客户端

  • 添加相应依赖

    
      de.codecentric
      spring-boot-admin-starter-client
      2.3.0
    
    
    
      org.springframework.boot
      spring-boot-starter-web
      2.3.1.RELEASE
    
    
  • 添加相应的配置类,这里由于原代码没法从容器获取ip,我重写了ip获取的规则。

    package com.lqd;
    
    import de.codecentric.boot.admin.client.config.InstanceProperties;
    import de.codecentric.boot.admin.client.registration.ApplicationFactory;
    import de.codecentric.boot.admin.client.registration.metadata.CompositeMetadataContributor;
    import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
    import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
    import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.autoconfigure.web.ServerProperties;
    import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
    import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.context.annotation.Primary;
    
    import javax.servlet.ServletContext;
    import java.util.Collections;
    import java.util.List;
    
    @Configuration
    @ConditionalOnProperty(value = "spring.boot.admin.client.enabled" ,
            havingValue = "true")
    public class ApplicationConfiguration {
    
        @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
        @AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
        public static class ServletConfiguration {
    
            @Bean
            @Lazy(false)
            @ConditionalOnMissingBean
            @Primary
            public ApplicationFactory applicationFactory(InstanceProperties instance, ManagementServerProperties management,
                                                         ServerProperties server, ServletContext servletContext, PathMappedEndpoints pathMappedEndpoints,
                                                         WebEndpointProperties webEndpoint, ObjectProvider> metadataContributors,
                                                         DispatcherServletPath dispatcherServletPath) {
                return new CustomApplicationFactory(instance, management, server, servletContext, pathMappedEndpoints,
                        webEndpoint,
                        new CompositeMetadataContributor(metadataContributors.getIfAvailable(Collections::emptyList)),
                        dispatcherServletPath);
            }
    
        }
    }
    
    package com.lqd;
    
    import de.codecentric.boot.admin.client.config.InstanceProperties;
    import de.codecentric.boot.admin.client.registration.ServletApplicationFactory;
    import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
    import io.micrometer.core.instrument.util.StringUtils;
    import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
    import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
    import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
    import org.springframework.boot.autoconfigure.web.ServerProperties;
    import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
    
    import javax.servlet.ServletContext;
    import java.net.Inet4Address;
    import java.net.InetAddress;
    import java.net.NetworkInterface;
    import java.util.Enumeration;
    
    //@Slf4j
    public class CustomApplicationFactory extends ServletApplicationFactory {
    
        public CustomApplicationFactory(InstanceProperties instance, ManagementServerProperties management, ServerProperties server, ServletContext servletContext, PathMappedEndpoints pathMappedEndpoints, WebEndpointProperties webEndpoint, MetadataContributor metadataContributor, DispatcherServletPath dispatcherServletPath) {
            super(instance, management, server, servletContext, pathMappedEndpoints, webEndpoint, metadataContributor, dispatcherServletPath);
        }
    
        @Override
        protected String getHost(InetAddress address) {
            String ip = getIpAddress();
            return StringUtils.isNotBlank(ip)?ip:super.getHost(address);
        }
    
        public static String getIpAddress() {
            try {
                Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
                InetAddress ip = null;
                while (allNetInterfaces.hasMoreElements()) {
                    NetworkInterface netInterface = allNetInterfaces.nextElement();
                    if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
                        continue;
                    } else {
                        Enumeration addresses = netInterface.getInetAddresses();
                        while (addresses.hasMoreElements()) {
                            ip = addresses.nextElement();
                            if (ip != null && ip instanceof Inet4Address) {
                                return ip.getHostAddress();
                            }
                        }
                    }
                }
            } catch (Exception e) {
               //log.error("IP地址获取失败" + e.toString());
            }
            return "";
        }
    
    }
    
  • 配置文件添加相应配置

    server.port=8091
    server.servlet.context-path=/
    spring.application.name=monitor-client
    spring.profiles.active=dev
    spring.boot.admin.client.username=admin
    spring.boot.admin.client.password=666666
    spring.boot.admin.client.enabled=true
    spring.boot.admin.client.url=http://localhost:38999/
    spring.boot.admin.client.instance.prefer-ip=true
    management.endpoints.web.exposure.include=*
    management.endpoint.health.show-details=always
    

3,登录验证

  • 浏览器访问http://localhost:38999/ ,输入对应的账号密码admin/666666,可以看到满足我们的要求。
32F4BF7B-8A82-4744-BCA7-B8BF3740E607.png
5156CD80-A322-4106-9373-850841E6AC6F.png
41479B53-D40D-4023-A847-4EB458EA37F7.png
F0993A1B-DF8A-4e0a-A965-4D60F5EEFF60.png
FB1431BC-D707-4471-B1AE-297E9ACE0E43.png

异常报错

  • springboot -admin 2.3.0搭建的server端部署linux后,会出现如下图所示错误,这是由于他依赖的reactor-netty有bug,会导致打开文件数激增,最后抛出too many open files错误。需要将reactor-netty的版本升级到0.9.9.RELEASE版本。


    image-20210727145855142.png
    image-20210727150026170.png
  • springboot admin若采用容器部署,获取的监控微服务的ip是127.0.0.1,需要如上所述重写客户端ip获取规则。

    image-20210727150523294.png
  • 客户端带账号密码正确请求报错:401,这是因为服务端security框架对所有的接口都验证权限,解决办法是需要在服务端配置指定的接口安全过滤。

    .ignoringAntMatchers(
            "/instances",
            "/actuator/**"
    );
    
  • 点击微服务的/actuator/health接口,返回的状态始终为down,服务处于离线状态。这时需要获取接口的详情消息,2.3.0版本的springboot可以在客户端配置如下参数,即可查找到down的原因。

    management.endpoint.health.show-details=always
    

参考

例子代码

你可能感兴趣的:(服务观测监控SpringBootAdmin)