美文网首页IT必备技能spring bootJAVA
服务观测监控SpringBootAdmin

服务观测监控SpringBootAdmin

作者: 勤_ | 来源:发表于2021-07-27 15:49 被阅读0次

    概述

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

        本文链接:https://www.haomeiwen.com/subject/agdomltx.html