SpringBootAdmin+钉钉机器人服务监控

钉钉开发平台-自定义机器人接入文档

image-20220314170921235

看了一下钉钉的文档,可发现通过调用Webhook地址可将告警消息发送到群聊里来实现消息通知的功能

获取调用地址

  • 先创建一个钉钉群,并创建自定义机器人

选择其中一项安全设置,可加强安全性,防止Webhook地址泄密被乱发消息

image-20220314171308258

  • 创建成功,复制Webhook地址,等下需要用到

image-20220314171328241

创建SpringBoot应用

添加yml配置

image-20220314173409968

spring:
  boot:
    admin:
  notify:
    dingtalk:
      enabled: true
      webhook-token: https://oapi.dingtalk.com/robot/send?access_token=ef4c4ff2c97e3b8d316c1d1d4febfc80a84814bf0bd9bba00283da24bbd0a26d

获取签名信息等(测试用)

因为我选的是加签的安全认证 所以测试时需要带上sign和时间戳信息

 public static void main(String[] args) throws Exception {
        //获取时间戳
        Long timestamp = System.currentTimeMillis();
        //定义密钥
        String secret = "SEC466154ca1061db3b135e4d76e989f3acc1091a8917ddecbb053524964f5beb80";
        //把时间戳和密钥拼接成字符串,中间加入一个换行符
        String stringToSign = timestamp + "\n" + secret;
        //声明一个Mac对象,用来操作字符串
        Mac mac = Mac.getInstance("HmacSHA256");
        //初始化Mac对象,设置Mac对象操作的字符串是UTF-8类型,加密方式是SHA256
        mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
        //把字符串转化成字节形式
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        //新建一个Base64编码对象
        Base64.Encoder encoder = Base64.getEncoder();
        //把上面的字符串进行Base64加密后再进行URL编码
        String sign = URLEncoder.encode(new String(encoder.encodeToString(signData)),"UTF-8");
        //分别输出时间戳和加密信息
        System.out.println(timestamp);
        System.out.println(sign);
    }

测试

测试命令: (这里需要将access_token换成自己的)

curl 'https://oapi.dingtalk.com/robot/send?access_token=ef4c4ff2c97e3b8d316c1d1d4febfc80a84814bf0bd9bba00283da24bbd0a26d' \
 -H 'Content-Type: application/json' \
 -d '{"msgtype": "text","text": {"content":"我就是我, 是不一样的烟火"}}'

也可以使用postman等其他工具进行测试:

image-20220314173642075

image-20220314173650559

image-20220314173319404

应用代码

这里其实可以使用SpringBoot Admin来搭配钉钉做应用监控的 我这里只是写的一个简单demo

引入httpclient

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.9</version>
</dependency>

 <!--钉钉官方依赖-->
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alibaba-dingtalk-service-sdk</artifactId>
    <version>1.0.1</version>
</dependency>
package com.eg.dingbot.robot;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.junit.Test;


import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.util.Base64;

/**
 * @author Eg
 * @date 2022-03-14
 */

public class NotifieBot {
    //请求地址以及access_token
    String Webhook = "https://oapi.dingtalk.com/robot/send?access_token=ef4c4ff2c97e3b8d316c1d1d4febfc80a84814bf0bd9bba00283da24bbd0a26d";
    //密钥
    String secret = "SEC466154ca1061db3b135e4d76e989f3acc1091a8917ddecbb053524964f5beb80";


    /**
     * @return 生成时间戳和加密结果
     * @throws Exception
     */
    public String encode() throws Exception {
        //获取时间戳
        Long timestamp = System.currentTimeMillis();
        //把时间戳和密钥拼接成字符串,中间加入一个换行符
        String stringToSign = timestamp + "\n" + this.secret;
        //声明一个Mac对象,用来操作字符串
        Mac mac = Mac.getInstance("HmacSHA256");
        //初始化,设置Mac对象操作的字符串是UTF-8类型,加密方式是SHA256
        mac.init(new SecretKeySpec(this.secret.getBytes("UTF-8"), "HmacSHA256"));
        //把字符串转化成字节形式
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        //新建一个Base64编码对象
        Base64.Encoder encoder = Base64.getEncoder();
        //把上面的字符串进行Base64加密后再进行URL编码
        String sign = URLEncoder.encode(new String(encoder.encodeToString(signData)),"UTF-8");
        System.out.println(timestamp);
        System.out.println(sign);
        String result = "&timestamp=" + timestamp + "&sign=" + sign;
        return result;
    };


    /**
     * 把传入的message发送给钉钉机器人
     * @param message 要发送的信息
     * @throws JSONException
     */
    public void dingRequest(String message) throws JSONException {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        String url = null;
        try {
            url = this.Webhook + this.encode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        HttpPost httpPost = new HttpPost(url);
        //设置http的请求头,发送json字符串,编码UTF-8
        httpPost.setHeader("Content-Type", "application/json;charset=utf8");
        //生成json对象传入字符
        JSONObject result = new JSONObject();
        JSONObject text = new JSONObject();
        text.put("content", message);
        result.put("text", text);
        result.put("msgtype", "text");
        String jsonString = JSON.toJSONString(result);
        StringEntity entity = new StringEntity(jsonString, "UTF-8");
        //设置http请求的内容
        httpPost.setEntity(entity);
        // 响应模型
        CloseableHttpResponse response = null;
        try {
            // 由客户端执行(发送)Post请求
            response = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();
            System.out.println("响应状态为:" + response.getStatusLine());
            if (responseEntity != null) {
                System.out.println("响应内容长度为:" + responseEntity.getContentLength());
                System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @Test
    public void test() throws Exception {
        NotifieBot notifieBot = new NotifieBot();
        notifieBot.dingRequest("test");
    }
}

经过测试 机器人已经可以正常收到消息了

image-20220314175029522

方法抽取

发送消息

 /**
     * 发送消息
     * @param messageText
     */
    private void sendTextMessage(String messageText){
        DingTalkClient client = new DefaultDingTalkClient(beforyMonitorProperties.getDingtalkNotify().getWebhookToken());
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(beforyMonitorProperties.getDingtalkNotify().getMsgType());
        OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
        text.setContent(messageText);
        request.setText(text);
        try {
            client.execute(request);
        } catch (ApiException e) {
            log.info("[ERROR] sendMessage", e);
        }
    }

配置类

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;

import de.codecentric.boot.admin.server.config.AdminServerNotifierAutoConfiguration.CompositeNotifierConfiguration;
import de.codecentric.boot.admin.server.config.AdminServerNotifierAutoConfiguration.NotifierTriggerConfiguration;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;

@Configuration
@ConditionalOnProperty(
        prefix = "spring.boot.admin.notify.dingtalk",
        name = {"webhook-token"}
)
@AutoConfigureBefore({NotifierTriggerConfiguration.class, CompositeNotifierConfiguration.class})
public class DingTalkNotifierConfiguration {
    @Bean
    @ConditionalOnMissingBean
    @ConfigurationProperties(prefix = "spring.boot.admin.notify.dingtalk")
    public DingTalkNotifier dingTalkNotifier(InstanceRepository repository) {
        return new DingTalkNotifier(repository);
    }

}

实现服务通知

import java.util.HashMap;
import java.util.Map;

import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

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.notify.AbstractStatusChangeNotifier;
import reactor.core.publisher.Mono;

@Data
@AllArgsConstructor
public class DingTalkNotifier extends AbstractStatusChangeNotifier {
    private static final String DEFAULT_MESSAGE = "*#{instance.registration.name}* (#{instance.id}) is *#{event.statusInfo.status}**";

    private final SpelExpressionParser parser = new SpelExpressionParser();
    private RestTemplate restTemplate = new RestTemplate();
    private String webhookToken;
    private String atMobiles;
    private String msgtype = "markdown";
    private String title = "服务告警";
    private Expression message;

    public DingTalkNotifier(InstanceRepository repository) {
        super(repository);
        this.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> restTemplate.postForEntity(webhookToken, createMessage(event, instance),Void.class));
    }


    private HttpEntity<Map<String, Object>> createMessage(InstanceEvent event,Instance instance) {
        Map<String, Object> messageJson = new HashMap<>();
        HashMap<String, String> params = new HashMap<>();
        params.put("text", this.getMessage(event, instance));
        params.put("title", this.title);
        messageJson.put("atMobiles", this.atMobiles);
        messageJson.put("msgtype", this.msgtype);
        messageJson.put(this.msgtype, params);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return new HttpEntity<>(messageJson, headers);
    }

    private String getAtMobilesString(String s) {
        StringBuilder atMobiles = new StringBuilder();
        String[] mobiles = s.split(",");
        for (String mobile : mobiles) {
            atMobiles.append("@").append(mobile);
        }
        return atMobiles.toString();
    }

    private String getMessage(InstanceEvent event,Instance instance) {
         Map<String, Object> root = new HashMap<>();
         root.put("event", event);
         root.put("instance", instance);
         root.put("lastStatus", getLastStatus(event.getInstance()));
         StandardEvaluationContext context = new StandardEvaluationContext(root);
         context.addPropertyAccessor(new MapAccessor());
         return message.getValue(context, String.class);
    }

}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