服务检查Spring Admin

使用原因

无论是在微服务下服务管理还是单体项目下的服务监控服务宕机的检测其实一直都显得非常重要,毕竟无论是B端的用户还是C端的用户服务不可用是非常糟糕的体验,当然我们可以多节点部署保证服务的高可用,那么即使在多节点部署的情况下我们也不能说不管服务宕机而引起的人肉运维的相关问题,所以SpringAdmin显得就尤为重要了。

简介

  • 微服务实践的过程中,我们会发现服务数量众多,如何监测我们哪些服务时正常的、哪些又是亚健康的服务呢?别急,springcloud既然叫微服务全家桶,当然它也有方案啦,我们可以轻松集成springadmin来对我们所有服务的健康状态做监控。

本文将介绍其springadmin如何使用,以及它提供的功能进行说明,内容有

  • springadmin都有哪些功能
  • 搭建安全的、高可用的springadmin监控服务
  • 动态调整日志打印级别
  • 配置服务状态变更邮件报警
  • 查看服状态,包括CPU、内存、磁盘、缓存、DB、MQ各种服务的连接状态等等
  • 查看java虚拟机状态、GC情况和堆栈信息
  • 配置监控springboot2.X版本的集群外服务

搭建安全的、高可用的springadmin监控服务

2.1 服务目录结构如下

┍— src/main/java

├----- StartServer.java #服务启动类

├-- src/java/test

├-- src/main/resources

├----- application.yml #可以把这些信息放在配置中心,发布后可以动态修改报警邮箱和规则等

├----- bootstrap.yml #服务的基本信息、启动等信息,可以被application.yml中的配置信息覆盖

┕-- pom.xml

2.2 引入依赖信息
  • 编辑pom.xml文件的内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-alibaba</artifactId>
        <groupId>com.snk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <description>服务监控项目</description>
    <artifactId>snk-admin</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--hutool工具包(难得糊涂)-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <!-- springBoot admin 监控 -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
        <!--Spring Boot Admin 支持Hazelcast的集群-->
        <dependency>
            <groupId>com.hazelcast</groupId>
            <artifactId>hazelcast</artifactId>
        </dependency>
        <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <!--       这样也可以把所有的xml文件,打包到相应位置。-->
        <finalName>snk-admin</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.yml</include>
                    <include>**/*.xml</include>
                    <include>**/*.tld</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.yml</include>
                    <include>**/*.xml</include>
                    <include>**/*.tld</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

        <!--  这里是保证主类不会找不到。-->
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- docker插件 -->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
                    <!--docker文件所在的目录-->
                    <dockerDirectory>src/main/docker</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>

    </build>
</project>
2.3 启动类信息
  • 编辑StartServer.java
package com.snk.admin;

import com.hazelcast.config.Config;
import com.hazelcast.config.EvictionPolicy;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.MergePolicyConfig;
import com.hazelcast.map.merge.PutIfAbsentMapMergePolicy;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableAdminServer
public class StartServer {

    public static void main(String[] args) {
        SpringApplication.run(StartServer.class, args);
    }

    @Bean
    public RestTemplate customRestTemplate() {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setConnectionRequestTimeout(3000);
        httpRequestFactory.setConnectTimeout(3000);
        httpRequestFactory.setReadTimeout(3000);
        return new RestTemplate(httpRequestFactory);
    }

    @Bean
    public Config hazelcastConfig() {//集群配置
        MapConfig mapConfig = new MapConfig("spring-boot-admin-event-store").setInMemoryFormat(InMemoryFormat.OBJECT)
                .setBackupCount(1)
                .setEvictionPolicy(EvictionPolicy.NONE)
                .setMergePolicyConfig(new MergePolicyConfig(
                        PutIfAbsentMapMergePolicy.class.getName(),
                        100
                ));
        return new Config().setProperty("hazelcast.jmx", "true").addMapConfig(mapConfig);
    }
}


2.4 配置信息
  • 编辑application.yml
server:
  port: 29000


