java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringCloud Alibaba Nacos服务注册中心

SpringCloud Alibaba Nacos服务注册中心解读

作者:Master_hl

这篇文章主要介绍了SpringCloud Alibaba Nacos服务注册中心使用详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1. 什么是注册中心?

注册中心是微服务架构中的一个重要组件,它用于实现服务注册服务发现

【思考一】什么叫服务注册 ? 什么叫服务发现 ?

Nacos 结合了两者的优势,提供了一个更加灵活和高效的服务发现机制。在默认情况下,Nacos 使用推模式来通知消费者,但消费者仍然会定期拉取服务列表,以应对可能出现的消息丢失或延迟等问题。

【思考二】为什么需要服务注册和服务发现呢 ?服务之间直接调用存在什么问题呢 ?

具体有以下几个问题:

基于以上问题,所以我们需要使用服务注册于服务发现来更好的实现服务与服务之间的通信!

注册中心的作用

2. SpringBoot 整合 Nacos 实现服务注册中心

2.1 将服务注册到 Nacos

实现之前,Nacos 服务要启动起来,创建好 SpringBoot 多模块项目(一个父模块和两个子模块)

如果不会创建 SpringBoot 多模块项目的,可以去看我的这篇文章:https://www.jb51.net/program/360523q80.htm

【实现步骤】

PS:完成以上三步后,当前项目就会自动注册到 Nacos 中!

Nacos 连接信息

spring:
  application:
    name: nacos-discovery-demo
    # Nacos 服务名 (命名不要使用下划线 _,早期的SpringCloud版本不支持)
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # 连哪个 Nacos 的注册中心
        username: nacos
        password: nacos
        # ephemeral: false   # false 设置此服务为永久实例,true 为临时实例
server:
  port: 0  # 动态端口号

实现接口

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private ServletWebServerApplicationContext context; // 用于获取动态端口号

    // 服务
    @RequestMapping("/getnamebyid")
    public String getNameById(Integer id) {
        return "provider-name-" + id +
                " | port:" + context.getWebServer().getPort();
    }
}

PS:接口的实现不重要,重要的是我们能把它注册到注册中心,然后能通过另一个项目,调用到它!

【思考】为什么服务注册到注册中心,需要使用动态端口? 而不使用固定端口?

只有当用户去使用 Nacos 的时候,才需要使用一个固定的端口号,它不能每次变化,每次变的话,用户就猜不出来这个端口号是多少。

而对于微服务这边,它只需要将服务注册到注册中心,供消费者来使用,具体端口号是多少,对于程序来说,它不必预先知道。

并且使用动态端口有以下好处:

【测试服务注册中心】

当我们将服务注册到注册中心后,就可以在 Nacos 控制台看到这些信息了:

这个时候,我们就可以测试一下微服务里边的接口好不好使,点击操作下面的详情:

我们拿着 IP + 动态端口去访问接口,是可以正常访问得到的:

【上线下线 Nacos 服务的作用】

服务的上线与下线通常是指服务实例的动态注册与注销,利用这一特性,可以实现以下几种发布策略:

PS:当服务被下线时,注册中心在公布服务列表的时候,就不会包含已经下线的服务了,即使它是健康状态。

【点击上线或下线报错问题】

当点击上线下线如果报错了,或者创建临时实例 / 永久实例,报错了,可以通过以下方法来解决:

① 停止 Nacos 服务 (Ctrl C 多按几遍)

② 删除 nacos/data 目录下的 protocol 文件夹 (非常好使)

③ 开启 Nacos 服务,再次点击上下线就不会报错了

2.2 实现消费者

【前置工作】

1. 创建一个消费者的 module,在 pom.xml 文件中声明父节点

<parent>
    <groupId>com.example</groupId>
    <artifactId>nacos-discovery-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

2. 删除不必要的依赖:test,nacos-discovery,java.version 等声明,以及 dependencyManagement 依赖。

3. 在父模块中声明子模块

<modules>
    <module>provider</module>
    <module>consumer</module>
</modules>

【实现步骤】

配置 Nacos 连信息

spring:
  application:
    name: nacos-consumer-demo
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
        register-enabled: false  #消费者(不需要将此服务注册到 nacos)
        
server:
  port: 8080  # 使用固定端口

开启 OpenFeign 功能

@SpringBootApplication
@EnableFeignClients   // 开启 OpenFeign
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}

PS:在启动类上加上@EnableFeignClients 注解!

声明 OpenFeign 式的 Service

@Service
@FeignClient("nacos-discovery-demo") //调用nacos中的nacos-discovery-demo服务
public interface UserService {

    @RequestMapping("/user/getnamebyid")  // 调用生产者的接口
    public String getNameById(@RequestParam("id") Integer id); //@RequestParam
}

方法中的参数最好添加 @RequestParam 注解,否则可能拿不到参数。

调用服务

