Nacos 2.0源码分析-拦截器机制
阅读原文时间:2021年07月27日阅读:1

温馨提示:

本文内容基于个人学习Nacos 2.0.1版本代码总结而来,因个人理解差异,不保证完全正确。如有理解错误之处欢迎各位拍砖指正,相互学习;转载请注明出处。

Nacos服务端在处理健康检查和心跳检查任务的时候它是使用拦截器链来执行的。拦截器链内部有多个拦截器,通过获取不同的拦截器链实例,在实例内部指定具体的拦截器类型来组成一组拦截器。这里使用了拦截器模式和模板模式来组织代码。拦截器模式体现在整体拦截机制的实现;模板模式主要体现在对拦截器链的抽象实现上。

拦截器模式有三个要素

  • 拦截器
  • 调度者
  • 业务逻辑

定义一个拦截器的基本功能,同时限定了传入的拦截对象类型必须为Interceptable。这里只定义了基本的功能和基本的限定拦截对象。这里将其描述为基本的功能,那就意味着它的实现将会有更高级的功能。

/**
 * Nacos naming interceptor.
 * 拦截器对象
 * @author xiweng.yy
 */
public interface NacosNamingInterceptor<T extends Interceptable> {

    /**
     * Judge whether the input type is intercepted by this Interceptor.
     * 此拦截器的实例将会判断传入的对象是否是他需要处理的类型,此方法可以实现不同拦截器处理不同对象的隔离操作
     * <p>This method only should judge the object type whether need be do intercept. Not the intercept logic.
     * @param type type
     * @return true if the input type is intercepted by this Interceptor, otherwise false
     */
    boolean isInterceptType(Class<?> type);

    /**
     * Do intercept operation.
     * 执行拦截操作
     * <p>This method is the actual intercept operation.
     * @param object need intercepted object
     * @return true if object is intercepted, otherwise false
     */
    boolean intercept(T object);

    /**
     * The order of interceptor. The lower the number, the earlier the execution.
     * 拦截器排序,数字越低,优先级越高
     * @return the order number of interceptor
     */
    int order();
}

Interceptable 定义了对拦截操作相关的执行方法,passIntercept()在未被拦截的时候需要执行,afterIntercept()在被拦截之后需要执行。被拦截对象的业务逻辑需要由拦截器负责调度。

/**
 * Interceptable Interface.
 *
 * @author xiweng.yy
 */
public interface Interceptable {

    /**
     * If no {@link NacosNamingInterceptor} intercept this object, this method will be called to execute.
     */
    void passIntercept();

    /**
     * If one {@link NacosNamingInterceptor} intercept this object, this method will be called.
     */
    void afterIntercept();
}

调度者主要是用来管理拦截器的组织方式,触发拦截器的拦截操作。下图展示了Naming模块的拦截器链的继承关系。

整体的构成由NacosNamingInterceptorChain定义基本框架,AbstractNamingInterceptorChain实现通用逻辑,HealthCheckInterceptorChainInstanceBeatCheckTaskInterceptorChain则分别服务于健康检查和心跳检查。

NacosNamingInterceptorChain

定义了拦截器链对象应该具有的基本行为:添加拦截器、执行拦截器。

/**
 * Nacos naming interceptor chain.
 * Nacos Naming模块的拦截器链接口,拦截器链用于存储并管理多个拦截器
 * @author xiweng.yy
 */
public interface NacosNamingInterceptorChain<T extends Interceptable> {

    /**
     * Add interceptor.
     * 添加指定类型的拦截器对象
     * @param interceptor interceptor
     */
    void addInterceptor(NacosNamingInterceptor<T> interceptor);

    /**
     * Do intercept by added interceptors.
     * 执行拦截的业务操作
     * @param object be interceptor object
     */
    void doInterceptor(T object);
}

AbstractNamingInterceptorChain

