com.hazelcast.eureka.one.EurekaOneDiscoveryStrategy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hazelcast-eureka-one Show documentation
Show all versions of hazelcast-eureka-one Show documentation
Eureka Service Discovery for Hazelcast Discovery SPI
The newest version!
/*
* Copyright 2020 Hazelcast Inc.
*
* Licensed under the Hazelcast Community License (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://hazelcast.com/hazelcast-community-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.hazelcast.eureka.one;
import com.google.common.annotations.VisibleForTesting;
import com.hazelcast.config.Config;
import com.hazelcast.config.properties.PropertyDefinition;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.NoLogFactory;
import com.hazelcast.cluster.Address;
import com.hazelcast.spi.discovery.AbstractDiscoveryStrategy;
import com.hazelcast.spi.discovery.DiscoveryNode;
import com.hazelcast.spi.discovery.SimpleDiscoveryNode;
import com.hazelcast.internal.util.UuidUtil;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.CloudInstanceConfig;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.MyDataCenterInstanceConfig;
import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.discovery.DefaultEurekaClientConfig;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.Application;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import static com.hazelcast.eureka.one.EurekaOneProperties.DATACENTER;
import static com.hazelcast.eureka.one.EurekaOneProperties.EUREKA_ONE_SYSTEM_PREFIX;
import static com.hazelcast.eureka.one.EurekaOneProperties.HZ_PROPERTY_DEFINITIONS;
import static com.hazelcast.eureka.one.EurekaOneProperties.NAME;
import static com.hazelcast.eureka.one.EurekaOneProperties.NAMESPACE;
import static com.hazelcast.eureka.one.EurekaOneProperties.SELF_REGISTRATION;
import static com.hazelcast.eureka.one.EurekaOneProperties.SKIP_EUREKA_REGISTRATION_VERIFICATION;
import static com.hazelcast.eureka.one.EurekaOneProperties.USE_CLASSPATH_EUREKA_CLIENT_PROPS;
import static com.hazelcast.eureka.one.EurekaOneProperties.USE_METADATA_FOR_HOST_AND_PORT;
final class EurekaOneDiscoveryStrategy
extends AbstractDiscoveryStrategy {
static final class EurekaOneDiscoveryStrategyBuilder {
private EurekaClient eurekaClient;
private String groupName = Config.DEFAULT_CLUSTER_NAME;
private ApplicationInfoManager applicationInfoManager;
private DiscoveryNode discoveryNode;
private ILogger logger = new NoLogFactory().getLogger(EurekaOneDiscoveryStrategy.class.getName());
private Map properties = Collections.emptyMap();
private StatusChangeStrategy changeStrategy;
EurekaOneDiscoveryStrategyBuilder setEurekaClient(final EurekaClient eurekaClient) {
this.eurekaClient = eurekaClient;
if (eurekaClient != null) {
this.applicationInfoManager = eurekaClient.getApplicationInfoManager();
}
return this;
}
EurekaOneDiscoveryStrategyBuilder setGroupName(final String groupName) {
this.groupName = groupName;
return this;
}
EurekaOneDiscoveryStrategyBuilder setApplicationInfoManager(
final ApplicationInfoManager applicationInfoManager) {
this.applicationInfoManager = applicationInfoManager;
return this;
}
EurekaOneDiscoveryStrategyBuilder setDiscoveryNode(final DiscoveryNode discoveryNode) {
this.discoveryNode = discoveryNode;
return this;
}
EurekaOneDiscoveryStrategyBuilder setILogger(final ILogger logger) {
this.logger = logger;
return this;
}
EurekaOneDiscoveryStrategyBuilder setProperties(final Map properties) {
this.properties = properties;
return this;
}
@VisibleForTesting
EurekaOneDiscoveryStrategyBuilder setStatusChangeStrategy(StatusChangeStrategy statusChangeStrategy) {
this.changeStrategy = statusChangeStrategy;
return this;
}
EurekaOneDiscoveryStrategy build() {
if (null == changeStrategy) {
changeStrategy = new DefaultUpdater();
}
if (null == discoveryNode) {
changeStrategy = new NoopUpdater();
}
return new EurekaOneDiscoveryStrategy(this);
}
}
@VisibleForTesting
static final String DEFAULT_NAMESPACE = "hazelcast";
@VisibleForTesting
static final int NUM_RETRIES = 5;
private static final int VERIFICATION_WAIT_TIMEOUT = 5;
private static final int DISCOVERY_RETRY_TIMEOUT = 1;
private final EurekaClient eurekaClient;
private final String groupName;
private final ApplicationInfoManager applicationInfoManager;
private final Boolean useClasspathEurekaClientProps;
private final String namespace;
private StatusChangeStrategy statusChangeStrategy;
private final Boolean skipEurekaRegistrationVerification;
private final Boolean useMetadataForHostAndPort;
private EurekaOneDiscoveryStrategy(final EurekaOneDiscoveryStrategyBuilder builder) {
super(builder.logger, builder.properties);
this.namespace = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, NAMESPACE, "hazelcast");
boolean selfRegistration = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, SELF_REGISTRATION, true);
this.useMetadataForHostAndPort = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, USE_METADATA_FOR_HOST_AND_PORT, false);
this.skipEurekaRegistrationVerification =
getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, SKIP_EUREKA_REGISTRATION_VERIFICATION, false);
this.useClasspathEurekaClientProps = getOrDefault(EUREKA_ONE_SYSTEM_PREFIX, USE_CLASSPATH_EUREKA_CLIENT_PROPS, true);
this.groupName = builder.groupName != null ? builder.groupName : Config.DEFAULT_CLUSTER_NAME;
// override registration if requested
if (!selfRegistration && !useMetadataForHostAndPort) {
statusChangeStrategy = new NoopUpdater();
} else if (useMetadataForHostAndPort && builder.discoveryNode != null) {
statusChangeStrategy = new MetadataUpdater(builder.discoveryNode, selfRegistration, this.groupName);
} else {
this.statusChangeStrategy = builder.changeStrategy;
}
if (builder.applicationInfoManager == null) {
this.applicationInfoManager = initializeApplicationInfoManager(builder.discoveryNode);
} else {
this.applicationInfoManager = builder.applicationInfoManager;
}
if (builder.eurekaClient == null) {
EurekaClientConfig eurekaClientConfig;
if (useClasspathEurekaClientProps) {
eurekaClientConfig = new EurekaOneAwareConfig(this.namespace);
} else {
eurekaClientConfig = new PropertyBasedEurekaClientConfig(
this.namespace,
getEurekaClientProperties(this.namespace, this.getProperties()));
}
this.eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
} else {
this.eurekaClient = builder.eurekaClient;
}
}
private String getAppname() {
Comparable name = this.getProperties().get(NAME.key());
return name == null ? "unknown" : name.toString();
}
private Map getEurekaClientProperties(String namespace, Map properties) {
Map result = new HashMap();
for (Map.Entry e : properties.entrySet()) {
result.put(namespace + "." + e.getKey(), e.getValue());
}
for (PropertyDefinition p : HZ_PROPERTY_DEFINITIONS) {
result.remove(namespace + "." + p.key());
}
return result;
}
private ApplicationInfoManager initializeApplicationInfoManager(DiscoveryNode localNode) {
EurekaInstanceConfig instanceConfig = buildInstanceConfig(localNode);
InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
ApplicationInfoManager manager = new ApplicationInfoManager(instanceConfig, instanceInfo);
statusChangeStrategy.update(manager, InstanceInfo.InstanceStatus.STARTING);
return manager;
}
private EurekaInstanceConfig buildInstanceConfig(DiscoveryNode localNode) {
try {
String value;
if (this.useClasspathEurekaClientProps) {
String configProperty = DynamicPropertyFactory
.getInstance()
.getStringProperty("eureka.client.props", "eureka-client").get();
String eurekaPropertyFile = String.format("%s.properties", configProperty);
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(eurekaPropertyFile);
if (url == null) {
throw new IllegalStateException("Cannot locate " + eurekaPropertyFile + " as a classpath resource.");
}
Properties props = new Properties();
props.load(url.openStream());
String key = String.format("%s.datacenter", this.namespace);
value = props.getProperty(key, "");
} else {
value = String.valueOf(getProperties().get(DATACENTER.key()));
}
if ("cloud".equals(value.trim().toLowerCase())) {
return new DelegatingInstanceConfig(new CloudInstanceConfig(this.namespace), localNode);
}
if (this.useClasspathEurekaClientProps) {
return new DelegatingInstanceConfig(new MyDataCenterInstanceConfig(this.namespace), localNode);
}
return new DelegatingInstanceConfig(new MyDataCenterInstanceConfig(this.namespace), localNode, getAppname());
} catch (IOException e) {
throw new IllegalStateException("Cannot build EurekaInstanceInfo", e);
}
}
private String getGroupNameFromMetadata(Map metadata) {
String groupName = Config.DEFAULT_CLUSTER_NAME;
if (metadata.containsKey(EurekaHazelcastMetadata.HAZELCAST_GROUP_NAME)) {
groupName = metadata.get(EurekaHazelcastMetadata.HAZELCAST_GROUP_NAME);
}
return groupName;
}
public Iterable discoverNodes() {
List nodes = new ArrayList();
String applicationName = applicationInfoManager.getEurekaInstanceConfig().getAppname();
Application application = null;
for (int i = 0; i < NUM_RETRIES; i++) {
application = eurekaClient.getApplication(applicationName);
if (application != null) {
break;
}
try {
TimeUnit.SECONDS.sleep(DISCOVERY_RETRY_TIMEOUT);
} catch (InterruptedException almostIgnore) {
Thread.currentThread().interrupt();
}
}
if (application != null) {
List instances = application.getInstancesAsIsFromEureka();
for (InstanceInfo instance : instances) {
// Only recognize up and running instances
if (instance.getStatus() != InstanceInfo.InstanceStatus.UP) {
continue;
}
Map metadata = instance.getMetadata();
@SuppressWarnings({"unchecked", "rawtypes"}) Map properties = (Map) metadata;
if (useMetadataForHostAndPort) {
addNodeUsingMetadata(nodes, instance, metadata, properties);
} else {
addNode(nodes, instance, properties);
}
}
}
return nodes;
}
private void addNodeUsingMetadata(List nodes, InstanceInfo instance, Map metadata,
Map properties) {
if (getGroupNameFromMetadata(metadata).equals(groupName)) {
InetAddress address = mapAddress(instance);
int port = mapPort(instance);
if (address != null) {
nodes.add(new SimpleDiscoveryNode(new Address(address, port), properties));
}
}
}
private void addNode(List nodes, InstanceInfo instance, Map properties) {
InetAddress address = mapAddress(instance);
if (null == address) {
return;
}
int port = instance.getPort();
nodes.add(new SimpleDiscoveryNode(new Address(address, port), properties));
}
@Override
public void start() {
statusChangeStrategy.update(applicationInfoManager, InstanceInfo.InstanceStatus.UP);
if (!skipEurekaRegistrationVerification) {
verifyEurekaRegistration();
}
}
@Override
public void destroy() {
statusChangeStrategy.update(applicationInfoManager, InstanceInfo.InstanceStatus.DOWN);
if (null != eurekaClient) {
eurekaClient.shutdown();
}
}
private InetAddress mapAddress(InstanceInfo instance) {
try {
if (useMetadataForHostAndPort) {
Map metadata = instance.getMetadata();
return InetAddress.getByName(metadata.get(EurekaHazelcastMetadata.HAZELCAST_HOST));
} else {
return InetAddress.getByName(instance.getIPAddr());
}
} catch (UnknownHostException e) {
getLogger().warning("InstanceInfo '" + instance + "' could not be resolved");
}
return null;
}
private int mapPort(InstanceInfo instance) {
Map metadata = instance.getMetadata();
if (metadata.containsKey(EurekaHazelcastMetadata.HAZELCAST_PORT)) {
return Integer.parseInt(metadata.get(EurekaHazelcastMetadata.HAZELCAST_PORT));
}
return -1;
}
@VisibleForTesting
void verifyEurekaRegistration() {
String applicationName = applicationInfoManager.getEurekaInstanceConfig().getAppname();
Application application;
do {
try {
getLogger().info("Waiting for registration with Eureka...");
application = eurekaClient.getApplication(applicationName);
if (application != null) {
getLogger().info("Registered in Eureka");
break;
}
} catch (Throwable t) {
if (t instanceof Error) {
throw (Error) t;
}
}
try {
TimeUnit.SECONDS.sleep(VERIFICATION_WAIT_TIMEOUT);
} catch (InterruptedException almostIgnore) {
Thread.currentThread().interrupt();
}
} while (true);
}
@VisibleForTesting
EurekaClient getEurekaClient() {
return eurekaClient;
}
private class EurekaOneAwareConfig extends DefaultEurekaClientConfig {
EurekaOneAwareConfig(String namespace) {
super(namespace);
}
@Override
public boolean shouldRegisterWithEureka() {
return statusChangeStrategy.shouldRegister();
}
}
private static final class DelegatingInstanceConfig
implements EurekaInstanceConfig {
private final EurekaInstanceConfig instanceConfig;
private final DiscoveryNode localNode;
private final String uuid;
private final String appname;
private DelegatingInstanceConfig(EurekaInstanceConfig instanceConfig, DiscoveryNode localNode) {
this(instanceConfig, localNode, instanceConfig.getAppname());
}
private DelegatingInstanceConfig(EurekaInstanceConfig instanceConfig, DiscoveryNode localNode, String appname) {
this.instanceConfig = instanceConfig;
this.localNode = localNode;
this.uuid = UuidUtil.newSecureUuidString();
this.appname = appname;
}
public String getInstanceId() {
return uuid;
}
public String getAppname() {
return appname;
}
public String getAppGroupName() {
return instanceConfig.getAppGroupName();
}
public boolean isInstanceEnabledOnit() {
return instanceConfig.isInstanceEnabledOnit();
}
public int getNonSecurePort() {
if (null == localNode) {
return instanceConfig.getNonSecurePort();
}
return localNode.getPrivateAddress().getPort();
}
public int getSecurePort() {
return instanceConfig.getSecurePort();
}
public boolean isNonSecurePortEnabled() {
return instanceConfig.isNonSecurePortEnabled();
}
public boolean getSecurePortEnabled() {
return instanceConfig.getSecurePortEnabled();
}
public int getLeaseRenewalIntervalInSeconds() {
return instanceConfig.getLeaseRenewalIntervalInSeconds();
}
public int getLeaseExpirationDurationInSeconds() {
return instanceConfig.getLeaseExpirationDurationInSeconds();
}
public String getVirtualHostName() {
return instanceConfig.getVirtualHostName();
}
public String getSecureVirtualHostName() {
return instanceConfig.getSecureVirtualHostName();
}
public String getASGName() {
return instanceConfig.getASGName();
}
public String getHostName(boolean refresh) {
return instanceConfig.getHostName(refresh);
}
public Map getMetadataMap() {
return instanceConfig.getMetadataMap();
}
public DataCenterInfo getDataCenterInfo() {
return instanceConfig.getDataCenterInfo();
}
public String getIpAddress() {
if (null == localNode) {
return instanceConfig.getIpAddress();
}
return localNode.getPrivateAddress().getHost();
}
public String getStatusPageUrlPath() {
return instanceConfig.getStatusPageUrlPath();
}
public String getStatusPageUrl() {
return instanceConfig.getStatusPageUrl();
}
public String getHomePageUrlPath() {
return instanceConfig.getHomePageUrlPath();
}
public String getHomePageUrl() {
return instanceConfig.getHomePageUrl();
}
public String getHealthCheckUrlPath() {
return instanceConfig.getHealthCheckUrlPath();
}
public String getHealthCheckUrl() {
return instanceConfig.getHealthCheckUrl();
}
public String getSecureHealthCheckUrl() {
return instanceConfig.getSecureHealthCheckUrl();
}
public String[] getDefaultAddressResolutionOrder() {
return instanceConfig.getDefaultAddressResolutionOrder();
}
public String getNamespace() {
return instanceConfig.getNamespace();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy