SpringCloud-Alibaba学习(二):Nacos注册中心

1、服务注册发现

1.1 简述

微服务架构下,服务分别部署到不同机器上,服务之间通过网络完成数据通信。我们知道,网络种两个机器要想通信,必须得知道对方的 ip端口,因此服务调用的第一步,就是获取对方的 ip 和端口。

如何做呢?简单做法可以直接将 ip 和端口写死在调用方服务上。但是当服务重新部署 ip 和端口可能会发生变动,那么依赖方也需要修改重启。当服务很多的时候,服务间的依赖关系也无法通过硬编码来维护。

所以就引出了服务注册中心,通过注册中心来管理服务的 ip 和端口

当服务提供方启动时,将自己的 ip 和端口发生给注册中心,注册中心将服务名与ip和端口保存起来,这个信息就叫做元数据信息,这个过程叫服务注册

服务调用方启动时,通过服务名去注册中心获取 ip和端口信息,这个过程叫服务发现

获取到服务提供方的地址信息后,就可以发起远程调用,如果有多个就需要进行负载均衡。

简单流程:

image

1.2 注册中心对比

注册中心是一个第三方的应用组件,有这么几种

  • nacos:阿里开源
  • eureka:netflix 公司的,闭源不维护了
  • consul:HashiCorp 公司的开源产品
  • zookeeper:apache 家的,一般用于 Dubbo 服务的注册发现

image

SpringCloud-Alibaba 使用 nacos 作为注册中心

2、Nacos 使用

2.1 简介

官网:
https://nacos.io/zh-cn/docs/quick-start.html

image

Nacos 两大核心功能:

  • Naming Service 名字服务
    通过指定的名字来获取资源或服务的地址、服务提供者的信息。用于实现注册中心。

  • Config Service 配置服务
    动态配置服务能够以中心化、外部化和动态化的方式管理所有环境的配置数据。动态配置能够实现配置变更时无需重新部署服务的需要。用于实现配置中心。

2.2 安装

下载地址:https://github.com/alibaba/nacos/releases
这里下载的是 1.4.1 版本

windows 版本安装

a)解压修改配置

打开目录下的 nacos\conf\application.properties 配置文件

image

b)创建数据库

创建名为nacos的数据,可以在配置文件种修改
导入 nacos\conf\nacos-mysql.sql sql 文件,创建表

c)配置 startup.cmdstandalone 方式启动

修改启动脚本 nacos\bin\startup.cmd,默认集群模式启动,这里先在 windows 环境下运行,改成单机模式启动

image

也可以不修改脚本,使用命令 startup.cmd -m standalone 启动

d)双击 startup.cmd 启动

控制台默认访问路径 http://localhost:8848/nacos/index.html
(窗口最上面有)
登录默认用户名密码是:nacos/nacos

image

2.3 注册中心基本工作流程

注册中心是部署的 Nacos Server 服务,各个微服务通过客户端 Nacos Client 与 Nacos Server 通信

  • 服务注册
    Nacos Client 会通过发送REST请求,向 Nacos Server注册自己的服务,提供自身的元数据,包含服务名称、ip 和端口等信息。Server 接收到注册请求后,会把这些元数据信息存储到内存中。
  • 服务心跳
    服务注册后,Client 会维护一个定时心跳保持与 Server 的连接,说明处于可用状态,防止被剔除,默认 5s 发送一次心跳。
  • 服务同步
    Nacos Server 集群直接会互相同步服务实例,用于保证服务信息的一致性。
  • 服务发现
    服务消费方在调用提供者的服务时,会发送 REST 请求到注册中心,根据服务名查询服务列表,查询到的服务实例列表会缓存到消费方本地,下次再发送请求时直接从本地获取服务地址。也具有一定的容错性,即使注册中心挂了,只要服务方没有变动,依然可以正常发送请求。同时 Nacos client 会开启一个定时任务,定时拉取注册中心最新的服务信息并更新本地缓存。
  • 服务健康检查
    Server 会开启一个定时任务来检查注册服务的健康状态,对于超过 15s 没收到客户端心跳的实例,会将它的 healthy 属性置为 false(服务发现过程时该服务不会被发现);如果超过 30s 没有收到心跳,直接剔除该服务,服务恢复心跳则会重新注册。

简单流程
image

3、入门案例

3.1 版本选择

SpringBoot 版本查看:https://spring.io/projects/spring-boot#learn
SpringCloud 版本查看:https://spring.io/projects/spring-cloud#learn
版本对应 (springcloud-netflix 参考):https://start.spring.io/actuator/info
SpringCloud-alibaba 版本参考:https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明

版本选择:

项目 版本
springcloud-alibaba 2.2.5.RELEASE
springcloud Hoxton.SR8
springboot 2.3.2.RELEASE

3.2 搭建工程

3.2.1 创建父工程

创建一个普通 maven 工程 cloud-nacos-demo作为父工程,打包为 pom
引入依赖

    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>

        <spring.boot.version>2.3.2.RELEASE</spring.boot.version>
        <spring.cloud.version>Hoxton.SR8</spring.cloud.version>
        <spring.cloud.alibaba.version>2.2.5.RELEASE</spring.cloud.alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springboot依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- springcloud-alibaba依赖 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--springcloud依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

3.2.2 公共模块

创建一个公共模块nacos-demo-common,用于服务提供方暴露数据传输对象 DTO,服务消费方引入依赖使用。
该模块不作为服务,不需要引入Spring相关依赖。

