sentinel心跳机制

sentinel心跳

sentinel分为客户端client和监控面板dashboard。client端启动后,需要向dashboard上报心跳。基础概念就不做过多介绍,不清楚的,可以网上参考其他资料。整理主要介绍心跳是如何上报的,如何扩展心跳报文内容?

心跳过程

心跳入口

心跳的入口,在HeartbeatSenderInitFunc.java中,这个类会被初始化方法加载,并执行init方法。过程可参照 sentinel框架基于SPI机制的二次开发 这篇文章。

// SPI机制中的加载顺序
@InitOrder(-1)
public class HeartbeatSenderInitFunc implements InitFunc {

    // 心跳任务线程池
    private ScheduledExecutorService pool = null;

    // 初始化线程池
    private void initSchedulerIfNeeded() {
        if (pool == null) {
            pool = new ScheduledThreadPoolExecutor(2,
                new NamedThreadFactory("sentinel-heartbeat-send-task", true),
                new DiscardOldestPolicy());
        }
    }

    // 入口方法
    @Override
    public void init() {
        // 创建心跳发送实例。这是一个扩展点,可以扩展出自己的心跳发送器。具体往下看
        HeartbeatSender sender = HeartbeatSenderProvider.getHeartbeatSender();
        // 未获取到发送器,报错
        if (sender == null) {
            RecordLog.warn("[HeartbeatSenderInitFunc] WARN: No HeartbeatSender loaded");
            return;
        }

        // 初始化心跳任务线程池
        initSchedulerIfNeeded();
        // 获取心跳间隔
        long interval = retrieveInterval(sender);
        // 将心跳间隔存储到系统变量中
        setIntervalIfNotExists(interval);
        // 创建心跳任务
        scheduleHeartbeatTask(sender, interval);
    }

    private boolean isValidHeartbeatInterval(Long interval) {
        return interval != null && interval > 0;
    }

    /**
    * 将心跳间隔存储到系统变量中
    */
    private void setIntervalIfNotExists(long interval) {
        SentinelConfig.setConfig(TransportConfig.HEARTBEAT_INTERVAL_MS, String.valueOf(interval));
    }

    /**
    * 获取心跳间隔
    */
    long retrieveInterval(/*@NonNull*/ HeartbeatSender sender) {
        // 从系统变量中获取
        // 此处是扩展点,可在配置文件中配置心跳间隔时间,则直接使用配置的时间
        // csp.sentinel.heartbeat.interval.ms
        Long intervalInConfig = TransportConfig.getHeartbeatIntervalMs();
        // 如果配置了心跳间隔,并且是符合条件的心跳间隔,则使用配置信息
        if (isValidHeartbeatInterval(intervalInConfig)) {
            RecordLog.info("[HeartbeatSenderInitFunc] Using heartbeat interval "
                + "in Sentinel config property: " + intervalInConfig);
            return intervalInConfig;
        } else {
            // 否则,从发送器中获取默认心跳间隔
            long senderInterval = sender.intervalMs();
            RecordLog.info("[HeartbeatSenderInit] Heartbeat interval not configured in "
                + "config property or invalid, using sender default: " + senderInterval);
            return senderInterval;
        }
    }

    /**
    * 创建心跳任务
    */
    private void scheduleHeartbeatTask(/*@NonNull*/ final HeartbeatSender sender, /*@Valid*/ long interval) {
        pool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    sender.sendHeartbeat();
                } catch (Throwable e) {
                    RecordLog.warn("[HeartbeatSender] Send heartbeat error", e);
                }
            }
        }, 5000, interval, TimeUnit.MILLISECONDS);
        RecordLog.info("[HeartbeatSenderInit] HeartbeatSender started: "
            + sender.getClass().getCanonicalName());
    }
}
心跳消息

sentinel原生支持http形式的心跳发送方式。实现类SimpleHttpHeartbeatSender。核心代码如下:

该实现类没有设置顺便注解,默认优先级最低。

public class SimpleHttpHeartbeatSender implements HeartbeatSender {

    private static final int OK_STATUS = 200;
    private static final long DEFAULT_INTERVAL = 1000 * 10;
    private final HeartbeatMessage heartBeat = new HeartbeatMessage();
    private final SimpleHttpClient httpClient = new SimpleHttpClient();

