RocketMQ源码 Broker-BrokerStatsManager Broker统计管理组件源码分析

news2024/9/27 15:27:25

前言

BrokerStatsManager 主要负责对broker端的系统指标进行统计,如QUEUE_GET_NUMS队列获取数量、QUEUE_GET_SIZE队列获取大小指标的 分钟、小时、天级别的统计数据。它针对的所有指标都是使用后台定时调度线程,对统计条目中的数据进行后台统计计算,存储在统计条目中的对应集合里,以便使用。


源码版本:4.9.3

源码架构图

核心数据结构

最核心的是维护了一个数据统计table,key为统计名称,value为统计条目Set。

// Broker统计信息管理
public class BrokerStatsManager {

    // Broker统计调度线程池
    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
        "BrokerStatsThread"));

    // 商业化统计调度线程池
    private final ScheduledExecutorService commercialExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
        "CommercialStatsThread"));

    // 核心数据结构,数据统计table,key为统计名称,value为统计条目Set
    private final HashMap<String, StatsItemSet> statsTable = new HashMap<String, StatsItemSet>();
    // 集群名称
    private final String clusterName;
    // 是否开启队列统计
    private final boolean enableQueueStat;

    // 统计条目跌落大小
    private final MomentStatsItemSet momentStatsItemSetFallSize = new MomentStatsItemSet(GROUP_GET_FALL_SIZE, scheduledExecutorService, log);
    // 统计条目跌落时间
    private final MomentStatsItemSet momentStatsItemSetFallTime = new MomentStatsItemSet(GROUP_GET_FALL_TIME, scheduledExecutorService, log);

}

统计条目集合内部结构,主要维护一个统计条目表, key为统计指标细分key, value为统计指标对象。

// 统计指标集合
public class StatsItemSet {
    // 统计指标表, key为统计指标细分key, value为统计指标对象
    private final ConcurrentMap<String/* key */, StatsItem> statsItemTable =
        new ConcurrentHashMap<String, StatsItem>(128);

    // 统计名称
    private final String statsName;
    private final ScheduledExecutorService scheduledExecutorService;
    private final InternalLogger log;
}

在深入看下统计条目内部结构,维护记录指标值的value、times和记录统计快照的csListMinute、csListHour、csListDay。记录统计快照集合是通过后台线程定时加工出来的,可以在数据行为小结的源码里找到。

// 统计项
public class StatsItem {

    // 统计值
    private final LongAdder value = new LongAdder();
    // 统计次数
    private final LongAdder times = new LongAdder();
    // 统计快照时间-单位分钟
    private final LinkedList<CallSnapshot> csListMinute = new LinkedList<CallSnapshot>();
    // 统计快照时间-单位小时
    private final LinkedList<CallSnapshot> csListHour = new LinkedList<CallSnapshot>();
    // 统计快照时间-单位天
    private final LinkedList<CallSnapshot> csListDay = new LinkedList<CallSnapshot>();

    // 统计名称
    private final String statsName;
    // 统计项名称
    private final String statsKey;
    private final ScheduledExecutorService scheduledExecutorService;
    private final InternalLogger log;
}

核心行为

以下是都有维护和使用上边的内存数据结构的行为的源码 + 注释。


// Broker统计信息管理
public class BrokerStatsManager {