AbstractNamingInterceptorChain实现了NacosNamingInterceptorChain所定义的对NacosNamingInterceptor的操作。在构造方法中提供了具体的拦截器实现类的加载,它这里使用了SPI方式加载。默认可以加载的拦截器必须是NacosNamingInterceptor的实例。在拦截器的执行方法doInterceptor()中会按优先级调用每一个拦截器,首先判断被拦截的对象是否是此拦截器处理,接着调用拦截器的intercept()方法,成功后调用被拦截对象的afterIntercept()方法。若未拦截成功则调用被拦截对象的passIntercept()方法。因此在拦截器中的intercept()方法中可以定义拦截器对被拦截对象的处理逻辑,而被拦截对象则可以在afterIntercept()和passIntercept()方法中定义自身的处理逻辑。从而实现在拦截器中被处理和自身处理任务依赖于拦截器来触发。

/**
 * Abstract Naming Interceptor Chain.
 * 抽象的命名服务拦截器链,用于定义拦截器链的工作流程
 * @author xiweng.yy
 */
public abstract class AbstractNamingInterceptorChain<T extends Interceptable> implements NacosNamingInterceptorChain<T> {

    // 存储多个拦截器
    private final List<NacosNamingInterceptor<T>> interceptors;

    // 限制使用范围为当前包或者其子类
    protected AbstractNamingInterceptorChain(Class<? extends NacosNamingInterceptor<T>> clazz) {
        this.interceptors = new LinkedList<>();
        // 使用SPI模式加载指定的拦截器类型
        // 而且NacosNamingInterceptor内部有判断它需要拦截对象的类型,因此非常灵活
        interceptors.addAll(NacosServiceLoader.load(clazz));
        // 对拦截器的顺序进行排序
        interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
    }

    /**
     * Get all interceptors.
     *
     * @return interceptors list
     */
    protected List<NacosNamingInterceptor<T>> getInterceptors() {
        return interceptors;
    }

    @Override
    public void addInterceptor(NacosNamingInterceptor<T> interceptor) {
        // 若手动添加,则需要再次进行排序
        interceptors.add(interceptor);
        interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
    }

    @Override
    public void doInterceptor(T object) {
        // 因为内部的拦截器已经排序过了,所以直接遍历
        for (NacosNamingInterceptor<T> each : interceptors) {
            // 若当前拦截的对象不是当前拦截器所要处理的类型则调过
            if (!each.isInterceptType(object.getClass())) {
                continue;
            }
            // 执行拦截操作成功之后,继续执行拦截后操作
            if (each.intercept(object)) {
                object.afterIntercept();
                return;
            }
        }
        // 未拦截的操作
        object.passIntercept();
    }
}

doInterceptor() 方法中使用当前拦截器链内部的所有拦截器对被拦截对象进行处理,并且组织了被拦截对象被拦截之后的方法调用流程。即:拦截之后执行被拦截对象的afterIntercept()方法,未拦截时执行passIntercept()方法。

HealthCheckInterceptorChain

健康检查拦截器链负责加载AbstractHealthCheckInterceptor类型的拦截器。

/**
 * Health check interceptor chain.
 * @author xiweng.yy
 */
public class HealthCheckInterceptorChain extends AbstractNamingInterceptorChain<NacosHealthCheckTask> {

    private static final HealthCheckInterceptorChain INSTANCE = new HealthCheckInterceptorChain();

    private HealthCheckInterceptorChain() {
        super(AbstractHealthCheckInterceptor.class);
    }

    public static HealthCheckInterceptorChain getInstance() {
        return INSTANCE;
    }
}

InstanceBeatCheckTaskInterceptorChain

实例心跳检查器链负责加载AbstractBeatCheckInterceptor类型的拦截器。

/**
 * Instance beat check interceptor chain.
 *
 * @author xiweng.yy
 */
public class InstanceBeatCheckTaskInterceptorChain extends AbstractNamingInterceptorChain<InstanceBeatCheckTask> {

