集成nacos,使用钉钉发送服务下线告警

简介: 我们在集成微服务框架的时候,涉及服务太多,如果是单节点的话,遇到凌晨服务挂起的问题会很麻烦。并且原生的监控也不是很理想。这里结合nacos,再通过钉钉来发送服务下线告警,这样可在第一时间确定服务异常并及时处理。

我们在集成微服务框架的时候,涉及服务太多,如果是单节点的话,遇到凌晨服务挂起的问题会很麻烦。并且原生的监控也不是很理想。这里结合nacos,再通过钉钉来发送服务下线告警,这样可在第一时间确定服务异常并及时处理。

一、引入相关依赖

pom.xml

<!-- SpringCloud Alibaba Nacos -->
   <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
   </dependency>

   <!-- SpringCloud Alibaba Nacos Config -->
   <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
   </dependency>

   <!-- SpringBoot Web -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

   <!-- Spring Security -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>

   <!-- Spring Context Support -->
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context-support</artifactId>
   </dependency>

   <dependency>
       <groupId>com.aliyun</groupId>
       <artifactId>alibaba-dingtalk-service-sdk</artifactId>
       <version>2.0.0</version>
   </dependency>
二、服务状态监控逻辑
@Slf4j
@Component
@RefreshScope
public class ServiceStatusListener implements InitializingBean {
   
    @Autowired
    private MonitorConfig config;
    @Value("${spring.cloud.nacos.discovery.server-addr}")
    private String nacosUrl;
    @Value("${spring.cloud.nacos.discovery.namespace}")
    private String namespace;
    static Map<String, Long> cache = new ConcurrentHashMap<>();
    @Autowired
    private DingtalkService dingtalkService;

    @PreDestroy
    public void preDestroy() {
   
        log.info("preDestroy....");
    }

    /**
     * 初始化监听服务上下线
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
   
        log.info("afterPropertiesSet........");
        Properties properties = System.getProperties();
        properties.setProperty("serverAddr", nacosUrl);
        properties.setProperty("namespace", namespace);
        NamingService naming = NamingFactory.createNamingService(properties);
        List<String> serviceNames = config.getServices();
        log.info("需要监控的服务数:{}", serviceNames.size());
        for (String service : serviceNames) {
   
            naming.subscribe(service, event -> {
   
                List<Instance> instances = ((NamingEvent) event).getInstances();
                String serviceName = ((NamingEvent) event).getServiceName();
                if (instances.size() == 0) {
   
                    log.info("服务【{}】未启动,加入监听列表", serviceName);
                    cache.put(serviceName, System.currentTimeMillis());
                    //开启告警
                    if (config.getEnabled()) {
   
                        dingtalkService.sendMsgByInterval(serviceName);
                    }
                } else {
   
                    log.info("服务【{}】已启动", serviceName);
                    cache.remove(serviceName);
                }
            });
        }
    }
}
三、钉钉发送告警逻辑
@Slf4j
@Component
@RefreshScope
public class DingtalkService {
   
    @Autowired
    private MonitorConfig config;
    private final String template = "【%s】服务下线, 服务异常下线告警";
    @Value("${dingtalk.webhook}")
    private String webhook;
    @Value("${dingtalk.secret}")
    private String secret;

    /**
     * 发送消息
     * 这里使用异步操作
     * @param serviceName
     */
    @Async("taskExecutor")
    public void sendMsgByInterval(String serviceName) {
   
        Long time = ServiceStatusListener.cache.get(serviceName);
        // 单位毫秒
        long interval = config.getInterval() * 60 * 1000;
        while (ServiceStatusListener.cache.containsKey(serviceName)) {
   
            long now = System.currentTimeMillis();
            // 当被监听的服务超时未启动则发送告警
            if ((now - time) >= interval) {
   
                sendMsg(serviceName);
                ServiceStatusListener.cache.remove(serviceName);
                break;
            }
            try {
   
                log.info("{}服务监控中...", serviceName);
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
   
            }
        }
    }

