文章
问答
冒泡
微服务组件-健康检查

背景

微服务架构,为了保证所有服务可用,当服务发生问题时能及时摘除有问题的服务需要定期检测服务可用性,即健康检查。通常健康健康检查包括TCP与HTTP两种。即定时发送TCP或HTTP请求,根据响应来确定服务是否可用。

总体架构

健康检查目标

a) 启动健康检查, 在发布过程中程序启动时候查看是否已满足启动检查需要,如果不满足, 使用b的方式暴露http接口提供给开发去检查

b) 运行时健康检查, 检查各个组件是否正常提供服务,可以分islive,readiness

健康状态检查方式

a) 暴露http、tcp端口,定期pull接口,通过http code、http body 查看服务状态,不同的endpoint查询不同的信息, 可以参考actuator, 需要开启web, 或者div自己配置下, 需要固定端口,普罗米修斯批量监控指标, 配置grafana监控告警,随机端口需要注册可以使用zk,ectd监听

b) 采用push的方式, 每分钟通过UDP上报数据到收集端(需要有个收集服务,落es等, 在grafana上查看和监控)

1573490902608-f44b8e32-14e7-42d7-b9dd-73965a9af591.png

接口实现

检查接口

public interface HealthChecker {

    /**
     * HealthCheck information.
     * @return
     */
    Health isHealthy();

    /**
     * HealthChecker name
     * @return
     */
    String getComponentName();

    /**
     * The number of retries after failure.
     * @return
     */
    default int getRetryCount() {
        return 0;
    }

    /**
     * The time interval for each retry after failure.
     * @return
     */
    default long getRetryTimeInterval(){
        return 0;
    }

    /**
     * Is it strictly checked?
     * If true, the final check result of isHealthy() is used as the result of the component's check.
     * If false, the final result of the component is considered to be healthy, but the exception log is printed.
     * @return
     */
    default boolean isStrictCheck() {
        return true;
    }
}

组件检查

public class ComponentHealthChecker implements HealthChecker {
   ...

    private SofaRuntimeContext sofaRuntimeContext;

    public ComponentHealthChecker(SofaRuntimeContext sofaRuntimeContext) {
        this.sofaRuntimeContext = sofaRuntimeContext;
    }

    @Override
    public Health isHealthy() {
        boolean allPassed = true;
        Health.Builder builder = new Health.Builder();
        for (ComponentInfo componentInfo : sofaRuntimeContext.getComponentManager().getComponents()) {
            HealthResult healthy = componentInfo.isHealthy();
            if (healthy.isHealthy()) {
                builder.withDetail(healthy.getHealthName(), "passed");
            } else {
                builder.withDetail(healthy.getHealthName(), healthy.getHealthReport());
                allPassed = false;
            }
        }

        if (allPassed) {
            return builder.status(Status.UP).build();
        } else {
            return builder.status(Status.DOWN).build();
        }
    }

    ...
}

模块检查

public class ModuleHealthChecker implements ApplicationContextAware, HealthChecker {

 ...

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public Health isHealthy() {
        Health.Builder builder = new Health.Builder();
        ApplicationRuntimeModel application = applicationContext.getBean(
            SofaBootConstants.APPLICATION, ApplicationRuntimeModel.class);

        for (DeploymentDescriptor deploymentDescriptor : application.getInstalled()) {
            builder.withDetail(deploymentDescriptor.getName(), "passed");
        }

        for (DeploymentDescriptor deploymentDescriptor : application.getAllInactiveDeployments()) {
            builder.withDetail(deploymentDescriptor.getName(), "inactive");
        }

        for (DeploymentDescriptor deploymentDescriptor : application.getFailed()) {
            builder.withDetail(deploymentDescriptor.getName(), "failed");
        }

        if (application.getFailed().size() == 0) {
            return builder.status(Status.UP).build();
        } else {
            return builder.status(Status.DOWN).build();
        }
    }

   ...
}

检查指示器

通过AbstractHealthIndicator 继承此类,重写方法实现检查逻辑

1573492188192-aa7f5c23-2ed9-498c-b64b-2960e1c8106a.png

@Override
public final Health health() {
	Health.Builder builder = new Health.Builder();
	try {
		doHealthCheck(builder);
	}
	catch (Exception ex) {
		if (this.logger.isWarnEnabled()) {
			String message = this.healthCheckFailedMessage.apply(ex);
			this.logger.warn(StringUtils.hasText(message) ? message : DEFAULT_MESSAGE,ex);
     }
	builder.down(ex);
	}
	return builder.build();
}

/**
* Actual health check logic.
* @param builder the {@link Builder} to report health status and details
* @throws Exception any {@link Exception} that should create a {@link Status#DOWN}
* system status.
*/
protected abstract void doHealthCheck(Health.Builder builder) throws Exception;

关注点

Http健康检查

一般通过TCP定期请求来判定网络层是否正常,而通过Http请求判断应用层是否正常。

Http健康检查检查通常涉及如下几个方面

检测接口

服务要配置好请求接口,检测服务定期向指定的接口发送http请求,并根据接口响应码和响应时间判断。这个响应时间一般根据业务特性来配置。

检测周期

发起检测时每隔一定周期就需要检测一次。可以是几百毫秒或几秒,实际情况根据业务来确定。当连续几个周期内响应异常,那么尝试标记节点状态为异常,并临时摘除。当后续检测正常后再加入的可用节点集合中。

超时时间

为接口设定一个超时时间,每个周期检测时超过这个时间说明有异常。

状态码

实际检测过程过关心的是节点是否可用,因此关注的是响应状态,具体响应内容并不关心。可以通过Http响应码来判断状态。对于Http检测接口来说2xx和3xx可视为检查正常。其它状态码认为节点异常。

异常情况

当出现Http连接异常等问题,那么就直接可断定节点不可用。

总结

通过Http健康检查保证及时发现应用层问题,及时摘除异常节点,保证服务正常运行。

参考资料

SofaBoot


关于作者

silen
三流码农
获得点赞
文章被阅读