介绍Nacos发现和订阅
服务发现
服务发现是指 服务提供者(Provider)自动注册到 Nacos,服务消费者(Consumer)可以通过 Nacos 获取最新的服务实例列表,实现动态调用。
Nacos 服务发现流程
1.服务提供者注册启动时,将自己的信息(IP、端口、元数据等)注册到 Nacos。
2.Nacos 维护服务实例,Nacos 维护一个服务实例列表,并定期进行健康检查。
3.服务消费者发现,消费者从 Nacos 获取可用实例列表,调用合适的实例。
服务订阅
服务订阅是指服务消费者主动监听服务实例的变化,如果实例下线、健康状态改变,Nacos 会主动推送最新的服务实例列表,而不是消费者自己轮询查询。
订阅的优势,实时感知服务变更,降低网络流量消耗(相比轮询方式),提高系统可用性,避免调用下线服务。
Nacos 订阅的底层机制:
1.消费者向 Nacos 注册监听器,通过 subscribe() 方法注册监听服务变更事件。
2.Nacos 维护长连接,采用 UDP / gRPC 长连接,实时推送服务变更。
3.Nacos 发现服务变更,主动推送当实例新增 / 下线 / 宕机,Nacos 立即通知所有订阅的消费者。
4.消费者更新本地缓存,消费者用最新的实例列表替换本地缓存,避免调用已下线的实例。
服务发现代码分析
服务发现的代码逻辑主要在Nacos客户端,我们首先从META-INF的spring.factory文件中找到NacosDiscoveryClientConfiguration这个类。
我们找到NacosDiscoveryClient的源码进行分析
//获取实例信息
public List<ServiceInstance> getInstances(String serviceId) {
try {
return (List)Optional.of(this.serviceDiscovery.getInstances(serviceId)).map((instances) -> {
ServiceCache.setInstances(serviceId, instances);
return instances;
}).get();
} catch (Exception var3) {
if (this.failureToleranceEnabled) {
return ServiceCache.getInstances(serviceId);
} else {
throw new RuntimeException("Can not get hosts from nacos server. serviceId: " + serviceId, var3);
}
}
}
//获取nacos上的服务信息
public List<String> getServices() {
try {
return (List)Optional.of(this.serviceDiscovery.getServices()).map((services) -> {
ServiceCache.setServiceIds(services);
return services;
}).get();
} catch (Exception var2) {
log.error("get service name from nacos server failed.", var2);
return this.failureToleranceEnabled ? ServiceCache.getServiceIds() : Collections.emptyList();
}
}
手动实现服务的发现
@RestController
@RequestMapping("/query")
public class Controller {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/services")
public List<String> getServices() {
return discoveryClient.getServices(); // 获取所有注册的服务
}
@GetMapping("/instances")
public List<ServiceInstance> getInstances() {
return discoveryClient.getInstances("service-provider"); // 获取某个服务的所有实例
}
}
服务的订阅实现
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.3</version>
</dependency>
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.InstanceEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
@Component
public class NacosServiceSubscriber {
private NamingService namingService;
@PostConstruct
public void init() throws Exception {
// 连接 Nacos 服务器
namingService = NacosFactory.createNamingService("127.0.0.1:8848");
// 订阅 service-provider 的变化
namingService.subscribe("service-provider", event -> {
if (event instanceof InstanceEvent) {
List<Instance> instances = namingService.getAllInstances("service-provider");
System.out.println("service-provider 发生变化,当前实例:" + instances);
}
});
}
}
订阅服务的比较
Spring Cloud DiscoveryClient Spring Cloud 项目 discoveryClient.getInstances() 简单易用。
Nacos SDK NamingService.subscribe() 更细粒度的监听 NamingService.subscribe() 主动监听,实时响应
Nacos OpenAPI 非 Java 语言、第三方服务 调用 HTTP API 轮询方式,可能有延迟