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的实例

Eureka关系图

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等.

results matching ""

    No results matching ""