All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.huawei.registry.service.client.ScDiscovery Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Based org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java
 * from the Apache ServiceComb Java Chassis project.
 */

package com.huawei.registry.service.client;

import com.huaweicloud.sermant.core.common.LoggerFactory;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

import org.apache.servicecomb.http.client.task.AbstractTask;
import org.apache.servicecomb.http.client.task.Task;
import org.apache.servicecomb.service.center.client.DiscoveryEvents.InstanceChangedEvent;
import org.apache.servicecomb.service.center.client.DiscoveryEvents.PullInstanceEvent;
import org.apache.servicecomb.service.center.client.ServiceCenterClient;
import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery;
import org.apache.servicecomb.service.center.client.exception.OperationException;
import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse;
import org.apache.servicecomb.service.center.client.model.Microservice;
import org.apache.servicecomb.service.center.client.model.MicroserviceInstance;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * service center实例发现, 原{@link ServiceCenterDiscovery}日志处理存在问题,因此此处重新实现
 *
 * @author zhouss
 * @since 2022-05-11
 */
public class ScDiscovery extends AbstractTask {
    private static final Logger LOGGER = LoggerFactory.getLogger();

    /**
     * 默认拉取间隔
     */
    private static final long DEFAULT_POLL_INTERVAL = 15000L;

    /**
     * endpoint最大长度,超过该长度直接截取
     */
    private static final int MAX_LEN_OF_ENDPOINT = 64;

    /**
     * 查询所有的版本号
     */
    private static final String ALL_VERSION = "0+";

    private final Object lock = new Object();

    private final ServiceCenterClient serviceCenterClient;

    private final EventBus eventBus;

    private final Map instancesCache = new ConcurrentHashMap<>();

    private final Map microserviceCache = new ConcurrentHashMap<>();

    private final List failedInstances = new ArrayList<>();

    private String myselfServiceId;

    private long pollInterval;

    private boolean started = false;

    /**
     * 构造器
     *
     * @param serviceCenterClient sc客户端
     * @param eventBus            事件总栈
     */
    public ScDiscovery(ServiceCenterClient serviceCenterClient, EventBus eventBus) {
        super("service-center-discovery-task");
        this.serviceCenterClient = serviceCenterClient;
        this.eventBus = eventBus;
        this.eventBus.register(this);
        this.pollInterval = DEFAULT_POLL_INTERVAL;
    }

    /**
     * 设置拉取间隔
     *
     * @param interval 拉取间隔, 单位ms
     */
    public void setPollInterval(long interval) {
        if (interval > ServiceCenterDiscovery.MAX_INTERVAL || interval < ServiceCenterDiscovery.MIN_INTERVAL) {
            return;
        }
        this.pollInterval = interval;
    }

    /**
     * 更新自身服务ID
     *
     * @param curServiceId 自身服务编号
     */
    public void updateMyselfServiceId(String curServiceId) {
        this.myselfServiceId = curServiceId;
    }

    /**
     * 开启相关定时任务,拉取实例数据
     */
    public void startDiscovery() {
        if (!started) {
            started = true;
            startTask(new PullInstanceTask());
        }
    }

    /**
     * 注册订阅者
     *
     * @param subscriptionKey 订阅者
     */
    public void registerIfNotPresent(SubscriptionKey subscriptionKey) {
        if (this.instancesCache.get(subscriptionKey) == null) {
            synchronized (lock) {
                if (this.instancesCache.get(subscriptionKey) == null) {
                    SubscriptionValue value = new SubscriptionValue();
                    pullInstance(subscriptionKey, value, Level.INFO);
                    this.instancesCache.put(subscriptionKey, value);
                }
            }
        }
    }

    /**
     * 获取实例缓存
     *
     * @param key 订阅者
     * @return 实例数据
     */
    public List getInstanceCache(SubscriptionKey key) {
        return this.instancesCache.get(key).instancesCache;
    }

    /**
     * 订阅拉取事件
     *
     * @param event 拉取事件
     */
    @Subscribe
    public void onPullInstanceEvent(PullInstanceEvent event) {
        pullAllInstance();
    }

