karyon
karyon copied to clipboard
[Proposal] karyon 3.x server context
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;
}
}