依赖:

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

数据传输对象

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GoodsDTO {

    private String goodsName;

    private Long price;
}

3.2.3 创建服务提供者

创建子工程nacos-demo-provider

引入依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 端点监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- nacos 注册中心场景依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
 
        <dependency>
            <groupId>cc.yuanspace</groupId>
            <artifactId>nacos-demo-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

基本应用配置:

# 服务端口
server.port=9001
server.servlet.context-path=/nacos-demo-provider
# 服务名称 必须有 保证唯一性
spring.application.name=nacos-demo-provider
# nacos server 的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

启动类

@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册与发现功能
public class App {

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

        System.out.println("**** 启动成功 ****");
    }
}

运行:
查看 nacos 控制台
image

服务 Controller 接口

@RestController
@RequestMapping("/goods")
public class GoodsController {

    @GetMapping("/{id}")
    public GoodsDTO findById(@PathVariable("id") Long id) {
        System.out.println("goods id = " + id);
        return GoodsDTO.builder().id(id).goodsName("小米").price(2998L * 100).build();
    }
}

3.2.4 创建服务消费者

创建子工程nacos-demo-consumer

依赖:

    <artifactId>nacos-demo-consumer</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 端点监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- nacos 注册中心场景依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>cc.yuanspace</groupId>
            <artifactId>nacos-demo-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

基本应用配置:
注意修改端口、应用名,保持唯一

# 服务端口
server.port=9002
server.servlet.context-path=/nacos-demo-consumer
# 服务名称 必须有 保证唯一性
spring.application.name=nacos-demo-consumer
# nacos server 的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

启动类:

@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册与发现功能
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);

        System.out.println("**** 启动成功 ****");
    }
}

运行:
俩服务都有了
image

3.2.5 远程调用

不同于Dubbo,SpringCloud 采用 HTTP 协议发送请求,所以可以使用任何能发起 HTTP 请求的工具包实现远程调用,如 HttpClient、RestTemplate

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
@RestController
@RequestMapping("/test")
public class TestController {

    private final RestTemplate restTemplate;

    public TestController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("")
    public Map<String, Object> test() {
        // 发起远程调用,(任何能发起http请求的都可以),使用RestTemplate,是spring对HttpClient的封装
        String url = "http://localhost:9001/nacos-demo-provider/goods/1";
        GoodsDTO goodsDTO = restTemplate.getForObject(url, GoodsDTO.class);
        System.out.println(goodsDTO);

        // 处理数据
        System.out.println("处理数据完成");
        return success(goodsDTO);
    }

    private Map<String, Object> success(Object data) {
        Map<String, Object> result = new HashMap<>();
        result.put("errcode", 0);
        result.put("errmsg", "success");
        result.put("data", data);
        return result;
    }
}

image

注意:
上面的请求 url 中服务的 ip 和端口还是硬编码,应用注册中心要使用服务名发起调用,但是服务名默认会被解析成 host 会出错,还需要使用 Ribbon 解析服务名做负载均衡。

nacos-discovery 已经引入了 Ribbon,无需再额外添加依赖,直接使用。
在注入 RestTemplate 的位置添加注解

    @Bean
    @LoadBalanced // 使Ribbon生效
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

远程调用将 ip和端口 换成 服务名

    @GetMapping("")
    public Map<String, Object> test() {
        // 发起远程调用,(任何能发起http请求的都可以),使用RestTemplate,是spring对HttpClient的封装
        String serviceName = "nacos-demo-provider";
        String url = String.format("http://%s/nacos-demo-provider/goods/1", serviceName);
        GoodsDTO goodsDTO = restTemplate.getForObject(url, GoodsDTO.class);
        System.out.println(goodsDTO);

        // 处理数据
        System.out.println("处理数据完成");
        return success(goodsDTO);
    }

3.2.6 服务集群调用

启动多个提供者服务,使用 idea 配置多个启动器

image

跑起来后,Nacos 控制台查看服务详情

image

image

发起调用后默认采用轮询的负载均衡策略调度

image

image

image

4、nacos 领域模型

nacos 的服务由三元组唯一确定(namespace、group、servicename)
nacos 的配置由三元组唯一确定(namespace、group、dataId)

不同的 namespace 是相互隔离的,相同的 namespace 但是不同的 group 也是相互隔离的

默认的 namespace 为 public,不能删除
默认的 group 是 DEFAULT_GROUP

4.1 创建 namespace

image

4.2 发布服务到指定 namespace

给提供者服务的配置文件中添加配置,注意配置上填的是 namespace 的 id。

# 服务发布到指定的namespace,不指定默认就是public
spring.cloud.nacos.discovery.namespace=qa
# 服务发布到指定的group,默认值是DEFAULT_GROUP
spring.cloud.nacos.discovery.group=my-group

给消费者服务添加配置,这里指定其他 namespace

# 服务发布到指定的namespace,不指定默认就是public
spring.cloud.nacos.discovery.namespace=pro
# 服务发布到指定的group,默认值是DEFAULT_GROUP
spring.cloud.nacos.discovery.group=my-group

服务发布到了不同的 namespace 中

image
image

调用服务报错,服务没有可用实例

java.lang.IllegalStateException: No instances available for nacos-demo-provider

官方示例

https://github.com/alibaba/spring-cloud-alibaba/tree/2.2.x/spring-cloud-alibaba-examples

posted @ 2022-06-29 15:44  liuyiyuan  阅读(253)  评论(0编辑  收藏  举报