Spring Cloud Eureka 是一个基于REST的服务,并且提供了基于Java的客户端组件,能够非常方便地将服务注册到Spring Cloud Eureka中进行统一管理。

服务治理是微服务架构中必不可少的一部分,阿里开源的Dubbo框架就是针对服务治理的。服务治理必须要有一个注册中心,除了用Eureka作为注册中心外,我们还可以使用Consul、Zookeeper等来作为服务的注册中心。

注册中心带来的好处就是,不需要知道有多少提供方,你只需要关注注册中心即可,就像顾客不必关心有多少火车在开,只需要去12306网站上看有没有票就可以了。

为什么Eureka比Zookeeper更合适作为注册中心呢??主要是因为Eureka是基于AP原则构建的,而Zookeeper是基于CP原则构建的。

在分布式系统领域有个著名的CAP定理。即 C 为数据一致性;A 为服务可用性;P 为服务对网络分区故障的容错性。这三个特性在任何分布式系统中都不能同时满足,最多同时满足两个。

Zookeeper有一个Leader,而且在这个Leader无法使用的时候通过Paxos(ZAB)算法选举出一个新的Leader。这个Leader的任务就是保证写数据的时候只向这个Leader写入,Leader会同步信息到其他节点。通过这个操作就可以保证数据的一致性。

总而言之,想要保证AP就要用Eureka,想要保证CP就要用Zookeeper。

1. 使用 Eureka 编写注册中心服务

首先使用Eureka搭建服务注册中心,创建一个Maven项目,取名为eureka-server,在pom.xml中配置Eureka的依赖信息,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
</dependencies>

接下来在src/main/resources下面创建一个application.properties属性文件,增加下面的配置:

1
2
3
4
5
6
7
8
9
10
11
# 服务器端口
server.port=8010

# 服务器名称
spring.application.name=eureka-server

# 是否将自己注册进注册中心
eureka.client.register-with-eureka=false

# 是否检索服务,注册中心的职责是维护服务实例,所以不检索服务
eureka.client.fetch-registry=false

【注意:eureka.client.register-with-eureka一定要设置为false,因为自身本来就是server端,没办法将自己注册进去,所设置为true会报错】。

创建一个启动类EurekaServerApplication,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.self.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}

这里所说的启动类,跟一般的SpringBoot应用没什么区别,只是多了一个@EnableEurekaServer注解,表示开启Eureka Server。

最后,直接运行EurekaServerApplication就可以启动我们的注册中心服务了。可以直接通过http://localhost:8010访问,然后便会看到Eureka提供的Web控制台。

2. 使用Eureka编写服务提供者

注册中心已经创建并且启动好了,接下来我们实现将一个服务提供者eureka-client-user-provider注册到Eureka中,并提供一个接口给其他服务调用。

2.1 创建项目注册到Eureka中

首先,创建一个Maven项目,然后在pom.xml中添加相关依赖,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
</dependencies>

接下来在src/main/resources下面创建一个application.properties属性文件,添加下面的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 服务端口
server.port=8011

# 服务应用名称
spring.application.name=eureka-client-user-provider

# Eureka注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:8010/eureka/

# 使用IP注册
eureka.instance.prefer-ip-address=true

# 定义实例ID格式
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}

创建一个启动类UserProviderApplication,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.self.serverprovider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class UserProviderApplication {

public static void main(String[] args) {
SpringApplication.run(UserProviderApplication.class,args);
}
}

我们可以进一步检查服务是否注册成功。回到之前打开的Eureka的Web控制台,刷新页面,就可以看到新注册的服务信息了。结果如下:

不过需要注意的是,虽然是可以将服务提供者注册进注册中心,但是服务器控制台会一直报连接8761的错误,Eureka 注册中心一直报Connect to localhost:8761 time out 的问题,错误如图:

解决这个问题很简单,只需要在注册中心的application.properties文件中加入defaultZone即可,因为默认是会连接8761端口,所以需要设置一个defaultZone进行覆盖默认的。

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

一般还会有一个警告,具体问题如下:

1
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

这是Eureka的自我保护模式被启动了,当Eureka Server节点在短时间内丢失过多的实例的连接时,那么这个节点就会进入自我保护模式。一旦进入到该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(即不会注销任何微服务)。

默认情况下,如果Eureka Server在一段时间内没有接受到某个微服务实例的心跳,便会注销该实例(默认是90秒),而一旦进入自我保护模式,那么即使关闭了指定实例,依然会发现该Eureka Server的注册实例中会存在被关闭的实例信息。

可以通过以下配置关闭Eureka的自我保护模式:

1
2
3
4
 #关闭自我保护机制,保证不可用服务被及时剔除
eureka.server.enable-self-preservation: false
#清理无效节点的时间间隔,默认60000毫秒,即60秒 (此处时间间隔设置为2s)
eureka.server.eviction-interval-timer-in-ms: 2000

建议:

对于开发环境的Eureka Server,建议关闭它的自我保护模式,因为你可能需要不断的开启和关闭实例,如果并未关闭自我保护模式,那么很容易就会触发自我保护模式,此时调试会相当麻烦。

2.2 提供消费者调用的接口

创建一个Controller,这是给其他服务(消费者)调用的接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.self.serverprovider.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

@GetMapping("hello")
public String hello(){

return "Hello world";
}
}

重启服务后,访问http:localhost:8011/user/hello,若能看到返回的Hello world 字符串,就证明接口提供成功。

3. 使用Eureka编写服务消费者

3.1 直接调用服务提供者提供的接口

创建服务消费者,消费上面岗编写的user/hello接口。首先同样需要先创建一个Maven项目eureka-client-article-consumer,然后添加依赖,依赖和服务提供者的一样,这里就不粘贴代码了。

application.properties代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 服务端口
server.port=8012

# 应用服务名称
spring.application.name=eureka-client-article-consumer

# Eureka注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8010/eureka/

# 使用IP注册
eureka.instance.prefer-ip-address=true

# 定义实例ID格式
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}

【注意:服务消费者也需要加入注册中心,所以在application.properties中添加注册中心的地址】

主启动类的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.self.articleconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ArticleConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(ArticleConsumerApplication.class,args);
}
}

接下来就可以调用user/hello实现功能,但是如何才能调用到另一个模块中的接口呢??

RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。我们通过配置RestTemplate来调用接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.self.articleconsumer.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanConfiguration {

@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}

创建接口,在接口中调用user/hello接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.self.articleconsumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ArticleController {

@Autowired
private RestTemplate restTemplate;

@GetMapping("/article/callHello")
public String callHello(){
return restTemplate.getForObject("http://localhost:8011/user/hello",String.class);
}
}

然后,将注册中心,服务提供者以及服务消费者的模块分别启动之后,就可以通过访问/article/callHello接口来看看是否有结果返回,若返回了就证明调用成功。访问地址为:http://localhost:8012/article/callHello。

结果如下截图:

3.2 通过Eureka来消费接口

上面提到的方法是直接通过服务接口的地址来调用的,这样是可行,但是这样就完全没有用到Eureka带给我们的便利。既然用来注册中心,那么客户端调用的时候肯定是不需要关心有多少个服务提供接口,下面我们来改造一下之前的调用代码。

首先改造RestTemplate的配置,添加一个@LoadBalanced注解,这个注解会自动改造LoadBalancerClient接口的实现类并注册到Spring容器中,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.self.articleconsumer.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanConfiguration {

@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}

@LoadBalanced注解:

作用:在使用RestTemplate的使用,如果RestTemplate上面有这个注解,那么这个RestTemplate调用的远程地址,会走负载均衡器。

原理:使用这个注解以后,会在restTemplate里面通过restTemplate.setInterceptors放入LoadBalancedInterceptor,这个拦截器会在请求远程接口的时候动态判断请求的域是不是负载均衡服务的地址,如果是,那么就会代理使用这个负载均衡器来调用。