    private static final InstanceBeatCheckTaskInterceptorChain INSTANCE = new InstanceBeatCheckTaskInterceptorChain();

    private InstanceBeatCheckTaskInterceptorChain() {
        super(AbstractBeatCheckInterceptor.class);
    }

    public static InstanceBeatCheckTaskInterceptorChain getInstance() {
        return INSTANCE;
    }
}

小结

通过模板模式来实现拦截器机制。

  • AbstractNamingInterceptorChain 抽象出连接器链对拦截器加载的通用方法,定义了拦截器对被拦截对象的通用处理流程。
  • AbstractHealthCheckInterceptor 定义了健康检查拦截器被拦截的对象类型
  • AbstractBeatCheckInterceptor 定义了心跳检查拦截器被拦截的对象类型

通过对拦截器链的组织方式梳理可以看到有明显的两条路线,一个是健康检查,一个是心跳检查。分析后续具体的拦截器,以及他们所要处理的任务就很清晰了。

业务逻辑是被拦截器拦截之后需要进行的操作。

健康检查类的被拦截对象

健康检查的抽象拦截器AbstractHealthCheckInterceptor定义了它的子类将要处理的任务类型为NacosHealthCheckTask

HealthCheckTaskV2

/**
 * Health check task for v2.x.
 * v2版本的健康检查
 * <p>Current health check logic is same as v1.x. TODO refactor health check for v2.x.
 *
 * @author nacos
 */
public class HealthCheckTaskV2 extends AbstractExecuteTask implements NacosHealthCheckTask {

    /**
     * 一个客户端对象(此客户端代表提供服务用于被应用访问的客户端)
     * 从这里可以看出,启动一个健康检查任务是以客户端为维度的
     */
    private final IpPortBasedClient client;

    private final String taskId;

    private final SwitchDomain switchDomain;

    private final NamingMetadataManager metadataManager;

    private long checkRtNormalized = -1;
    /**
     * 检查最佳响应时间
     */
    private long checkRtBest = -1;

    /**
     * 检查最差响应时间
     */
    private long checkRtWorst = -1;

    /**
     * 检查上次响应时间
     */
    private long checkRtLast = -1;

    /**
     * 检查上上次响应时间
     */
    private long checkRtLastLast = -1;

    /**
     * 开始时间
     */
    private long startTime;

    /**
     * 任务是否取消
     */
    private volatile boolean cancelled = false;

    public HealthCheckTaskV2(IpPortBasedClient client) {
        this.client = client;
        this.taskId = client.getResponsibleId();
        this.switchDomain = ApplicationUtils.getBean(SwitchDomain.class);
        this.metadataManager = ApplicationUtils.getBean(NamingMetadataManager.class);
        // 初始化响应时间检查
        initCheckRT();
    }

    /**
     * 初始化响应时间值
     */
    private void initCheckRT() {
        // first check time delay
        // 2000 + (在5000以内的随机数)
        checkRtNormalized =
                2000 + RandomUtils.nextInt(0, RandomUtils.nextInt(0, switchDomain.getTcpHealthParams().getMax()));
        // 最佳响应时间
        checkRtBest = Long.MAX_VALUE;
        // 最差响应时间为0
        checkRtWorst = 0L;
    }

    public IpPortBasedClient getClient() {
        return client;
    }

    @Override
    public String getTaskId() {
        return taskId;
    }

