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

com.tencent.polaris.client.api.SDKContext Maven / Gradle / Ivy

/*
 * Tencent is pleased to support the open source community by making Polaris available.
 *
 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * 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.
 */

package com.tencent.polaris.client.api;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.tencent.polaris.api.config.Configuration;
import com.tencent.polaris.api.config.global.ClusterConfig;
import com.tencent.polaris.api.config.global.ClusterType;
import com.tencent.polaris.api.config.global.SystemConfig;
import com.tencent.polaris.api.config.plugin.DefaultPlugins;
import com.tencent.polaris.api.control.Destroyable;
import com.tencent.polaris.api.exception.ErrorCode;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.api.plugin.Manager;
import com.tencent.polaris.api.plugin.PluginType;
import com.tencent.polaris.api.plugin.Supplier;
import com.tencent.polaris.api.plugin.TypeProvider;
import com.tencent.polaris.api.plugin.common.InitContext;
import com.tencent.polaris.api.plugin.common.ValueContext;
import com.tencent.polaris.api.plugin.compose.Extensions;
import com.tencent.polaris.api.plugin.compose.ServerServiceInfo;
import com.tencent.polaris.api.plugin.impl.PluginManager;
import com.tencent.polaris.api.plugin.server.ReportClientRequest;
import com.tencent.polaris.api.plugin.server.ReportClientResponse;
import com.tencent.polaris.api.plugin.server.ServerConnector;
import com.tencent.polaris.api.plugin.stat.ReporterMetaInfo;
import com.tencent.polaris.api.plugin.stat.StatReporter;
import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.api.utils.StringUtils;
import com.tencent.polaris.client.flow.AbstractFlow;
import com.tencent.polaris.client.util.NamedThreadFactory;
import com.tencent.polaris.factory.ConfigAPIFactory;
import com.tencent.polaris.factory.config.ConfigurationImpl;
import com.tencent.polaris.logging.LoggerFactory;
import com.tencent.polaris.version.Version;
import org.slf4j.Logger;

import java.io.Closeable;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * SDK初始化相关的上下文信息
 *
 * @author andrewshan, Haotian Zhang
 */
public class SDKContext extends Destroyable implements InitContext, AutoCloseable, Closeable {

    private static final Logger LOG = LoggerFactory.getLogger(SDKContext.class);
    private static final String DEFAULT_ADDRESS = "127.0.0.1";
    /**
     * 配置对象
     */
    private final Configuration configuration;
    /**
     * 初始化标识
     */
    private final AtomicBoolean initialized = new AtomicBoolean(false);
    /**
     * 插件管理器
     */
    private final Manager plugins;
    private final ValueContext valueContext;
    private final Extensions extensions = new Extensions();
    private final Object lock = new Object();
    private final List destroyHooks = new ArrayList<>();
    private final Collection serverServices;

    private final ScheduledExecutorService reportClientExecutorService;