    public void sendMsg(String service) {
   
        log.info("【{}}】服务下线,发送钉钉提醒", service);
        SendMessage error = new SendMessage();
        error.setSecret(secret);
        error.setWebhook(webhook);
        error.setText(String.format(template, service));
        // 通过钉钉发送消息
        DingTalkUtil.sendMsg(error);
    }
}
四、钉钉推送工具

推送到钉钉群,可@单人,也可以@所有人
DingTalkUtil

public static void sendMsg(SendMessage msg) {
   
   try {
   
     Long timestamp = System.currentTimeMillis();
     String secret = msg.getSecret();

     String stringToSign = timestamp + "\n" + secret;
     Mac mac = Mac.getInstance("HmacSHA256");
     mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
     byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
     String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");


     DingTalkClient client = new DefaultDingTalkClient(msg.getWebhook() + "&timestamp=" + timestamp + "&sign=" + sign);
     OapiRobotSendRequest request = new OapiRobotSendRequest();

     OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
     //推送所有人
     at.setIsAtAll(true);
     request.setAt(at);

     //文本消息
     request.setMsgtype("text");
     OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
     text.setContent(msg.getText());
     request.setText(text);

     OapiRobotSendResponse response = client.execute(request);
     log.info("钉钉推送返回结果:" + response);
 } catch (Exception e) {
   
     log.error("钉钉通知异常", e);
 }
}
五、多线程配置
@Slf4j
@EnableAsync
@Configuration
public class ThreadPoolTaskConfig {
   
    /**
     * 核心线程数(默认线程数)
     */
    private static final int CORE_POOL_SIZE = 10;
    /**
     * 最大线程数
     */
    private static final int MAX_POOL_SIZE = 15;
    /**
     * 允许线程空闲时间(单位:默认为秒)
     */
    private static final int KEEP_ALIVE_TIME = 10;
    /**
     * 缓冲队列大小
     */
    private static final int QUEUE_CAPACITY = 20;
    /**
     * 线程池名前缀
     */
    private static final String THREAD_NAME_PREFIX = "monitor-";

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
   
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);

        // 线程池对拒绝任务的处理策略
        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}

还有一个服务监控的配置类省略掉了,可以根据配置文件自己生成

六、相关配置
# Spring
spring:
  cloud:
    nacos:
      discovery:
        # 服务注册地址
        server-addr: xxx:8849
        # 命名空间
        namespace: xxx
      config:
        # 配置中心地址
        server-addr: xxx:8849
        # 命名空间
        namespace: xxx
        # 配置文件格式
        file-extension: yml
        # 共享配置
        shared-configs:
          - application-{
   mathJaxContainer[0]}{
   spring.cloud.nacos.config.file-extension}

# 以下配置可以放到nacos上面
# 服务监控
monitor:
  alarm:
    # 是否开启提醒
    enabled: true
    # 服务监控列表
    services: xxx,xxxx  
    # 服务最多停止几分钟(启动时),大于这个时间则可判断为离线
    interval: 2

# 钉钉机器人
dingtalk:
  webhook: https://oapi.dingtalk.com/robot/send?access_token=xxx
  secret: xxx

关键的逻辑代码已经贴出来了,希望对大家有所帮助

