如果注册中心为nacos,怎样将springcloud项目中的所有注册服务的健康状态进行监控,并在不健康时发送邮件?

发布于 2022-09-13 00:16:25 字数 6325 浏览 17 评论 0

假设springcloud项目中服务有service-a,serivce-b,service-c,
并使用nacos做为注册中心,那么有什么办法可以监测所有服务的健康状态并在健康状态DOWN时发送邮件预警?

后脑勺当初想的是,将所有注册服务作为一个常量类管理或者通过zuul,getway配置文件拿到相关注册服务的路径/名称等并统一设置actuator,并对这些服务设定定时任务获取/health接口status状态.
但是这样做的问题可能会对相关服务造成影响,毕竟定时任务和接口调用如果想及时,也会对原本的服务做了一次浪费的接口调用,而且当扩展到了service-abcd-1112等这样有更多的服务注册到nacos后,对这些接口的调用/health以及状态判断就有些资源浪费.

所以最好的方式还是通过nacos自身提供的相关API或者接口重写能实现这个功能就最好了.
当然这是最好的,因为本来nacos就和eureka等注册中心会向注册的服务发送心跳检测,检测服务是否存活或者健康,但是没有发邮件这一说嘛.
所以就在相关nacos或eureka发送心跳检测的同时,在判断健康状态的那个逻辑中加入邮件发送的服务就应该可以实现.

后来查了下有相关的nacos里的Task类中设定了健康检查,ClientBeatCheckTask

服务端接受到客户端的服务注册请求后,在创建空的Service后,就会开启健康检查任务,代码在com.alibaba.nacos.naming.core.ServiceManager#putServiceAndInit:

上面Init方法中的包含的service.init()方法中的HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
是对应开启健康检查任务的代码.

private void putServiceAndInit(Service service) throws NacosException {
    putService(service);
    //初始化service,会开启健康检查任务
    service.init();
    consistencyService
            .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
    consistencyService
            .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
    Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
}

public void init() {
    //健康检测任务
    HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
    for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
        entry.getValue().setService(this);
        entry.getValue().init();
    }
}

实际nacos代码在这一行:
上![image.png
不过init()方法好像在新版本中去除了,搜了下1.21版本的还存在
image.png
并且这个是在Test测试类里.

而最新的issue也是2021年4月份的
image.png

而实际的类在这里:(不知道为什么github搜没有直接先展示这个类)
https://github.com/alibaba/na...

而其实在这个healthcheck包下的都与健康检测有关:
https://github.com/zmyouknow/...
image.png

具体:
image.png
HTTP检查,checkFail会报错
image.png
MYSQL检查,checkFail会捕获
那如果数据库是Oracle怎么办呢?答,可能要写一个OracleHealthCheckProcessor

继续查看两个类判断时用到的HealthCheckCommon
https://github.com/zmyouknow/...
那这里面的
checkFile(..)
checkFileNow(...)
具体有何区别?

    /**
     * Health check fail, when instance check failed count more than max failed time, set unhealthy.
     *
     * @param ip   instance
     * @param task health check task
     * @param msg  message
     */
    public void checkFail(Instance ip, HealthCheckTask task, String msg) {

一个是当实例检查统计超过最大设定的failed time,设置unhealthy

    /**
     * Health check fail, set instance unhealthy directly.
     *
     * @param ip   instance
     * @param task health check task
     * @param msg  message
     */
    public void checkFailNow(Instance ip, HealthCheckTask task, String msg) {

而这个是直接设定该实例unhealthy

可以看到checkFileNow有3处引用
!image.png
checkFile暂时没有点开,无法看到
checkOk也有4处引用

初始化健康检查的代码,看起来像用全局的线程进行同步执行一个post请求

    private static LinkedBlockingDeque<HealthCheckResult> healthCheckResults = new LinkedBlockingDeque<>(1024 * 128);
    
    /**
     * Init Health check.
     */
    public void init() {
        GlobalExecutor.scheduleNamingHealthCheck(() -> {
            List list = Arrays.asList(healthCheckResults.toArray());
            healthCheckResults.clear();
            
            Collection<Member> sameSiteServers = memberManager.allMembers();
            
            if (sameSiteServers == null || sameSiteServers.size() <= 0) {
                return;
            }
            
            for (Member server : sameSiteServers) {
                if (server.getAddress().equals(NetUtils.localServer())) {
                    continue;
                }
                Map<String, String> params = new HashMap<>(10);
                params.put("result", JacksonUtils.toJson(list));
                if (Loggers.SRV_LOG.isDebugEnabled()) {
                    Loggers.SRV_LOG.debug("[HEALTH-SYNC] server: {}, healthCheckResults: {}", server,
                            JacksonUtils.toJson(list));
                }
                
                RestResult<String> httpResult = HttpClient.httpPost(
                        "http://" + server.getAddress() + EnvUtil.getContextPath()
                                + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/api/healthCheckResult", null, params);
                
                if (!httpResult.ok()) {
                    Loggers.EVT_LOG.warn("[HEALTH-CHECK-SYNC] failed to send result to {}, result: {}", server,
                            JacksonUtils.toJson(list));
                }
                
            }
            
        }, 500, TimeUnit.MILLISECONDS);
    }

时间间隔是500毫秒

虽然没有执行相关代码
如果将nacos的健康检查加入健康检查unhealthy时发送邮件,
那大致的方式就是在
https://github.com/zmyouknow/...
的checkFail,checkFailNow重写,加入邮件发送,如果数据库不同就写一个类继承Mysql Health...实现的process接口,并配置初始化?即可。

也希望思否的大佬们提供建议!谢谢咯!

虽然还没有实践,但是健康检测的原理其实很简单,就是看请求有没有超时,从http请求到sql请求,超时超过最大限制时长则记录为unhealthy。
不过没有发现这里和actuator端点检测有什么区别。


上面有个代码贴的版本低了,其实有了v2的相关代码。
也许做一个针对该checkFail方法的切面就更简单易行。

查看资料:
https://blog.csdn.net/jb84006...
github nacos源码

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文