SpringCloudAlibaba--Nacos

1,212 阅读9分钟

一、介绍

springCloud第一代与第二代:

名称第一代第二代
网关Spring Cloud ZuulSpring Cloud Gateway
服务注册Eureka、Consul、ZKSpring Cloud Alibaba Nacos
配置中心Spring Cloud ConfigSpring Cloud Alibaba Nacos
负载均衡RibbonSpring Cloud loadbalancer
熔断器HystrixSpring Cloud Alibaba Sentinel
  • 第一代使用Netflix系列开源组件
  • 第二代实际上是自己研发和国内优秀微服务框架整合
    • Spring Cloud Gateway 网关
    • Spring Cloud Loadbalancer 客户端负载均衡
    • Spring Cloud r4j 服务保护
    • Spring Cloud Alibaba Nacos 服务注册
    • Spring Cloud Alibaba Nacos 分布式配置中心
    • Spring Cloud Alibaba Sentinel 服务保护
    • Spring Cloud Alibaba Seata 分布式事务解决框架
    • Alibaba Cloud OSS 阿里云存储
    • Alibaba Cloud SchedulerX 分布式任务调度平台
    • Alibaba Cloud SMS 分布式短信系统

Alibaba解决方案:

  • 这么多小服务,如何管理他们? 【Nacos】
  • 这么多小服务,他们之间如何通讯?【restful、rpc、dubbo、feign】
  • 这么多小服务,客户端怎么访问?【Gateway】
  • 这么多小服务,一旦出现问题,应该如何自处理?【容错Sentinel】
  • 这么多小服务,一旦出现问题,应该如何排错?【链路追踪Skywalking】

image.png

二、Nacos服务注册与发现

Nacos是SpringCloudAlibaba中分布式服务注册中心、分布式配置中心;相当于之前的SpringCloudEruika+Config。

image.png

2.1 传统RPC远程调用的问题

  • RPC远程调用框架:HttpClient、gRpc、dubbo、rest、openFeign

问题:服务与服务之间URL地址怎么管理?

  • 例如A服务要调用B服务,传统手段在A服务端写死,一旦B服务除了问题,要改A服务的代码才能切换。

在微服务架构通讯,服务之间依赖关系非常大。如果通过传统手段管理服务URL,一旦地址发生变化,还需要手动修改。因此需要使用URL治理技术,可以实现对我们整个实现动态服务注册与发现,本地负载均衡,容错等。

2.2 注册中心

  • 注册中心就是管理服务的URL地址信息,能够实现动态感知。

  • 常用的注册中心:Dubbo依赖ZK、Eureka、Consul、Nacos

image.png

值得注意的是,Nacos支持CP和AP的切换。首先在分布式系统中,需要保证其P分区容错性;在此基础上,如果使用主从架构,就是保证其一致性,但是这种保证了CP的系统,就无法保障其可用性A,当一半以上的节点不可用时整个架构不可用。另一个方向就是不使用主从架构,而是不同服务在不同的nacos中进行注册,这种情况保证了其可用性AP,但是就无法保障一致性C。Nacos默认保证了AP,可以通过Nacos提供的restful接口进行CP的切换。
CAP:

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分区容错性(Partition tolerance)
  • 整个微服务注册中心的实现原理:
  1. 生产者:单个服务启动后,会将自己的IP、端口、服务名称等基本信息发送给注册中心。注册中心存放Map<String, List<String>>中。
  2. 消费者:其他服务会先去注册中心找服务名称,获取服务信息列表;采用负载均衡器选择一个地址进行RPC远程调用。

整体流程示意图

image.png

  • NacosClient会默认每5s一次发送心跳
  • NacosServer对于超过15s没有收到心跳的服务,会将其健康值置位false,超过30s没有心跳就会将其剔除

2.3 Nacos安装

链接:传送门

这里下载的是源码,因此需要本地环境搭建。

  • 进入到nacos源码目录,使用如下mvn命令,开始本地编译:
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U 
  • 开启单机模式,添加虚拟机参数:
-Dnacos.standalone=true
  • 运行后访问http://localhost:8848/nacos/#/login