@RestController // 消费者
public class BusinessController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getnamebyid")
    public String getNameById(Integer id) {
        return userService.getNameById(id);
    }
}

【测试消费者】

开启两个生产者和一个消费者(测试负载均衡默认的轮询方式)

根据配置信息,使用 localhost:8080/getnamebyid?id=2 进行访问>>

第一次访问:

刷新后:(轮询的方式)

3. 服务列表各个参数的含义、作用以及应用场景

1. 服务名:服务的唯一标识符,用于区分不同的服务,对应我们配置文件写的 spring.application.name.

2.分组名称:服务的分组标识,用于将服务进行逻辑隔离的。我们可以根据不同的环境或用途为服务设置不同的分组,例如“测试组”、“开发组”、“生产组”等。这样的隔离机制有助于管理和维护服务。

3. 集群数目:在一个服务内,可以有多个集群,每个集群下有多个服务实例。

4. 实例数:当前服务注册到Nacos的总实例数量,包括健康和不健康的实例。

5. 健康实力数:可以正常提供服务,没有故障的实例。

6. 触发保护阈值:它是一个 0-1 之间的数,表示当健康实例数低于此阈值时,Nacos会阻止所有服务实例的自动注销,以保护剩余的健康实例。(可以在服务详情里面的编辑服务中进行设置)

为什么要有保护阈值 ?

它是为了防止服务雪崩的。比如说服务集群里边原本有 1000 个实例,但是现在有999 个实例都挂了,只剩下一个实例了,那么原本 1000 个人干的活,现在就只剩一个人了,如果我们再把所有的活再派给这一个人来干,那么这个人他肯定也不在了,他肯定离职了。对于咱们系统来说也是一样,如果集群实例数太少的话,这时候还把所有的流量分发过去,那就会造成服务瘫痪,进而造成上游调用这个服务的整体瘫痪,进而造成服务雪崩。所以需要保护阈值。

保护阈值它是如何防止服务雪崩的 ?

想要解决服务雪崩,无非就是加锁排队,但是 Nacos 它本身又不做限流,只有注册中心和配置中心,那它是怎么做到既没有限流功能,又要保证不会发生雪崩问题的呢?它的做法是 "躺平",当保护阈值为 true 的时候,它会将所有的请求分发给所有的服务实例(不管健康与否),即使有些服务实例已经挂掉了,以此来保护所剩无几的健康实例!

【保护阈值演示案例】

① 准备两个永久服务实例,一个消费者,然后停掉一个服务

如何快速创建相同实例 >>

PS:永久实例 - 对应配置文件中的ephemeral: false

② 将保护阈值设置为 0.5,这时保护阈值就变为 true 了

③ 使用消费者调用服务

例如:localhost:8080/getnamebyid?id=2

当我们更改了权重,阈值等参数,它默认是会有缓存的,感知不到,所以需要重启消费者才能看到 nacos 的防止雪崩的策略。

重启消费者后 >>

第一次访问:

第二次访问:

PS:第三次访问和第一次访问一样,第四次访问又报错,这就是 Nacos 的一个解决服务雪崩的手段!

【服务详情中的一些参数的含义】

1. 元数据:与服务相关的额外信息,可以是键值对形式的任意数据。(它会自动设置进去)

2. 服务路由类型:用于指定如何在消费者和成产者之间进行路由决策的,它可以实现请求的负载均衡。

PS:服务路由类型最主要的作用就是实现 CMDB,地域就近访问的,什么叫地域就近访问

当有北京的调用者来获取服务的时候,他肯定是调用北京的服务是最快的,因为对于北京的调用者来说,北京的网络,路由调的次数肯定比深圳少,所以它的速度肯定快,那么深圳的调用者肯定是调用深圳的服务最快,这就是地域就近访问。

3. 权重:实例级别的设置。范围为 0-10000,用于负载均衡决策,权重越大,分配给该实例的流量越大。当权重为 0 的时候,和点击下线功能是一样的效果。

Nacos 的负载均衡策略总的来说有两种方式:

① 基于健康检测和权重

② 基于第三方 CMDB 的标签负载均衡器

这在官方的 《Nacos架构与原理》一书中原话是这样说的:

在 Nacos 0.7.0 版本中,我们除了提供基于健康检查和权重的负载均衡方式外,
还新提供了基于第三方 CMDB 的标签负载均衡器

临时实例 VS 永久实例

① 定义和删除方式不同

如何删除永久实例:1.停服务;2. 删除 nacos/data 目录下的 protocol 文件夹;3. 启动 nacos 服务

② 健康检测机制不同

PS:临时实例每隔 5 秒就会主动上报一次自己的健康状态,发送的数据包叫做心跳包,发送心跳包的机制叫做心跳机制。如果Nacos 服务端在 15 秒都没收到心跳,就会将实例设置为不健康,在 30 秒没收到心跳时就会将这个临时实例摘除。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文