【Springboot系列】监控kafka topic,钉钉报警

1、需求的诞生

前几天公司我们部门需要演示一个应用,应用依赖kafka的数据,但是kafka的数据来自其他部门的投递。

一些原因导致数据无法给到,导致我们部门的演示也很有问题,所以想做一个简单的kafka topic的监控,在没有数据的时候及时发现并找兄弟部门沟通

这里记录下原因,因为机房的带宽只有500M,其他部门在做一些视频录制的工作,导致带宽满了,往kafka生产数据的producer无法发送到。

2、kafka监测

kafka的检测有很多方案,但是因为我们在测试环境使用,讲究一个轻量级,所以直接写一个小程序监控就得了。

kafka的监控没搞过,但是用过Offset Explorer,Offset中可以看到总的消息数量,延续这个思路,只要这个数值有变化,证明又在投递。这是思路

2.1 加入依赖

创建一个Springboot的项目,这个没难度,也不说了

这里把所有的依赖都放这了,主要是kafka和等会要用的钉钉的sdk

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alibaba-dingtalk-service-sdk</artifactId>
    <version>2.0.0</version>
</dependency>

2.2 看下配置文件

application.yml

yaml 复制代码
spring:
  kafka:
    bootstrap-servers: kafka地址
    consumer:
      auto-offset-reset: earliest
      group-id: test-xiangcai
dingding:
  webHook: https://oapi.dingtalk.com/robot/send?access_token=xxx
  topics: topic1,topic2
  sign: sign

2.3 写一个类获取topic的数据

ini 复制代码
@Component
public class KafkaTools {
    private static KafkaTools _this;
    @Autowired
    private ConsumerFactory<Long, String> consumerFactory;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    Consumer<Long, String> consumer;
    @PostConstruct
    public void init() {
        _this = this;
        _this.consumer = consumerFactory.createConsumer();
    }
    public static Long getTopicCount(String topic) {
        List<PartitionInfo> partitionInfos = _this.kafkaTemplate.partitionsFor(topic);
        long count = 0L;
        for (PartitionInfo str : partitionInfos) {
            TopicPartition topicPartition = new TopicPartition(topic, str.partition());
            long logEndOffset = _this.consumer.endOffsets(List.of(topicPartition)).get(topicPartition);
            _this.consumer.assign(List.of(topicPartition));
            long currentOffset = _this.consumer.position(topicPartition);
            count += (logEndOffset - currentOffset);
            System.err.println( "  topic  " + topic + "  最后的offset is  " + logEndOffset + "    currentOffset  " + currentOffset + "  totalCount  " + (logEndOffset - currentOffset));
        }
        return count;
    }
}

解释下重点代码

LOG-END-OFFSET :每个分区当前最新生产的消息的位移值

CURRENT-OFFSET :该消费者组当前最新消费消息的位移值

注:因为内部是使用的单机的kafka,并且也只有一个partition

3、发送到钉钉

想做一个提醒,最初的方案是发送的邮箱,没有养成看邮件的习惯,所以想要发送到微信或者钉钉,看了下方案,钉钉是最容易实现的,所以这里选择使用钉钉

3.1 钉钉webhook

创建一个钉钉群,点击右上角群设置,机器人,进入到机器人的设置界面,然后点击添加机器人,选择自定义机器人,进入界面就好

这里可以设置机器人的头像,机器人的名字,同时也展示webhook的地址,可以通过这个url 发送消息到群里。

这里选择的安全设置加签的方式,可以参考说明文档,等会代码展示

3.2 钉钉发送消息到群里

看下代码,这里设置的消息为文本,并且使用了at全体人

ini 复制代码
/**
 * 发送消息到钉钉
 *
 * @param topic
 */
private void sendMsgToDingDing(String topic) {
 
    try {
        String url = makeUrl(dingDingConfig.getWebHook(), dingDingConfig.getSign());
        DingTalkClient client = new DefaultDingTalkClient(url);
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype("text");
        OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
        text.setContent("192.168.2.8 kafka  " + topic + "超过2分钟没有数据,检查下");
 
        OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
        // isAtAll类型如果不为Boolean,请升级至最新SDK
        at.setIsAtAll(true);
        request.setAt(at);
        request.setText(text);
 
        client.execute(request);
    } catch (Exception e) {
    }
}
 
