eureka 服務治理

Eureka服務注冊與發(fā)現(xiàn)

創(chuàng)建服務注冊中心

  • 添加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界面


創(chuàng)建服務提供者

  • 添加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/
  • 啟動服務
    啟動服務天通,如果控制臺輸入如下的日志,說明服務注冊成功


    并且此時再去查看服務注冊中心熄驼,會發(fā)現(xiàn)會有一個服務實力已注冊到注冊中心


  • 源碼分析
    使用@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發(fā)現(xiàn)配置(特別是)诺祸。使用這個(可選),如果你想要發(fā)現(xiàn)祭芦,
并確定它是你想要的Eureka筷笨。它所做的一切就是打開發(fā)現(xiàn),并讓自動配置在可用的情況下找到eureka類(即龟劲,您也需要類路徑上的Eureka)胃夏。

閱讀上面的注釋,我們很清楚的知道昌跌,其實@EnableEurekaClient注解就是一種方便使用eureka的注解而已仰禀,可以說使用其他的注冊中心后,都可以使用@EnableDiscoveryClient注解蚕愤,但是使用@EnableEurekaClient的情景答恶,就是在服務采用eureka作為注冊中心的時候,使用場景較為單一萍诱。

我們在看一下EnableDiscoveryClient的源碼:

/**
 * Annotation to enable a DiscoveryClient implementation.
* 注釋以啟用DiscoveryClient實現(xiàn)悬嗓。
 * @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的接口烫扼,它定義了用來發(fā)現(xiàn)服務的常用方法曙求,通過該接口有效的屏蔽服務治理的實現(xiàn)細節(jié)碍庵,所以使用SpringCloud構建的微服務應用可以很方便的切換不同的服務治理框架映企,而不改動程序代碼,只需要添加一些針對服務治理框架的配置即可静浴。如堰氓,該接口的實現(xiàn)類有如下幾種,我們使用了其中的一種苹享。
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient是對該接口的實現(xiàn)双絮,從命名來看,它實現(xiàn)的是對Eureka發(fā)現(xiàn)服務的封裝得问,他們都是Netflix開源包中的內(nèi)容囤攀,主要定義了針對Eureka的發(fā)現(xiàn)服務的抽象方法,而真正實現(xiàn)發(fā)現(xiàn)服務的則是Netflix包中的com.netflix.discovery.DiscoveryClient類宫纬。
我們看一看com.netflix.discovery.DiscoveryClient類的注解:

該類的核心功能就是完成向Eureka Server服務中心的注冊焚挠,續(xù)約,當服務shutdown的時候取消租約漓骚,查詢注冊到Eureka Server的服務實例列表蝌衔。

  • 服務注冊
    我們可以看到帶有@Inject注解的DiscoverClient的構造函數(shù)
@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");
        }
    }

"服務續(xù)約"蝌蹂,"服務注冊"在同一個if語句中噩斟,服務注冊到Eureka Server后,需要有一個心跳去續(xù)約孤个,表明自己還活著剃允,可用,防止被剔除齐鲤。對于服務續(xù)約相關的時間控制參數(shù):

#指示eureka客戶端需要向eureka服務器發(fā)送心跳的頻率(以秒為單位)硅急,以指示該服務器仍處于活動狀態(tài)
eureka.instance.lease-renewal-interval-in-seconds=30
#指示eureka服務器在接收到最后一個心跳之后等待的時間(以秒為單位),然后才可以從視圖中刪除該實例佳遂,
eureka.instance.lease-expiration-duration-in-seconds=90

可以繼續(xù)查看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()完成了服務的注冊功能营袜。繼續(xù)查看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......等等跪另,該對象就是注冊時客戶端發(fā)送給服務端的元數(shù)據(jù)。

  • 服務獲取與服務續(xù)約
    我們可以繼續(xù)查看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,大部分情況下免绿,我們不需要關心。為了定期更新服務端的服務清單擦盾,以保證客戶端能夠能夠訪問確實健康的服務實例嘲驾,服務獲取的請求不會只限于服務啟動淌哟,而是一個定時執(zhí)行的任務,eureka.client.registry-fetch-interval-seconds=30辽故,默認值是30s徒仓,意思是從eureka獲取注冊表信息的頻率(秒)是30秒。我們繼續(xù)查看源碼如下:


服務續(xù)約
/**
     * The heartbeat task that renews the lease in the given intervals.
     */
    private class HeartbeatThread implements Runnable {

        public void run() {
            if (renew()) {
                lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
            }
        }
    }