    public static final String QUEUE_PUT_NUMS = "QUEUE_PUT_NUMS";
    public static final String QUEUE_PUT_SIZE = "QUEUE_PUT_SIZE";
    public static final String QUEUE_GET_NUMS = "QUEUE_GET_NUMS";
    public static final String QUEUE_GET_SIZE = "QUEUE_GET_SIZE";
    public static final String TOPIC_PUT_NUMS = "TOPIC_PUT_NUMS";
    public static final String TOPIC_PUT_SIZE = "TOPIC_PUT_SIZE";
    public static final String GROUP_GET_NUMS = "GROUP_GET_NUMS";
    public static final String GROUP_GET_SIZE = "GROUP_GET_SIZE";
    public static final String SNDBCK_PUT_NUMS = "SNDBCK_PUT_NUMS";
    public static final String BROKER_PUT_NUMS = "BROKER_PUT_NUMS";
    public static final String BROKER_GET_NUMS = "BROKER_GET_NUMS";
    public static final String GROUP_GET_FROM_DISK_NUMS = "GROUP_GET_FROM_DISK_NUMS";
    public static final String GROUP_GET_FROM_DISK_SIZE = "GROUP_GET_FROM_DISK_SIZE";
    public static final String BROKER_GET_FROM_DISK_NUMS = "BROKER_GET_FROM_DISK_NUMS";
    public static final String BROKER_GET_FROM_DISK_SIZE = "BROKER_GET_FROM_DISK_SIZE";
    // For commercial
    public static final String COMMERCIAL_SEND_TIMES = "COMMERCIAL_SEND_TIMES";
    public static final String COMMERCIAL_SNDBCK_TIMES = "COMMERCIAL_SNDBCK_TIMES";
    public static final String COMMERCIAL_RCV_TIMES = "COMMERCIAL_RCV_TIMES";
    public static final String COMMERCIAL_RCV_EPOLLS = "COMMERCIAL_RCV_EPOLLS";
    public static final String COMMERCIAL_SEND_SIZE = "COMMERCIAL_SEND_SIZE";
    public static final String COMMERCIAL_RCV_SIZE = "COMMERCIAL_RCV_SIZE";
    public static final String COMMERCIAL_PERM_FAILURES = "COMMERCIAL_PERM_FAILURES";
    public static final String COMMERCIAL_OWNER = "Owner";
    // Message Size limit for one api-calling count.
    public static final double SIZE_PER_COUNT = 64 * 1024;

    public static final String GROUP_GET_FALL_SIZE = "GROUP_GET_FALL_SIZE";
    public static final String GROUP_GET_FALL_TIME = "GROUP_GET_FALL_TIME";
    // Pull Message Latency
    public static final String GROUP_GET_LATENCY = "GROUP_GET_LATENCY";