    /**
     * 构造器
     *
     * @param configuration 配置
     * @param plugins 插件工厂
     * @param valueContext 上下文
     */
    public SDKContext(Configuration configuration, Manager plugins, ValueContext valueContext) {
        this.configuration = configuration;
        this.plugins = plugins;
        this.valueContext = valueContext;
        this.valueContext.setClientId(generateClientId(this.valueContext.getHost()));
        List services = new ArrayList<>();
        //加载系统服务配置
        SystemConfig system = configuration.getGlobal().getSystem();
        ClusterConfig discoverCluster = system.getDiscoverCluster();
        if (clusterAvailable(discoverCluster)) {
            services.add(new ServerServiceInfo(ClusterType.SERVICE_DISCOVER_CLUSTER, discoverCluster));
        }
        ClusterConfig configCluster = system.getConfigCluster();
        if (clusterAvailable(configCluster)) {
            services.add(new ServerServiceInfo(ClusterType.SERVICE_CONFIG_CLUSTER, configCluster));
        }
        ClusterConfig healthCheckCluster = system.getHealthCheckCluster();
        if (clusterAvailable(healthCheckCluster)) {
            services.add(new ServerServiceInfo(ClusterType.HEALTH_CHECK_CLUSTER, healthCheckCluster));
        }
        ClusterConfig monitorCluster = system.getMonitorCluster();
        if (clusterAvailable(monitorCluster)) {
            services.add(new ServerServiceInfo(ClusterType.MONITOR_CLUSTER, monitorCluster));
        }
        this.serverServices = Collections.unmodifiableCollection(services);
        this.reportClientExecutorService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("polaris-report-client"));
    }

    private static String generateClientId(String host) {
        return host + "-" + getProcessId("0");
    }

    private static String getProcessId(String fallback) {
        // Note: may fail in some JVM implementations
        // therefore fallback has to be provided

        // something like '@', at least in SUN / Oracle JVMs
        final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
        final int index = jvmName.indexOf('@');

        if (index < 1) {
            // part before '@' empty (index = 0) / '@' not found (index = -1)
            return fallback;
        }

        try {
            return Long.toString(Long.parseLong(jvmName.substring(0, index)));
        } catch (NumberFormatException e) {
            // ignore
        }
        return fallback;
    }

    /**
     * 通过默认配置初始化SDKContext
     *
     * @return SDKContext对象
     * @throws PolarisException 初始化异常
     */
    public static SDKContext initContext() throws PolarisException {
        Configuration configuration = ConfigAPIFactory.defaultConfig();
        return initContextByConfig(configuration);
    }

    /**
     * 通过配置对象初始化SDK上下文
     *
     * @param config 配置对象
     * @return SDK上下文
     * @throws PolarisException 初始化过程的异常
     */
    public static SDKContext initContextByConfig(Configuration config) throws PolarisException {
        try {
            ((ConfigurationImpl) config).setDefault();
            config.verify();
            ObjectMapper mapper = new JsonMapper();
            LOG.info("SDKContext config {} ", mapper.writeValueAsString(config));
        } catch (IllegalArgumentException e) {
            throw new PolarisException(ErrorCode.INVALID_CONFIG, "fail to verify configuration", e);
        } catch (JsonProcessingException ignore) {
        }
        ServiceLoader providers = ServiceLoader.load(TypeProvider.class);
        List types = new ArrayList<>();
        for (TypeProvider provider : providers) {
            types.addAll(provider.getTypes());
        }
        PluginManager manager = new PluginManager(types);
        ValueContext valueContext = new ValueContext();
        valueContext.setHost(parseHost(config));
        valueContext.setServerConnectorProtocol(parseServerConnectorProtocol(config));
        SDKContext initContext = new SDKContext(config, manager, valueContext);

        try {
            manager.initPlugins(initContext);
        } catch (Throwable e) {
            manager.destroyPlugins();
            if (e instanceof PolarisException) {
                throw e;
            }
            throw new PolarisException(ErrorCode.PLUGIN_ERROR, "plugin error", e);
        }
        return initContext;
    }

    public static String parseHost(Configuration configuration) {
        String hostAddress = configuration.getGlobal().getAPI().getBindIP();
        if (!StringUtils.isBlank(hostAddress)) {
            return hostAddress;
        }
        String nic = configuration.getGlobal().getAPI().getBindIf();
        if (StringUtils.isNotBlank(nic)) {
            return resolveAddress(nic);
        }
        try {
            return getHostByDial(configuration);
        } catch (IOException e) {
            LOG.info("[ReportClient]get address by dial failed: {}", e.getMessage());
        }
        return DEFAULT_ADDRESS;
    }

    /**
     * Get protocol of server connector, such as:
     * 
    *
  • {@link DefaultPlugins#SERVER_CONNECTOR_COMPOSITE}
  • *
  • {@link DefaultPlugins#SERVER_CONNECTOR_GRPC}
  • *
  • {@link DefaultPlugins#SERVER_CONNECTOR_CONSUL}
  • *
* * @param configuration * @return */ public static String parseServerConnectorProtocol(Configuration configuration) { String protocol; if (CollectionUtils.isNotEmpty(configuration.getGlobal().getServerConnectors())) { // Composite server connector first protocol = DefaultPlugins.SERVER_CONNECTOR_COMPOSITE; } else { // If composite server connector does not exist. protocol = configuration.getGlobal().getServerConnector().getProtocol(); } return protocol; } private static String getHostByDial(Configuration configuration) throws IOException { String serverAddress; if (CollectionUtils.isNotEmpty(configuration.getGlobal().getServerConnectors())) { // Composite server connector first serverAddress = configuration.getGlobal().getServerConnectors().get(0).getAddresses().get(0); } else { // If composite server connector does not exist. serverAddress = configuration.getGlobal().getServerConnector().getAddresses().get(0); } String[] tokens = serverAddress.split(":"); try (Socket socket = new Socket(tokens[0], Integer.parseInt(tokens[1]))) { return socket.getLocalAddress().getHostAddress(); } } private static NetworkInterface resolveNetworkInterface(String nic) { NetworkInterface ni = null; try { ni = NetworkInterface.getByName(nic); } catch (SocketException e) { LOG.error("[ReportClient]get nic failed, nic:{}", nic, e); } if (null != ni) { return ni; } //获取第一张网卡 try { Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); if (!networkInterface.isLoopback() && networkInterface.getInetAddresses().hasMoreElements()) { return networkInterface; } } } catch (SocketException e) { LOG.error("[ReportClient]get all network interfaces failed", e); } return null; } /** * 解析网卡IP * * @param nic 网卡标识,如eth1 * @return 地址信息 */ private static String resolveAddress(String nic) { NetworkInterface ni = resolveNetworkInterface(nic); if (null == ni) { return DEFAULT_ADDRESS; } Enumeration inetAddresses = ni.getInetAddresses(); if (inetAddresses.hasMoreElements()) { InetAddress inetAddress = inetAddresses.nextElement(); return inetAddress.getCanonicalHostName(); } return DEFAULT_ADDRESS; } public synchronized void init() throws PolarisException { if (!initialized.compareAndSet(false, true)) { return; } extensions.init(configuration, plugins, valueContext); plugins.postContextInitPlugins(extensions); extensions.initHttpServer(plugins); reportClient(extensions); registerDestroyHook(extensions); } private boolean clusterAvailable(ClusterConfig clusterConfig) { if (null == clusterConfig) { return false; } if (StringUtils.isBlank(clusterConfig.getNamespace()) || StringUtils.isBlank(clusterConfig.getService())) { return false; } if (clusterConfig.isSameAsBuiltin()) { return false; } return true; } /** * Report prometheus http server metadata periodic * * @param extensions extensions */ private void reportClient(Extensions extensions) { if (extensions.getConfiguration().getGlobal().getAPI().isReportEnable()) { reportClientExecutorService.scheduleAtFixedRate(() -> { ServerConnector serverConnector = extensions.getServerConnector(); ReportClientRequest reportClientRequest = new ReportClientRequest(); reportClientRequest.setClientHost(extensions.getValueContext().getHost()); reportClientRequest.setVersion(Version.VERSION); List statPlugins = extensions.getStatReporters(); List reporterMetaInfos = new ArrayList<>(); for (StatReporter statPlugin : statPlugins) { ReporterMetaInfo reporterMetaInfo = statPlugin.metaInfo(); if (StringUtils.isNotBlank(reporterMetaInfo.getProtocol())) { reporterMetaInfos.add(reporterMetaInfo); } } reportClientRequest.setReporterMetaInfos(reporterMetaInfos); try { ReportClientResponse reportClientResponse = serverConnector.reportClient(reportClientRequest); LOG.debug("Report client success, response:{}", reportClientResponse); } catch (PolarisException e) { LOG.error("Report client failed.", e); } }, 0L, 60L, TimeUnit.SECONDS); } } public Extensions getExtensions() { return extensions; } public Configuration getConfig() { return configuration; } public Supplier getPlugins() { return plugins; } @Override protected void doDestroy() { synchronized (lock) { for (Destroyable destroyable : destroyHooks) { destroyable.destroy(); } } plugins.destroyPlugins(); if (Objects.nonNull(reportClientExecutorService)) { reportClientExecutorService.shutdown(); } } public ValueContext getValueContext() { return valueContext; } @Override public Collection getServerServices() { return serverServices; } public void registerDestroyHook(Destroyable destroyable) { synchronized (lock) { destroyHooks.add(destroyable); } } @Override public void close() { destroy(); } private static T loadFlow(String name, Class clazz) { ServiceLoader flows = ServiceLoader.load(clazz); for (T flow : flows) { if (StringUtils.equals(flow.getName(), name)) { return flow; } } throw new PolarisException(ErrorCode.INVALID_CONFIG, String.format("unknown flow name %s, type is %s", name, clazz.getCanonicalName())); } @SuppressWarnings("unchecked") public T getOrInitFlow(Class clazz) { synchronized (clazz) { Object flowObject = valueContext.getValue(clazz.getCanonicalName()); if (null != flowObject) { return (T) flowObject; } String flowName = configuration.getGlobal().getSystem().getFlow().getName(); T flow = loadFlow(flowName, clazz); flow.setSDKContext(this); valueContext.setValue(clazz.getCanonicalName(), flow); return flow; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy