0%

SpringCloud下基于Eureka的服务注册与发现实践

Spring Cloud Eureka作为一个服务注册与发现组件,是基于Netflix Eureka的二次封装,是Spring Cloud的重要组成部分

abstract.png

单机版Eureka

搭建Eureka注册中心

POM依赖

在Eureka中具体包含两个组件:Eureka Server、Eureka Client。前者即为注册中心;后者则用于完成服务向注册中心的注册与发现。故这里先建立一个SpringBoot项目,用于作为Eureka Server。其POM中相关依赖如下所示

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
32
33
<dependencyManagement>
<dependencies>

<!--Spring Boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

</dependencies>
</dependencyManagement>

<dependencies>

<!--Eureka Server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

</dependencies>

配置文件

为了便于后续演示,我们在hosts文件中先添加映射,如下所示

figure 1.jpeg

配置文件application.yml如下所示。这里我们不需要注册中心对自身进行注册,故register-with-eureka、fetch-registry设为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 7001

spring:
application:
name: eureka server

eureka:
instance:
hostname: eureka7001.com # Eureka Server的实例名称
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: false
# 是否从Eureka Server获取注册信息, 默认为true
fetch-registry: false
service-url:
# 单机版Eureka Server: 指向自己
defaultZone: http://localhost:7001/eureka/

Java实现

作为Eureka Server而言,Java代码比较简单。我们只需提供一个SpringBoot启动类即可。值得一提的是,需要在该启动类上添加 @EnableEurekaServer 注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

搭建服务提供者

POM依赖

同样再次建立一个SpringBoot项目Payment用于对外提供服务。由于该服务需要注册到Eureka中,故需要向其中引入Eureka Client依赖。如下所示

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
32
33
<dependencyManagement>
<dependencies>

<!--Spring Boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

</dependencies>
</dependencyManagement>

<dependencies>

<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

</dependencies>

配置文件

在配置文件中,我们定义了payment服务的两个实例,分别使用8001、8002端口。在启动时中通过设置不同的spring.profiles.active配置,即可启动两个使用不同端口的payment服务

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
32
33
34
35
36
37
38
39
40
41
# 通用配置
spring:
application:
name: payment
profiles:
active: payment1

eureka:
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: true
# 是否从Eureka Server获取注册信息, 默认为true
# 单机版Eureka Server无所谓,集群版Eureka Server下必须设置为true才能配合Ribbon使用负载均衡
fetch-registry: true
service-url:
# 单机版 Eureka Server
defaultZone: http://localhost:7001/eureka

---
# payment服务实例1配置
spring:
profiles: payment1

server:
port: 8001

eureka:
instance:
instance-id: payment-8001

---
# payment服务实例2配置
spring:
profiles: payment2

server:
port: 8002

eureka:
instance:
instance-id: payment-8002

Java实现

为便于测试,我们提供一个Controller,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
@RequestMapping("pay")
public class PaymentController {

@Value("${server.port}")
private String serverPort;


@GetMapping("/getServerPort")
public String getServerPort() {
String msg = "[Payment Service]: Port: " + serverPort;
return msg;
}

@GetMapping("/hello")
public String hello(@RequestParam String name) {
String msg = "[Payment Service-"+ serverPort +"]: " + name;
return msg;
}

}

同时在启动类上添加 @EnableEurekaClient 注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaClient
public class PaymentApplication {
public static void main(String[] args) {
SpringApplication.run(PaymentApplication.class, args);
}
}

搭建服务消费者

POM依赖

类似地,建立SpringBoot项目Order作为服务消费者。同理该项目也需要引入Eureka Client依赖。如下所示

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
32
33
<dependencyManagement>
<dependencies>

<!--Spring Boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

</dependencies>
</dependencyManagement>

<dependencies>

<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

</dependencies>

配置文件

一般地,微服务中一个服务既是提供者也是消费者,故我们也将该服务注册到Eureka中。配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 80

spring:
application:
name: order

eureka:
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: true
# 是否从Eureka Server获取注册信息, 默认为true
fetch-registry: true
service-url:
# 单机版 Eureka Server
defaultZone: http://localhost:7001/eureka
instance:
instance-id: order-80

Java实现

首先声明创建RestTemplate实例。一个是普通的RestTemplate实例,一个则是添加了 @LoadBalanced 注解的RestTemplate实例。前者可进行基于IP、Port服务调用;后者由于添加了 @LoadBalanced 注解,其一方面可进行基于服务名的服务调用,另一方面支持负载均衡

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
@Configuration
public class RestTemplateConfig {

/**
* 普通的restTemplate实例, 可利用IP、Port调用服务
* @return
*/
@Bean
public RestTemplate restTemplate1() {
return new RestTemplate();
}

/**
* @LoadBalanced 注解作用:
* 1. 基于服务名调用的restTemplate实例
* 2. 支持负载均衡
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate2() {
return new RestTemplate();
}

}

然后通过Controller调用Payment服务接口。下面展现了 传统地基于IP、Port信息 和 基于服务名 两种形式的服务调用

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
32
@RestController
@RequestMapping("order")
public class OrderController {

@Qualifier("restTemplate1")
@Autowired
private RestTemplate restTemplate1;

@Qualifier("restTemplate2")
@Autowired
private RestTemplate restTemplate2;

// 使用 固定IP、Port
private static final String PAYMENT_URL_1 = "http://localhost:8001";

// 使用 注册到Eureka中的 服务名称
private static final String PAYMENT_URL_2 = "http://PAYMENT";

@GetMapping("/test1")
public String test1(@RequestParam String name) {
String msg = restTemplate1.getForObject(PAYMENT_URL_1 +"/pay/hello?name={1}", String.class, name);
String result = "[Order Service #test1]: " + msg;
return result;
}

@GetMapping("/test2")
public String test2(@RequestParam String name) {
String msg = restTemplate2.getForObject(PAYMENT_URL_2 +"/pay/hello?name={1}", String.class, name);
String result = "[Order Service #test2]: " + msg;
return result;
}
}

最后,在启动类上添加 @EnableEurekaClient 注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}

测试

启动Eureka Server、Payment、Order三个服务。其中,Payment服务启动了两个实例,如下所示

figure 2.jpeg

打开 http://localhost:7001 ,进入Eureka Server的Web页面,可以看到Payment、Order服务都已经被成功注册到Eureka当中。其中Payment服务下存在两个实例

figure 3.jpeg

现在我们通过Order服务来调用Payment服务的接口,测试结果如下,符合预期。与此同时,红框结果同时表明实现了负载均衡

figure 4.jpeg

集群版Eureka环境

Eureka集群环境搭建也比较简单,只是相关的配置文件发生变化。这里就Server、Client侧分别说明

Eureka Server配置

在Eureka集群环境中,各实例的 eureka.client.service-url.defaultZone 不再是指向自己,而是指向集群下的其它实例。下面即是一个包含三实例的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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 通用配置
spring:
application:
name: eureka server
profiles:
active: eurekaServer1

---
# Eureka Server服务实例1配置
spring:
profiles: eurekaServer1

server:
port: 7001

eureka:
instance:
hostname: eureka7001.com # Eureka Server的实例名称
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: true
# 是否从Eureka Server获取注册信息, 默认为true
fetch-registry: true
service-url:
# 集群版 Eureka Server: 指向集群中的其它Eureka Server
defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

---
# Eureka Server服务实例2配置
spring:
profiles: eurekaServer2

server:
port: 7002

eureka:
instance:
hostname: eureka7002.com # Eureka Server的实例名称
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: true
# 是否从Eureka Server获取注册信息, 默认为true
fetch-registry: true
service-url:
# 集群版 Eureka Server: 指向集群中的其它Eureka Server
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/

---
# Eureka Server服务实例3配置
spring:
profiles: eurekaServer3

server:
port: 7003

eureka:
instance:
hostname: eureka7003.com # Eureka Server的实例名称
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: true
# 是否从Eureka Server获取注册信息, 默认为true
fetch-registry: true
service-url:
# 集群版 Eureka Server: 指向集群中的其它Eureka Server
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

启动该集群环境后,进入任一一个Eureka实例的Web页面,即可看到整个集群环境已经建立完毕。如下所示

figure 5.jpeg

Eureka Client配置

Eureka Client的配置就比较简单,我们以payment服务为例。只需将 eureka.client.service-url.defaultZone 配置项指向Eureka集群的各实例即可,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 通用配置
spring:
application:
name: payment
profiles:
active: payment1

eureka:
client:
# 是否将自己注册到Eureka Server, 默认为true
register-with-eureka: true
# 是否从Eureka Server获取注册信息, 默认为true
# 单节点无所谓,集群必须设置为true才能配合Ribbon使用负载均衡
fetch-registry: true
service-url:
# 集群版Eureka Server
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

Note

  • IDEA下启动某个服务的多实例,需要先勾选 Allow parallel 配置

figure 6.jpeg

  • 在配置Eureka Server时,各实例的eureka.instance.hostname配置项不允许重复。所以对于单机版Eureka Server而言,该项是可以直接使用localhost。但为了便于验证集群环境,上文我们在hosts中添加了多条指向localhost的映射

  • Eureka中的服务名称默认为应用名称大写

参考文献

  1. Spring微服务实战 John Carnell著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