    /**
     * read disk follow stats
     */
    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_STATS_LOGGER_NAME);
    private static final InternalLogger COMMERCIAL_LOG = InternalLoggerFactory.getLogger(LoggerName.COMMERCIAL_LOGGER_NAME);

    // Broker统计调度线程池
    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
        "BrokerStatsThread"));

    // 商业化统计调度线程池
    private final ScheduledExecutorService commercialExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
        "CommercialStatsThread"));

    // 核心数据结构,数据统计table,key为统计名称,value为统计条目Set
    private final HashMap<String, StatsItemSet> statsTable = new HashMap<String, StatsItemSet>();
    // 集群名称
    private final String clusterName;
    // 是否开启队列统计
    private final boolean enableQueueStat;

    // 统计条目跌落大小
    private final MomentStatsItemSet momentStatsItemSetFallSize = new MomentStatsItemSet(GROUP_GET_FALL_SIZE, scheduledExecutorService, log);
    // 统计条目跌落时间
    private final MomentStatsItemSet momentStatsItemSetFallTime = new MomentStatsItemSet(GROUP_GET_FALL_TIME, scheduledExecutorService, log);

    public BrokerStatsManager(String clusterName, boolean enableQueueStat) {
        this.clusterName = clusterName;
        this.enableQueueStat = enableQueueStat;
        // 初始化统计条目集合
        if (enableQueueStat) {
            this.statsTable.put(QUEUE_PUT_NUMS, new StatsItemSet(QUEUE_PUT_NUMS, this.scheduledExecutorService, log));
            this.statsTable.put(QUEUE_PUT_SIZE, new StatsItemSet(QUEUE_PUT_SIZE, this.scheduledExecutorService, log));
            this.statsTable.put(QUEUE_GET_NUMS, new StatsItemSet(QUEUE_GET_NUMS, this.scheduledExecutorService, log));
            this.statsTable.put(QUEUE_GET_SIZE, new StatsItemSet(QUEUE_GET_SIZE, this.scheduledExecutorService, log));
        }
        this.statsTable.put(TOPIC_PUT_NUMS, new StatsItemSet(TOPIC_PUT_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(TOPIC_PUT_SIZE, new StatsItemSet(TOPIC_PUT_SIZE, this.scheduledExecutorService, log));
        this.statsTable.put(GROUP_GET_NUMS, new StatsItemSet(GROUP_GET_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(GROUP_GET_SIZE, new StatsItemSet(GROUP_GET_SIZE, this.scheduledExecutorService, log));
        this.statsTable.put(GROUP_GET_LATENCY, new StatsItemSet(GROUP_GET_LATENCY, this.scheduledExecutorService, log));
        this.statsTable.put(SNDBCK_PUT_NUMS, new StatsItemSet(SNDBCK_PUT_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(BROKER_PUT_NUMS, new StatsItemSet(BROKER_PUT_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(BROKER_GET_NUMS, new StatsItemSet(BROKER_GET_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(GROUP_GET_FROM_DISK_NUMS, new StatsItemSet(GROUP_GET_FROM_DISK_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(GROUP_GET_FROM_DISK_SIZE, new StatsItemSet(GROUP_GET_FROM_DISK_SIZE, this.scheduledExecutorService, log));
        this.statsTable.put(BROKER_GET_FROM_DISK_NUMS, new StatsItemSet(BROKER_GET_FROM_DISK_NUMS, this.scheduledExecutorService, log));
        this.statsTable.put(BROKER_GET_FROM_DISK_SIZE, new StatsItemSet(BROKER_GET_FROM_DISK_SIZE, this.scheduledExecutorService, log));

        this.statsTable.put(COMMERCIAL_SEND_TIMES, new StatsItemSet(COMMERCIAL_SEND_TIMES, this.commercialExecutor, COMMERCIAL_LOG));
        this.statsTable.put(COMMERCIAL_RCV_TIMES, new StatsItemSet(COMMERCIAL_RCV_TIMES, this.commercialExecutor, COMMERCIAL_LOG));
        this.statsTable.put(COMMERCIAL_SEND_SIZE, new StatsItemSet(COMMERCIAL_SEND_SIZE, this.commercialExecutor, COMMERCIAL_LOG));
        this.statsTable.put(COMMERCIAL_RCV_SIZE, new StatsItemSet(COMMERCIAL_RCV_SIZE, this.commercialExecutor, COMMERCIAL_LOG));
        this.statsTable.put(COMMERCIAL_RCV_EPOLLS, new StatsItemSet(COMMERCIAL_RCV_EPOLLS, this.commercialExecutor, COMMERCIAL_LOG));
        this.statsTable.put(COMMERCIAL_SNDBCK_TIMES, new StatsItemSet(COMMERCIAL_SNDBCK_TIMES, this.commercialExecutor, COMMERCIAL_LOG));
        this.statsTable.put(COMMERCIAL_PERM_FAILURES, new StatsItemSet(COMMERCIAL_PERM_FAILURES, this.commercialExecutor, COMMERCIAL_LOG));
    }

    public MomentStatsItemSet getMomentStatsItemSetFallSize() {
        return momentStatsItemSetFallSize;
    }

    public MomentStatsItemSet getMomentStatsItemSetFallTime() {
        return momentStatsItemSetFallTime;
    }

    public void start() {
    }

    public void shutdown() {
        this.scheduledExecutorService.shutdown();
        this.commercialExecutor.shutdown();
    }

    // 获取指定统计名称下的统计key的统计条目
    public StatsItem getStatsItem(final String statsName, final String statsKey) {
        try {
            return this.statsTable.get(statsName).getStatsItem(statsKey);
        } catch (Exception e) {
        }

        return null;
    }

    // 删除指定topic的统计数据
    public void onTopicDeleted(final String topic) {
        this.statsTable.get(TOPIC_PUT_NUMS).delValue(topic);
        this.statsTable.get(TOPIC_PUT_SIZE).delValue(topic);
        if (enableQueueStat) {
            this.statsTable.get(QUEUE_PUT_NUMS).delValueByPrefixKey(topic, "@");
            this.statsTable.get(QUEUE_PUT_SIZE).delValueByPrefixKey(topic, "@");
        }
        this.statsTable.get(GROUP_GET_NUMS).delValueByPrefixKey(topic, "@");
        this.statsTable.get(GROUP_GET_SIZE).delValueByPrefixKey(topic, "@");
        this.statsTable.get(QUEUE_GET_NUMS).delValueByPrefixKey(topic, "@");
        this.statsTable.get(QUEUE_GET_SIZE).delValueByPrefixKey(topic, "@");
        this.statsTable.get(SNDBCK_PUT_NUMS).delValueByPrefixKey(topic, "@");
        this.statsTable.get(GROUP_GET_LATENCY).delValueByInfixKey(topic, "@");
        this.momentStatsItemSetFallSize.delValueByInfixKey(topic, "@");
        this.momentStatsItemSetFallTime.delValueByInfixKey(topic, "@");
    }

    // 根据group删除统计数据
    public void onGroupDeleted(final String group) {
        this.statsTable.get(GROUP_GET_NUMS).delValueBySuffixKey(group, "@");
        this.statsTable.get(GROUP_GET_SIZE).delValueBySuffixKey(group, "@");
        if (enableQueueStat) {
            this.statsTable.get(QUEUE_GET_NUMS).delValueBySuffixKey(group, "@");
            this.statsTable.get(QUEUE_GET_SIZE).delValueBySuffixKey(group, "@");
        }
        this.statsTable.get(SNDBCK_PUT_NUMS).delValueBySuffixKey(group, "@");
        this.statsTable.get(GROUP_GET_LATENCY).delValueBySuffixKey(group, "@");
        this.momentStatsItemSetFallSize.delValueBySuffixKey(group, "@");
        this.momentStatsItemSetFallTime.delValueBySuffixKey(group, "@");
    }

    // 增加队列的put统计数据1次
    public void incQueuePutNums(final String topic, final Integer queueId) {
        if (enableQueueStat) {
            this.statsTable.get(QUEUE_PUT_NUMS).addValue(buildStatsKey(topic, queueId), 1, 1);
        }
    }

    // 增加队列的put统计数据n次
    public void incQueuePutNums(final String topic, final Integer queueId, int num, int times) {
        if (enableQueueStat) {
            this.statsTable.get(QUEUE_PUT_NUMS).addValue(buildStatsKey(topic, queueId), num, times);
        }
    }
    // 增加队列的put统计数据大小
    public void incQueuePutSize(final String topic, final Integer queueId, final int size) {
        if (enableQueueStat) {
            this.statsTable.get(QUEUE_PUT_SIZE).addValue(buildStatsKey(topic, queueId), size, 1);
        }
    }

    // 增加队列的get统计数据1次
    public void incQueueGetNums(final String group, final String topic, final Integer queueId, final int incValue) {
        if (enableQueueStat) {
            final String statsKey = buildStatsKey(topic, queueId, group);
            this.statsTable.get(QUEUE_GET_NUMS).addValue(statsKey, incValue, 1);
        }
    }
    // 增加队列的get统计数据大小
    public void incQueueGetSize(final String group, final String topic, final Integer queueId, final int incValue) {
        if (enableQueueStat) {
            final String statsKey = buildStatsKey(topic, queueId, group);
            this.statsTable.get(QUEUE_GET_SIZE).addValue(statsKey, incValue, 1);
        }
    }

    // 增加topic的put统计数据1次
    public void incTopicPutNums(final String topic) {
        this.statsTable.get(TOPIC_PUT_NUMS).addValue(topic, 1, 1);
    }

    public void incTopicPutNums(final String topic, int num, int times) {
        this.statsTable.get(TOPIC_PUT_NUMS).addValue(topic, num, times);
    }

    public void incTopicPutSize(final String topic, final int size) {
        this.statsTable.get(TOPIC_PUT_SIZE).addValue(topic, size, 1);
    }

    // 增加group的get统计数据1次
    public void incGroupGetNums(final String group, final String topic, final int incValue) {
        final String statsKey = buildStatsKey(topic, group);
        this.statsTable.get(GROUP_GET_NUMS).addValue(statsKey, incValue, 1);
    }

    public String buildStatsKey(String topic, String group) {
        StringBuilder strBuilder;
        if (topic != null && group != null) {
            strBuilder = new StringBuilder(topic.length() + group.length() + 1);
        } else {
            strBuilder = new StringBuilder();
        }
        strBuilder.append(topic).append("@").append(group);
        return strBuilder.toString();
    }

    public String buildStatsKey(String topic, int queueId) {
        StringBuilder strBuilder;
        if (topic != null) {
            strBuilder = new StringBuilder(topic.length() + 5);
        } else {
            strBuilder = new StringBuilder();
        }
        strBuilder.append(topic).append("@").append(queueId);
        return strBuilder.toString();
    }

    public String buildStatsKey(String topic, int queueId, String group) {
        StringBuilder strBuilder;
        if (topic != null && group != null) {
            strBuilder = new StringBuilder(topic.length() + group.length() + 6);
        } else {
            strBuilder = new StringBuilder();
        }
        strBuilder.append(topic).append("@").append(queueId).append("@").append(group);
        return strBuilder.toString();
    }

    public String buildStatsKey(int queueId, String topic, String group) {
        StringBuilder strBuilder;
        if (topic != null && group != null) {
            strBuilder = new StringBuilder(topic.length() + group.length() + 6);
        } else {
            strBuilder = new StringBuilder();
        }
        strBuilder.append(queueId).append("@").append(topic).append("@").append(group);
        return strBuilder.toString();
    }

    public void incGroupGetSize(final String group, final String topic, final int incValue) {
        final String statsKey = buildStatsKey(topic, group);
        this.statsTable.get(GROUP_GET_SIZE).addValue(statsKey, incValue, 1);
    }

    // 增加group的get延迟统计数据1次
    public void incGroupGetLatency(final String group, final String topic, final int queueId, final int incValue) {
        String statsKey;
        if (enableQueueStat) {
            statsKey = buildStatsKey(queueId, topic, group);
        } else {
            statsKey = buildStatsKey(topic, group);
        }
        this.statsTable.get(GROUP_GET_LATENCY).addRTValue(statsKey, incValue, 1);
    }

    public void incBrokerPutNums() {
        this.statsTable.get(BROKER_PUT_NUMS).getAndCreateStatsItem(this.clusterName).getValue().add(1);
    }

    public void incBrokerPutNums(final int incValue) {
        this.statsTable.get(BROKER_PUT_NUMS).getAndCreateStatsItem(this.clusterName).getValue().add(incValue);
    }

    public void incBrokerGetNums(final int incValue) {
        this.statsTable.get(BROKER_GET_NUMS).getAndCreateStatsItem(this.clusterName).getValue().add(incValue);
    }

    public void incSendBackNums(final String group, final String topic) {
        final String statsKey = buildStatsKey(topic, group);
        this.statsTable.get(SNDBCK_PUT_NUMS).addValue(statsKey, 1, 1);
    }

    public double tpsGroupGetNums(final String group, final String topic) {
        final String statsKey = buildStatsKey(topic, group);
        return this.statsTable.get(GROUP_GET_NUMS).getStatsDataInMinute(statsKey).getTps();
    }

    public void recordDiskFallBehindTime(final String group, final String topic, final int queueId,
        final long fallBehind) {
        final String statsKey = buildStatsKey(queueId, topic, group);
        this.momentStatsItemSetFallTime.getAndCreateStatsItem(statsKey).getValue().set(fallBehind);
    }

    public void recordDiskFallBehindSize(final String group, final String topic, final int queueId,
        final long fallBehind) {
        final String statsKey = buildStatsKey(queueId, topic, group);
        this.momentStatsItemSetFallSize.getAndCreateStatsItem(statsKey).getValue().set(fallBehind);
    }

    public void incCommercialValue(final String key, final String owner, final String group,
        final String topic, final String type, final int incValue) {
        final String statsKey = buildCommercialStatsKey(owner, topic, group, type);
        this.statsTable.get(key).addValue(statsKey, incValue, 1);
    }

    public String buildCommercialStatsKey(String owner, String topic, String group, String type) {
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.append(owner);
        strBuilder.append("@");
        strBuilder.append(topic);
        strBuilder.append("@");
        strBuilder.append(group);
        strBuilder.append("@");
        strBuilder.append(type);
        return strBuilder.toString();
    }

    public enum StatsType {
        SEND_SUCCESS,
        SEND_FAILURE,
        SEND_BACK,
        SEND_TIMER,
        SEND_TRANSACTION,
        RCV_SUCCESS,
        RCV_EPOLLS,
        PERM_FAILURE
    }
}

// 统计条目数据集合
public class MomentStatsItemSet {
    // 核心数据结构:Map<统计条目key名称,统计条目>
    private final ConcurrentMap<String/* key */, MomentStatsItem> statsItemTable =
        new ConcurrentHashMap<String, MomentStatsItem>(128);

    // 统计名称
    private final String statsName;
    // 定时调度线程池
    private final ScheduledExecutorService scheduledExecutorService;
    private final InternalLogger log;

    public MomentStatsItemSet(String statsName, ScheduledExecutorService scheduledExecutorService, InternalLogger log) {
        this.statsName = statsName;
        this.scheduledExecutorService = scheduledExecutorService;
        this.log = log;
        this.init();
    }

    public ConcurrentMap<String, MomentStatsItem> getStatsItemTable() {
        return statsItemTable;
    }

    public String getStatsName() {
        return statsName;
    }

    public void init() {

        // 每隔5分钟打印一次
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    printAtMinutes();
                } catch (Throwable ignored) {
                }
            }
        }, Math.abs(UtilAll.computeNextMinutesTimeMillis() - System.currentTimeMillis()), 1000 * 60 * 5, TimeUnit.MILLISECONDS);
    }

    private void printAtMinutes() {
        // 迭代遍历,打印当前统计指标所有key对应的数据
        Iterator<Entry<String, MomentStatsItem>> it = this.statsItemTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, MomentStatsItem> next = it.next();
            next.getValue().printAtMinutes();
        }
    }

    // 对某一个统计指标对应的统计key,设置值
    public void setValue(final String statsKey, final int value) {
        MomentStatsItem statsItem = this.getAndCreateStatsItem(statsKey);
        statsItem.getValue().set(value);
    }

    // 删除统计指标对应的统计key
    public void delValueByInfixKey(final String statsKey, String separator) {
        Iterator<Entry<String, MomentStatsItem>> it = this.statsItemTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, MomentStatsItem> next = it.next();
            if (next.getKey().contains(separator + statsKey + separator)) {
                it.remove();
            }
        }
    }

    // 删除统计指标对应的统计key(指定了后缀)
    public void delValueBySuffixKey(final String statsKey, String separator) {
        Iterator<Entry<String, MomentStatsItem>> it = this.statsItemTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, MomentStatsItem> next = it.next();
            if (next.getKey().endsWith(separator + statsKey)) {
                it.remove();
            }
        }
    }

    // 获取统计指标对应的统计条目key对应的统计条目
    public MomentStatsItem getAndCreateStatsItem(final String statsKey) {
        MomentStatsItem statsItem = this.statsItemTable.get(statsKey);
        if (null == statsItem) {
            statsItem =
                new MomentStatsItem(this.statsName, statsKey, this.scheduledExecutorService, this.log);
            MomentStatsItem prev = this.statsItemTable.putIfAbsent(statsKey, statsItem);

            if (null != prev) {
                statsItem = prev;
                // statsItem.init();
            }
        }

        return statsItem;
    }
}

// 统计条目
public class MomentStatsItem {

    // 指标值
    private final AtomicLong value = new AtomicLong(0);
    // 统计指标名称
    private final String statsName;
    // 统计条目key
    private final String statsKey;
    private final ScheduledExecutorService scheduledExecutorService;
    private final InternalLogger log;

    public MomentStatsItem(String statsName, String statsKey,
        ScheduledExecutorService scheduledExecutorService, InternalLogger log) {
        this.statsName = statsName;
        this.statsKey = statsKey;
        this.scheduledExecutorService = scheduledExecutorService;
        this.log = log;
    }

    public void init() {
        // 每5分钟打印一次
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    printAtMinutes();

                    MomentStatsItem.this.value.set(0);
                } catch (Throwable e) {
                }
            }
        }, Math.abs(UtilAll.computeNextMinutesTimeMillis() - System.currentTimeMillis()), 1000 * 60 * 5, TimeUnit.MILLISECONDS);
    }

    public void printAtMinutes() {
        log.info(String.format("[%s] [%s] Stats Every 5 Minutes, Value: %d",
            this.statsName,
            this.statsKey,
            this.value.get()));
    }

    public AtomicLong getValue() {
        return value;
    }

    public String getStatsKey() {
        return statsKey;
    }

    public String getStatsName() {
        return statsName;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1310141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode—108.将有序数组转换为二叉搜索树【简单】

2023每日刷题&#xff08;五十八&#xff09; Leetcode—108.将有序数组转换为二叉搜索树 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ struct TreeNode* dfs(int …

Halcon 模板匹配基于相关性

文章目录 基于相关性使用匹配助手 基于相关性 适用场景 模板匹配&#xff1a;当你需要在图像中找到一个已知的模板时&#xff0c;例如在工业生产线上检测产品的特定标识或零件的特征时&#xff0c;相关性匹配是一种简单而有效的方法。实时应用&#xff1a;相关性匹配通常具有较…

scratch认识图形 2023年12月中国电子学会 图形化编程 scratch编程等级考试二级真题和答案解析

目录 scratch认识图形 一、题目要求 1、准备工作 2、功能实现 二、案例分析

(0-1)分布

假设离散型随机变量X只可能取到0、1两个值&#xff0c;它的分布律为&#xff1a; &#xff0c;其中&#xff0c; 那么称X服从参数为p的0-1分布&#xff0c;也叫两点分布。 其实上面公式就是将下面两个式子写在一起&#xff1a;

【RTOS学习】任务创建 | 任务启动 | 任务切换 | 任务暂停和恢复 | 任务阻塞和唤醒 | 临界资源保护

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f30f;任务创建&#x1f9ed;TCB和栈&#x1f9ed;伪造现场&#x1f9ed;链表操作 &am…

solidity 特性导致的漏洞

目录 1、默认可见性 2、浮点数精度缺失 3、错误的构造函数 4、自毁函数 5、未初始化指针-状态变量覆盖 1、默认可见性 Solidity 的函数和状态变量有四种可见性&#xff1a;external、public、internal、private。函数可见性默认为 public&#xff0c;状态变量可见性默认为…

51单片机控制1602LCD显示屏输出自定义字符二

51单片机控制1602LCD显示屏输出自定义字符二 1.概述 1602LCD除了内置的字符外还提供自定义字符功能&#xff0c;当内置的字符中没有我们想要输出的字符时&#xff0c;我们就可以自己创造字符让他显示&#xff0c;下面介绍1602如何创建自定义字符。 2.1602LCD创建字符原理 自…

2023 re:Invent使用 PartyRock 和 Amazon Bedrock 安全高效构建 AI 应用程序

前言 本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 “Your Data, Your AI, Your Future.&#xff08;你的数据&#xff0c;你的AI&…

利用Microsoft Visual Studio Installer Projects打包安装包

利用Microsoft Visual Studio Installer Projects打包安装包 具体步骤步骤1&#xff1a;安装扩展步骤2&#xff1a;创建 Setup 项目步骤3&#xff1a;设置属性步骤4&#xff1a;添加输出步骤5&#xff1a;添加文件步骤6&#xff1a;添加桌面快捷方式步骤7&#xff1a;添加菜单快…

使用Pytorch从零开始构建StyleGAN

本文介绍的是当今最好的 GAN 之一&#xff0c;来自论文《A Style-Based Generator Architecture for Generative Adversarial Networks》的 StyleGAN &#xff0c;我们将使用 PyTorch 对其进行干净、简单且可读的实现&#xff0c;并尝试尽可能接近原始论文。 如果您没有阅读过…

如何实现电脑文件夹自动备份?以下是图解教程

在当今迅猛发展的科技时代&#xff0c;电脑已经成为不可或缺的办公工具。随着使用时间的增加&#xff0c;存储在电脑中的文件数量也逐渐增多。然而&#xff0c;由于设备故障、手动误删等原因&#xff0c;文件的丢失问题成为一个不可忽视的风险。如果丢失的文件具有重要性&#…

文字转语音自动合成系统源码:让你的语音自动转成文字 附带完整的搭建教程

人工智能技术的不断发展&#xff0c;语音识别和自然语言处理技术已经逐渐成熟。文字转语音自动合成系统就是结合了这两项技术&#xff0c;将文字信息转化为语音输出&#xff0c;为用户提供更加便捷、高效的信息获取方式。这种系统在语音助手、智能客服、教育学习等领域有着广泛…

54 代码审计-TP5框架审计写法分析及代码追踪

目录 知识点1知识点2演示案例:demo代码段自写和规则写分析hsycms-TP框架-不安全写法-未过滤weipan21-TP框架-规则写法-内置过滤 知识点1 调试&#xff0c;访问&#xff0c;路由&#xff0c;配置&#xff0c;版本等 知识点2 自写写法&#xff1a;自己写代码&#xff0c;一步步…

PyQt6 简单介绍与安装

前文&#xff0c;参考文章&#xff1a; 参考文章一 参考文章二 PyQt6 简单介绍与安装 1、简单介绍2、PyQt6安装3、PyQt6版本查看4、PyQt6模块4.1 界面承载部分( 控件 )4.2 界面框架部分&#xff08;布局&#xff09;4.3 界面组件部分&#xff08;其实也是Widget类&#xff0…

Json数据报文解析-Gson库-JsonObject类-JsonParse类-JsonArray类

一、前言 本文我们将介绍如何解析Json数据&#xff0c;主要通过Gson库中的相关类来实现。 二、详细步骤 首先&#xff0c;我们要拿到一个基础的Json数据&#xff0c;这里将以下面的Json数据作为示例&#xff1a; {"code":"1","msg":"ok&q…

电脑监控软件丨老板的“管理神器”?员工的“噩梦伊始”?

不得不承认&#xff0c;老板们都很喜欢用电脑监控软件来管控员工的工作情况。但是面对这个话题&#xff0c;他们却又有不一样的感受。 老板的“管理神器”&#xff1f;——首先来说老板 不得不说&#xff0c;老板确实很喜欢用域之盾软件--电脑管控功能https://www.yuzhidun.cn…

mfc140u.dll丢失的解决方法的详细介绍,六种解决mfc140u.dll丢失的方法

今天的这篇文章将向各位分享一个有关电脑出现关于丢失mfc140u.dll错误的弹窗问题&#xff0c;这是一个很常见的问题。无论你是一名大学生还是其他身份&#xff0c;都可能会遇到这个问题。下面我会对mfc140u.dll丢失的解决方法进行详细的介绍。 一.六种解决mfc140u.dll丢失的方法…

浪潮信息大突破:全面开源1026亿参数模型源2.0

近日&#xff0c;浪潮信息发布了一项重大成就&#xff0c;宣布全面开源其1026亿参数的基础大模型——源2.0。该举措在AI产业界引起了广泛关注&#xff0c;被视为推动生成式人工智能产业快速发展的关键一步。 源2.0模型概览 源2.0是一个多参数级别的大模型&#xff0c;提供了1…

‘BLEUUID‘ does not name a type错误怎么解决?

摘要&#xff1a;arduino环境下对esp32蓝牙编程时会遇到BLEUUID does not name a type错误&#xff0c;本文介绍解决方法。 硬件设备是安信可ESP32-S模组。 错误发生在代码最开始的地方&#xff0c;include了一个蓝牙设备头文件&#xff0c;然后定义了UUID&#xff0c;注意看&a…

Conda 使用教程大全来啦

什么是 Conda&#xff1f; Conda 是一款功能强大的软件包管理器和环境管理器&#xff0c;您可以在 Windows 的 Anaconda 提示符或 macOS 或 Linux 的终端窗口中使用命令行命令 Conda 可以快速安装、运行和更新软件包及相关依赖项。Conda 可以在本地计算机上创建、保存、加载和…