AI 摘要
AI
正在生成摘要...

本篇是Spring Cloud Alibaba微服务系列第三篇,主要关于Nacos的服务发现机制。笔者将实践应用三大核心API:@EnableDiscoveryClientDiscoveryClientNacosDiscoveryClient,并通过完整代码示例展示如何在实际项目中应用它们。

一、三大核心API详解

1. @EnableDiscoveryClient - 服务发现开关

作用:声明当前应用需要启用服务发现功能

位置:Spring Boot启动类

JAVA
package top.strarry.product;

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

@EnableDiscoveryClient
@SpringBootApplication
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
}

注意事项

  • Spring Cloud 2020版本后无需显式添加(在编写启动类的时候并没有加上注解,但服务成功启动。总结就是有依赖就行)
  • 保留注解可提高代码可读性
  • 支持自定义服务注册行为:
JAVA
@EnableDiscoveryClient(autoRegister = false) // 禁用自动注册

2. DiscoveryClient - 通用服务发现接口

核心方法

  • List<String> getServices():获取所有注册服务名称
  • List<ServiceInstance> getInstances(String serviceId):获取指定服务的实例列表
JAVA
@SpringBootTest
public class DiscoveryTest {

    @Autowired
    DiscoveryClient discoveryClient;
    
    @Test
    void discoveryClientTest() {
        List<String> services = discoveryClient.getServices();
        for (String serviceName : services) {
            System.out.println(\"service\" + serviceName);
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
            //获取ip+port
            for (ServiceInstance instance : instances) {
                System.out.println(instance.getHost() + \":\" + instance.getPort());
            }
        }
    }
}

3. NacosDiscoveryClient - 增强功能实现

特有功能

  • 获取服务的健康状态
  • 查询集群信息
  • 基于元数据的复杂查询
  • 服务订阅机制
JAVA
@SpringBootTest
public class DiscoveryTest {
    
	@Autowired
    NacosDiscoveryClient nacosDiscoveryClient;

	@Test
    public void nacosDiscoveryClientTest() {
        List<String> services = nacosDiscoveryClient.getServices();
        for (String service : services) {
            System.out.println(\"service = \" + service);
            List<ServiceInstance> instances = nacosDiscoveryClient.getInstances(service);
            for (ServiceInstance instance : instances) {
                System.out.println(\"instance.getHost() = \" + instance.getHost());
                System.out.println(\"instance.getPort() = \" + instance.getPort());
            }
        }
    }
}

二者运行结果均一致:
1.png

二、远程调用

1.编写Order订单实体类

JAVA
@Data
public class Order {
    private Long id;
    private BigDecimal totalAmount;
    private Long userId;
    private String nickname;
    private String address;
    private List<Product> productList;
}

2.编写Product商品实体类

JAVA
@Data
public class Product {
    private Long id;
    private BigDecimal price;
    private String productName;
    private int num;
}

3.编写创建订单接口

JAVA
@RestController
public class OrderController {

    @Autowired
    OrderService orderService;

    @GetMapping(\"/create\")
    public Order createOrder(@RequestParam(\"userId\") Long userId,
                             @RequestParam(\"productId\") Long productId) {
        Order order = orderService.createOrder(userId, productId);
        return order;
    }
}

4.编写创建订单服务接口

JAVA
@Service
public interface OrderService {

    Order createOrder(Long productId, Long userId);
}

5.编写创建订单服务实现类

JAVA
@Service
public class OrderServiceImpl implements OrderService {
    @Override
    public Order createOrder(Long userId, Long productId) {
        Order order = new Order();
        order.setId(1L);
        //TODO 总金额
        order.setTotalAmount(new BigDecimal(\"0\"));
        order.setUserId(userId);
        order.setNickName(\"张三\");
        order.setAddress(\"北京\");
        //TODO 远程查询商品列表
        order.setProductList(null);
        return order;
    }
}

6.编写商品接口

JAVA
@RestController
public class ProductController {

    @Autowired
    ProductService productService;

    @GetMapping(\"/product/{id}\")
    public Product getProduct(@PathVariable(\"id\") Long productId) {
        Product product = productService.getProductById(productId);
        return product;
    }
}

7.编写商品服务接口

JAVA
@Service
public interface ProductService {
    Product getProductById(Long productId);
}

8.编写商品服务实现类

JAVA
@Service
public class ProductServiceImpl implements ProductService {
    @Override
    public Product getProductById(Long productId) {
        Product product = new Product();
        product.setId(productId);
        product.setPrice(new BigDecimal(\"99\"));
        product.setProductName(\"苹果\");
        product.setNum(2);
        return product;
    }
}

9.测试接口

测试商品接口:http://localhost:9000/product/2,返回如下:

JSON
{
  \"id\": 2,
  \"price\": 99,
  \"productName\": \"苹果\",
  \"num\": 2
}

测试订单接口:http://localhost:8000/create?userId=2&productId=23,返回如下:

JSON
{
  \"id\": 1,
  \"totalAmount\": 198,
  \"userId\": 23,
  \"nickname\": \"zhangsan\",
  \"address\": \"hebei\",
  \"productList\": [
    {
      \"id\": 2,
      \"price\": 99,
      \"productName\": \"苹果\",
      \"num\": 2
    }
  ]
}

三、远程调用

1.常见的远程调用对比

方式 优点 缺点 适用场景
RestTemplate 灵活可控,简单直接 需要手动拼接URL 简单调用,快速原型
OpenFeign 声明式编程,自动生成实现类 学习曲线较陡 标准RESTful接口调用
WebClient 响应式非阻塞,高性能 异步编程模型复杂 高并发,响应式系统

2.实现订单的远程调用

①将RestTemplate注册到spring容器

JAVA
@Configuration
public class OrderConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

②编写订单的远程调用实现类

JAVA
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {   
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;

	@Override
    public Order createOrder(Long productId, Long userId) {
        Product productFromRemot = getProductFromRemote(productId);

        Order order = new Order();
        //TODO 总金额
        order.setTotalAmount(productFromRemot.getPrice().multiply(new BigDecimal(productFromRemot.getNum())));
        order.setId(1L);
        order.setUserId(userId);
        order.setNickname(\"zhangsan\");
        order.setAddress(\"hebei\");
        //TODO 远程查询商品列表
        order.setProductList(Arrays.asList(productFromRemot ));

        return order;
    }

	//1.发送请求
    private Product getProductFromRemote(Long productId) {
        //1、获取商品服务所在的所有机器IP+Port
        List<ServiceInstance> instances = discoveryClient.getInstances(\"service-product\");
        ServiceInstance serviceInstance = instances.get(0);
        //2.拼接远程url
        String url =  \"http://\"+serviceInstance.getHost()+\":\"+ serviceInstance.getPort()+\"/product/\"+productId;
        log.info(\"远程请求路径:{}\",url);
        //3.发送远程请求
        Product forObject = restTemplate.getForObject(url, Product.class);
        return forObject;
    }
}

3.测试接口

测试远程调用:http://localhost:8000/create?userId=2&productId=1,返回内容如下:

JSON
{
  \"id\": 1,
  \"totalAmount\": 198,
  \"userId\": 23,
  \"nickname\": \"zhangsan\",
  \"address\": \"hebei\",
  \"productList\": [
    {
      \"id\": 2,
      \"price\": 99,
      \"productName\": \"苹果\",
      \"num\": 2
    }
  ]
}

控制台打印出远程调用所拼接的URL地址信息:

2.png

四、总结

本次我们深入探讨了Nacos服务发现的三大核心API:

  1. @EnableDiscoveryClient - 服务发现的开关
    • 声明式启用服务发现
    • 支持自定义注册行为
    • 新版本可自动启用
  2. DiscoveryClient - 通用服务发现接口
    • 获取服务列表和实例信息
    • 与Spring Cloud标准集成
    • 支持元数据查询
  3. NacosDiscoveryClient - 增强实现
    • 获取Nacos特有信息
    • 支持服务订阅机制
    • 提供高级查询能力

下一步回顾

[Nacos配置中心]

[OpenFeign声明式调用]

评论