在微服务世界中, 服务注册表和发现扮演着重要的角色,因为我们很可能会运行多个服务实例,并且我们需要一种机制来调用其他服务而不用硬编码它们的主机名或端口号。 除此之外,在云环境中,服务实例可以随时启动和关闭。 因此,我们需要一些自动服务注册和发现机制。 Spring Cloud像往常一样提供Service Registry and Discovery功能,并具有多个选项。 我们可以使用Netflix Eureka或Consul进行服务注册和发现。 在本文中,我们将学习如何使用SpringCloud Netflix Eureka进行服务注册和发现。
使用Spring Boot和Spring Cloud的微服务
- 第1部分:MicroServices:Spring Boot和Spring Cloud概述
- 第2部分:MicroServices:使用Spring Cloud Config和Vault进行配置管理
- 第3部分:MicroServices:Spring Cloud Service注册和发现
在我之前的文章MicroServices –第2部分:使用Spring Cloud Config和Vault进行配置管理中 ,我们学习了如何在配置服务器中外部存储配置参数以及如何在Vault中安全存储机密。
在这篇文章中,我们将学习:
- 什么是服务注册和发现?
- 基于Spring Cloud Netflix Eureka的服务注册表
- 将微服务注册为Eureka客户端
- 使用Eureka Client发现其他服务
什么是服务注册和发现?
假设我们有2个微服务目录服务和库存服务,并且我们正在http:// localhost:8181 /和http:// localhost:8282 /上运行2个库存服务实例。 现在,我们要从目录服务调用一些清单服务REST端点。 我们应该打哪个URL? 通常,在这些情况下,我们使用负载均衡器来配置这两个要委派的URL,然后在负载均衡器URL上调用REST端点。 精细。
但是,如果您想根据负载动态启动新实例呢? 即使只运行几个服务器节点,在负载均衡器配置中手动更新服务器节点详细信息也容易出错且乏味。 这就是为什么我们需要自动服务注册机制,并且能够使用一些逻辑服务ID而不是使用特定的IP地址和端口号来调用服务的原因。
我们可以使用Netflix Eureka Server创建服务注册表,并将微服务作为Eureka客户端,这样,一旦启动微服务,它将以逻辑服务ID自动在Eureka Server中注册。 然后,其他微服务(也是Eureka客户端)可以使用服务ID来调用REST端点。
Spring Cloud使使用负载平衡RestTemplate创建服务注册表和发现其他服务变得非常容易。
基于Spring Cloud Netflix Eureka的服务注册表
让我们使用Netflix Eureka创建一个Service Registry,这不过是带有Eureka Server启动程序的SpringBoot应用程序。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
我们需要添加@EnableEurekaServer批注,以使我们的SpringBoot应用程序成为基于Eureka Server的服务注册表。
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
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客户端
在第2部分MicroServices:使用Spring Cloud Config和Vault进行配置管理中,我们创建了目录服务。 让我们作为Eureka客户端提供此服务并在Eureka服务器中注册。
将Eureka Discovery启动器添加到catalog-service ,这将添加以下依赖项。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
使用classpath上的spring-cloud-starter-netflix-eureka-client时,我们只需要在application.properties中配置eureka.client.service-url.defaultZone属性即可自动向Eureka Server注册。
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实例正在运行。
让我们使用以下命令在不同的端口上启动另一个Catalog-service实例。
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} ,它将给出当前可用的数量作为响应。
{
productCode: "P001",
availableQuantity: 250
}
使用Web , JPA , H2 / MySQL , Actuator , Config Client和Eureka Discovery启动器创建清单服务 SpringBoot应用程序。
创建REST控制器以返回给定产品代码的库存详细信息。
@RestController
@Slf4j
public class InventoryController {
private final InventoryItemRepository inventoryItemRepository;
@Autowired
public InventoryController(InventoryItemRepository inventoryItemRepository) {
this.inventoryItemRepository = inventoryItemRepository;
}
@GetMapping("/api/inventory/{productCode}")
public ResponseEntity<InventoryItem> findInventoryByProductCode(@PathVariable("productCode") String productCode) {
log.info("Finding inventory for product code :"+productCode);
Optional<InventoryItem> inventoryItem = inventoryItemRepository.findByProductCode(productCode);
if(inventoryItem.isPresent()) {
return new ResponseEntity(inventoryItem, HttpStatus.OK);
} else {
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
}
}
请查看GitHub Repository中的InventoryItem,InventoryItemRepository等代码。
通过在src / main / resources / bootstrap.properties中配置Eureka serviceUrl在Eureka服务器上注册清单服务 。
spring.application.name=inventory-service
server.port=8282
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
现在构建清单服务,并通过运行以下命令启动它的2个实例。
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仪表板 http:// localhost:8761 /并查看2个注册的库存服务实例。
假设我们要从目录服务中调用清单服务REST端点。 我们可以使用RestTemplate调用REST端点,但是有2个实例正在运行。
我们可以使用@LoadBalanced批注将RestTemplate注册为Spring bean。 具有@LoadBalanced批注的RestTemplate将在内部使用Ribbon LoadBalancer解析ServiceID并使用可用服务器之一调用REST端点。
@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}处调用库存服务端点。
@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<Product> findProductByCode(String code) {
Optional<Product> productOptional = productRepository.findByCode(code);
if(productOptional.isPresent()) {
log.info("Fetching inventory level for product_code: "+code);
ResponseEntity<ProductInventoryResponse> itemResponseEntity =
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进行服务注册和发现 。 在下一篇文章中,我们将研究使用Netflix Hystrix实现Circuit Breaker模式。