spring:
  boot:
    admin:
      notify:
        web-server:
          enabled: true
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: -1s
        min-idle: 0
    timeout: 100ms
  main:
    ##后定义的覆盖项目中同名的bean
    allow-bean-definition-overriding: true
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 主库数据源
    url: jdbc:mysql://127.0.0.1:3306/user-provider?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    druid:
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 10
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # 设置白名单,不填则允许所有访问
        allow:
        url-pattern: /druid/*
        # 控制台管理用户名和密码
        login-username:
        login-password:
      filter:
        stat:
          enabled: true
          # 慢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
  • 编辑bootstap.yml
##微服务配置
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yml
        name: snk-admin
        group: DEFAULT_GROUP
        namespace:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace:
  application:
    name: snk-admin
feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        #Feign的连接建立超时时间,默认为10秒
        connectTimeout: 100000
        #Feign的请求处理超时时间,默认为10秒
        readTimeout: 100000
hystrix:
  command:
    default:
      execution:
        enable: true
        isolation:
          thread:
            timeoutInMilliseconds: 5000
#ribbon的超时时间
ribbon:
  ReadTimeout: 5000 #设置请求处理的超时时间
  ConnectTimeout: 5000  #设置请求链接的超时时间
logging:
  level:
    com.alibaba.nacos.client.config.impl: WARN
    com:
      alibaba:
        nacos:
          client:
            naming: warn
2.5 自定义通知方法
package com.snk.admin.config;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractStatusChangeNotifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import reactor.core.publisher.Mono;

/**
 * @program: admin-server
 * @description: webServer通知
 * @author: tianfn
 * @create: 2021-09-14 17:26
 **/
public class WebServerNotifier extends AbstractStatusChangeNotifier {

    private static final Logger logger = LoggerFactory.getLogger(WebServerNotifier.class);


    @Autowired
    private RestTemplate restTemplate;


    public WebServerNotifier(InstanceRepository repository) {
        super(repository);
    }

   /**
     * 一定要重写doNotify方法,默认将[从UNKNOWN到UP]的通知忽略掉了,导致服务启动识别不到
     * @param event
     * @param instance
     * @return
     */
    @Override
    protected boolean shouldNotify(InstanceEvent event, Instance instance) {
        if (event instanceof InstanceStatusChangedEvent) {
            return true;
        }
        return false;
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        InstanceStatusChangedEvent statusChange = (InstanceStatusChangedEvent) event;
        String from = getLastStatus(event.getInstance());
        String to = statusChange.getStatusInfo().getStatus();
        logger.info("=================== {}服务从{}状态,变为{}: ===================", instance.getRegistration().getName(), from, to);
        return Mono.fromRunnable(() ->
                doHttpRequest(instance));
    }


    public void doHttpRequest(Instance instance) {
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("appName", instance.getRegistration().getName());
        paramMap.add("serverId", instance.getId().getValue());
        paramMap.add("serverStats", instance.getStatusInfo().getStatus());

        //TODO instance.getStatusInfo().getStatus()当值为 DOWN/OFFLINE 是执行异步通知服务宕机
        logger.info("检测上线下线通知参数{}", JSON.toJSONString(paramMap));
//        restTemplate.postForObject(url, paramMap, String.class);
    }


    public RestTemplate getRestTemplate() {
        return restTemplate;
    }

    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

}

package com.snk.admin.config;

import de.codecentric.boot.admin.server.config.AdminServerNotifierAutoConfiguration;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: admin-server
 * @description: web-server通知配置
 * @author: tianfn
 * @create: 2021-09-14 17:28
 **/
@Configuration
@ConditionalOnProperty(
        prefix = "spring.boot.admin.notify.web-server",
        name = "enabled",
        havingValue = "true"
)
@AutoConfigureBefore({AdminServerNotifierAutoConfiguration.NotifierTriggerConfiguration.class, AdminServerNotifierAutoConfiguration.CompositeNotifierConfiguration.class})
public class WebServerNotifierConfiguration {
    @Bean
    @ConditionalOnMissingBean
    @ConfigurationProperties(prefix = "spring.boot.admin.notify.web-server")
    public WebServerNotifier webServerNotifier(InstanceRepository repository) {
        return new WebServerNotifier(repository);
    }
}
客户端配置
<dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>${spring-admin.version}</version>
        </dependency>
spring:
 application:
   name: service-name
 boot:
   admin: #监控服务相关配置
     client:
       url: http://localhost:1202 #springadminserver地址
       username: admin #spring admin server用户名
       password: 123456 #spring admin server密码
       instance:
         name: ${spring.application.name} #该服务在springadmin中展示的名字配置
         service-url:http://localhost:8282 #此服务暴露给springadmin的地址,用来拉取此服务的健康指标信息
         metadata:
           user.name: admin
           user.password: 123456
           
#监控端点暴露            
management:
 endpoint:
   health:
     show-details: ALWAYS
 endpoints:    
   web:
     exposure:
       include: "*"  

参考文章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值