接下来就是写调用接口的代码,我们不再直接写固定地址,而是写成服务的名称,这个名称就是我们注册到Eureka中的名称,即application.properties中的spring.application.name,相关代码如下:

1
2
3
4
@GetMapping("article/callHello2")
public String callHello2(){
return restTemplate.getForObject("http://eureka-client-user-provider/user/hello",String.class);
}

效果与之前的结果并没有什么区别。只是调用的接口不一样,如下:

4. Eureka注册中心开启密码认证

因为Eureka自带了一个Web的管理界面,方便我们查询注册到上面的实例信息,但是也有一个问题:如果在实际使用中,注册中心地址有公网IP的话,必然能直接访问到,这样是不安全的。所以我们需要对Eureka进行改造,加上权限认证来保证安全性。

改造我们的eureka-server,通过Spring-Security来进行安全认证。

在pom.xml中添加Spring-Security的依赖包,代码如下:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>

然后在application.properties中加入认证的配置信息:

1
2
3
# spring-security的配置信息:
spring.security.user.name=xiaozeng
spring.security.user.password=123456

添加Security的配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.self.eurekaserver.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
//关闭csrf
http.csrf().disable();
//支持httpBasic
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}

至此,重新启动注册中心,访问http://localhost:8010/此时浏览器会提示你输入用户名和密码,输入正确后才能继续访问Eureka提供的管理界面。然后将在application.properties中设置的用户名和密码填写就可以访问,效果截图如下:

注意:在Eureka开启认证后,客户端注册的配置也要加上认证的用户名和密码信息,否则客户端是无法将服务注册进注册中心:

1
2
# Eureka注册中心地址
eureka.client.service-url.defaultZone=http://xiaozeng:123456@localhost:8010/eureka/

5. Eureka集群搭建

前面我们搭建的注册中心只适合本地开发使用,在生产环境中必须搭建一个集群来保证高可用。Eureka的集群搭建方法很简单:每一台Eureka只需要在配置中指定另外多个Eureka的地址就可以实现一个集群的搭建了。

下面我们以2个节点为例来说明搭建方式。假设我们有master和slave1两台机器,需要做的就是:

  • 将master注册到slave1中。
  • 将slave1注册到master中。

如果是3台机器,以此类推:

  • 将master注册到slave1和slave2中。
  • 将slave1注册到master和slave2中。
  • 将slave2注册到master和slave1中。

搭建集群步骤:

创建一个新的项目eureka-server-cluster,在该项目下分别创建eureka-server-master和eureka-server-slave1。代表的是master和slave1。

结构如下图:

在master和slave1项目的pom.xml中都导入下面的这些依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>

接下来写master的application.properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 服务器端口
server.port=8100

# 服务应用名称
spring.application.name=eureka-server-master

eureka.instance.hostname=eureka-server-master

# 此应用为注册中心,false:不向注册中心注册自己。
eureka.client.register-with-eureka = false

# 注册中心职责是维护服务实例,false:不检索服务。
eureka.client.fetch-registry=false

# 指向从节点的地址
eureka.client.service-url.defaultZone=http://xiaozeng:123456@eureka-server-slave1:8110/eureka/

# spring-security的配置信息:
spring.security.user.name=xiaozeng
spring.security.user.password=123456

slave的application.properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 服务器端口
server.port=8110

# 服务应用名称
spring.application.name=eureka-server-slave1

eureka.instance.hostname=eureka-server-slave1

# 此应用为注册中心,false:不向注册中心注册自己。
eureka.client.register-with-eureka = false

# 注册中心职责是维护服务实例,false:不检索服务。
eureka.client.fetch-registry=false

# 指向主节点的地址
eureka.client.service-url.defaultZone=http://xiaozeng:123456@eureka-server-master:8100/eureka/

