0
点赞
收藏
分享

微信扫一扫

服务观测监控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,搭建服务器端

  • 引入对应的依赖

        <dependency>
          <groupId>de.codecentric</groupId>
          <artifactId>spring-boot-admin-starter-server</artifactId>
          <version>2.3.0</version>
          <exclusions>
            <exclusion>
              <groupId>io.projectreactor.netty</groupId>
              <artifactId>reactor-netty</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>io.projectreactor.netty</groupId>
          <artifactId>reactor-netty</artifactId>
          <version>0.9.9.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <version>2.3.1.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
          <version>2.3.1.RELEASE</version>
        </dependency>
    
  • 配置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=<span>Monitor</span>
    spring.boot.admin.ui.title=Monitor
    

2,搭建客户端

  • 添加相应依赖

    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-client</artifactId>
      <version>2.3.0</version>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>
    
  • 添加相应的配置类,这里由于原代码没法从容器获取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<List<MetadataContributor>> 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<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
                InetAddress ip = null;
                while (allNetInterfaces.hasMoreElements()) {
                    NetworkInterface netInterface = allNetInterfaces.nextElement();
                    if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
                        continue;
                    } else {
                        Enumeration<InetAddress> 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,可以看到满足我们的要求。

异常报错

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


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

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

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

    management.endpoint.health.show-details=always
    

参考

例子代码

举报

相关推荐

0 条评论