- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
微服务将自己的实例注册到 nacos 注册中心,nacos服务端存储了注册列表,然后通过 ribbon 调用服务,具体是如何调用?如果 nacos 服务挂了,还能正常调用服务吗?调用的服务列表发生变化,调用方是如何感知变化的?带着这些问题,来探索一下服务发现的原理.
Nacos Server:2.1.1
spring-cloud-starter-alibaba:2.1.1.RELEASE
spring-boot:2.1.1.RELEASE
spring-cloud-starter-netflix-ribbon:2.1.1.RELEASE
客户端和服务端版本号都为 2.1.1 .
使用 ribbon 来调用服务,就添加 ribbon 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
ribbon 依赖包含 spring-cloud-commons 依赖,而在 spring-cloud-commons 包中 spring.factories 自动配置 LoadBalancerAutoConfiguration 类:
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
只要标注了 @LoadBalanced 注解的 restTemplates 都会添加负载均衡拦截器 LoadBalancerInterceptor .
使用 Ribbon 组件调用服务
restTemplate.getForObject("http://service-name",String.class);
restTemplate 的 http 请求方法,最终会调用到 doExecute 方法。 doExecute 在发起 http 请求之前,会先执行 LoadBalancerInterceptor 负载均衡拦截器的 intercept 方法。 该方法调用 execute 方法.
而在 execute 方法中,主要有两个方法
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
execute 先通过 getLoadBalancer 获取 ILoadBalancer 实例,然后再通过 getServer 获取 Server 实例.
getLoadBalancer 最终会调用 Ribbon 的 ServerList 接口,具体调用流程
getLoadBalancer() ->
ZoneAwareLoadBalancer ->
DynamicServerListLoadBalancer ->
restOfInit()->
updateListOfServers()->
ServerList.getUpdatedListOfServers()->
Nacos 实现类 NacosServerList 实现了 ServerList 接口.
总之我们在进行微服务调用的时候, Ribbon 最终会调用 NacosServerList 类中的 getUpdatedListOfServers 方法.
NacosServerList 类的 getUpdatedListOfServers 方法调用了该类的 getServers 方法:
private List<NacosServer> getServers() {
try {
// 获取分组
String group = discoveryProperties.getGroup();
// 重点,查询实例列表
List<Instance> instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true);
return instancesToServerList(instances);
}
catch (Exception e) {
throw new IllegalStateException(
"Can not get service instances from nacos, serviceId=" + serviceId,
e);
}
}
重点看 NacosNamingService 类的 selectInstances 方法,会调用以下 selectInstances 三个重载方法:
@Override
public List<Instance> selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException {
return selectInstances(serviceName, groupName, healthy, true);
}
@Override
public List<Instance> selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException {
return selectInstances(serviceName, groupName, new ArrayList<String>(), healthy, subscribe);
}
@Override
public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
// 默认订阅
if (subscribe) {
// 获取服务,这是重点
serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
} else {
serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
}
return selectInstances(serviceInfo, healthy);
}
最后一个 selectInstances 方法里面的 hostReactor.getServiceInfo 方法是获取服务的核心方法
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
String key = ServiceInfo.getKey(serviceName, clusters);
if (failoverReactor.isFailoverSwitch()) {
return failoverReactor.getService(key);
}
// 先在本地缓存查询
ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
// 查询不到
if (null == serviceObj) {
serviceObj = new ServiceInfo(serviceName, clusters);
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
updatingMap.put(serviceName, new Object());
// 请求Nacos Server实例,并更新服务实例
updateServiceNow(serviceName, clusters);
updatingMap.remove(serviceName);
} else if (updatingMap.containsKey(serviceName)) {
if (UPDATE_HOLD_INTERVAL > 0) {
// hold a moment waiting for update finish
synchronized (serviceObj) {
try {
serviceObj.wait(UPDATE_HOLD_INTERVAL);
} catch (InterruptedException e) {
NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
}
}
}
}
// 定时更新本地缓存
scheduleUpdateIfAbsent(serviceName, clusters);
return serviceInfoMap.get(serviceObj.getKey());
}
getServiceInfo 是服务发现的核心方法,先查询 serviceInfoMap 集合中查询本地缓存,本地缓存查询不到就请求 Nacos Server 实例,并更新本地缓存.
请求 Nacos Server 实例,实际就是发送 http 请求 Nacos Server :
public void updateServiceNow(String serviceName, String clusters) {
ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
try {
// 调用 Nacos Server 查询服务
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false);
// 结果不为空,更新缓存
if (StringUtils.isNotEmpty(result)) {
processServiceJSON(result);
}
} catch (Exception e) {
NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
} finally {
if (oldService != null) {
synchronized (oldService) {
oldService.notifyAll();
}
}
}
}
//向 Nacos Server发起 HTTP 列表查询
public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)throws NacosException {
final Map<String, String> params = new HashMap<String, String>(8);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put("clusters", clusters);
params.put("udpPort", String.valueOf(udpPort));
params.put("clientIP", NetUtils.localIP());
params.put("healthyOnly", String.valueOf(healthyOnly));
return reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/list", params, HttpMethod.GET);
}
queryList 方法主要封装号请求参数,然后向 Nacos Server 服务端发送 http 请求.
当服务端实例发生改变时, Nacos Server 会推送最新的实例给服务端.
服务发现是先获取本地缓存,如果没有本地缓存,就请求 Nacos Server 服务端获取数据,如果 Nacos Server 挂了,也不会影响服务的调用.
Ribbon
Ribbon
发起服务请求开始,最终会调用到拦截器的拦截方法。 ServerList
获取实例接口,而 NacosServerList
实现获取实例列表。 Nacos
调用服务
NacosServerList
实现了获取服务实例列表。 NacosServerList
类 selectInstances
方法最终调用了 hostReactor.getServiceInfo
方法 getServiceInfo
方法先从 serviceInfoMap
集合中获取本地缓存,如果本地缓存找不到,就请求 Nacos Server
获取服务实例,并更新本地缓存。 Spring Cloud nacos Ribbon整合源码分析 。
服务发现:服务之间调用请求链路分析 。
最后此篇关于Nacos服务发现原理分析的文章就讲到这里了,如果你想了解更多关于Nacos服务发现原理分析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
1. 引言 很多同学了解r-nacos特性后最开始只将r-nacos用于开发测试环境。 经过一段时间的使用后,部分同学有打算生产环境也从nacos迁移到r-nacos。 一些之前使用nacos服
学习不用那么功利,二师兄带你从更高维度轻松阅读源码~ 上篇文章,我们分析了Nacos客户端订阅的核心流程:Nacos客户端通过一个定时任务,每6秒从注册中心获取实例列表,当发现实例发生变化时,
Nacos 的部署,我使用的时docker 部署(单机模式 Mysql),官网文档:https://nacos.io/zh-cn/docs/quick-start-docker.html 拉取代码
目录 前言 准备工作 Nacos安装及使用入门 准备三个SpringBoot服务,引入Nacos及Kafka 业务解读
前置内容 1、SpringCloud Alibaba简介和Nacos【注册中心】 1、Nacos(下) 1.1、服务配置中心演示 1.1.1、基础配置 1. 建Module Module的名称为clo
前言: 我们经常在springboot单体项目中,集成swagger来整合接口文档; 但是在微服务springcloud项目下,业务模块众多,如果再像之前一样单独访问每个模块的 swagger-ui.
前面我们分析了携程的 apollo(见 详解apollo的设计与使用),现在再来看看阿里的 nacos。和 apollo 一样,nacos 也是一款配置中心,同样可以实现配置的集中管理、分环境管理、即
默认你已经看过我之前的教程了,并且拥有上个教程完成的项目, 。 之前的教程 https://www.cnblogs.com/leafstar/p/17638782.html 。 项目链接
本文转载自微信公众号「Java中文社群」,作者磊哥 。转载本文请联系Java中文社群公众号。 在 Nacos 中,服务调用主要是通过 RestTemplate + Ribbon 实现的,RestT
目录 前言 1、Nacos环境准备 1、启动Nacos配置中心并创建路由配置 2、连接Nacos配置中心 2、项目
首先从github上下载nacos的压缩包:https://github.com/alibaba/nacos/releases 下载完成之后,通过WinSCP把文件传到linux服务器上 接着
一、简介 Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast
前言 其实也就是相当于之前用的 eureka 和 config (或者阿波罗)。 本篇提供给初学者,最快速最简单地去使用上nacos 。 正文 1.首先以最快的速度 ,本地(windows环境) ,把
动态规则 规则 Sentinel 的理念是开发者只需要关注资源的定义,当资源定义成功后可以动态增加各种流控降级规则。 在实际使用过程中,设置规则之后,保存在内存中,应用重启后,规则失效,而目前s
目录 1、SpringBoot 使用 Nacos Config 实现多环境切换 1. 现象 2. 引入依赖 3. 添加bootst
Sentinel 规则配置,一旦我们重启服务过后,所有的规则都会消失。我们可以通过 Zookeeper , Applo , Nacos 等配置中心将这些规则配置存储起来,让服务重启或者启动多节点
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界. 这篇CFSDN的博客文章Spring Cloud Alibaba Nacos 入门详解由作者收集
1、spring-cloud-starter-alibaba-nacos-discovery 这里依赖报红,无法引入,或显示无法找到,更换版本也无法解决,启动项目后nacos中也无法发现服务 ①错
我是一名优秀的程序员,十分优秀!