相关文章
|
5月前
|
NoSQL Java Nacos
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
174 3
|
26天前
|
Dubbo Cloud Native 应用服务中间件
阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。
在云原生时代,微服务架构成为主流。阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。示例代码展示了如何在项目中实现两者的整合,通过 Nacos 动态调整服务状态和配置,适应多变的业务需求。
37 2
|
1月前
|
数据管理 Nacos 开发者
"Nacos架构深度解析:一篇文章带你掌握业务层四大核心功能,服务注册、配置管理、元数据与健康检查一网打尽!"
【10月更文挑战第23天】Nacos 是一个用于服务注册发现和配置管理的平台,支持动态服务发现、配置管理、元数据管理和健康检查。其业务层包括服务注册与发现、配置管理、元数据管理和健康检查四大核心功能。通过示例代码展示了如何在业务层中使用Nacos,帮助开发者构建高可用、动态扩展的微服务生态系统。
104 0
|
1月前
|
SQL 关系型数据库 数据库连接
"Nacos 2.1.0版本数据库配置写入难题破解攻略:一步步教你排查连接、权限和配置问题,重启服务轻松解决!"
【10月更文挑战第23天】在使用Nacos 2.1.0版本时,可能会遇到无法将配置信息写入数据库的问题。本文将引导你逐步解决这一问题,包括检查数据库连接、用户权限、Nacos配置文件,并提供示例代码和详细步骤。通过这些方法,你可以有效解决配置写入失败的问题。
71 0
|
4月前
|
负载均衡 监控 Java
SpringCloud常见面试题(一):SpringCloud 5大组件,服务注册和发现,nacos与eureka区别,服务雪崩、服务熔断、服务降级,微服务监控
SpringCloud常见面试题(一):SpringCloud 5大组件,服务注册和发现,nacos与eureka区别,服务雪崩、服务熔断、服务降级,微服务监控
SpringCloud常见面试题(一):SpringCloud 5大组件,服务注册和发现,nacos与eureka区别,服务雪崩、服务熔断、服务降级,微服务监控
|
5月前
|
监控 安全 网络安全
inishConnect(..) failed: Connection refused,服务本地正常服务器网关报400,nacos服务实例不能下线
总之,这种问题需要通过多方面的检查和校验来定位和解决,并可能需要结合实际环境的具体情况来进行相应的调整。在处理分布式系统中这类问题时,耐心和细致的调试是必不可少的。
114 13
|
4月前
|
Kubernetes Nacos 微服务
【技术难题破解】Nacos v2.2.3 + K8s 微服务注册:强制删除 Pod 却不消失?!7步排查法+实战代码,手把手教你解决Nacos Pod僵死问题,让服务瞬间满血复活!
【8月更文挑战第15天】Nacos作为微服务注册与配置中心受到欢迎,但有时会遇到“v2.2.3 k8s 微服务注册nacos强制删除 pod不消失”的问题。本文介绍此现象及其解决方法,帮助开发者确保服务稳定运行。首先需检查Pod状态与事件、配置文件及Nacos配置,确认无误后可调整Pod生命周期管理,并检查Kubernetes版本兼容性。若问题持续,考虑使用Finalizers、审查Nacos日志或借助Kubernetes诊断工具。必要时,可尝试手动强制删除Pod。通过系统排查,通常能有效解决此问题。
92 0
|
4月前
|
Java Nacos 开发工具
【Nacos】心跳断了怎么办?!8步排查法+实战代码,手把手教你解决Nacos客户端不发送心跳检测问题,让服务瞬间恢复活力!
【8月更文挑战第15天】Nacos是一款广受好评的微服务注册与配置中心。然而,“客户端不发送心跳检测”的问题时有发生,可能导致服务实例被视为离线。本文介绍如何排查此类问题:确认Nacos服务器地址配置正确;检查网络连通性;查看客户端日志;确保Nacos SDK版本兼容;调整心跳检测策略;验证服务实例注册状态;必要时重启应用;检查影响行为的环境变量。通过这些步骤,通常可定位并解决问题,保障服务稳定运行。
277 0
|
4月前
|
网络安全 Nacos 开发者
【Nacos】神操作!节点提示暂时不可用?别急!7步排查法+实战代码,手把手教你解决Nacos服务实例状态异常,让服务瞬间满血复活!
【8月更文挑战第15天】Nacos作为微服务注册与配置中心,虽广受好评,但仍可能遇到“节点提示暂时不可用”的问题。本文解析此现象及其解决之道。首先需理解该提示意味着服务实例未能正常响应。解决步骤包括:检查服务状态与网络、审查Nacos配置、调整健康检查策略、重启服务及分析日志。通过系统化排查,可有效保障服务稳定运行。
141 0
|
4月前
|
JSON 机器人 Go
go接收alertmanager告警并发送钉钉
go接收alertmanager告警并发送钉钉

热门文章

最新文章

下一篇
DataWorks