image.png

  • 登录查看,账号密码都是Nacos image.png
  • 如果不使用源码,就在github中下载对应release版本,因为我这里使用的mac,我就下载对应.zip文件,不同Spring Cloud Alibaba版本,对应nacos版本不同,版本对照表
  • 下载好后,首先确保环境变量没有问题(Mac配置java环境变量),然后进入到nacos文件夹下的/bin目录,执行bin sh startup.sh -m standalone,然后直接访问http://localhost:8848/nacos/#/login即可访问

2.4 Nacos管理界面

  • 服务管理

image.png

  • 查看服务详细数据

对某个服务点击“详情”

image.png

  • 查看订阅者列表 image.png

三、客户端搭建

我们需要构造一个如下的环境:

image.png

3.1 Nacos服务注册与发现的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>0.2.2.RELEASE</version>
</dependency>

3.2 生产者服务

  1. POM文件添加3.1所述依赖
  2. 编写yaml配置
server:
  port: 8001
spring:
  application:
    name: order-service # 这里是服务的名字,多个服务时可以一致
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 填写服务注册中心的地址
  1. 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {

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

}
  1. controller
@RestController
public class OrderController {
    @Value("${server.port}")
    private String port;

    @GetMapping("/port")
    public String getPort() {
        return port;
    }
}

为了能够说明Nacos自带Ribbon负载均衡的效果,我们需要多个生产者,因此进行如下操作

  1. 多个生产者 最开始还像傻子一样复制之前的生产者然后改配置,这样很容易出现问题,而且改的地方很多。其实IDEA有很便捷的方式,我们先启动生产者服务,然后在上面右键点击Copy Configuration

image.png

然后在弹出界面中进行配置修改: image.png

启动在这里,点击:
image.png

3.3 服务消费者

  1. 添加POM依赖
  2. 修改yaml文件
server:
  port: 8003
  baseUrl: http://order-service # 这里是需要远程调用的服务名称,就是3.2.2中服务的名字
spring:
  application:
    name: consumer-8003 # 自己服务的名字
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 注册中心
  1. 启动类

@SpringBootApplication
@EnableDiscoveryClient
public class NacosComsumer8003Application {

    public static void main(String[] args) {
        SpringApplication.run(NacosComsumer8003Application.class, args);
    }
    
    /**
     * 这里要注意,因为消费者要通过远程调用访问生产者的对应端口,这里需要配置restTemplate
     * 因为Nacos远程调用是自带负载均衡,这里一定要写LoadBalance,否则会报错说找不到对应服务
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
  1. controller
@RestController
public class ComsumerController {
    /* 自动注入restTemplate */
    @Autowired
    private RestTemplate restTemplate;

    /* 这里就是在配置文件中配置的生产者服务跟地址,即 “http://order-service” */
    @Value("${server.baseUrl}")
    private String baseUrl;

    @GetMapping("/port")
    public String getServer() {
        return restTemplate.getForObject(baseUrl + "/port", String.class);
    }
}

3.4 启动服务

启动服务前先保证Nacos注册中心是启动的

  • 启动两个生产者

image.png

单独访问生产者可以正常返回其端口

  • 启动消费者

三个服务我们都启动后,查看Nacos: image.png

  • 我们访问消费者,测试负载均衡

image.png

Nacos自带了Ribbon负载均衡器,默认是轮训的方式,我们也可以从依赖看出来这一点

image.png

3.5 Nacos配置项

在上面小结中,我们在配置文件里对Nacos的地址进行了配置,它的常用配置项如下:

配置项Key默认值说明
服务端地址spring.cloud.nacos.discovery.server-addrNacosServer启动监听的ip地址和端口
服务名spring.cloud.nacos.discovery.service${spring.application.name}给当前服务命名
服务分组spring.cloud.nacos.discovery.groupDEFAULT_GROUP设置读物所处的分组
权重spring.cloud.nacos.discovery.weight1取值范围1100,数值越大权重越大,可以结合带权重的负载均衡策略
网卡名spring.cloud.nacos.discovery.nerwork-interface当IP未配置时,注册的IP为此网卡对应IP地址,如果此项也未配置,则默认取缔一块网卡的地址
注册的IP地址spring.cloud.nacos.discovery.ip优先级最高
注册的端口spring.cloud.nacos.discovery.port-1默认情况下不会配置,自动探测
命名空间spring.cloud.nacos.discovery.namespace常用场景之一是不同环境的注册区分隔离,dev、master
AccessKeyspring.cloud.nacos.discovery.access-key当要上阿里云时云账号名
SecretKeyspring.cloud.nacos.discovery.secret-key当要上阿里云时云密码
MetaDataspring.cloud.nacos.discovery.metadata使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息。例如版本号
日志文件spring.cloud.nacos.discovery.log-name
集群spring.cloud.nacos.discovery.cluster-nameDEFAULT配置Nacos集群名称
接入点spring.cloud.nacos.discovery.enpointUTF-8地域的某个服务入口域名,通过此域名可以动态拿到服务器地址
是否集成Ribbonribbon.nacos.enabledtrue一般设置为true即可
是否开启NacosWatchspring.cloud.nacos.discovery.watch.enabledtrue
是否作为临时实例spring.cloud.nacos.discovery.ephemeraltruetrue表示是临时实例,存在内存中,如果没有心跳超过一定时间就会被删除;对应的是持久化实例,实例会被写入数据库,即使没有心跳也是会存在的,标记为不健康

在雪崩保护时,如果健康实例的比例小于保护阈值时,不健康实例也是可以被访问。例如有两个生产者,每个服务上限是10w,因此可以承载20w访问量,当服务雪崩时,如果20w访问一起冲到一台服务器去,就会导致整个服务瘫痪。因此此时让不健康实例也可以访问,虽然这样做会有10w的用户访问不成功,但是另外的10w可以正常访问,不至于整个服务访问不了。

四、Nacos源码分析

思考:Nacos服务端有成百上千的服务同时写(注册),还有成百上千的服务同时读(获取注册表),那么Nacos服务器是如何解决读写高可用的问题?

  • CopyOnWrite写时复制:写注册表时,将注册表复制一份进行写副本。因此不用加锁。

4.1 启动Nacos

image.png

通过注解@SpringBootApplication可以看出这是一个SpringBoot项目,实际上Nacos服务注册中心就是一个web服务器,对外提供了很多的HTTPAPI,应用服务通过API对外接口来实现将自己注册和发送心跳等功能。

4.2 客户端如何进行注册

刚才提到,Nacos本身就是web应用,提供了很多API,然后客户端通过远程服务调用来进行各个功能实现。我们可以通过API文档找到服务注册的API: 官方链接

image.png

我们先从客户端代码看起,我们可以通过全局搜索/instance,就能够找到在UtilAndComs.java文件中定义了服务注册的对应路径,然后我们再次全局产找看哪里使用了NACOS_URL_INSTANCE

public static String NACOS_URL_INSTANCE = NACOS_URL_BASE + "/instance";

最终可以在NamingProcy.java文件中的registerService方法中看到使用了这个路径,我们在此处打上断点,然后启动客户端,看是否能够断住:

image.png

还有一种方法,可以找到这个断点,就是在对应包下的spring.factories中找到自动装配的类

image.png

查看NacosDiscoveryAutoConfiguration类中,可以看到它向容器中添加了一个Bean,其中最重要的就是nacosAutoServiceRegistration

image.png

点进NacosAutoServiceRegistration类中,该类继承自AbstractAutoServiceRegistration:

image.png

进入AbstractAutoServiceRegistration,里面有一个@EventListener,会调用this.start方法:

image.png

this.start方法继续调用register()方法

image.png

register()方法最终会调用到NacosServiceRegistry的同名方法: image.png

而这里最终会调用咱们断点的位置。

以上只是提供一种找到这个断点的方式。不用过度深究

我们打好断点之后,启动客户端,可以看到代码走到了这里,说明我们找的没有错,这里可以看到服务注册的相关信息:

image.png

接着跟进reqAPI方法,可以看到底层是使用了HttpClient来进行远程服务调用:

image.png

4.3 服务端如何处理注册请求

我们在Nacos源码中可以看到有一个naming的模块,里面有各种controller。可以看到如图的Controller正是对应我们的注册请求: image.png

...未完