了解有关设置服务注册表和发现或你的Spring Cloud和Spring Boot微服务开发的信息。抽丝剥茧 细说架构那些事——【优锐课】
在微服务世界中,服务注册表和发现扮演着重要的角色,因为我们很可能会运行多个服务实例,并且我们需要一种机制来调用其他服务而不用硬编码它们的主机名或端口号。除此之外,在云环境中,服务实例可以随时启动和关闭。因此,我们需要一些自动服务注册和发现机制。Spring Cloud像往常一样提供Service Registry and Discovery功能,并具有多个选项。我们可以使用Netflix Eureka或Consul进行服务注册和发现。在本文中,我们将学习如何使用SpringCloud Netflix Eureka进行服务注册和发现。
使用Spring Boot和Spring Cloud的微服务
在我之前的文章微服务系列详解—Part 2:使用Spring Cloud Config和Vault进行配置管理中,我们学习了如何在配置服务器中外部存储配置参数以及如何在Vault中安全存储机密。
在这篇文章中,我们将学习:
• 什么是服务注册和发现?
• 基于Spring Cloud Netflix Eureka的服务注册表
• 将微服务注册为Eureka客户
• 使用Eureka Client发现其他服务
假设我们有两个微服务目录服务和库存服务,并且我们正在http://localhost:8181/和http://localhost:8282/上运行两个库存服务实例。现在,我们要从目录服务调用一些清单服务REST端点。我们应该打哪个URL?通常,在这些情况下,我们使用负载均衡器来配置这两个要委派的URL,然后在负载均衡器URL上调用REST端点。精细。
但是,如果你想根据负载动态启动新实例呢?即使只运行几个服务器节点,在负载均衡器配置中手动更新服务器节点详细信息也容易出错且乏味。这就是为什么我们需要自动服务注册机制,并且能够使用一些逻辑服务ID而不是使用特定的IP地址和端口号来调用服务的原因。
我们可以使用Netflix Eureka Server创建服务注册表,并将我们的微服务作为Eureka客户端,这样,一旦启动微服务,它将以逻辑服务ID自动在Eureka Server中注册。然后,其他微服务(也是Eureka客户端)可以使用服务ID来调用REST端点。
Spring Cloud使使用负载平衡RestTemplate创建服务注册表和发现其他服务变得非常容易。
让我们使用Netflix Eureka创建一个Service Registry,这不过是带有Eureka Server启动程序的SpringBoot应用程序。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
我们需要添加@EnableEurekaServer批注,以使你的Spring Boot应用程序成为基于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。
在微服务系列详解—Part 2:使用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客户端,并且还尝试了注册同一服务的多个实例。
现在,我们将创建另一个微服务库存服务,该服务公开一个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);
}
}
}
通过在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}直接{code}。
通过这种自动服务注册和发现机制,我们无需担心正在运行多少实例以及它们的主机名和端口等。
在本文中,我们学习了如何使用Spring Cloud Netflix Eureka进行服务注册和发现。
在下一篇文章中,我们将研究使用Netflix Hystrix实现Circuit Breaker模式。微服务系列详解-Part4:使用Netflix Hystrix的Spring Cloud断路器
感谢阅读!
另外还有一些资源分享给各位,需要的可以免费领取~