Eureka
Eureka
1. SpringBoot 配置
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2. Eureka
2.1 EnableDiscoveryClient注解
/**
* Annotation to enable a DiscoveryClient implementation.
* @author Spencer Gibb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
/**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}
主要用来开启DiscoveryClient的实例

org.springframework.cloud.client.discovery.DiscoveryClient(左侧) : Spring Cloud的接口,定义了用户发现服务的常用抽象方法
/**
* DiscoveryClient represents read operations commonly available to Discovery service such as
* Netflix Eureka or consul.io
*/
public interface DiscoveryClient {
/**
* A human readable description of the implementation, used in HealthIndicator
* @return the description
*/
String description();
/**
* Get all ServiceInstances associated with a particular serviceId
* @param serviceId the serviceId to query
* @return a List of ServiceInstance
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return all known service ids
*/
List<String> getServices();
}
com.netflix.discovery.DiscoveryClient(右侧): Netflix继承Eureka的发现服务的方法, 真正实现发现服务的类
Eureka Client负责的任务:
- 向Eureka Server注册服务实例
- 向Eureka Server服务续租
- 当服务关闭期间, 向Eureka Server取消租约
- 查询Eureka Server中的服务实例列表
@ImplementedBy(DiscoveryClient.class)
public interface EurekaClient extends LookupService {
// ========================
// getters for InstanceInfo
// ========================
public Applications getApplicationsForARegion(@Nullable String region);
public Applications getApplications(String serviceUrl);
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure);
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure, @Nullable String region);
public List<InstanceInfo> getInstancesByVipAddressAndAppName(String vipAddress, String appName, boolean secure);
// ===========================
// healthcheck related methods
// ===========================
public void registerHealthCheck(HealthCheckHandler healthCheckHandler);
public void registerEventListener(EurekaEventListener eventListener);
public boolean unregisterEventListener(EurekaEventListener eventListener);
public HealthCheckHandler getHealthCheckHandler();
// =============
// other methods
// =============
public void shutdown();
public EurekaClientConfig getEurekaClientConfig();
public ApplicationInfoManager getApplicationInfoManager();
}
@Singleton
public class DiscoveryClient implements EurekaClient {
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider) {
...
try {
// default size of 2 - 1 each for heartbeat and cacheRefresh
scheduler = Executors.newScheduledThreadPool(2,
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-%d")
.setDaemon(true)
.build());
heartbeatExecutor = new ThreadPoolExecutor(
1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
cacheRefreshExecutor = new ThreadPoolExecutor(
1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
eurekaTransport = new EurekaTransport();
scheduleServerEndpointTask(eurekaTransport, args);
AzToRegionMapper azToRegionMapper;
if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig);
} else {
azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig);
}
if (null != remoteRegionsToFetch.get()) {
azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(","));
}
instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, clientConfig.getRegion());
} catch (Throwable e) {
throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
}
...
}
/**
* 使用HTTP的形式注册服务
* Register with the eureka service by making the appropriate REST call.
*/
boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == 204;
}
/**
* 服务续约和服务注册
* Initializes all scheduled tasks.
*/
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
scheduler.schedule(
new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}
if (clientConfig.shouldRegisterWithEureka()) {
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);
// Heartbeat timer
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);
} else {
logger.info("Not registering with Eureka server per configuration");
}
}
}
2.2 Eureka配置
配置参考:
org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean
org.springframework.cloud.netflix.eureka.EurekaClientConfigBean
服务注册
- 服务注册中心地址
- 服务获取的间隔时间
- 可用区域等
均以eureka.instance为前缀
| 参数名 | 说明 | 默认值 |
|---|---|---|
| preferIpAddress | 是否优先使用IP地址作为主机名的标识 | false |
| leaseRenewalIntervalInSeconds | Eureka客户端向服务端发送心跳的时间间隔,单位为秒 | 30 |
| leaseExpirationDurationInSeconds | Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒。超过该时间之后服务端会将该服务实例从服务清单中剔除,从而禁止服务调用请求被发送到该实例上 | 90 |
| nonSecurePort | 非安全的通信端口号 | 80 |
| securePort | 安全的通信端口号 | 443 |
| nonSecurePortEnabled | 是否启用非安全的通信端口号 | true |
| securePortEnabled | 是否启用安全的通信端口号 | |
| appname | 服务名,默认取spring.application.name的配置值,如果没有则为unknown | |
| hostname | 主机名,不配置的时候讲根据操作系统的主机名来获取 |
服务实例
- 名称
- IP地址
- 端口号
- 健康检查路径
常用配置均已eureka.client为前缀
| 参数名 | 说明 | 默认值 |
|---|---|---|
| enabled | 启用Eureka客户端 | true |
| registryFetchIntervalSeconds | 从Eureka服务端获取注册信息的间隔时间,单位为秒 | 30 |
| instanceInfoReplicationIntervalSeconds | 更新实例信息的变化到Eureka服务端的间隔时间,单位为秒 | 30 |
| initialInstanceInfoReplicationIntervalSeconds | 初始化实例信息到Eureka服务端的间隔时间,单位为秒 | 40 |
| eurekaServiceUrlPollIntervalSeconds | 轮询Eureka服务端地址更改的间隔时间,单位为秒。当我们与Spring CLoud Config整合,动态刷新Eureka的serviceURL地址时需要关注该参数 | 300 |
| eurekaServerReadTimeoutSeconds | 读取Eureka Server信息的超时时间,单位为秒 | 8 |
| eurekaServerConnectTimeoutSeconds | 链接Eureka Server的超时时间,单位为秒 | 5 |
| eurekaServerTotalConnections | 从Eureka客户端到所有Eureka服务端的连接总数 | 200 |
| eurekaServerTotalConnectionsPerHost | 从Eureka客户端到每个Eureka服务端主机的连接总数 | 50 |
| eurekaConnectionIdleTimeoutSeconds | Eureka服务端连接的空闲关闭时间,单位为秒 | 30 |
| heartbeatExecutorThreadPoolSize | 心跳连接池的初始化线程数 | 2 |
| heartbeatExecutorExponentialBackOffBound | 心跳超时重试延迟时间的最大乘数值 | 10 |
| cacheRefreshExecutorThreadPoolSize | 缓存刷新线程池的初始化线程数 | 2 |
| cacheRefreshExecutorExponentialBackOffBound | 缓存刷新重试延迟时间的最大乘数值 | 10 |
| useDnsForFetchingServiceUrls | 使用DNS来获取Eureka服务端的serviceUrl | false |
| registerWithEureka | 是否要将自身的实例信息注册到Eureka服务端 | true |
| preferSameZoneEureka | 是否偏好使用处于相同Zone的Eureka服务端 | true |
| filterOnlyUpInstances | 获取实例时是否过滤,仅保留UP状态的实例 | true |
| fetchRegistry | 是否从Eureka服务端获取注册信息 | true |
3. 跨平台支持
Eureka的通信机制使用了 HTTP 的 REST 接口实现, 这也是 Eureka 同其他服务注册工具的一个关键不同点. 由于 HTTP 的平台无关性, 虽然Eureka Server 通过Java实现, 但是在其他的微服务应用中并不限于使用Java来进行开发.
跨平台本事就是微服务架构的九大特性之一, 只有实现了对技术平台的透明, 才能更好地发挥不同余元对不同业务处理能力的优势,从而打造更为强大的大型系统.
处理Eureka 的java客户端只有, 还有其它语言平台对其支持, 比如 eureka-js-client, python-eureka等.