/**
 * 组装url
 *
 * @param url
 * @param secret
 * @return
 * @throws Exception
 */
public static String makeUrl(String url, String secret) throws Exception {
    Long timestamp = System.currentTimeMillis();
 
    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");
 
    return url + String.format("&timestamp=%d&sign=%s", timestamp, sign);
}

看下效果

4、定时器功能

定时器直接使用springboot 的 schedule,在application上添加 直接 @EnableScheduling

创建定时job,并且设置2分钟执行一次,两分钟发现没有数据更新就会发送消息到钉钉提醒

ini 复制代码
public static Map<String, Long> countMap = new HashMap<>();
 
//    @Scheduled(cron = "0/5 * *  * * ? ")   //每5秒执行一次
@Scheduled(cron = "0 0/2 * * * ?") //  2分钟执行一次
public void execute() {
    for (String topic : dingDingConfig.getTopics().split(",")) {
        Long topicCount = KafkaTools.getTopicCount(topic);
        Long orDefault = countMap.getOrDefault(topic, 0L);
        if (orDefault.equals(topicCount)) {
            if (System.currentTimeMillis() - preDingTime > 60 * 60 * 1000L) {
                sendMsgToDingDing(topic);
                preDingTime = System.currentTimeMillis();
            }
        } else {
            countMap.put(topic, topicCount);
        }
    }
    System.err.println("job 执行时间  " + new Date());
}

这里做了一些处理

第一个点就是 在内存中保存数量,没有使用数据库,主要考虑到轻量级,在测试环境

第二个就是钉钉消息的发送,防止出现消息喷涌,这里限制了1小时才可以发送一次

5、打包部署

看下dockerfile

bash 复制代码
#项目所依赖的jdk镜像
FROM openjdk:11-jdk
RUN mkdir -p /app
WORKDIR /app
#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
#将maven构建好的jar添加到镜像中,第二个为别名
COPY start.sh start.sh
ADD target/kafkaMonitor.jar kafkaMonitor.jar
#镜像所执行的命令
ENTRYPOINT ["sh","start.sh"]

看下启动脚本

bash 复制代码
echo "192.168.2.8 kafka.tyjt.com" >> /etc/hosts
java -jar kafkaMonitor.jar
echo 'finish start!!'

很简单直接启动,这个程序主打就是一个轻便

5、总结

这只是一个小的需求,没有使用一些重量级的组件,尽可能的快速的解决问题。

这里手动操作kafka,可以在每次订阅之后直接跳到最新的进行消费

kafkaTools中的很多操作可以在其他地方使用,比如获取partition,获取最新的offset

切换到最新拉取

相关推荐
IT_陈寒4 分钟前
Spring Boot 3.2性能翻倍!我仅用5个技巧就让接口响应时间从200ms降到50ms
前端·人工智能·后端
风象南15 分钟前
Spring Boot 手撸一个自助报表系统
后端
donotshow17 分钟前
Spring Boot 整合 ShedLock 处理定时任务重复
java·后端
木土雨成小小测试员17 分钟前
简单创建一个flask项目
后端·python·flask
Victor35630 分钟前
Redis(100)如何防止Redis的数据丢失?
后端
Victor35631 分钟前
Redis(101)Redis为什么是单线程的?
后端
程序员三明治1 小时前
选 Redis Stream 还是传统 MQ?队列选型全攻略(适用场景、优缺点与实践建议)
java·redis·后端·缓存·rocketmq·stream·队列
cj6341181506 小时前
【MySQL】mysqldump使用方法
java·后端
JIngJaneIL6 小时前
停车场管理|停车预约管理|基于Springboot的停车场管理系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·停车场管理系统
雪域迷影7 小时前
Go语言中通过get请求获取api.open-meteo.com网站的天气数据
开发语言·后端·http·golang·get