# spring-security的配置信息:
spring.security.user.name=xiaozeng
spring.security.user.password=123456

然后就是master和slave的主启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.self.master;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class MasterApplication {

public static void main(String[] args) {
SpringApplication.run(MasterApplication.class,args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.self.slave;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class SlaveApplication {

public static void main(String[] args) {
SpringApplication.run(SlaveApplication.class,args);
}
}

最后,可以在master和slave中都配置上spring-security的配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.self.slave.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
//关闭csrf
http.csrf().disable();
//支持httpBasic
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}

到这一步之后就可以启动master和slave模块,分别访问http://localhost:8100、http://localhost:8110可以出现以下结果代表配置集群搭建成功。结果如下图:

上图是主节点中注册了从节点,下图中是从节点中注册了主节点:

这样就将master注册到了slave1中,将slave1注册到master中,无论谁出现问题,应用都能继续使用存活的注册中心。

6. Eureka的InstanceID配置。

1. 自定义Eureka的InstanceID

客户端在注册时,服务的Instance ID的默认值的格式如下:

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

翻译过来就是:”主机名:服务名称:服务端口”。当我们在Eureka的Web控制台查看服务注册信息的时候,就是这样的格式:

1
UP (1) - eureka-client-user-provider:8011

很多时候,我们想把IP显示在上述的格式中,此时,只要把主机名替换成IP就可以了,或者调整顺序也可以。可以改成下面的样子,用 “服务名称:服务所在IP:服务端口”的格式来定义:

1
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}

定义之后我们看到的就是eureka-client-user-provider:192168.41.5:8011,这样做的话,一眼就能知道是哪个服务,在哪台机器上,端口是多少。

我们还可以点击服务的instance ID进行跳转,这时候显示的名称虽然变成了IP,但是跳转的链接却还是主机名。

所以还需要加一个配置才能让跳转的链接变成我们想要的样子,使用IP进行注册:

1
eureka.instance.prefer-ip-address=true

2. 自定义实例跳转链接

通过刚刚的配置实现了用IP进行注册,当点击InstanceID进行跳转的时候,就可以用IP跳转了,跳转的地址默认是IP + Port/info。我们可以自定义这个跳转的地址:

1
eureka.instance.status-page-url=http://www.baidu.com

重启服务服务提供者后,当点击InstanceID的时候就可以跳往百度页面:

【注意:需要理解的是,将IP注册的配置和实例跳转链接的配置是需要配置在服务提供者,而非配置在注册中心。需要理清楚哪些配置配置在哪很重要】

7. Eureka开发时快速移除失效服务

在实际开发过程中,我们可能会不停地重启服务,由于Eureka有自己的保护机制,故节点下线后,服务信息还会一直存在于Eureka中。我们可以通过增加一些配置让移除的速度更快一些,当然只在开发环境下使用,生产环境不推荐使用。

首先在我们的 eureka-server 中增加两个配置,分别是关闭自我保护和清理间隔:

1
2
3
eureka.server.enable-self-preservation=false
# 清理间隔,默认 60000 毫秒
eureka.server.eviction-interval-timer-in-ms=5000

然后在具体的客户端服务中配置下面的内容:

1
2
3
4
5
eureka.client.healthcheck.enabled=true
# 默认 30 秒
eureka.instance.lease-renewal-interval-in-seconds=5
# 默认 90 秒
eureka.instance.lease-expiration-duration-in-seconds=5

eureka.client.healthcheck.enabled用于开启健康检查,需要在pom.xml中引入actuator的依赖,代码如下:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

其中:

  • eureka.instance.lease-renewal-interval-in-seconds 表示Eureka Client 发送心跳给server端的频率。

  • eureka.instance.lease-expiration-duration-in-seconds 表示Eureka Server 至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没有收到下一次心跳,则移除该instance。

更多的Instance配置信息可参考源码中的配置类:

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean。

更多的Server配置信息可参考源码中的配置类:

org.springframework.cloud.netflix.server.EurekaServerConfigBean。

8. Eureka的REST API及API扩展

这里讲解一些经常用到的配置信息以及Eureka的REST API,通过API 可以做一些扩展。

8.1 Eureka REST API

Eureka作为注册中心,其本质是存储了每个客户端的注册信息,Ribbon在转发的时候会获取注册中心的服务列表,然后根据对应的路由规则来选择一个服务给Feign来进行调用。如果我们不是Spring Cloud技术选型,也想用Eureka,可以吗??完全可以。

如果不是Spring Cloud技术栈,笔者推荐用Zookeeper,这样会方便些,当然用Eureka也是可以的,这样的话就会涉及如何注册信息、如何获取注册信息等操作,其实Eureka也考虑到了这点,提供了很多REST接口来给我们调用。

其实利用Eureka提供的API我们可以获取某个服务的实例信息,获取某个服务的注册信息,可以直接GET请求:

1
http://localhost:8010/eureka/apps/eureka-client-article-consumer

其中,localhost:8010是服务注册中心地址,eureka-client-article-consumer是应用名称(注册到注册中心的服务),即spring.application.name。

在浏览器中,数据的显示格式默认是XML格式的,如下图:

如果想反悔JSON数据的格式,可以用一些借口测试工具请求,比如Postman,在请求头添加下面两行代码即可。

1
2
Content-Type:application/json 
Accept: application/json

如果Eureka开启了认证,记得添加认证信息,用户名和密码必须是Base64编码过的Authorization: Basic 用户名:密码。Postman直接支持Basic认证,将选项从Headers切换到Authorization,选择认证方式为Basic Auth 就可以填写用户信息。

填写完成后直接发起请求就可以获取JSON数据的服务注册信息:

8.2 元数据的使用

Eureka的元数据有两种类型,分别是框架定好了的标准元数据和用户自定义数据。标准元数据指的是主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务列表中,用于服务之间的调用。自定义元数据可以使用eureka.instance.metadataMap进行配置。

自定义元数据说得通俗点就是自定义配置,我们可以为每个Eureka Client定义一些属于自己的配置,这个配置不会影响Eureka的功能。

自定义元数据可以用来做一些扩展信息,比如灰度发布之类的功能,可以用元数据来存储灰度发布的状态数据,Ribbon转发的时候就可以根据服务的元数据来做一些处理。当不需要灰度发布的时候可以调用Eureka提供的REST API将元数据清除掉。

下面我们可以自定义一个简单的元数据,在配置文件中配置:

1
eureka.instance.metadataMap.name=xiaozeng

上述代码中定义了一个key为name的配置,value是xiaozeng。重启服务,然后通过Eureka提供的REST API 来查看刚刚配置的元数据是否已经存在于Eureka中。

【注意: 元数据的配置是添加在客户端的配置文件中,而不是注册中心中】

8.3 EurekaClient的使用

当我们的项目集成了Eureka之后,可以通过EurekaClient来获取一些我们想要的数据,比如上面说的元数据,我们就可以通过EurekaClient来获取,不用再去调用Eureka提供的REST API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.self.articleconsumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EurekaClient {

@Autowired
private com.netflix.discovery.EurekaClient eurekaClient;

@GetMapping("/article/infos")
public Object getMetaData(){
return eurekaClient.getInstancesByVipAddress("eureka-client-article-consumer",false);
}
}

也可以用过Postman来调用接口查看返回的数据,这时我们会发现,通过EurekaClient 获取到的数据跟我们自己去调API获取的数据是一样的。从使用的角度来说,前者更方便。

除了使用EurekaClient,还可以使用DiscoveryClient,这个不是Feign自带的,是Spring Cloud重新封装的,类的路径为:org.springframework.cloud.client.discovery.DiscoveryClient。

1
2
3
4
5
6
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/article/infos")
public Object getMetaData() {
return discoveryClient.getInstances("eureka-client-article-consumer");
}