美文网首页Spring cloud
eureka 服务治理

eureka 服务治理

作者: 突突兔007 | 来源:发表于2018-09-21 17:51 被阅读98次

    Eureka服务注册与发现

    创建服务注册中心

    • 添加pom依赖
    <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
          </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    • 启动一个服务注册中心,只需要一个注解@EnableEurekaServer,这个注解需要在springboot工程的启动application类上加
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run( EurekaServerApplication.class, args );
        }
    }
    
    • 默认情况下erureka server也是一个eureka client ,必须要指定一个 server,eureka server的配置文件如下
    server.port=8761
    eureka.instance.hostname=localhost
    #防止自己注册自己
    eureka.client.register-with-eureka=false
    #注册中心的职责就是维护服务实例,它并不需要去检索服务,所以设置成false
    eureka.client.fetch-registry=false
    eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
    
    • 启动Eureka-server,查看UI界面


    创建服务提供者

    • 添加pom依赖
    <dependencies>
        <dependency>
           <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    <dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    • 通过注解@EnableEurekaClient 表明自己是一个eurekaclient.
    @SpringBootApplication
    @EnableEurekaClient
    @RestController
    public class ServiceHiApplication {
    
        public static void main(String[] args) {
            SpringApplication.run( ServiceHiApplication.class, args );
        }
    }
    
    • 配置文件配置
    server.port=8762
    #为服务命名
    spring.application.name=eureka-client
    #指定服务注册中心地址
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
    
    • 启动服务
      启动服务,如果控制台输入如下的日志,说明服务注册成功


      并且此时再去查看服务注册中心,会发现会有一个服务实力已注册到注册中心


    • 源码分析
      使用@EnableEurekaClient注解或者@EnableDiscoveryClient注解都可以完成服务的注册,我们可以先看下@EnableEurekaClient的源码,
    /**
     * Convenience annotation for clients to enable Eureka discovery configuration
     * (specifically). Use this (optionally) in case you want discovery and know for sure that
     * it is Eureka you want. All it does is turn on discovery and let the autoconfiguration
     * find the eureka classes if they are available (i.e. you need Eureka on the classpath as
     * well).
     *
     * @author Dave Syer
     * @author Spencer Gibb
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface EnableEurekaClient {
    
    }
    

    什么多余的注解都没有,只能看到这是一个简单的注解标识,我们可以看一下注释:

    /**
     * Convenience annotation for clients to enable Eureka discovery configuration
     * (specifically). Use this (optionally) in case you want discovery and know for sure that
     * it is Eureka you want. All it does is turn on discovery and let the autoconfiguration
     * find the eureka classes if they are available (i.e. you need Eureka on the classpath as
     * well).
     *
     * @author Dave Syer
     * @author Spencer Gibb
     */
    

    简单的翻译一下:

    为客户端提供方便的注释,以启用Eureka发现配置(特别是)。使用这个(可选),如果你想要发现,
    并确定它是你想要的Eureka。它所做的一切就是打开发现,并让自动配置在可用的情况下找到eureka类(即,您也需要类路径上的Eureka)。
    

    阅读上面的注释,我们很清楚的知道,其实@EnableEurekaClient注解就是一种方便使用eureka的注解而已,可以说使用其他的注册中心后,都可以使用@EnableDiscoveryClient注解,但是使用@EnableEurekaClient的情景,就是在服务采用eureka作为注册中心的时候,使用场景较为单一。

    我们在看一下EnableDiscoveryClient的源码:

    /**
     * Annotation to enable a DiscoveryClient implementation.
    * 注释以启用DiscoveryClient实现。
     * @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的实力。查看DiscoveryClient相关源码梳理得到如下图所示的关系:


    右边的org.springframework.cloud.client.discovery.DiscoveryClient是SpringCloud的接口,它定义了用来发现服务的常用方法,通过该接口有效的屏蔽服务治理的实现细节,所以使用SpringCloud构建的微服务应用可以很方便的切换不同的服务治理框架,而不改动程序代码,只需要添加一些针对服务治理框架的配置即可。如,该接口的实现类有如下几种,我们使用了其中的一种。 org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient是对该接口的实现,从命名来看,它实现的是对Eureka发现服务的封装,他们都是Netflix开源包中的内容,主要定义了针对Eureka的发现服务的抽象方法,而真正实现发现服务的则是Netflix包中的com.netflix.discovery.DiscoveryClient类。
    我们看一看com.netflix.discovery.DiscoveryClient类的注解:

    该类的核心功能就是完成向Eureka Server服务中心的注册,续约,当服务shutdown的时候取消租约,查询注册到Eureka Server的服务实例列表。
    • 服务注册
      我们可以看到带有@Inject注解的DiscoverClient的构造函数
    @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
            } catch (Throwable e) {
                throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
            }
    
            if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
                try {
                    if (!register() ) {
                        throw new IllegalStateException("Registration error at startup. Invalid server response.");
                    }
                } catch (Throwable th) {
                    logger.error("Registration error at startup: {}", th.getMessage());
                    throw new IllegalStateException(th);
                }
            }
        
    
            // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
            initScheduledTasks();
            ......
            ......
            initTimestampMs = System.currentTimeMillis();
            logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}",
                    initTimestampMs, this.getApplications().size());
        }
    

    可以看到 initScheduledTasks()方法,我们在具体看initScheduledTasks()方法

     /**
         * Initializes all scheduled tasks.
         */
        private void initScheduledTasks() {
            ...
    
            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);
    
                // InstanceInfo replicator
                instanceInfoReplicator = new InstanceInfoReplicator(
                        this,
                        instanceInfo,
                        clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                        2); // burstSize
                ......
                ......
                instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
            } else {
                logger.info("Not registering with Eureka server per configuration");
            }
        }
    

    "服务续约","服务注册"在同一个if语句中,服务注册到Eureka Server后,需要有一个心跳去续约,表明自己还活着,可用,防止被剔除。对于服务续约相关的时间控制参数:

    #指示eureka客户端需要向eureka服务器发送心跳的频率(以秒为单位),以指示该服务器仍处于活动状态
    eureka.instance.lease-renewal-interval-in-seconds=30
    #指示eureka服务器在接收到最后一个心跳之后等待的时间(以秒为单位),然后才可以从视图中删除该实例,
    eureka.instance.lease-expiration-duration-in-seconds=90
    

    可以继续查看InstanceInfoReplicator的run方法

     public void run() {
            try {
                discoveryClient.refreshInstanceInfo();
    
                Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
                if (dirtyTimestamp != null) {
                    discoveryClient.register();
                    instanceInfo.unsetIsDirty(dirtyTimestamp);
                }
            } catch (Throwable t) {
                logger.warn("There was a problem with the instance info replicator", t);
            } finally {
                Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
                scheduledPeriodicRef.set(next);
            }
        }
    

    可以看到discoveryClient.register()完成了服务的注册功能。继续查看register()的方法

    /**
         * 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;
        }
    

    可以看到注册的时候传入了instanceInfo对象,我们可以看看instanceInfo对象

    public class InstanceInfo {
    
        private static final String VERSION_UNKNOWN = "unknown";
    
        /**
         * {@link InstanceInfo} JSON and XML format for port information does not follow the usual conventions, which
         * makes its mapping complicated. This class represents the wire format for port information.
         */
        public static class PortWrapper {
            private final boolean enabled;
            private final int port;
    
            @JsonCreator
            public PortWrapper(@JsonProperty("@enabled") boolean enabled, @JsonProperty("$") int port) {
                this.enabled = enabled;
                this.port = port;
            }
    
            public boolean isEnabled() {
                return enabled;
            }
    
            public int getPort() {
                return port;
            }
        }
    
        private static final Logger logger = LoggerFactory.getLogger(InstanceInfo.class);
    
        public static final int DEFAULT_PORT = 7001;
        public static final int DEFAULT_SECURE_PORT = 7002;
        public static final int DEFAULT_COUNTRY_ID = 1; // US
    
        // The (fixed) instanceId for this instanceInfo. This should be unique within the scope of the appName.
        private volatile String instanceId;
    
        private volatile String appName;
        @Auto
        private volatile String appGroupName;
    
        private volatile String ipAddr;
    
        private static final String SID_DEFAULT = "na";
        @Deprecated
        private volatile String sid = SID_DEFAULT;
    
        private volatile int port = DEFAULT_PORT;
        private volatile int securePort = DEFAULT_SECURE_PORT;
    
        @Auto
        private volatile String homePageUrl;
        @Auto
        private volatile String statusPageUrl;
        @Auto
        private volatile String healthCheckUrl;
        @Auto
        private volatile String secureHealthCheckUrl;
        @Auto
        private volatile String vipAddress;
        @Auto
        private volatile String secureVipAddress;
        @XStreamOmitField
        private String statusPageRelativeUrl;
        @XStreamOmitField
        private String statusPageExplicitUrl;
        @XStreamOmitField
        private String healthCheckRelativeUrl;
        @XStreamOmitField
        private String healthCheckSecureExplicitUrl;
        @XStreamOmitField
        private String vipAddressUnresolved;
        @XStreamOmitField
        private String secureVipAddressUnresolved;
        @XStreamOmitField
        private String healthCheckExplicitUrl;
        @Deprecated
        private volatile int countryId = DEFAULT_COUNTRY_ID; // Defaults to US
        private volatile boolean isSecurePortEnabled = false;
        private volatile boolean isUnsecurePortEnabled = true;
        private volatile DataCenterInfo dataCenterInfo;
        private volatile String hostName;
        private volatile InstanceStatus status = InstanceStatus.UP;
        private volatile InstanceStatus overriddenStatus = InstanceStatus.UNKNOWN;
        @XStreamOmitField
        private volatile boolean isInstanceInfoDirty = false;
        private volatile LeaseInfo leaseInfo;
        @Auto
        private volatile Boolean isCoordinatingDiscoveryServer = Boolean.FALSE;
        @XStreamAlias("metadata")
        private volatile Map<String, String> metadata;
        @Auto
        private volatile Long lastUpdatedTimestamp;
        @Auto
        private volatile Long lastDirtyTimestamp;
        @Auto
        private volatile ActionType actionType;
        @Auto
        private volatile String asgName;
        private String version = VERSION_UNKNOWN;
    

    我们可以看到注册服务的信息包括port、instanceId、appName、ipAddr、homePageUrl、vipAddress......等等,该对象就是注册时客户端发送给服务端的元数据。

    • 服务获取与服务续约
      我们可以继续查看DiscoveryClient类的initScheduledTasks()方法
     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);
            }
            ......
            ......
    }
    

    默认情况下,eureka.client.fetch-registry=true,大部分情况下,我们不需要关心。为了定期更新服务端的服务清单,以保证客户端能够能够访问确实健康的服务实例,服务获取的请求不会只限于服务启动,而是一个定时执行的任务,eureka.client.registry-fetch-interval-seconds=30,默认值是30s,意思是从eureka获取注册表信息的频率(秒)是30秒。我们继续查看源码如下:


    服务续约
    /**
         * The heartbeat task that renews the lease in the given intervals.
         */
        private class HeartbeatThread implements Runnable {
    
            public void run() {
                if (renew()) {
                    lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
                }
            }
        }
    

    继续查看renew()方法,可以看到续约方式是 通过合适的rest方式调用完成与eureka service的续约。
    发送的信息也很简单,只需要三个参数:
    instanceInfo.getAppName():name of the application registering with discovery.
    instanceInfo.getId() :the unique id of the instance
    instanceInfo:The class that holds information required for registration with Eureka Server and to be discovered by other components.

     /**
         * Renew with the eureka service by making the appropriate REST call
         */
        boolean renew() {
            EurekaHttpResponse<InstanceInfo> httpResponse;
            try {
                httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
                logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
                if (httpResponse.getStatusCode() == 404) {
                    REREGISTER_COUNTER.increment();
                    logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
                    long timestamp = instanceInfo.setIsDirtyWithTime();
                    boolean success = register();
                    if (success) {
                        instanceInfo.unsetIsDirty(timestamp);
                    }
                    return success;
                }
                return httpResponse.getStatusCode() == 200;
            } catch (Throwable e) {
                logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
                return false;
            }
        }
    
    • 服务注册中心
      下面我们来看服务注册中心如何处理各类客户端的Rest请求的。Eureka Server对于各类REST请求的定义都位于com.netflix.eureka.resources包下,我们查看一下com.netflix.eureka.registry.AbstractInstanceRegistryregister()方法
    /**
         * Registers a new instance with a given duration.
         *
         * @see com.netflix.eureka.lease.LeaseManager#register(java.lang.Object, int, boolean)
         */
        public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
                ...
                read.lock();
                Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
                REGISTER.increment(isReplication);
                if (gMap == null) {
                    final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
                    gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
                    if (gMap == null) {
                        gMap = gNewMap;
                    }
                }
               ......
         
        }
    

    我们可以看到服务注册的信息被存储在了registry对象中,该对象是ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>,注册中心存储了两层Map结构,第一层的key存储服务名:InstanceInfo中的appName属性,第二层的key存储实例名:InstanceInfo中的instanceId属性。

    配置详解

    在Eureka的服务治理体系中,主要分为客户端和服务端两个不同的角色,服务端为服务注册中心,而客户端为各个提供接口的微服务应用。

    Eureka客户端的配置主要分为以下两个方面:

    1. 服务注册相关的配置信息,包括服务注册中心的地址、服务获取的时间间隔、可用区域等
    2. 服务实例相关的配置信息,包括服务实例的名称、IP地址、端口号、健康检查路径等。
      Eureka Server的配置均已eureka.server作为前缀。

    服务注册类配置

    服务注册类的配置信息,可用查看org.springframework.cloud.netflix.eureka.EurekaClientConfigBean。这些配置信息都已eureka.client为前缀。我们针对一些常用的配置信息做一些进一步的介绍和说明。

    指定注册中心

    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

    当构建高可用的服务注册中心集群时,可以为value的值配置多个注册中心地址,以逗号分隔,如:eureka.client.service-url.defaultZone=http://localhost:8761/eureka/,eureka.client.service-url.defaultZone=http://localhost:8762/eureka/
    为了服务注册中心的安全考虑,可以为注册中心加上安全校验,在配置serviceUrl的时候,value的值格式如下:http://<username>:<password>@localhost:8761/eureka/。其中,<username>为安全校验信息的用户名,<password>为该用户的密码。

    其他配置

    下面整理了org.springframework.cloud.netflix.eureka.EurekaClientConfigBean中定义的常用配置参数以及对应的说明和默认值,这些参数均已erureka.client为前缀,我们可以看下EurekaClientConfigBean的定义

    public static final String PREFIX = "eureka.client";
    
    参数名 说明 默认值
    eureka.client.enabled 标志,指示启用了Eureka客户端。 true
    eureka.client.encoder-name 这是一个临时配置,一旦最新的编解码器稳定下来,就可以删除(因为只有一个)  
    eureka.client.register-with-eureka 此实例是否应向eureka服务器注册其信息以供其他人发现。在某些情况下,您不希望您的实例被发现,而您只是希望发现其他实例。 true
    eureka.client.registry-fetch-interval-seconds 指示从eureka服务器获取注册表信息的频率(秒)。 30
    eureka.client.instance-info-replication-interval-seconds 更新实例信息的变化到Eureka服务端的间隔时间,单位为秒 30
    eureka.client.eureka-server-read-timeout-seconds 读取Eureka Server的超时时间,单位秒 8
    eureka.client.heartbeat-executor-exponential-back-off-bound 心跳超时重试延迟时间的最大乘数值 10
    eureka.client.heartbeat-executor-thread-pool-size 心跳连接池的初始化线程数 2
    eureka.client.fetch-registry 客户端是否应从eureka服务器获取eureka注册表信息。 true

    服务实例类配置

    关于服务实例类的信息我们可以看org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean源码,这些信息以eureka.instance为前缀。针对常用配置做一些讲解

    元数据

    EurekaInstanceConfigBean的配置中,有一大部分内容都是针对服务实力元数据(用来描述自身服信息的对象,包括服务名称,实例名称,ip,端口等,以及一些用于负载均衡策略或是其他特殊用途的自定义元数据信息)的配置。我们可以通过eureka.instance.<properties>=<value>的格式对标准化的元数据直接进行配置。自定义元数据:eureka.instance.metaMap.<key>=<value>。如eureka.instance.metaMap.zone=shanghai

    实例名配置

    InstanceInfo中的instanceId属性。区分同一服务中不同实例的唯一标识。在Spring Cloud Eureka的配置中,针对同一主机上启动多个实例的情况,对实例名的默认命名组了更为合理的扩展,它采用了如下规则:

    ${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
    

    对于实例名的命名规则,我们可以通过eureka.instance.instanceId参数配置
    如:eureka.instance.instanceId=${spring.application.name}:${random.int},这样子可以解决同一主机上,不指定端口就能轻松启动多个实例的效果。

    端点配置

    为了服务的正常运行,我们必须确保Eureka客户端的/health端点在发送元数据的时候,是一个能被注册中心访问的地址,否则服务注册中心不会根据应用的健康检查来更改状态(仅当开启healthcheck功能时,以该端点信息作为健康检查标准)有时候为了安全考虑,也有可能修改/info和/heath端点的原始路径。这个时候,我们需要做一些特殊的配置

    #endpoints.info.path=/appInfo弃用
    #endpoints.health.path==/checkHealth 弃用,可以使用如下配置
    management.endpoints.web.path-mapping.info=/appInfo
    management.endpoints.web.path-mapping.health=/checkHealth
    
    eureka.instance.status-page-url-path=${management.endpoints.web.path-mapping.info}
    eureka.instance.health-check-url-path=${management.endpoints.web.path-mapping.health}
    

    当客户端服务以https方式暴露服务和监控端点时,相对路径的配置方式就无法满足需求了。所以Spring Cloud Eureka还提供了绝对路径的配置参数,具体示例如下:

    eureka.instance.home-page-url=https://${eureka.instance.hostname}/home
    eureka.instance.health-check-url=https://${eureka.instance.hostname}/health
    eureka.instance.status-page-url=https://${eureka.instance.hostname}/info
    

    健康检查

            默认情况下, Eureka中各个服务实例的健康检测并不是通过 spring-boot- actuator模块的/ health端点来实现的,而是依靠客户端心跳的方式来保持服务实例的存活。在Eureka的服务续约与剔除机制下,客户端的健康状态从注册到注册中心开始都会处于UP状态,除非心跳终止一段时间之后,服务注册中心将其剔除。默认的心跳实现方式可以有效检查客户端进程是否正常运作,但却无法保证客户端应用能够正常提供服务。由于大多数微服务应用都会有一些其他的外部资源依赖,比如数据库、缓存、消息代理等,如果我的应用与这些外部资源无法联通的时候,实际上已经不能提供正常的对外服务了,但是因为客户端心跳依然在运行,所以它还是会被服务消费者调用,而这样的调用实际上并不能获得预期的结果。
            在 Spring Cloud Eureka中,我们可以通过简单的配置,把 Eureka客户端的健康检测交给 spring-boot- actuator模块的/ health端点,以实现更加全面的健康状态维护。详细的配置步骤如下所示:

    • 在pom,xm1中引入 spring-boot- starter- actuator模块的依赖。
    • 在 application, properties中增加参数配置 eureka.client.healthcheck.enabled=true。
    • 如果客户端的/ health端点路径做了一些特殊处理,请参考前文介绍端点配置时的方法进行配置,让服务注册中心可以正确访问到健康检测端点。

    AWS的区域和可用区概念解释

    eureka官网

    相关文章

      网友评论

      本文标题:eureka 服务治理

      本文链接:https://www.haomeiwen.com/subject/vrcnwftx.html