Bootstrap

【JAVA】利用钉钉自定义机器人监控NACOS服务,实现实时下线通知

利用钉钉自定义机器人监控NACOS服务,实现实时下线通知

自定义机器人步骤

  1. 创建一个钉钉群,拉人创建
  2. 在群设置中,选择自定义机器人
  3. 记住webhook 和 accessKey,用于代码中配置

钉钉实体类

@Data
public class MessageSender {
    // 消息类型
    private String messageType;
    // 钉钉的 webhook URL
    private String webhookUrl;
    // 安全密钥
    private String accessKey;
    // 消息内容
    private String messageContent;
    // 指定的接收者手机号列表
    private List<String> recipientMobiles;
    // 是否发送给所有人
    private boolean notifyAll;
}

监控代码

@Component
public class ServiceStatusMonitor {
    private static final Logger logger = LoggerFactory.getLogger(ServiceStatusMonitor.class);

    @Value("${dingtalk.webhook}")
    private String webhook;

    @Value("${dingtalk.secret}")
    private String secret;

    @Value("${dingtalk.userMobil}")
    private String[] userMobil;

    @Value("${dingtalk.namespace}")
    private String namespace;

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String nacosUrl;

    private static Map<String, Integer> instanceCache = new ConcurrentHashMap<>();

    // 初始化服务监控
    @PostConstruct
    public void initialize() throws Exception {
        List<String> mobileList = Arrays.asList(userMobil);

        Properties properties = System.getProperties();
        properties.setProperty("serverAddr", nacosUrl);
        properties.setProperty("namespace", namespace);
        NamingService namingService = NamingFactory.createNamingService(properties);
        
        List<String> monitoredServices = Arrays.asList("order", "serviceA", "serviceB"); // 监控的服务列表
        
        for (String serviceName : monitoredServices) {
            namingService.subscribe(serviceName, event -> {
                List<Instance> instances = ((NamingEvent) event).getInstances();
                
                instanceCache.computeIfAbsent(serviceName, k -> instances.size());
                
                if (instances.size() < instanceCache.get(serviceName)) {
                    MessageSender alertMessage = new MessageSender();
                    alertMessage.setNotifyAll(false);
                    alertMessage.setMessageType("text");
                    alertMessage.setAccessKey(secret);
                    alertMessage.setWebhookUrl(webhook);
                    alertMessage.setRecipientMobiles(mobileList);
                    alertMessage.setMessageContent(serviceName + " 服务下线,当前在线节点数:" + instances.size());
                    
                    DingTalkUtil.sendMessage(alertMessage);
                    logger.info("服务下线: " + serviceName);
                    instanceCache.put(serviceName, instances.size());
                } else {
                    logger.info("服务上线: " + serviceName + ", 当前在线节点数:" + instances.size());
                }
            });
        }
    }
}

封装工具类

public class DingTalkUtils {
    private static final Logger logger = LoggerFactory.getLogger(DingTalkUtils.class);
    // 消息类型常量
    private static final String MESSAGE_TYPE_TEXT = "text";

    /**
     * 发送钉钉消息
     **/
    public static void sendMessage(MessageSender message) {
        try {
            logger.info("准备发送钉钉消息:" + message);
            Long timestamp = System.currentTimeMillis();
            String secret = message.getAccessKey();

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

            DingTalkClient client = new DefaultDingTalkClient(message.getWebhookUrl() + "&timestamp=" + timestamp + "&sign=" + signature);
            OapiRobotSendRequest request = new OapiRobotSendRequest();

            OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
            if (message.isNotifyAll() || message.getRecipientMobiles() == null || message.getRecipientMobiles().isEmpty()) {
                // 发送给所有人
                at.setIsAtAll(true);
            } else {
                // 发送给指定用户
                at.setAtMobiles(message.getRecipientMobiles());
                at.setIsAtAll(false);
            }
            request.setAt(at);

            // 处理文本消息
            if (MESSAGE_TYPE_TEXT.equals(message.getMessageType())) {
                request.setMsgtype(MESSAGE_TYPE_TEXT);
                OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
                text.setContent(message.getMessageContent());
                request.setText(text);
            }

            OapiRobotSendResponse response = client.execute(request);
            logger.info("钉钉消息发送结果:" + response);
        } catch (Exception e) {
            logger.error("钉钉消息发送异常", e);
        }
    }

    public static void main(String[] args) {
        List<String> mobileList = new ArrayList<>();
        mobileList.add("13026578156");

        MessageSender message = new MessageSender();
        message.setMessageType(MESSAGE_TYPE_TEXT);
        message.setNotifyAll(false);
        message.setRecipientMobiles(mobileList);
        message.setWebhookUrl("https://oapi.dingtalk.com/robot/send?access_token=a4dxxxxxxxxxxxx347e4b1267dd4f39");
        message.setAccessKey("SECe4f7ef42exxxxxxxxxxxxxxxxxxxxxxx07d07287769f16c91d");
        message.setMessageContent("监控消息通知");
        sendMessage(message);
    }
}


yml配置

#钉钉消息推送
dingtalk:
  webhook: https://oapi.dingtalk.com/robot/send?access_token=a4dab6098cf65xxxxxxxxxxxxxxxxxxxb1267dd4f39
  secret: SECe4f7ef42ea7d10df102xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx07287769f16c91d
  userMobil: 1xxxxxx156
  namespace: dev

钉钉依赖

    <!--钉钉消息推送依赖-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
            <version>2.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
;