    private void pullInstance(SubscriptionKey key, SubscriptionValue value, Level level) {
        if (myselfServiceId == null) {
            // registration not ready
            return;
        }
        try {
            FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient
                .findMicroserviceInstance(myselfServiceId, key.appId, key.serviceName, ALL_VERSION, value.revision);
            if (instancesResponse.isModified()) {
                List instances =
                    instancesResponse.getMicroserviceInstancesResponse().getInstances()
                        == null ? Collections.emptyList()
                        : instancesResponse.getMicroserviceInstancesResponse().getInstances();
                setMicroserviceInfo(instances);
                LOGGER.info(String.format(Locale.ENGLISH,
                    "Instance changed event, current: revision={%s}, instances={%s}; "
                        + "origin: revision={%s}, instances={%s}; appId={%s}, serviceName={%s}",
                    instancesResponse.getRevision(),
                    instanceToString(instances),
                    value.revision,
                    instanceToString(value.instancesCache),
                    key.appId,
                    key.serviceName
                ));
                value.instancesCache = instances;
                value.revision = instancesResponse.getRevision();
                eventBus.post(new InstanceChangedEvent(key.appId, key.serviceName,
                    value.instancesCache));
            }
        } catch (OperationException ex) {
            String message = String.format(Locale.ENGLISH,
                "find service {%s}#{%s} instance failed. caused by %s, if you are run with mode migration, please "
                    + "ignore it!", key.appId, key.serviceName,
                ex.getMessage());
            LOGGER.log(level, message);
            LOGGER.log(Level.FINE, message, ex);
            failedInstances.add(key);
        }
    }

    private void setMicroserviceInfo(List instances) {
        instances.forEach(instance -> {
            Microservice microservice = microserviceCache
                .computeIfAbsent(instance.getServiceId(), id -> {
                    try {
                        return serviceCenterClient.getMicroserviceByServiceId(id);
                    } catch (OperationException ex) {
                        LOGGER.log(Level.INFO, String.format(Locale.ENGLISH,
                            "Find microservice by id={%s} failed", id), ex);
                        throw ex;
                    }
                });
            instance.setMicroservice(microservice);
        });
    }

    private synchronized void pullAllInstance() {
        instancesCache.forEach((key, value) -> this.pullInstance(key, value, Level.FINE));
        if (failedInstances.isEmpty()) {
            return;
        }
        failedInstances.forEach(instancesCache::remove);
        failedInstances.clear();
    }

    private static String instanceToString(List instances) {
        if (instances == null) {
            return "";
        }

        StringBuilder sb = new StringBuilder();
        for (MicroserviceInstance instance : instances) {
            for (String endpoint : instance.getEndpoints()) {
                sb.append(endpoint.length() > MAX_LEN_OF_ENDPOINT
                    ? endpoint.substring(0, MAX_LEN_OF_ENDPOINT) : endpoint);
                sb.append("|");
            }
            sb.append(instance.getServiceName());
            sb.append("|");
        }
        sb.append("#");
        return sb.toString();
    }

    /**
     * 定时拉取任务
     *
     * @since 2022-05-11
     */
    class PullInstanceTask implements Task {
        @Override
        public void execute() {
            pullAllInstance();

            startTask(new BackOffSleepTask(pollInterval, new PullInstanceTask()));
        }
    }

    /**
     * 订阅实例
     *
     * @since 2022-05-11
     */
    public static class SubscriptionValue {
        String revision;

        List instancesCache;
    }

    /**
     * 订阅者
     *
     * @since 2022-05-11
     */
    public static class SubscriptionKey {
        final String appId;

        final String serviceName;

        /**
         * 订阅者
         *
         * @param appId       appName
         * @param serviceName 服务名
         */
        public SubscriptionKey(String appId, String serviceName) {
            this.appId = appId;
            this.serviceName = serviceName;
        }

        @Override
        public boolean equals(Object target) {
            if (this == target) {
                return true;
            }
            if (target == null || getClass() != target.getClass()) {
                return false;
            }
            SubscriptionKey that = (SubscriptionKey) target;
            return appId.equals(that.appId) && serviceName.equals(that.serviceName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(appId, serviceName);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy