在微服务领域,Service Registry和Discovery起着重要作用,因为我们很可能运行多个服务实例,我们需要一种机制来调用其他服务而无需对其主机名或端口号进行硬编码。除此之外,在云环境中,服务实例可能会随时出现并停止运行。所以我们需要一些自动服务注册和发现机制。与往常一样,Spring Cloud提供多种选项的Service Registry和Discovery功能。我们可以使用Netflix Eureka或Consul进行Service Registry and Discovery。在这篇文章中,我们将学习如何使用SpringCloud Netflix Eureka进行Service Registry和Discovery。
使用Spring Boot和Spring Cloud的MicroServices
- 第1部分:MicroServices:Spring Boot和Spring Cloud概述
- 第2部分:MicroServices:使用Spring Cloud Config和Vault进行配置管理
- 第3部分:MicroServices:Spring Cloud Service Registry and Discovery
- 第4部分:MicroServices:使用Netflix Hystrix的Spring Cloud断路器
- 第5部分:MicroServices:Spring Cloud Zuul Proxy作为API网关
- 第6部分:MicroServices:使用Spring Cloud Sleuth和Zipkin进行分布式跟踪
在我之前的帖子“ MicroServices – 第2部分:使用Spring Cloud Config和Vault的配置管理”中,我们学习了如何在配置服务器外部存储配置参数以及如何在Vault中安全地存储机密。
在这篇文章中,我们将学习:
- 什么是Service Registry和Discovery?
- Spring Cloud Netflix基于Eureka的Service Registry
- 将微服务注册为Eureka客户端
- 使用Eureka Client发现其他服务
什么是Service Registry和Discovery?
假设我们有2个微服务目录服务和库存服务,我们在http:// localhost:8181 /和http:// localhost:8282 /上运行2个库存服务实例。现在假设我们想要从catalog-service调用一些库存服务REST端点。我们应该打哪个网址?通常,在这些情况下,我们使用负载平衡器配置这两个URL以委派给我们,我们将在负载均衡器URL上调用REST端点。精细。
但是,如果您想根据负载动态启动新实例,该怎么办?即使您只运行少量服务器节点,手动更新负载均衡器配置中的服务器节点详细信息也容易出错且繁琐。这就是我们需要自动服务注册机制并能够使用某个逻辑服务ID而不是使用特定IP地址和端口号来调用服务的原因。
我们可以使用Netflix Eureka Server创建Service Registry并将我们的微服务作为Eureka客户端,这样一旦我们启动微服务,它将自动使用逻辑服务ID向Eureka Server注册。然后,其他微服务(也是Eureka客户端)可以使用服务ID来调用REST端点。
Spring Cloud使用Load Balanced RestTemplate创建Service Registry并发现其他服务变得非常容易。
Spring Cloud Netflix基于Eureka的Service Registry
让我们使用Netflix Eureka创建一个Service Registry,它只是一个带有Eureka Server启动器的SpringBoot应用程序。
1
2
3
4
|
<
dependency
>
<
groupId
>org.springframework.cloud
groupId
>
<
artifactId
>spring-cloud-starter-netflix-eureka-server
artifactId
>
dependency
>
|
我们需要添加@EnableEurekaServer注释,以使我们的SpringBoot应用程序成为基于Eureka Server的Service Registry。
1
2
3
4
五
6
7
8
9
10
11
12
|
import
org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public
class
ServiceRegistryApplication {
public
static
void
main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.
class
, args);
}
}
|
默认情况下,每个Eureka服务器也是Eureka客户端,并且至少需要一个服务URL来定位对等体。由于我们将有一个Eureka Server节点(独立模式),我们将通过在application.properties文件中配置以下属性来禁用此客户端行为。
application.properties
1
2
3
4
五
6
|
spring.application.name=service-registry
server.port=
8761
eureka.instance.hostname=localhost
eureka.instance.client.registerWithEureka=
false
eureka.instance.client.fetchRegistry=
false
eureka.instance.client.serviceUrl.defaultZone=http:
//${eureka.instance.hostname}:${server.port}/eureka/
|
Netflix Eureka Service提供UI,我们可以在其中查看有关注册服务的所有详细信息。
现在运行ServiceRegistryApplication并访问http:// localhost:8761,它将显示类似于下面屏幕截图的UI。
将微服务注册为Eureka客户端
在Part-2 MicroServices:使用Spring Cloud Config和Vault的配置管理中,我们创建了目录服务。让我们将此服务作为Eureka客户端进行注册并注册Eureka Server。
将Eureka Discovery启动程序添加到catalog-service,这将添加以下依赖项。
1
2
3
4
|
<
dependency
>
<
groupId
>org.springframework.cloud
groupId
>
<
artifactId
>spring-cloud-starter-netflix-eureka-client
artifactId
>
dependency
>
|
使用类路径上的spring-cloud-starter-netflix-eureka-client,我们只需要在application.properties中配置eureka.client.service-url.defaultZone属性即可自动注册Eureka Server。
1
|
eureka.client.service-url.defaultZone=http:
//localhost:8761/eureka/
|
当服务在Eureka Server注册时,它会持续发送心跳一定的时间间隔。如果Eureka服务器没有从任何服务实例接收到心跳,它将假定服务实例已关闭并从池中取出。
使用此配置,启动catalog-service并访问http:// localhost:8761。您应该看到catalog-service已使用SERVICE ID注册为CATALOG-SERVICE。您还可以注意到状态为UP(1),这意味着服务已启动并正在运行,并且正在运行一个目录服务实例。
让我们使用以下命令在另一个端口上启动另一个catalog-service实例。
1
|
java -jar -Dserver.port=
9797
target/catalog-service-
0.0
.
1
-SNAPSHOT-exec.jar
|
现在,如果你去http:// localhost:8761,你会注意到2个catalog-service实例已经注册,你也可以看到它们的主机名:端口细节。
使用Eureka Client发现其他服务
在上一节中,我们学习了如何将服务注册为Eureka客户端,并且我们还尝试注册同一服务的多个实例。
现在我们将创建另一个微服务库存服务,它公开一个REST端点http:// localhost:8282 / api / invenory / {productCode},它将当前可用的数量作为响应。
1
2
3
4
|
{
productCode:
"P001"
,
availableQuantity: 250
}
|
使用Web,JPA,H2 / MySQL,Actuator,Config Client和Eureka Discovery启动器创建库存服务SpringBoot应用程序。
创建REST控制器以返回给定产品代码的库存详细信息。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@RestController
@Slf4j
public
class
InventoryController {
private
final
InventoryItemRepository inventoryItemRepository;
@Autowired
public
InventoryController(InventoryItemRepository inventoryItemRepository) {
this
.inventoryItemRepository = inventoryItemRepository;
}
@GetMapping
(
"/api/inventory/{productCode}"
)
public
ResponseEntity
@PathVariable
(
"productCode"
) String productCode) {
log.info(
"Finding inventory for product code :"
+productCode);
Optional
if
(inventoryItem.isPresent()) {
return
new
ResponseEntity(inventoryItem, HttpStatus.OK);
}
else
{
return
new
ResponseEntity(HttpStatus.NOT_FOUND);
}
}
}
|
请查看InventoryItem,InventoryItemRepository等代码的GitHub存储库。
通过在src / main / resources / bootstrap.properties中配置Eureka serviceUrl,向Eureka服务器注册库存服务。
1
2
3
|
spring.application.name=inventory-service
server.port=
8282
eureka.client.service-url.defaultZone=http:
//localhost:8761/eureka/
|
现在构建库存服务并通过运行以下命令启动它的2个实例。
1
2
3
|
java -jar -Dserver.port=
9898
target/inventory-service-
0.0
.
1
-SNAPSHOT-exec.jar
java -jar -Dserver.port=
9999
target/inventory-service-
0.0
.
1
-SNAPSHOT-exec.jar
|
现在您可以访问Eureka Dashboard http:// localhost:8761 /并查看2个库存服务实例已注册。
假设我们想要从catalog-service调用库存服务REST端点。我们可以使用RestTemplate来调用REST端点,但是有2个实例正在运行。
我们可以使用@LoadBalanced注释将RestTemplate注册为Spring bean 。带有@LoadBalanced注释的RestTemplate将在内部使用Ribbon LoadBalancer来解析ServiceID并使用其中一个可用服务器调用REST端点。
1
2
3
4
五
6
7
8
9
10
11
12
13
|
@SpringBootApplication
public
class
CatalogServiceApplication {
@Bean
@LoadBalanced
public
RestTemplate restTemplate() {
return
new
RestTemplate();
}
public
static
void
main(String[] args) {
SpringApplication.run(CatalogServiceApplication.
class
, args);
}
}
|
现在我们可以使用RestTemplate在http:// inventory-service / api / inventory / {productCode}调用库存服务端点。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
|
@Service
@Transactional
@Slf4j
public
class
ProductService {
private
final
ProductRepository productRepository;
private
final
RestTemplate restTemplate;
@Autowired
public
ProductService(ProductRepository productRepository, RestTemplate restTemplate) {
this
.productRepository = productRepository;
this
.restTemplate = restTemplate;
}
public
Optional
Optional
if
(productOptional.isPresent()) {
log.info(
"Fetching inventory level for product_code: "
+code);
ResponseEntity
restTemplate.getForEntity(
"http://inventory-service/api/inventory/{code}"
,
ProductInventoryResponse.
class
,
code);
if
(itemResponseEntity.getStatusCode() == HttpStatus.OK) {
Integer quantity = itemResponseEntity.getBody().getAvailableQuantity();
log.info(
"Available quantity: "
+quantity);
productOptional.get().setInStock(quantity>
0
);
}
else
{
log.error(
"Unable to get inventory level for product_code: "
+code +
", StatusCode: "
+itemResponseEntity.getStatusCode());
}
}
return
productOptional;
}
}
@Data
public
class
ProductInventoryResponse {
private
String productCode;
private
int
availableQuantity;
}
|
请注意,我们使用了http:// inventory-service / api / inventory / {code}而不是http:// localhost:9898 / api / inventory / {code}或http:// localhost:9999 / api / inventory / {code}直接。
使用这种自动服务注册和发现机制,我们无需担心运行了多少实例以及它们的主机名和端口等。
您可以在https://github.com/sivaprasadreddy/spring-boot-microservices-series找到本文的源代码。
概要
在这篇文章中,我们学习了如何将Spring Cloud Netflix Eureka用于Service Registry和Discovery。在下一篇文章中,我们将介绍如何使用Netflix Hystrix实现Circuit Breaker模式。
参考文献:
- Spring Cloud Netflix