❶ 3. nacos服务发现
1. nacos服务发现原理
2. spring cloud服务协作流程
3.搭建nacos服务端
4. 搭建nacos服务发现客户端
5. nacos服务发现的数据模型
有两个微服务A和B, A调用B, 那么A是如何调用B的呢?我们可以通过http请求,进行调用. 也可以使用rpc进行调用.
不管使用什么方式, A需要知道B的地址和端口号. 那么A是如何知道B的地址和端口好的呢? 如上图:
1. B服务启动的时候, 会注册到服务发现中心, 告诉他,我的ip和端口号是什么?这里应该也是接口调用,通知服务发现中心的.
2. A服务启动的时候, 也会注册的服务发现中心, 告诉他, 我的ip和端口号是什么? 同时, 服务发现中心会告诉我, 当前已注册的服务的ip和端口号. 这里通过一个接口请求和参数返回就可以实现.
3. 拿到了B服务的ip和port, 接下来就可以调用服务B了.
我们要基于spring cloud生态环境进行开发. 所以,先来了解spring cloud的服务写作流程
前面注册和发现就都不说了, 上面说过了, 这里多了两个东西, 一个是Ribbon, 另一个是feign.
Ribbon是负载均衡器, Feign是远程调用, 自动进行http请求.
客户端Service A 要调用ServiceB的实例1和实例2. 那么到底调用ServiceB的哪个实例呢? 使用Ribbon负载均衡, 要看使用什么样的算法了, 可以使用轮询算法, 或者其他算法, 如加权算法
负载均衡有两种: 服务端负载均衡, 客户端负载均衡
在负载均衡中维护一个可用的服务实例清单, 当客户端请求来临时, 负载均衡服务器按照某种配置好的规则(负载均衡算法), 从可用服务实例清单中, 选取其一去处理客户端请求, 这就是服务端负载均衡, 例如Nginx. 通过nginx进行负载均衡, 客户端发送请求值Nginx, nginx通过负载均衡算法, 在多个服务器之间选择一个进行访问.
接下来, 我们要讲的ribbon, 就属于客户端负载均衡, 在ribbon客户端会有一个服务实例地址列表, 在发送请求前, 通过负载均衡算法, 选择一个服务实例, 然后进行访问, 这是客户端负载均衡. 即在客户端进行负载均衡算法分配.
服务的调用方, 我们就可以理解为是一个客户端.
负载均衡算法:
可通过下面的方式, 在spring boot配置中修改默认的负载均衡的策略
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">account-service.ribbon.NFLoadBalanceRuleClassName=com.netflix.loadBalancer.RandomRule</pre>
其中: account-service: 是调用的服务的名称. 后面的组成部分是固定的.
feign是服务端http接口的调用.
feign可以帮助我们更快捷, 优雅的调用httpApi. 原来我们在调用http请求的时候, 需要使用的是RestTemplate, 传输域名和端口号, 进行http调用. 使用feign后, 不用再使用RestTemplate模拟请求了, feign能够通过服务名, 找到对应的接口. 不需要我们在拼接地址+端口了, 提供了简单方便的操作.
使用方法:
2. 声明feign客户端
新建一个类, 声明为FeignClient类型的接口. 指定调用的服务名. 然后将指定接口. 这样在调用的时候, 就可以通过服务名, + 接口, 自动找到对应的项目接口了.
这里上一节已经搭建过了(地址: https://www.cnblogs.com/ITPower/articles/12630193.html ), 在服务的最后, 我们搭建了nacos的集群
因为对于服务发现来说, 有很多配置都是公用的, 因此, 我们搭建一个父工程, 将通用的配置都添加到里面取.
创建一个maven工厂, 引入jar包即可. pom文件如下
在父工程下创建一个子工程. 我们要做一下三件事
指定服务端口号, nacos服务的地址
需要增加两个引入. 一个启用服务发现, 另一个是启用feign
最终项目结构如下:
其中前3步骤和创建服务生产者是一样的
在父工程下创建一个子工程. 我们要做一下三件事
指定服务端口号, nacos服务的地址
需要增加两个引入. 1个启用服务发现, 另一个是启用feign
nacos生产上已经注册发现了两题服务
同时调用接口, 可以获取到proctor返回的内容.
我们可以通过启动多个服务的方式, 来测试服务的负载均衡策略.
1. 修改proctor的启动端口号为动态端口号. 目的是方便启动的时候动态配置端口号, 启动集群
** 2. 修改配置, 添加动态端口号**
vm options配置中设置动态端口号为-Dport=56010, 56011. 点击复制按钮, 可以增加一个应用, 然后配置参数后, 启动两个应用.
3. 在nacos控制台查看启动效果
我们看到, nacos的proctor 有两台服务实例. 点击详情可查看具体的服务实例信息:
4. 调用consumer的接口, 访问proctor, 默认采用轮询的负载均衡算法
http://localhost:56020/consumer
nacos的注册发现是一种三层模型: 即 服务--集群--实例.如下图:
nacos最外层是服务. 最里层是多台实例. 多个实例之间组成一个服务集群.
命名空间不仅适用于配置管理, 同样适用于服务发现. namespace的常用场景之一是不同环境的配置隔离.如: 开发, 生成, 测试环境资源的隔离.
proctor启动了两个实例, 点击详情进去可以看到他是一个集群. 集群里有两台实例.
5.2 服务.
在命名空间下, 有各个服务.比如我们上面定义的是在public命名空间下, 定义了两个服务. 一个是proctor, 一个是consumer.
服务有 服务名 和 实例. ****远程调用的时候, 需要指定服务名.
5.3 实例
实例是基于网络协议通讯, 所以必须要有一个ip:端口号
5.4 元信息
在及群里点击某一个实例-->编辑, 可以看到如下页面, 可以设置元信息
那么元信息是什么呢? 每台服务器都可能会设置自己的个性化的信息. 这就是每台服务器的元数据信息
5.5 实操---指定集群的命名空间为dev
只需要在配置中增加命名空间就可以了.
我们也可以修改集群的名字, 集群的默认名字是DEFAULT. 我们这里将其修改为default. 在控制台dev命名空间下, 可以看到启动了服务customer.
❷ nacos原理
0、服务容器负责启动,加载,运行服务提供者。
1、服务提供者在启动时,向注册中心注册自己提供的服务。
2、服务消费者在启动时,向注册中心订阅自己所需的服务。
3、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Registry 就是注册中心,负责服务的注册与发现。Dubbo 有自己的 Registry 实现,而 Nacos 则是另一种 Registry 的实现。
负载均衡有很多种实现方式,包括轮询法,随机方法法,对请求ip做hash后取模等等,从负载的维度考虑又分为:服务端负载均衡和客户端负载均衡。
Nacos 的客户端在获取到服务的完整实例列表后,会在客户端进行负载均衡算法来获取一个可用的实例,模式使用的是随机获取的方式。
❸ 八(一) nacos 本地多实例(伪集群)
ncaos 参考文档: https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
一,ncaos 配置两个
(1)复制一份ncaos1.4,修改application.properties文件端口为8849
(2)使用 cluster.conf 文件,配置ip:port
(3)分别启动 http://localhost:8848/nacos/index.html#/login , http://localhost:8849/nacos/index.html#/login
二,使用 第三章 nacosa项目
(1)将config.server-addr 配置为 localhost:8848,localhost:8849
(2)数据源使用同一个地址的库
(3)本地通过修改port 启动多个nacosa实例启动,查看nacos 中心
(4)任意修改配置nacosa.yml 文件,发现两个实例均同步nacos数据
❹ nacos原理
nacos目前是集成到spring cloud alibaba里去的,也就是在spring cloud的标准之下实现了一些东西,spring cloud自己是有一个接口,叫做ServiceRegistry,也就是服务注册中心的概念,nacos中有一个它的实现类NacosServiceRegistry,实现了register、deregister、close、setStatus、getStatus之类的方法。
自动装配是一个spring boot的一个概念,自动装配的意思,其实就是说系统启动的时候,自动装配机制会运行,实现一些系统的初始化,自动运行,也就是系统启动时自动去调用NacosServiceRegistry的register方法去进行服务注册。而且除了注册之外,还会通过schele线程池去提交一个定时调度任务,源码如下:
this.exeutorService.schele(new BeatReactor.BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS),这就是一个心跳机制,定时发送心跳给nacos server。
然后会访问nacos server的open api,其实就是http接口,他有一个接口:http://31.208.59.24:8848/nacos/v1/ns/instance?serviceName=xx&ip=xx&port=xx,这么一个东西,也没什么特别的,这里就是访问注册接口罢了
nacos server那里是基于一个ConcurrentHashMap作为注册表来放服务信息的,直接会构造一个Service放到map里,然后对Service去addInstance添加一个实例,本质里面就是在维护信息,同时还会建立定时检查实例心跳的机制。最后还会基于一致性协议,比如说raft协议,去把注册同步给其他节点。
服务发现的本质其实也是nacos server上的一个http接口,就是:http://31.208.59.24:8848/nacos/v1/ns/instance/list?serviceName=xx,就这么一个接口,然后就会启动定时任务,每隔10s拉取一次最新的实例列表,然后服务端还会监听他服务的状态,有异常就会基于UDP协议反向通知客户端这次服务异常变动。
❺ Nacos动态配置原理浅谈
SpringBoot环境引入配置中心依赖
查看spring-cloud-starter-alibaba-nacos-config的spring.factories文件
首先加载初始化:org.springframework.cloud.alibaba.nacos.的NacosPropertySourceLocator对象
ps:何时加载 请参考: https://blog.csdn.net/m0_37607945/article/details/107762760 )
重点关注NacosPropertySourceLocator.locate方法
那locate方法具体什么时候执行呢?
spring容器在初始化,准备上下文时,会调用所有实现ApplicationContextInitializer接口的类然后遍历执行其initialize()方法
而nacos则正是利用了spring的这种自定义PropertySourceLoader加载机制与spring完美结合,说明一个好的框架的扩展性设计是多么重要,同样如果从事自研中间件的小伙伴也必须对spring的各种机制,功能点必须非常熟悉才能写出优秀的框架。
接下来就回到了:NacosPropertySourceLocate的locate方法
利用反射创建NacosConfigService实例
接下来我们看看其构造函数都做了什么操作
依次初始化命名空间,以及http的包装类,实际执行的是serverHttpAgent,以及ClientWorker(长轮询),重点看一下ClientWorker
可以看到ClientWorker里面初始化了两个线程池,一个是定时执行任务线程池,一个是不定长线程池,同时启动了定时任务线程池,设定每10毫秒执行一次:checkConfigInfo()方法。(先记得有这么回事)
继续看locate 加载方法:
先加载共享配置类文件,即配置:spring.cloud.nacos.config. shared-dataids的文件
再加载配置为:spring.cloud.nacos.config.ext-config 的列表文件,
再去加载系统默认配置
内部方法调用逻辑大同小异,都是调用loadNacosDataIfPresent方法
继续跟,走到loadNacosData方法
走到NacosConfigService的getConfig方法,getConfig方法会先去查询本地文件(降级策略),本地文件存在则返回,本地文件不存在则调用http接口获取,至此,配置中心初始化拉取数据完毕。
我们在nacos控制台修改了数据,客户端又是如何快速感知到的呢?
入口在:NacosConfigAutoConfiguration的nacosContextRefresher方法
nacosContextRefresher实现了ApplicationListener<ApplicationReadyEvent>,会在spring容器发布ApplicationReadyEvent事件时,触发监听操作
针对每个配置文件注册监听
首先声明一个Lister逻辑,然后放到listerMap中,key为dataId,lister内部逻辑主要是收到更新配置后,更新md5值,然后利用spring applicaiton发布RefreshEvent事件。
紧接着调用
configService.addListener(dataId, group, listener);
看下其内部处理逻辑
简单概括就是将配置信息封装成了一个cacheData对象,然后放到hashmap中
再次回到上文中的,ClientWorker的定时任务线程池中checkConfigInfo方法,每隔10s会去执行一次,
此处的longintTaskCount 自己理解应该一直是0,因为listenerSize 为配置文件数目不会超过3000,然后ParamUtil.getPerTaskConfigSize()也是固定值为3000,因此longingTaskCount为0,currentLongingTaskCount也为0,也就是if条件会永远不满足,但debug发现longingTaskCount会变为1,但是不知道为啥(希望大神解惑)
继续看LongPollingRunnable的run方法
如果没有用到本地配置,并且本地配置文件确实存在,则采用本地配置
如果是采用的本地配置。并且本地文件删除了 ,则设置setUseLocalConfigInfo(false)
检查md5值是否有变更,如有通知发送监听
执行lister的receiveConfigInfo()方法
总结:客户端通过定时任务线程池来监听配置,当服务端配置发生变更时,客户端会通过拉(长轮询)方式得到最新的值并保存在cacheData中,然后于cacheData的listener的md5值做对比,如果有更新则通知,触发lister的reveiveConfig方法;
来看下服务端的长轮询处理:
发起长轮询请求,对应http接口:post请求,/v1/cs/configs/listener,并设置超时时间30s,逻辑是如果30s内配置发生了变更,则会立马返回,否则等待29s后执行检查判断配置是否发生变更返回。然后继续发起轮询请求,循环往复
服务端长轮询接口处理逻辑:
将请求设为异步,并封装成ClientLongPolling
ClientLongPolling 的run逻辑:
1.创建一个调度的任务,调度的延时时间为 29.5s,(29.5由客户端默认传递超时时间30s-服务端设置的500ms得来)
2.将该 ClientLongPolling 自身的实例添加到一个 allSubs 中去
3.延时时间到了之后,首先将该 ClientLongPolling 自身的实例从 allSubs 中移除
4.获取服务端中保存的对应客户端请求的 groupKeys 是否发生变更,将结果写入 response 返回给客户端
allSubs则必然和客户端配置变更有必然联系,查看服务端修改配置方法:post /v1/cs/configs/
先持久化,再去发布configDataChangeEvent事件
而我们的LongPollService 监听的则是LocalDataChangeEvent事件,似乎和ConfigDataChangeEvent没关系,其实不然
继续跟进ConfigController的ConfigChangePublisher
.notifyConfigChange(new ConfigDataChangeEvent(....)))方法
AsyncNotifyService中注册监听逻辑
会执行一个AsyncTask任务,从而触发一个http get接口:
也就是:
mpService是负责将配置保存到磁盘的服务类
看到确实发布了LocalDataChangeEvent事件,
然后又回到了上图:LongPollingService 的onEvent方法,接着看DataChangeTask的逻辑,
首先遍历allStubs队列,然后找出当前的ClientLongPolling,
从队列中移除,然后response写入变更的groupKey
总结:可以看到nacos实际上是利用了推+拉 结合的方式来获取配置,当没有配置发生变更时,会hang住请求,默认等待(30-0.5)29.5秒后返回,而一旦发生数据变更,又会立刻推送变更数据写入到response,然后客户端更新配置;
以上则是动态配置原理,如果有不对的地方请指出;
参考: https://www.jianshu.com/p/acb9b1093a54
❻ 聊聊nacos的LocalConfigInfoProcessor
本文主要研究一下nacos的LocalConfigInfoProcessor
nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java
nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java
LocalConfigInfoProcessor的getFailover方法首先会通过getFailoverFile获取本地配置文件,然后通过readFile读取;getSnapshot方法首先通过getSnapshotFile获取snapshot文件,然后通过readFile读取;saveSnapshot方法会存储新的config;cleanAllSnapshot方法会清除snapshot目录下所有缓存文件
❼ nacos和eureka的区别是什么
nacos和eureka的区别区别如下:
springcloud eureka是注册中心,负责微服务的注册与发现,起到承上启下的作用,在微服务架构中相当于人体的 大脑,很重要,nacos是阿里巴巴出的,功能类似eureka。
nacos的部署方式与springcloud eureka不太一样,euraka是需要创建springboot项目,然后将euraka服务端通过gav的方式加载进来,然后部署项目。nacos是直接从阿里巴巴nacos的官网下载jar包,启动服务。
Eureka Server:
之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
❽ Nacos服务发现
Nacos 另一个非常重要的特性就是服务注册与发现,说到服务的注册与发现相信大家应该都不陌生,它们是服务治理的最基础功能。
Nacos 支持几乎所有主流类型的 “服务” 的发现、配置和管理。
了解过 Dubbo 的同学,应该对 Dubbo 的架构非常熟悉,最经典的一张架构图如下所示:
图中的6个步骤的含义解释如下:
0、服务容器负责启动,加载,运行服务提供者。
1、服务提供者在启动时,向注册中心注册自己提供的服务。
2、服务消费者在启动时,向注册中心订阅自己所需的服务。
3、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
其中图中最上方的 Registry 就是注册中心,负责服务的注册与发现。Dubbo 有自己的 Registry 实现,而 Nacos 则是另一种 Registry 实现。
相对服务注册而言服务发现就简单很多了。就是Nacos客户端调用Open API或者SDK查询服务列表,服务端接受到请求后根据将查询到服务包装成json格式返回。
既然如此,客户端是啥时候发起服务列表查询?
如果客户端查的时差内,刚好有服务实例有down掉,那客户端的请求岂不是有请求到刚好down的服务实例?下面进行解答。
客户端有一个HostReactor类,在com.alibaba.nacos.client.naming.core包下。
HostReactor它里面有一个UpdateTask线程,每 1s 发送一次pull拉取请求,获取服务最新的地址列表。
更新服务的核心逻辑在updateService方法中:
再看看processServiceJson方法, 本地维护一个Map<String,ServiceInfo> serviceInfoMap 存储服务信息,同时调用 DiskCache.write(serviceInfo, this.cacheDir) 方法把服务信息写入本地缓存文件中;
服务端采取的是基于push的方式向客户端通知,由于服务端和服务提供者(各个微服务provider)建立了心跳机制,一旦某个服务出现故障,服务端察觉出后,会发送一个push消息给Nacos客户端,也就是我们的消费者。这个push消息是使用DatagramSocket来实现的。
服务消费者收到服务端发来的push消息之后,使用HostReactor中提供的ServiceInfo processServiceJson(String json)方法解析消息,并更新本地服务地址列表。
可以参照下面的图更容易理解服务动态感知原理,包括:客户端主动轮训查询服务列表及服务端Push变故后的服务列表。
❾ 软件更新丨Spring Cloud Alibaba发布第二个版本,Spring发来贺电
还是熟悉的面孔,还是熟悉的味道,不同的是,这次的 配方升级 了。
时隔 51天,Spencer Gibb再次在Spring官网的博客页面宣布:Spring Cloud Alibaba发布了其开源后的 第二个版本0.2.1 ,随后,Spring Cloud 官方Twitter也转发了此消息。圣诞节的前一周,Josh Long向他的老朋友许晓斌发来贺电:
视频地址:https://spring.io/blog/2018/12/26/spring-tips-bootiful-alibaba
一、新版本概要
Spring Cloud Alibaba RocketMQ
Spring Cloud Alibaba SchelerX
Spring Cloud Alibaba Nacos Config
Spring Cloud Alibaba Nacos Discovery
Spring Cloud Alibaba Sentinel
二、新版本背后的思考
Spring Cloud Alibaba Nacos Discovery
Nacos Discovery 在这个版本最大的更新就是支持在初始化的时候不使用本地文件缓存,目前初始化的时候已经默认不使用本地文件缓存。
为什么要有缓存?首先我们来了解一下本地缓存的概念,为什么需要这个本地缓存?
我们都知道,服务注册与发现应该只是服务调用中的辅助性的一个环节,而不是一个关键的环节。一个良好的服务注册与发现的设计,需要保证以下两点。
要实现以上两点,缓存就不可或缺,而为了适应不同的场景,缓存又可以分成内存缓存和本地文件缓存,他们的概念和适用场景如下。
内存中的缓存
将服务提供者列表维护在内存中,每次调用时直接从内存中的列表获取节点即可。内存缓存通过定时任务更新,或者在收到服务注册中心的推送之后再更新。确保了即使在服务注册中心宕机的情况下,也能保证服务仍能正常调用。
本地文件缓存
将上述提到的内存中的缓存,保留在本地的某个文件中,这样在调用服务注册中心失败的时候,可以从本机的文件缓存中获取服务提供者列表。这样就保证了在服务注册中心宕机的情况下,应用在重启后也能找到服务提供者。
为什么要关闭
有了以上背景知识后,读者可能会有疑问,既然缓存这么好,你们为什么默认要把它置为默认关闭呢?
我们在发布出第一个版本之后,很多用户反馈,为什么我服务下线之后还是有节点,仍旧可以被查询到呢?这样导致我这个监控数据完全不准,你们这个有 bug,完全不对。其实这是阿里巴巴在多年业务积累的经验,对于服务发现来说,一个即使是已经过时的节点,也比没有任何数据好。而且还有可能其实这个服务只是和服务注册中心失去了心跳,但是应用本身是正常的。
当然,这也暴露了我们设计中存在的一些问题,没有把服务发现本身,以及缓存的分层给做好,两者糅合在一块。所以在这一次的版本更新中我们还是选择适配开源标准,默认关闭了本地文件缓存,留了一个开关给用户自由选择。
Spring Cloud Alibaba Nacos Config
Nacos Config 在这个版本中有两个大的特性,支持了“共享配置”,修正了动态刷新的语义和行为。
共享配置的实现
在第一个版本还没发布到时候,社区里对配置管理中心的讨论就没停止过,其中听到最多的反馈应该就是支持多个应用共享一个配置。我们也通过 github issue 的方式,征集了很多意见,详情见 #12 , #141。
后来我们将这个模型抽象了一下,认清这些需求本质是一个应用可以从多个 DataID 和 GroupID 组合中获取配置,并且这些配置还可以单独指定优先级和是否动态刷新。
最后我们推荐了这种使用方式,既保证了使用场景的灵活性,又保证了业务语义的明确性。更多详情可以参考 WIKI。
注意 data-id 的值必须带文件扩展名,文件扩展名支持 properties、yaml 和 yml。通过这种自定义扩展的配置项,既可以支持一个应用从多个配置项中获取数据,也解决多个应用间配置共享的问题。
头脑风暴,@fudali 同学还提出了更加灵活的一种方式 #161,就是可以通过一个配置项来配置所有的 DataID 的信息,然后可以通过修改这个配置项,可以修改整体配置项的逻辑。
这是一个非常好的想法,只不过这一期中我们没有实现,原因是这种方式太灵活了。我们认为配置管理其实是一件很严肃的事情,太灵活导致生产中变更比较不可控。
虽然目前的逻辑也可以支持这种用法,但是我们并没有把这种模式当做推荐模式,后续如果社区有更多的反馈认为这是一个强烈的需求,欢迎提 PR。
动态刷新的修正
简单好用、实时可监控的动态刷新也许是 Nacos Config 相对于其他开源中间件相比最核心的优势了,不同于 Spring Cloud Config Server 必须使用 Spring Cloud Bus 才能实现动态刷新,Nacos Config 无需依赖其他任何中间件就可以实现实时动态刷新,而且推送成功与否和刷新 历史 还支持实时查询。
但是我们发现在第一个版本中的实现发现两个问题:
在这个版本中,我们修复了这两个问题。
首先,动态刷新不再是直接去调用 ContextRefresher.refresh() 方法,而是 publish 了一个 RefreshEvent,让 spring-cloud-commons 里的 RefreshEventListener 去触发这个 ContextRefresher.refresh() 方法。
其次,我们修正了动态刷新的语义后,这一次是真正做到了,只有 refresh 属性为 true 的配置项,才会在运行的过程中变更为新的值,refresh 属性为 false 的配置项再也不用担心应用在运行的过程中发生莫名其妙的变更了。
更深入一点,在上个月 SpringOne Tour 中,我们和 Spring Cloud 的创始人 Spencer 聊到 Spring Cloud 的 Context.refresh() 成本太高,会刷新整个 Spring Context。他反复强调了两次 Context.refresh() 只对 @RefreshScope 和 @ConfigurationProperties 有效,成本一点也不高。
之前我们接收到很多社区的反馈都是 Nacos Config 动态刷新支不支持 xxxx,支不支持 xxxx。之前我们都是回答只支持 @RefreshScope 和 @ConfigurationProperties ,如果他内置没有支持,那就得自己加上相应的注解。
今天我们可以很愉快地回复,他监听了 RefreshEvent 也能直接支持。而且如果添加 @RefreshScope 和 @ConfigurationProperties 都不满足你的需求时,可以通过实现自己的 RefreshEventListener 更多高级的玩法。
Spring Cloud Alibaba Sentinel
Sentinel 在这个版本中有三个大的特性:全面支持 FeignClient ,完善了 RestTemplate 的支持,添加了热点限流、集群限流。
FeignClient 集成 Sentinel
其实在这之前,Sentinel 支持 FeignClient 已经设计了很久了,之前我们的想法做一个兼容性较强的方案,支持 Sentinel 和 Hystrix 在 FeignClient 中同时使用,尽量做到对原有应用的侵入性做到最小。
这个方案我们也思考调研了很久,但是实现难度确实比较大,需要修改 FeignClient 的代码才能实现两者共存。正好前段时间在 Spring Cloud 届最大的新闻就是 Hystrix 宣布不在维护了,于是我们就换了一个思路,直接使用 Sentinel 替代 Hystrix,不再去追求支持两者共存。
我们实现了自己的 Feign.Builder,在构建的 FeignClient 执行调用的过程中,通过 SentinelInvocationHandler 完成 Sentinel 的流量统计和保护的动作。如果想使用 Sentinel 为 FeignClient 限流降级,首先需要引入 sentinel-starter 的依赖,然后打开 Sentinel 限流降级的开关 feign.sentinel.enabled=true ,就完成了 Sentinel 的接入。如果需要使用更加定制化的功能,则需要在 @FeignClient 添加 fallback 和 configuration 这些属性的配置。
注意 @FeignClient 注解中的所有属性,Sentinel 都做了兼容。
RestTemplate 集成 Sentinel
Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,补全了 Hystrix 这一块的空白。接入的方式也不复杂,在构造 RestTemplate bean 的时候需要加上 @SentinelRestTemplate 注解,然后在控制台配置相应的规则即可。
在触发了限流降级时,默认的处理方式是返回 RestTemplate request block by sentinel 信息。
RestTemplate 的限流降级 ?Sentinel 也承包了!
热点参数限流和集群限流
首先解释一下什么是热点参数限流和集群限流。
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
集群流控主要解决的问题是:当我们需要控制整个集群流量总量,但是单机流量可能会不均匀,如果是单机维度去限制的话会无法精确地限制总体流量,因此需要引入集群维度的流量控制。
Sentinel v1.4.0 的 新功能 ,也能第一时间愉快地在 Spring Cloud Alibaba 上使用了。
三、新组件
Spring Cloud Alibaba RocketMQ
Spring Cloud Stream 是一个用于构建基于消息的微服务应用框架,它基于 SpringBoot 来创建具有生产级别的单机 Spring 应用,并且使用 Spring Integration 与 Broker 进行连接。它提供了消息中间件的统一抽象,推出了 publish-subscribe、consumer groups、partition 这些统一的概念。
RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。具有以下特点:能够保证严格的消息顺序、提供丰富的消息拉取模式、高效的订阅者水平扩展能力、实时的消息订阅机制、亿级消息堆积能力。
在这次的 Spring Cloud Stream Binder RocketMQ 的实现中,我们适配了 Spring Cloud Stream 对于 message 抽象的 API,支持了 RocketMQ 的事务消息。消息订阅时支持以 tags、SQL 表达式过滤消息,同时还支持顺序、并发、延迟以及广播消费模式。
Spring Cloud Alibaba SchelerX
SchelerX 是阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务,同时提供分布式的任务执行模型,如网格任务,网格任务支持海量子任务均匀分配到所有 Worker(schelerx-client)上执行。
简单易用的轻量分布式任务调度
您不需要关心调度逻辑,只需要在在 JobProcessor 接口的实现中添加业务逻辑即可,然后在自主运维控制台配置上一个 Job 即可完成使用。
高可用的分布式任务
不管是 SchelerX 服务端还是客户端都是分布式架构设计,任务可以在多台客户端机器里的任何一台机器执行,如果客户端出现宕机的情况,服务端会自动选择正常运行的客户端去执行 Job,每个 Job 在服务端的不同机器均有备份,SchelerX 服务端任意宕掉部分机器仍能保证 Job 正常调度。
友好的用户界面
SchelerX 提供了非常友好的页面方便您创建、删除或修改 Job,提供了立即触发执行一次的功能,方便您测试以及关键时刻手动立即执行一次,还提供了 历史 执行记录查询的功能,您可以看到任何一个 Job 过去 100 次的 历史 执行记录。
功能强大
提供了秒级、精准的定时任务调度服务,且提供了丰富的任务执行模型,包括单机执行,广播执行,以及子任务的分布式执行。
四、What's Next?
Spring Cloud Alibaba Cloud SLS 针对日志类数据的一站式服务,在阿⾥巴巴集团经历大量大数据场景锤炼⽽成。您⽆需开发就能快捷地完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。
Spring Cloud Alibaba Dubbo Dubbo 是一款流行的开源 RPC 框架,我们会把 Dubbo 整合到 Spring Cloud Alibaba 中,让大家在开发 Dubbo 时也能享受到 Spring Cloud 带来的便利。
致谢
Spring Cloud Alibaba 从开源建设以来,受到了很多社区同学的关注。社区的每一个 issue ,每一个 PR,都是对整个项目的帮助,都在为建设更好用的 Spring Cloud 添砖加瓦。
↓↓↓
❿ Nacos Config Spring Cloud
这里分析下Nacos Config在Spring Cloud下的使用。整体调用链路如下表所示。
加载了spring.cloud.nacos相关配置,生成NacosConfigProperties。
初始化了NacosConfigManager,主要是根据NacosConfigProperties初始化了ConfigService,这个也是Nacos Client 对外保留的核心对象,用于获取配置,添加监听等。
用于初始化了NacosPropertySourceLocator对象,实现了PropertySourceLocator接口,用于加载额外的配置信息。
加载分布式的配置,形如spring.cloud.nacos.config.shared-configs[0]=xxx 的配置。
加载扩展配置,如spring.cloud.nacos.config.extension-configs[0]=xxx ,其实我没搞明白这俩有啥区别。
加载应用主配置信息,这里会加载默认的两个配置,dataIdPrefix+group与dataIdPrefix.fileExtension+group,如果不想加载可以通过配置spring.cloud.nacos.config.group=来避免加载,只加载上面的shared-configs与extension-configs的配置。
这里会判断是否有父Context并且包含了NacosConfigProperties,如果有从父Context中直接获取。
初始化了NacosRefreshHistory,用于存储Nacos数据更新历史,最大存储20条。
初始化了NacosConfigManager,与上面的一致。
初始化了NacosContextRefresher,这个是比较核心的类,这个类实现了ApplicationListener,监听ApplicationReadyEvent事件,在Application完成的时候,执行Nacos变化监听。
() --> registerNacosListener() --> 这里会新建一个Listener,并通过configService#addListener()方法添加到Nacos的变化监听中。当收到变化的时候,首先添加了刷新纪录nacosRefreshHistory#addRefreshRecord(),其次发布了RefreshEvent事件。
在发布了RefreshEvent事件之后,就可以使用 Spring Cloud自带的RefreshScope机制 来实现属性的刷新了。在Spring Cloud 监听到变化,会执行ContextRefresher#refresh()。首先会重新加载引导信息,也就会重新调用NacosPropertySourceLocator#locate(),从而从Nacos中读取最新的配置。其次会清空缓存对象,重新以最新的Enviroment生成对象,来达到属性更新的目的。
整体来说Nacos Config在Spring Cloud的实现比较简单。核心使用了Spring Cloud的RefreshScope机制来实现对象属性的动态刷新。