繼續(xù)查看renew()方法,可以看到續(xù)約方式是 通過合適的rest方式調(diào)用完成與eureka service的續(xù)約誊垢。
發(fā)送的信息也很簡單掉弛,只需要三個參數(shù):
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. 服務注冊相關的配置信息携取,包括服務注冊中心的地址、服務獲取的時間間隔帮孔、可用區(qū)域等
  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中定義的常用配置參數(shù)以及對應的說明和默認值勉失,這些參數(shù)均已erureka.client為前綴,我們可以看下EurekaClientConfigBean的定義

public static final String PREFIX = "eureka.client";
參數(shù)名 說明 默認值
eureka.client.enabled 標志原探,指示啟用了Eureka客戶端乱凿。 true
eureka.client.encoder-name 這是一個臨時配置,一旦最新的編解碼器穩(wěn)定下來咽弦,就可以刪除(因為只有一個) ?
eureka.client.register-with-eureka 此實例是否應向eureka服務器注冊其信息以供其他人發(fā)現(xiàn)徒蟆。在某些情況下,您不希望您的實例被發(fā)現(xiàn)型型,而您只是希望發(fā)現(xiàn)其他實例段审。 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 心跳超時重試延遲時間的最大乘數(shù)值 10
eureka.client.heartbeat-executor-thread-pool-size 心跳連接池的初始化線程數(shù) 2
eureka.client.fetch-registry 客戶端是否應從eureka服務器獲取eureka注冊表信息。 true

服務實例類配置

關于服務實例類的信息我們可以看org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean源碼绷落,這些信息以eureka.instance為前綴姥闪。針對常用配置做一些講解

元數(shù)據(jù)

EurekaInstanceConfigBean的配置中,有一大部分內(nèi)容都是針對服務實力元數(shù)據(jù)(用來描述自身服信息的對象砌烁,包括服務名稱筐喳,實例名稱,ip函喉,端口等,以及一些用于負載均衡策略或是其他特殊用途的自定義元數(shù)據(jù)信息)的配置避归。我們可以通過eureka.instance.<properties>=<value>的格式對標準化的元數(shù)據(jù)直接進行配置。自定義元數(shù)據(jù):eureka.instance.metaMap.<key>=<value>管呵。如eureka.instance.metaMap.zone=shanghai

實例名配置

InstanceInfo中的instanceId屬性梳毙。區(qū)分同一服務中不同實例的唯一標識。在Spring Cloud Eureka的配置中捐下,針對同一主機上啟動多個實例的情況顿天,對實例名的默認命名組了更為合理的擴展,它采用了如下規(guī)則:

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

對于實例名的命名規(guī)則蔑担,我們可以通過eureka.instance.instanceId參數(shù)配置
如:eureka.instance.instanceId=${spring.application.name}:${random.int},這樣子可以解決同一主機上牌废,不指定端口就能輕松啟動多個實例的效果。

端點配置

