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提供了三级的服务分级概念。

img

  1. 服务:即一类服务
  2. 集群:在某一机房的一批服务实例
  3. 实例:具体的一个服务实例

这样做的好处:

  1. 容灾
  2. 如果集群内部有对应的服务,直接使用集群内部的本地服务,性能更高

配置集群#

你可以通过spring.cloud.nacos.discovery.cluster-name来配置服务所在的集群名称

cloud:
    nacos:
        discovery:
            server-addr: localhost:8848
            cluster-name: HZ

下面,我们通过命令行参数来启动多个实例,并将它们分到不同集群。

右击服务列表中的服务,点击CopyConfiguration

img

通过弹出的界面添加命令行参数并修改服务名,添加命令行参数在Modify options选项中

img

启动这三个服务,记得以同样的方式给它们分配不同的端口,现在去Nacos控制台查看:

img

NacosRule负载均衡#

根据Ribbon的学习经验,我们知道选择哪个服务完全是由负载均衡策略决定的,所以上面,即使你将服务消费者配置到了某个已存在的集群中,它依然不会去优先选择本地集群中的服务,而依然是使用默认的负载均衡策略。

Nacos提供了一个IRule实现类,叫NacosRule,你只需要把策略改成它即可。

@Configuration
public class LoadBalanceConfig {
    @Bean
    public IRule loadBalanceRule() {
        return new NacosRule();
    }
}

现在如果服务消费者希望消费的服务如果在本地集群中存在,那么它就优先选择本地集群中的服务提供者,如下这个OrderService只会选择杭州集群中的两个服务:

img

如果本地集群中不存在对应服务,就跨集群访问,并在日志中输出一个警告。

img

NacosRule在集群内部使用随机策略

负载均衡权重设置#

通过spring.cloud.nacos.discovery.weight设置或者通过Nacos控制台动态设置。

环境隔离#

最外层的隔离,不同命名空间中的服务不能相互访问,是互相隔离的

img

Nacos与Eureka对比#

临时与永久实例#

  1. Nacos提供临时实例的概念,并且默认所有实例都是临时的
  2. 临时实例提供和Eureka一样的心跳检测,即实例向Nacos发请求报告自己的健康状况
  3. 对于非临时实例,则是Nacos主动询问实例的健康状况,并且在它处于不健康状态时不会从服务列表中移除

临时实例通过spring.cloud.nacos.discovery.ephemeral配置

服务变更推送#

Nacos的消费者虽然也会主动拉取服务,但一旦Nacos发现服务下线了,它会主动推送消息到消费者,所以相比Eureka,Nacos能更及时的发现服务下线,以免发生一些失败的服务访问。

Nacos配置管理#

除了服务注册,Nacos还提供了配置中心的功能,我们不再需要其它单独的配置中心组件。

新建配置#

可以在Nacos控制台中添加配置,配置的Data ID用于一个服务唯一定位它的配置,它的命名格式和SpringBoot所使用的完全一致,是服务名-环境名.后缀名

img

如果要配置一个非特定环境的配置文件,你可以省略环境名,就像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;
    }

}

img

总结

  1. 在Nacos控制台中编写配置
  2. 导入Nacos Config的Starter
  3. 将Nacos配置移动到bootstrap.yaml
  4. 在bootstrap.yaml中配置服务名,环境名,配置文件后缀名以匹配Nacos中配置的DataID
  5. 读取配置

配置热更新#

实际上,现在我们的程序已经能够感知到Nacos中配置的更新了,当你在Nacos中更新配置时,控制台会打印这样的消息

img

但我们发现当我们更改配置后,使用@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读取的配置应该会变成新的:

img

这再次证明了,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集群搭建#

架构

img

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来访问其中的机器了

集群中的三台机器的端口号分别是884888498850

Nginx负载均衡#

下面,我们使用一个Nginx将请求负载均衡到集群中的机器上。

http {
  upstream backend{
    server localhost:8848;
    server localhost:8849;
    server localhost:8850;
  }

  server {
    listen 8855;
    location / {
      proxy_pass http://backend;
    }
  }
}

启动nginx

img

现在访问8855即可将请求负载均衡到nacos集群中。

使用SpringCloud访问#

先在nacos集群中创建一个配置

img

这里有个小坑,如果你和我一样不幸,创建配置时可能会出现配置创建失败的提示,这是因为默认情况下的mysql版本和nacos版本不匹配,这时我们去配置文件中固定下它的版本即可。打开example/cluster-hostname.yaml,然后将其中的nacos镜像版本全都改成1.4.1,将mysql版本改成8.0.16

img

img

spring:
  application:
    name: user-service
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8855
      config:
        file-extension: yaml

通过SpringCloud程序访问:

img

创建集群总结#

Docker帮我们隐藏了许多创建集群的细节,但我们要知道

  1. 需要多台服务器,并将集群中的所有服务器地址告知集群中的每一个Nacos实例
  2. 需要使用外部数据库,需要在外部数据库中创建Nacos所需要的数据库以及表结构,并告知集群中的每一个Nacos实例
  3. 使用Nginx配置负载均衡
  4. 在Nacos版本与MySQL版本不兼容时,可能会出现错误。
posted @   yudoge  阅读(114)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示
主题色彩