    /**
     * 开始执行健康检查任务
     */
    @Override
    public void doHealthCheck() {
        try {
            // 获取当前传入的Client所发布的所有Service
            for (Service each : client.getAllPublishedService()) {
                // 只有当Service开启了健康检查才执行
                if (switchDomain.isHealthCheckEnabled(each.getGroupedServiceName())) {
                    // 获取Service对应的InstancePublishInfo
                    InstancePublishInfo instancePublishInfo = client.getInstancePublishInfo(each);
                    // 获取集群元数据
                    ClusterMetadata metadata = getClusterMetadata(each, instancePublishInfo);
                    // 使用Processor代理对象对任务进行处理
                    ApplicationUtils.getBean(HealthCheckProcessorV2Delegate.class).process(this, each, metadata);
                    if (Loggers.EVT_LOG.isDebugEnabled()) {
                        Loggers.EVT_LOG.debug("[HEALTH-CHECK] schedule health check task: {}", client.getClientId());
                    }
                }
            }
        } catch (Throwable e) {
            Loggers.SRV_LOG.error("[HEALTH-CHECK] error while process health check for {}", client.getClientId(), e);
        } finally {
            // 若任务执行状态为已取消,则再次启动
            if (!cancelled) {
                HealthCheckReactor.scheduleCheck(this);
                // worst == 0 means never checked
                if (this.getCheckRtWorst() > 0) {
                    // TLog doesn't support float so we must convert it into long
                    long checkRtLastLast = getCheckRtLastLast();
                    this.setCheckRtLastLast(this.getCheckRtLast());
                    if (checkRtLastLast > 0) {
                        long diff = ((this.getCheckRtLast() - this.getCheckRtLastLast()) * 10000) / checkRtLastLast;
                        if (Loggers.CHECK_RT.isDebugEnabled()) {
                            Loggers.CHECK_RT.debug("{}->normalized: {}, worst: {}, best: {}, last: {}, diff: {}",
                                    client.getClientId(), this.getCheckRtNormalized(), this.getCheckRtWorst(),
                                    this.getCheckRtBest(), this.getCheckRtLast(), diff);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void passIntercept() {
        doHealthCheck();
    }

    @Override
    public void afterIntercept() {
        // 若任务执行状态为已取消,则再次启动
        if (!cancelled) {
            HealthCheckReactor.scheduleCheck(this);
        }
    }

    @Override
    public void run() {
        // 调用健康检查
        doHealthCheck();
    }

    /**
     * 获取集群元数据
     * @param service               服务信息
     * @param instancePublishInfo   服务对应的ip等信息
     * @return
     */
    private ClusterMetadata getClusterMetadata(Service service, InstancePublishInfo instancePublishInfo) {
        Optional<ServiceMetadata> serviceMetadata = metadataManager.getServiceMetadata(service);
        if (!serviceMetadata.isPresent()) {
            return new ClusterMetadata();
        }
        String cluster = instancePublishInfo.getCluster();
        ClusterMetadata result = serviceMetadata.get().getClusters().get(cluster);
        return null == result ? new ClusterMetadata() : result;
    }

    public long getCheckRtNormalized() {
        return checkRtNormalized;
    }

    public long getCheckRtBest() {
        return checkRtBest;
    }

    public long getCheckRtWorst() {
        return checkRtWorst;
    }

    public void setCheckRtWorst(long checkRtWorst) {
        this.checkRtWorst = checkRtWorst;
    }

    public void setCheckRtBest(long checkRtBest) {
        this.checkRtBest = checkRtBest;
    }

    public void setCheckRtNormalized(long checkRtNormalized) {
        this.checkRtNormalized = checkRtNormalized;
    }

    public boolean isCancelled() {
        return cancelled;
    }

    public void setCancelled(boolean cancelled) {
        this.cancelled = cancelled;
    }

    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    public long getCheckRtLast() {
        return checkRtLast;
    }

    public void setCheckRtLast(long checkRtLast) {
        this.checkRtLast = checkRtLast;
    }

    public long getCheckRtLastLast() {
        return checkRtLastLast;
    }

    public void setCheckRtLastLast(long checkRtLastLast) {
        this.checkRtLastLast = checkRtLastLast;
    }
}

心跳检查类的被拦截对象

ClientBeatCheckTaskV2

虽然它继承了NacosHealthCheckTask,但内部只使用了InstanceBeatCheckTaskInterceptorChain,没有使用HealthCheckInterceptorChain, 按理说应该划分到"心跳检查类的被拦截对象" 这个类别的。不知道为何这样设计,已提issues。

/**
 * Client beat check task of service for version 2.x.
 * @author nkorange
 */
public class ClientBeatCheckTaskV2 extends AbstractExecuteTask implements BeatCheckTask, NacosHealthCheckTask {

    private final IpPortBasedClient client;

    private final String taskId;

    /**
     * 使用拦截器链
     */
    private final InstanceBeatCheckTaskInterceptorChain interceptorChain;

    public ClientBeatCheckTaskV2(IpPortBasedClient client) {
        this.client = client;
        this.taskId = client.getResponsibleId();
        this.interceptorChain = InstanceBeatCheckTaskInterceptorChain.getInstance();
    }

    public GlobalConfig getGlobalConfig() {
        return ApplicationUtils.getBean(GlobalConfig.class);
    }

    @Override
    public String taskKey() {
        return KeyBuilder.buildServiceMetaKey(client.getClientId(), String.valueOf(client.isEphemeral()));
    }

    @Override
    public String getTaskId() {
        return taskId;
    }

    @Override
    public void doHealthCheck() {

        try {
            // 获取所有的Service
            Collection<Service> services = client.getAllPublishedService();
            for (Service each : services) {
                logger.info("开始对Service进行拦截操作,{}", each.getName());
                // 获取Service对应的InstancePublishInfo
                HealthCheckInstancePublishInfo instance = (HealthCheckInstancePublishInfo) client.getInstancePublishInfo(each);
                // 创建一个InstanceBeatCheckTask,并交由拦截器链处理
                interceptorChain.doInterceptor(new InstanceBeatCheckTask(client, each, instance));
            }

        } catch (Exception e) {
            Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
        }
    }

    @Override
    public void run() {
        doHealthCheck();
    }

    @Override
    public void passIntercept() {
        doHealthCheck();
    }

    @Override
    public void afterIntercept() {
    }
}

InstanceBeatCheckTask

/**
 * Instance beat check task.
 * Instance心跳检查任务,此处它作为一个可被拦截器拦截的对象使用。
 * @author xiweng.yy
 */
public class InstanceBeatCheckTask implements Interceptable {

    // 心跳检查者列表
    private static final List<InstanceBeatChecker> CHECKERS = new LinkedList<>();

    // 客户端对象(因为实例就代表的是客户端)
    private final IpPortBasedClient client;

    // 服务对象
    private final Service service;

    // 健康检查信息
    private final HealthCheckInstancePublishInfo instancePublishInfo;

    static {
        // 添加不健康实例检查器
        CHECKERS.add(new UnhealthyInstanceChecker());
        // 添加过期实例检查器
        CHECKERS.add(new ExpiredInstanceChecker());
        // 添加用户自定义的心跳检查器
        CHECKERS.addAll(NacosServiceLoader.load(InstanceBeatChecker.class));
    }

    public InstanceBeatCheckTask(IpPortBasedClient client, Service service, HealthCheckInstancePublishInfo instancePublishInfo) {
        this.client = client;
        this.service = service;
        this.instancePublishInfo = instancePublishInfo;
    }

    @Override
    public void passIntercept() {
        // 未被拦截的时候执行自身逻辑
        for (InstanceBeatChecker each : CHECKERS) {
            each.doCheck(client, service, instancePublishInfo);
        }
    }

    @Override
    public void afterIntercept() {
    }

    public IpPortBasedClient getClient() {
        return client;
    }

    public Service getService() {
        return service;
    }

    public HealthCheckInstancePublishInfo getInstancePublishInfo() {
        return instancePublishInfo;
    }
}
  • 拦截器链确定了要加载的拦截器类型
  • 拦截器确定了要拦截的对象类型
  • 被拦截的对象又建立了自己的检查策略