為了服務的正常運行啤握,我們必須確保Eureka客戶端的/health端點在發(fā)送元數(shù)據(jù)的時候鸟缕,是一個能被注冊中心訪問的地址,否則服務注冊中心不會根據(jù)應用的健康檢查來更改狀態(tài)(僅當開啟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方式暴露服務和監(jiān)控端點時侵贵,相對路徑的配置方式就無法滿足需求了。所以Spring Cloud Eureka還提供了絕對路徑的配置參數(shù)缘薛,具體示例如下:

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端點來實現(xiàn)的,而是依靠客戶端心跳的方式來保持服務實例的存活窍育。在Eureka的服務續(xù)約與剔除機制下,客戶端的健康狀態(tài)從注冊到注冊中心開始都會處于UP狀態(tài),除非心跳終止一段時間之后,服務注冊中心將其剔除。默認的心跳實現(xiàn)方式可以有效檢查客戶端進程是否正常運作,但卻無法保證客戶端應用能夠正常提供服務宴胧。由于大多數(shù)微服務應用都會有一些其他的外部資源依賴,比如數(shù)據(jù)庫漱抓、緩存、消息代理等,如果我的應用與這些外部資源無法聯(lián)通的時候,實際上已經(jīng)不能提供正常的對外服務了,但是因為客戶端心跳依然在運行,所以它還是會被服務消費者調(diào)用,而這樣的調(diào)用實際上并不能獲得預期的結果恕齐。
????????在 Spring Cloud Eureka中,我們可以通過簡單的配置,把 Eureka客戶端的健康檢測交給 spring-boot- actuator模塊的/ health端點,以實現(xiàn)更加全面的健康狀態(tài)維護乞娄。詳細的配置步驟如下所示:

  • 在pom,xm1中引入 spring-boot- starter- actuator模塊的依賴。
  • 在 application, properties中增加參數(shù)配置 eureka.client.healthcheck.enabled=true显歧。
  • 如果客戶端的/ health端點路徑做了一些特殊處理,請參考前文介紹端點配置時的方法進行配置,讓服務注冊中心可以正確訪問到健康檢測端點仪或。

AWS的區(qū)域和可用區(qū)概念解釋

eureka官網(wǎng)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市士骤,隨后出現(xiàn)的幾起案子溶其,更是在濱河造成了極大的恐慌,老刑警劉巖敦间,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓶逃,死亡現(xiàn)場離奇詭異,居然都是意外死亡廓块,警方通過查閱死者的電腦和手機厢绝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來带猴,“玉大人昔汉,你說我怎么就攤上這事∷┣澹” “怎么了靶病?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長口予。 經(jīng)常有香客問我娄周,道長,這世上最難降的妖魔是什么沪停? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任煤辨,我火速辦了婚禮裳涛,結果婚禮上,老公的妹妹穿的比我還像新娘众辨。我一直安慰自己端三,他們只是感情好,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布鹃彻。 她就那樣靜靜地躺著郊闯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛛株。 梳的紋絲不亂的頭發(fā)上团赁,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音泳挥,去河邊找鬼然痊。 笑死至朗,一個胖子當著我的面吹牛屉符,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锹引,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼矗钟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嫌变?” 一聲冷哼從身側響起吨艇,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤牵舱,失蹤者是張志新(化名)和其女友劉穎著觉,沒想到半個月后睡榆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體校坑,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡窟蓝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年谒撼,在試婚紗的時候發(fā)現(xiàn)自己被綠了胖喳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丸边。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡凸舵,死狀恐怖祖娘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啊奄,我是刑警寧澤渐苏,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站菇夸,受9級特大地震影響琼富,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庄新,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一公黑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦凡蚜、人聲如沸人断。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恶迈。三九已至,卻和暖如春谱醇,著一層夾襖步出監(jiān)牢的瞬間暇仲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工副渴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奈附,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓煮剧,卻偏偏與公主長得像斥滤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子勉盅,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理佑颇,服務發(fā)現(xiàn),斷路器草娜,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • Eureka是什么挑胸,SpringCloud用Eureka來干什么等等相關概念就不細說了,網(wǎng)上有大把大把的解釋宰闰,甚至...
    liuyiyou閱讀 1,144評論 0 50
  • 1茬贵、服務治理三個核心點、服務提供者移袍、服務消費者解藻、服務注冊中心 結合Ribbon 服務治理圖 服務治理時序圖 服務提...
    高山之水閱讀 359評論 0 0
  • 并不是每個女人天生都會化妝舆逃,化妝時有很多禁忌,不少都是日常生活中你不經(jīng)意的化妝習慣戳粒,別小看這些小習慣路狮,也許會毀你容...
    美體美顏美好生活閱讀 170評論 0 0
  • 成熟是種心境,豁達樂觀蔚约。 不去抱怨奄妨,不去怪罪,不怨天尤人苹祟,不消沉感懷砸抛。 明得今之境评雌,皆緣我命,即以注定直焙,便又為何玻...
    若小言閱讀 510評論 5 11