Nacos
和Eureka不同,Nacos具有独立运行的服务器,你需要下载它的服务器并启动,而Eureka是通过一个Java项目启动的。
安装过程不记录了,直接去github repo上下载对应Release。
配置#
父pom的dependencyManagement
中添加SpringCloudAlibaba的依赖管理
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
子项目中添加Nacos组件
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置:
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
Nacos服务分级模型#
为了提高容错性,避免某个机房不可用而导致服务整体不可用,Nacos提供了三级的服务分级概念。
- 服务:即一类服务
- 集群:在某一机房的一批服务实例
- 实例:具体的一个服务实例
这样做的好处:
- 容灾
- 如果集群内部有对应的服务,直接使用集群内部的本地服务,性能更高
配置集群#
你可以通过spring.cloud.nacos.discovery.cluster-name
来配置服务所在的集群名称
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HZ
下面,我们通过命令行参数来启动多个实例,并将它们分到不同集群。
右击服务列表中的服务,点击CopyConfiguration
通过弹出的界面添加命令行参数并修改服务名,添加命令行参数在Modify options
选项中
启动这三个服务,记得以同样的方式给它们分配不同的端口,现在去Nacos控制台查看:
NacosRule负载均衡#
根据Ribbon的学习经验,我们知道选择哪个服务完全是由负载均衡策略决定的,所以上面,即使你将服务消费者配置到了某个已存在的集群中,它依然不会去优先选择本地集群中的服务,而依然是使用默认的负载均衡策略。
Nacos提供了一个IRule实现类,叫NacosRule
,你只需要把策略改成它即可。
@Configuration
public class LoadBalanceConfig {
@Bean
public IRule loadBalanceRule() {
return new NacosRule();
}
}
现在如果服务消费者希望消费的服务如果在本地集群中存在,那么它就优先选择本地集群中的服务提供者,如下这个OrderService只会选择杭州集群中的两个服务:
如果本地集群中不存在对应服务,就跨集群访问,并在日志中输出一个警告。
NacosRule
在集群内部使用随机策略。
负载均衡权重设置#
通过spring.cloud.nacos.discovery.weight
设置或者通过Nacos控制台动态设置。
环境隔离#
最外层的隔离,不同命名空间中的服务不能相互访问,是互相隔离的
Nacos与Eureka对比#
临时与永久实例#
- Nacos提供临时实例的概念,并且默认所有实例都是临时的
- 临时实例提供和Eureka一样的心跳检测,即实例向Nacos发请求报告自己的健康状况
- 对于非临时实例,则是Nacos主动询问实例的健康状况,并且在它处于不健康状态时不会从服务列表中移除
临时实例通过spring.cloud.nacos.discovery.ephemeral
配置
服务变更推送#
Nacos的消费者虽然也会主动拉取服务,但一旦Nacos发现服务下线了,它会主动推送消息到消费者,所以相比Eureka,Nacos能更及时的发现服务下线,以免发生一些失败的服务访问。
Nacos配置管理#
除了服务注册,Nacos还提供了配置中心的功能,我们不再需要其它单独的配置中心组件。
新建配置#
可以在Nacos控制台中添加配置,配置的Data ID
用于一个服务唯一定位它的配置,它的命名格式和SpringBoot所使用的完全一致,是服务名-环境名.后缀名
如果要配置一个非特定环境的配置文件,你可以省略环境名,就像
application.yaml
一样,创建一个服务名.后缀名
的配置文件
自动读取配置#
由于我们希望nacos的配置文件在application.yaml之前被加载并于application.yaml合并,所以,我们不能在application.yaml中配置nacos了,因为nacos需要在那之前被加载。
SpringBoot的配置文件中,有一个bootstrap.yaml
,它会在application.yaml加载前被加载,我们可以将Nacos相关的配置配在这里。
先引入Nacos的配置starter
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
然后添加bootstrap.yaml
spring:
application:
name: user-service
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
file-extension: yaml
这里,你要指定服务名,环境和配置文件后缀,它们要与配置中心中的Data Id
对应。
现在,可以回到application.yaml
中删除这些已有的配置。
在Controller中对配置中心的配置进行读取测试:
@RestController
@RequestMapping("/user")
public class UserController {
@Value("${custom.value}")
private String value;
@GetMapping("/getValue")
public String getValue() {
return value;
}
}
总结
- 在Nacos控制台中编写配置
- 导入Nacos Config的Starter
- 将Nacos配置移动到bootstrap.yaml
- 在bootstrap.yaml中配置服务名,环境名,配置文件后缀名以匹配Nacos中配置的DataID
- 读取配置
配置热更新#
实际上,现在我们的程序已经能够感知到Nacos中配置的更新了,当你在Nacos中更新配置时,控制台会打印这样的消息
但我们发现当我们更改配置后,使用@Value
注解读取的属性并没发生变化,仔细想想其实也对,这个Bean已经创建完成并且是单例的,它的属性也已经在Bean初始化阶段注入完成,即使配置更改,它也不会重新读取了。
下面我们做个测试,我们把Controller中的代码改成这样:
@Value("${custom.value}")
private String value;
@Autowired
private Environment env;
@GetMapping("/getValue")
public String getValue() {
String valueFromEnv = env.getProperty("custom.value");
return value + ", " + valueFromEnv;
}
上面的代码与之前唯一的不同就是,除了使用Value注入属性,它还注入了一个Environment,并且每次请求时都从Environment中读取这个属性。如果Nacos在配置属性更新后通知我们的微服务,并且我们的微服务已经修改好了配置,那么通过Environment读取的配置应该会变成新的:
这再次证明了,Nacos能主动的在配置更新时通知微服务,并且微服务会更新配置,但你需要以某种方式让你Bean中的属性发生更新
RefreshScope#
@Scope("refresh")
public @interface RefreshScope
RefreshScope是一个组合注解,它就是一个SpringCloud中提供的新的作用域类型而已,和Singleton、Prototype与SpringMVC中提供的Request,Session是一个东西。
那么RefreshScope
这个作用域的特点就是,它可以在运行时刷新,任何使用它们的组件都将在下一次方法调用时获得一个新实例,完全初始化并注入所有依赖。
这样的话,只需要在你的Controller
上添加RefreshScope
就可以让每次客户端请求时都重新加载配置了
虽然这样可以达到目的,但每次调用方法生成一个新的Bean对象,即使旧的Bean对象会被丢弃,但在高并发的场景下,这样不会增加GC要处理的垃圾对象的压力吗?
ConfigurationProperties#
ConfigurationProperties
有热更新的能力,当配置更新时,Bean中对应的属性会更新,我不知道这是不是SpringCloud扩展的能力,但是,用就完了。
@Data
@Component
@ConfigurationProperties(prefix = "custom")
public class CustomConfig {
private String value;
}
@Autowired
private CustomConfig config;
@GetMapping("/getValue")
public String getValue() {
return config.getValue();
}
这样,配置也能够热更新。
虽然不知道@ConfigurationProperties
的自动更新是如何实现的,但是我们可以猜测,因为UserController
中持有了配置对象,那么配置对象肯定没有发生变化,就是说过程中没有创建新的对象,那么@ConfigurationProperties
肯定是在配置更新时将新配置设置到配置对象中了。这要比刚刚那个@RefreshScope
优雅的多。
本地和远端配置优先级#
远端 > 本地
Nacos集群搭建#
架构
Nacos默认使用内嵌的服务器,但当你要建立集群时,集群中的Nacos应该共享数据,所以你应该使用它们共用的MySQL。这个MySQL可以组织成主从集群也可以是单体的。
Docker搭建集群#
使用Docker进行Nacos集群的配置非常简单,这里不使用手动配置了。
git clone https://github.com/nacos-group/nacos-docker
cd nacos-docker
docker-compose -f example/cluster-hostname.yaml up
这样,三台nacos的集群以及它们所使用的共享MySQL服务器就配置成功了。
最开始你可能会看到控制台不断地刷新
nacos is starting...
,没关系,等一会儿集群就建立成功了,然后就可以通过http://localhost:8848/nacos
来访问其中的机器了
集群中的三台机器的端口号分别是
8848
、8849
和8850
Nginx负载均衡#
下面,我们使用一个Nginx将请求负载均衡到集群中的机器上。
http {
upstream backend{
server localhost:8848;
server localhost:8849;
server localhost:8850;
}
server {
listen 8855;
location / {
proxy_pass http://backend;
}
}
}
启动nginx
现在访问8855即可将请求负载均衡到nacos集群中。
使用SpringCloud访问#
先在nacos集群中创建一个配置
这里有个小坑,如果你和我一样不幸,创建配置时可能会出现配置创建失败的提示,这是因为默认情况下的mysql版本和nacos版本不匹配,这时我们去配置文件中固定下它的版本即可。打开
example/cluster-hostname.yaml
,然后将其中的nacos镜像版本全都改成1.4.1,将mysql版本改成8.0.16
spring:
application:
name: user-service
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: localhost:8855
config:
file-extension: yaml
通过SpringCloud程序访问:
创建集群总结#
Docker帮我们隐藏了许多创建集群的细节,但我们要知道
- 需要多台服务器,并将集群中的所有服务器地址告知集群中的每一个Nacos实例
- 需要使用外部数据库,需要在外部数据库中创建Nacos所需要的数据库以及表结构,并告知集群中的每一个Nacos实例
- 使用Nginx配置负载均衡
- 在Nacos版本与MySQL版本不兼容时,可能会出现错误。
作者:Yudoge
出处:https://www.cnblogs.com/lilpig/p/16555255.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
欢迎按协议规定转载,方便的话,发个站内信给我嗷~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通