karyon icon indicating copy to clipboard operation
karyon copied to clipboard

[Proposal] karyon 3.x server context

Open qiangdavidliu opened this issue 9 years ago • 0 comments

Karyon server metadata are useful by different components of karyon 3.x. For example karyon3-eureka needs metadata for registration, and karyon3-admin can expose similar metadata for admin purposes.

It would be nice if karyon 3.x exposed a common way to define and store these metadata, and also be able to translate the metadata to other formats for different components to consume.

Some example code below:

/**
 * Marker interface for specific context implementations. This interface explicitly 
 * sets no restrictions on what actual implementations should contain. 
 * Any implementers of an actual context should also need to implement 
 * {@link KaryonContextConverter}s to the necessary components.
 */
public interface KaryonContext {
}
/**
 * A converter definition to translate karyon contexts to other formats for component use
 */
public abstract class KaryonContextConverter<C extends KaryonContext, T> {

    protected final C context;

    protected KaryonContextConverter(C context) {
        this.context = context;
    }

    /**
     * @return the context mapped to and instance of type T
     */
    public abstract T convert();
}
import com.netflix.archaius.annotations.Configuration;
import com.netflix.archaius.annotations.DefaultValue;
import com.netflix.karyon.context.KaryonContext;

/**
 * An implementation of karyon context based on archaius2 property loading 
 */
@Configuration(prefix="karyon.context")
public interface ArchaiusKaryonContext extends KaryonContext {

    String getAppName();

    @DefaultValue("localhost")
    String getHostname();

    @DefaultValue("8080")
    int getPort();
}
import com.netflix.appinfo.InstanceInfo;
import com.netflix.karyon.archaius.context.ArchaiusKaryonContext;
import com.netflix.karyon.context.KaryonContextConverter;

/**
 * An example converter for karyon3-eureka to convert karyon context to an instanceInfo builder
 */
public class ArchaiusKaryonContextConverter extends KaryonContextConverter<ArchaiusKaryonContext, InstanceInfo.Builder> {

    protected ArchaiusKaryonContextConverter(ArchaiusKaryonContext context) {
        super(context);
    }

    @Override
    public InstanceInfo.Builder convert() {
        return InstanceInfo.Builder.newBuilder()
                .setAppName(context.getAppName())
                .setHostName(context.getHostname())
                .setPort(context.getPort());
    }
}

import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.LeaseInfo;
import com.netflix.karyon.eureka.context.ArchaiusKaryonContextConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Provider;
import java.util.Map;

/**
 * An example eureka InstanceInfo provider that merges data from karyon context as well as eurekaInstanceConfig
 * to resolve the final server InstanceInfo for registration.
 * 
 * @author David Liu
 */
public class KaryonInstanceInfoProvider implements Provider<InstanceInfo> {
    private static final Logger logger = LoggerFactory.getLogger(KaryonInstanceInfoProvider.class);

    private final ArchaiusKaryonContextConverter context;
    private final EurekaInstanceConfig eurekaInstanceConfig;

    private InstanceInfo instanceInfo;

    @Inject
    public KaryonInstanceInfoProvider(ArchaiusKaryonContextConverter context, EurekaInstanceConfig eurekaInstanceConfig) {
        this.context = context;
        this.eurekaInstanceConfig = eurekaInstanceConfig;
    }

    @Override
    public synchronized InstanceInfo get() {
        if (instanceInfo == null) {
            // Build the lease information to be passed to the server based
            // on config
            LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder
                    .newBuilder()
                    .setRenewalIntervalInSecs(eurekaInstanceConfig.getLeaseRenewalIntervalInSeconds())
                    .setDurationInSecs(eurekaInstanceConfig.getLeaseExpirationDurationInSeconds());

            InstanceInfo.Builder builder = context.convert()
                    .setNamespace(eurekaInstanceConfig.getNamespace())
                    .setAppGroupName(eurekaInstanceConfig.getAppGroupName())
                    .setDataCenterInfo(eurekaInstanceConfig.getDataCenterInfo())
                    .setIPAddr(eurekaInstanceConfig.getIpAddress())
                    .enablePort(InstanceInfo.PortType.UNSECURE, eurekaInstanceConfig.isNonSecurePortEnabled())
                    .setSecurePort(eurekaInstanceConfig.getSecurePort())
                    .enablePort(InstanceInfo.PortType.SECURE, eurekaInstanceConfig.getSecurePortEnabled())
                    .setVIPAddress(eurekaInstanceConfig.getVirtualHostName())
                    .setSecureVIPAddress(eurekaInstanceConfig.getSecureVirtualHostName())
                    .setHomePageUrl(eurekaInstanceConfig.getHomePageUrlPath(), eurekaInstanceConfig.getHomePageUrl())
                    .setStatusPageUrl(eurekaInstanceConfig.getStatusPageUrlPath(), eurekaInstanceConfig.getStatusPageUrl())
                    .setHealthCheckUrls(eurekaInstanceConfig.getHealthCheckUrlPath(), eurekaInstanceConfig.getHealthCheckUrl(), eurekaInstanceConfig.getSecureHealthCheckUrl())
                    .setASGName(eurekaInstanceConfig.getASGName());

            // Start off with the STARTING state to avoid traffic
            if (!eurekaInstanceConfig.isInstanceEnabledOnit()) {
                InstanceInfo.InstanceStatus initialStatus = InstanceInfo.InstanceStatus.STARTING;
                logger.info("Setting initial instance status as: " + initialStatus);
                builder.setStatus(initialStatus);
            } else {
                logger.info("Setting initial instance status as: {}. This may be too early for the instance to advertise itself as available. "
                        + "You would instead want to control this via a healthcheck handler.", InstanceInfo.InstanceStatus.UP);
            }

            // Add any user-specific metadata information
            for (Map.Entry<String, String> mapEntry : eurekaInstanceConfig.getMetadataMap().entrySet()) {
                String key = mapEntry.getKey();
                String value = mapEntry.getValue();
                builder.add(key, value);
            }

            instanceInfo = builder.build();
            instanceInfo.setLeaseInfo(leaseInfoBuilder.build());
        }
        return instanceInfo;
    }
}

qiangdavidliu avatar Aug 14 '15 17:08 qiangdavidliu