    @Override
    public boolean sendHeartbeat() throws Exception {
        ... ...

        // 获取心跳发送地址
        InetSocketAddress addr = new InetSocketAddress(addrInfo.r1, addrInfo.r2);
        // 创建心跳请求
        SimpleHttpRequest request = new SimpleHttpRequest(addr, TransportConfig.getHeartbeatApiPath());
        // 设置心跳参数
        request.setParams(heartBeat.generateCurrentMessage());
        try {
            // 发送心跳
            SimpleHttpResponse response = httpClient.post(request);
            if (response.getStatusCode() == OK_STATUS) {
                return true;
            } else if (clientErrorCode(response.getStatusCode()) || serverErrorCode(response.getStatusCode())) {
                RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addr
                    + ", http status code: " + response.getStatusCode());
            }
        } catch (Exception e) {
            RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addr, e);
        }
        return false;
    }

    /**
    * 获取心跳间隔
    */
    @Override
    public long intervalMs() {
        return DEFAULT_INTERVAL;
    }
    
    ... ...

}

关心的问题就在于心跳消息。

heartBeat.generateCurrentMessage()

看这个方法:

public class HeartbeatMessage {

    private final Map<String, String> message = new HashMap<String, String>();

    /**
    * 默认心跳参数
    */
    public HeartbeatMessage() {
        message.put("hostname", HostNameUtil.getHostName());
        message.put("ip", TransportConfig.getHeartbeatClientIp());
        message.put("app", AppNameUtil.getAppName());
        // Put application type (since 1.6.0).
        message.put("app_type", String.valueOf(SentinelConfig.getAppType()));
        message.put("port", String.valueOf(TransportConfig.getPort()));
    }

    /**
    * 添加自定义参数,可通过此方法扩展心跳消息参数
    */
    public HeartbeatMessage registerInformation(String key, String value) {
        message.put(key, value);
        return this;
    }

    /**
    * 附加参数
    */
    public Map<String, String> generateCurrentMessage() {
        // Version of Sentinel.
        message.put("v", Constants.SENTINEL_VERSION);
        // Actually timestamp.
        message.put("version", String.valueOf(TimeUtil.currentTimeMillis()));
        message.put("port", String.valueOf(TransportConfig.getPort()));
        return message;
    }
}

心跳扩展

发送器扩展

上面说到的发送器扩展,可以具体看心跳包的provider

public final class HeartbeatSenderProvider {

    private static HeartbeatSender heartbeatSender = null;

    static {
        // 静态方法,类加载时,即执行
        resolveInstance();
    }

    /**
    * 解析发送器
    */
    private static void resolveInstance() {
        // 通过SPI获取级别最高的实现类。即@InitOrder标签设置的顺序
        HeartbeatSender resolved = SpiLoader.loadHighestPriorityInstance(HeartbeatSender.class);
        if (resolved == null) {
            RecordLog.warn("[HeartbeatSenderProvider] WARN: No existing HeartbeatSender found");
        } else {
            heartbeatSender = resolved;
            RecordLog.info("[HeartbeatSenderProvider] HeartbeatSender activated: " + resolved.getClass()
                .getCanonicalName());
        }
    }

    /**
     * Get resolved {@link HeartbeatSender} instance.
     *
     * @return resolved {@code HeartbeatSender} instance
     */
    public static HeartbeatSender getHeartbeatSender() {
        return heartbeatSender;
    }

    private HeartbeatSenderProvider() {}
}

如果需要扩展,则可以自行实现HeartbeatSender接口,并设置@InitOrder(Integer.MIN_VALUE),这边加载最新的发送器时,就会将顺序最小的加载出来。比如,如果项目支持rpc调用,则可以不适用http方式来发送心跳,而是直接使用rpc接口调用来注册心跳。示例代码如下:

@InitOrder(Integer.MIN_VALUE)
public class MyDemoHeartbeatSender implements HeartbeatSender {

    private static final int OK_STATUS = 200;

    // 默认的心跳间隔
    private static final long DEFAULT_INTERVAL = 1000 * 10;

    public MyDemoHeartbeatSender() {
    }

    @Override
    public boolean sendHeartbeat() throws Exception {
        // todo your heartbeat send code
        return false;
    }

    @Override
    public long intervalMs() {
        return DEFAULT_INTERVAL;
    }

}
心跳包扩展

通过心跳消息的自定义参数方法扩展心跳包。

private final HeartbeatMessage heartBeat = new HeartbeatMessage();

heartBeat.registerInformation("key","value");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值