学习一下 SpringCloud (六)-- 注册中心与配置中心 Nacos、网关 Gateway

2022-12-04,,,,

(1) 相关博文地址:

学习一下 SpringCloud (一)-- 从单体架构到微服务架构、代码拆分(maven 聚合): https://www.cnblogs.com/l-y-h/p/14105682.html
学习一下 SpringCloud (二)-- 服务注册中心 Eureka、Zookeeper、Consul、Nacos :https://www.cnblogs.com/l-y-h/p/14193443.html
学习一下 SpringCloud (三)-- 服务调用、负载均衡 Ribbon、OpenFeign : https://www.cnblogs.com/l-y-h/p/14238203.html
学习一下 SpringCloud (四)-- 服务降级、熔断 Hystrix、Sentinel : https://www.cnblogs.com/l-y-h/p/14364167.html
学习一下 SpringCloud (五)-- 配置中心 Config、消息总线 Bus、链路追踪 Sleuth、配置中心 Nacos : https://www.cnblogs.com/l-y-h/p/14447473.html

(2)代码地址:

https://github.com/lyh-man/SpringCloudDemo

一、了解一下 SpringCloud Alibaba

1、SpringCloudAlibaba

(1)简单说明

【说明:】
通过前面几篇博客的介绍,已对 SpringCloud 部分框架有了一些认识,比如: Eureka、Ribbon、Hystrix、Config、Bus 等。
但这些框架都有类似的问题:进入了维护模式 或者 功能不完善(使用场景有限,无法很好地适应新需求)
注:
模块置于维护模式意味着 Spring Cloud 团队不再向模块添加新功能。仅修复拦截器漏洞和安全问题。 SpringCloud 版本迭代速度挺快的,这就导致了一个问题: 一些旧版本出现的问题还未解决就推出了一个新的版本。
而某模块进入了维护模式,即不再开发新功能,其使用价值有限,使用人数也就逐渐减少,相当于 打入冷宫,难有翻身余地。 一个旧技术的没落,必定有一个新技术取而代之。
而 SpringCloudAlibaba 就是这个新技术之一,有必要重点学习一下。

(2)SpringCloudAlibaba

【SpringCloudAlibaba:】
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。 【相关地址:】
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html
https://github.com/alibaba/spring-cloud-alibaba/wiki 【版本管理:】
当前 Spring Cloud Alibaba 最新版本为 2.2.5.RELEASE。
其推荐对应 Spring Cloud 版本为 Spring Cloud Hoxton.SR8,
其推荐对应 Spring Boot 版本为 2.3.2.RELEASE。
可参考:
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

(3)引入依赖
  使用 SpringCloudAlibaba 与使用 SpringCloud 类似,一般都在父工程的 pom.xml 文件中通过 <dependencyManagement> 标签进行版本管理。然后在使用其组件时,直接引入相关组件依赖,无需管理版本。
  当然直接引入相关组件依赖 并 指定版本号的方式亦可。

【在父工程中管理版本:】
<properties>
<spring.cloud.alibaba.version>2.2.5.RELEASE</spring.cloud.alibaba.version>
</properties> <dependencyManagement>
<dependencies>
<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>
</dependencies>
</dependencyManagement>

2、SpringCloudAlibaba 主要功能 与 实现组件

(1)SpringCloudAlibaba 主要功能 与 实现组件

【功能与实现组件:】
服务限流降级:
基本说明:
默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,
可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
实现组件:
Sentinel: 把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 服务注册与发现:
基本说明:
适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
实现组件:
Nacos: 一个更易于构建云原生应用的动态服务发现和服务管理平台。 分布式配置管理:
基本说明:
支持分布式系统中的外部化配置,配置更改时自动刷新。
实现组件:
Nacos: 一个更易于构建云原生应用的配置管理平台。 消息驱动能力:
基本说明:
基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
实现组件:
RocketMQ: 一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。 分布式事务:
基本说明:
使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
实现组件:
Seata: 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。 阿里云对象存储:
基本说明:
阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
实现组件:
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。 分布式任务调度:
基本说明:
提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
实现组件:
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。 阿里云短信服务:
基本说明:
覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
实现组件:
Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

(2)开发中常使用的技术搭配

【开发中使用的技术:】
在平时开发中使用的技术搭配,我一般采用一下方案:
SpringCloud:
OpenFeign: 声明式 HTTP 客户端(即 远程服务调用)。
Ribbon: 负载均衡(OpenFeign 中已集成,无需单独引入)。
Gateway: API 网关
Sleuth: 分布式链路追踪(即 请求调用链监控)。 SpringCloudAlibaba:
Nacos: 注册中心 以及 配置中心(即 服务注册、发现 以及 动态配置管理)。
Sentinel: 服务容错(即限流、降级、熔断)。
Seata: 解决分布式事务。 注:
通过前面几篇博客的学习,知道了 OpenFeign、Ribbon、Sleuth、Sentinel 相关操作。
OpenFeign、Ribbon 相关操作详见:https://www.cnblogs.com/l-y-h/p/14238203.html
Sleuth 相关操作详见:https://www.cnblogs.com/l-y-h/p/14447473.html#_label4
Sentinel 相关操作详见:https://www.cnblogs.com/l-y-h/p/14364167.html#_label2 下面将逐个学习 Nacos、Gateway、Seata。

二、服务注册中心、配置中心 -- Nacos

1、什么是 Nacos?

(1)Nacos

【Nacos:】
Nacos 即 Dynamic Naming and Configuration Service(动态命名与配置服务)。由 Naming 前两个字母,Configuration 前两个字母,以及 Service 首字母组成。
Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理 以及 服务管理平台。
简单的理解:
Nacos 就是 注册中心 + 配置中心。
即 Nacos = Eureka + Config + Bus。 【相关地址:】
https://nacos.io/zh-cn/index.html
https://github.com/alibaba/nacos

(2)如何使用 Nacos

【如何使用 Nacos:】
Nacos 分为 Server、Client,其中 Server 作为注册中心以及配置中心,可以独立部署。
而想要使用 Nacos,仅需在 微服务当中引入 client 相关依赖即可。
其中:
Server 最新版本为 1.4.1,根据实际情况可以自行选择版本。
使用注册中心功能,需要引入 spring-cloud-starter-alibaba-nacos-discovery 依赖。
使用配置中心功能,需要引入 spring-cloud-starter-alibaba-nacos-config 依赖。
注:
下载地址:https://github.com/alibaba/nacos/releases/download/1.4.1/nacos-server-1.4.1.tar.gz

2、安装 Nacos Server 单机版(Linux 直接下载并启动、持久化数据到 MySQL 8)

(1)说明:
  Nacos 默认使用内嵌数据库(Derby)实现数据的存储,一般使用 单机版 Nacos Server 无需额外配置持久化操作,直接启动即可。
  但大型的项目中,Nacos Server 一般采用集群方式部署,若仍使用默认数据库进行数据存储,那么各个 Nacos Server 之间的数据一致性就是一个头疼的问题。 Nacos 支持持久化数据到 MySQL 中,集群中所有节点共享 MySQL 数据源,从而保证数据一致性。

(2)下载、并解压 nacos-server

【下载:】
wget https://github.com/alibaba/nacos/releases/download/1.4.1/nacos-server-1.4.1.tar.gz 【解压:】
tar -axvf nacos-server-1.4.1.tar.gz

(3)直接启动
  未进行持久化配置,使用默认的 Derby 进行数据存储。

【进入解压后的 bin 目录:】
cd ./nacos/bin 【以单机模式启动:】
sh startup.sh -m standalone
注:
通过执行 sh shutdown.sh 可以关闭 Nacos。 【登录 Nacos:】
默认通过 8848 端口可以访问 Nacos。
账号、密码默认均为 nacos
比如:
http://120.26.184.41:8848/nacos

(4)持久化数据到 MySQL 中
Step1:
  连接数据库,并构建数据表。

【构建数据表:】
进入解压后的 nacos 的 conf 目录,可以看到有 nacos-mysql.sql 文件。
连接上 MySQL,新建一个数据库(nacos_config),并执行 nacos-mysql.sql 文件中的 SQL 语句。
注:
可以直接使用官方提供的 nacos-mysql.sql 文件。
https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql 注:
MySQL 请自行安装,此处不赘述。
可参考如下博客(仅供参考,长时间未更新,可能会出现一些问题):
Windows 安装 MySQL:https://www.cnblogs.com/l-y-h/p/11700113.html
Linux 安装 MySQL:https://www.cnblogs.com/l-y-h/p/12576633.html
Docker 安装 MySQL:https://www.cnblogs.com/l-y-h/p/12622730.html#_label5
Docker Compose 安装 MySQL:https://www.cnblogs.com/l-y-h/p/12622730.html#_label8_2

Step2:
  配置 nacos 与 mysql 连接信息。

【配置数据源信息:】
同样在 conf 目录下,有一个 application.properties 文件。
编辑该文件,在文件末尾添加如下数据源信息(根据 MySQL 信息自行修改): spring.datasource.platform=mysql db.num=1
db.url.0=jdbc:mysql://120.26.184.41:3306/nacos_config?useUnicode=true&characterEncoding=utf8
db.user=root
db.password=123456

Step3:
  重新启动 nacos,再次登录nacos (http://120.26.184.41:8848/nacos) ,并新增一个配置文件。
  此时在 MySQL 中可以看到相关信息。

3、安装 Nacos Server 单机版(docker-compose 启动,持久化数据到 MySQL 8)

(1)说明
  若需持久化到 mysql,同样也得在 mysql 中生成相应的表,相关 SQL 文件可以参考上面 持久化操作(此处省略)。
  可以直接使用官方提供的 MySQL 镜像,nacos/nacos-mysql:8.0.16,其有一个数据库 nacos_devtest 已包含了相关的表结构。

【nacos-docker 相关链接:】
https://nacos.io/zh-cn/docs/quick-start-docker.html
https://github.com/nacos-group/nacos-docker 【docker-compose 使用参考链接:】
https://www.cnblogs.com/l-y-h/p/12622730.html#_label8_2 【nacos-mysql SQL:】
https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql

(2)使用已有的 mysql 文件。
  简化操作,直接使用上面的 mysql。
  若需启动新的 mysql,需要手动执行 SQL,用于创建 nacos 所需相关表结构。
注:
  https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql

【docker-compose.yml】
version: '3.7'
services:
nacos:
image: nacos/nacos-server:1.4.1
container_name: nacos
restart: always
environment:
- MODE=standalone
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=120.26.184.41
- MYSQL_SERVICE_PORT=3306
- MYSQL_SERVICE_DB_NAME=nacos_config
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=123456
- JVM_XMS=50m
- JVM_MMS=50m
volumes:
- ./standalone-logs/:/home/nacos/logs
ports:
- 8848:8848

相关参数设置如下(对于高版本 Nacos 参数有些许不同):
  NACOS_SERVER_PORT 需要改为 NACOS_APPLICATION_PORT
  多个带有 MASTER 的参数需要去掉 MASTER,比如:MYSQL_MASTER_SERVICE_HOST 需要改为 MYSQL_SERVICE_HOST。

通过 docker-compose up -d 启动后,访问 http://120.26.184.41:8848/nacos。
成功登录后,可以看到之前配置的文件,即启动 nacos 成功。

(3)使用 nacos 官方提供的 mysql 镜像进行持久化操作。
注意:
  此处使用的 nacos 镜像为:nacos/nacos-server:1.4.1。
  使用的 mysql 镜像为:nacos/nacos-mysql:8.0.16。

坑:
  执行 docker-compose up -d 后,访问 nacos。若访问 nacos 失败时,可以查看日志文件 start.out,若出现 Caused by: java.lang.IllegalStateException: No DataSource set 的问题,可以重新启动一下容器(docker-compose restart)。若仍然出错,则将 nacos-mysql:8.0.16 更换为 nacos-mysql:5.7 。

【docker-compose.yml】
version: '3.7'
services:
mysql:
container_name: nacos-mysql
image: nacos/nacos-mysql:8.0.16
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=nacos_devtest
- MYSQL_USER=nacos
- MYSQL_PASSWORD=nacos
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/log:/var/log/mysql
ports:
- "3307:3306"
nacos:
image: nacos/nacos-server:1.4.1
container_name: nacos
restart: always
environment:
- MODE=standalone
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=120.26.184.41
- MYSQL_SERVICE_PORT=3307
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=50m
- JVM_MMS=50m
volumes:
- ./standalone-logs/:/home/nacos/logs
depends_on:
- mysql
ports:
- 8848:8848

4、Nacos 作为 服务注册中心(服务注册与发现)

(1)说明:

【说明:】
通过前面的操作,已经成功启动了 Nacos Server,接下来整合 Nacos Client 即可。
此处的 Nacos Server 为注册中心。
新建三个模块 nacos_client_7100、nacos_client_7101、nacos_client_7102 用于演示服务提供者。
新建一个模块 nacos_client_consumer 用于演示服务消费者(使用 OpenFeign 调用服务)。
Nacos 已集成 Ribbon 并提供负载均衡。
注:
此处模块创建仅 描述 主要流程,不详细截图(可参考之前的博客)。 【参考链接:】
https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-discovery
https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md

(2)新建模块 nacos_client_7100
Step1:
  修改 pom.xml,引入 nacos-discovery 依赖。

【引入 Nacos 依赖:】
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

Step2:
  修改 application.yml,配置 nacos。

【application.yml】
server:
port: 7100 spring:
application:
name: nacos-client
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848

Step3:
  编写一个测试 controller,简单测试一下是否正常。

【TestController】
package com.lyh.springcloud.nacos_client_7100.controller; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import java.util.HashMap;
import java.util.List;
import java.util.Map; @RequestMapping("/nacos")
@RestController
public class TestController {
@Value("${server.port}")
private String port; @Value("${spring.application.name}")
private String name; @Autowired
private DiscoveryClient discoveryClient; @RequestMapping("/info")
public String getInfo() {
return name + "-" + port;
} @GetMapping("/discovery")
public Map<String, List<ServiceInstance>> discovery() {
// 获取服务名列表
List<String> servicesList = discoveryClient.getServices(); // 根据服务名 获取 每个服务名下的 各个服务的信息
Map<String, List<ServiceInstance>> map = new HashMap<>();
servicesList.stream().forEach(service -> {
map.put(service, discoveryClient.getInstances(service));
}); return map;
}
}

Step4:
  启动当前服务。
  在启动类上添加 @EnableDiscoveryClient 注解(不添加貌似也没啥影响)。

Step5:
  访问 http://120.26.184.41:8848/nacos/
  登陆后,选择 “服务管理” ==》“服务列表”,即可看到当前有哪些服务注册进来了。

访问 http://localhost:7100/nacos/discovery

访问 http://localhost:7100/nacos/info

(3)新建模块 nacos_client_7101、nacos_client_7102
  这两个模块与 nacos_client_7100 类似,仅端口号不同(创建过程此处省略)。
  启动服务后,再次查看 nacos 控制台,可以看到出现 3 个实例。

(4)新建模块 nacos_client_consumer
  nacos-discovery 已集成 Ribbon,默认实现负载均衡方式为 轮询。

Step1:
  引入 nacos-discovery 、openfeign 依赖。

【依赖:】
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

Step2:
  修改 application.yml 文件。

【application.yml】
server:
port: 7200 spring:
application:
name: nacos-client-consumer
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848

Step3:
  编写 openfeign 接口,调用 nacos_client 服务。

【ConsumerFeign】
package com.lyh.springcloud.nacos_client_consumer.service; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping; @Component
@FeignClient(name = "nacos-client")
public interface ConsumerFeign { @RequestMapping("/nacos/info")
String getInfo();
}

Step4:
  编写 controller,用于测试。

【TestController】
package com.lyh.springcloud.nacos_client_consumer.controller; import com.lyh.springcloud.nacos_client_consumer.service.ConsumerFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RequestMapping("/consumer")
@RestController
public class TestController { @Autowired
private ConsumerFeign consumerFeign; @RequestMapping("/info")
public String getInfo() {
return consumerFeign.getInfo();
}
}

Step5:
  简单测试一下 负载均衡。

访问:http://localhost:7200/consumer/info  ,默认轮询调用 nacos-client 服务。

5、Nacos 作为配置中心(自动刷新)

(1)说明

【说明:】
前面使用 Nacos Server 作为注册中心,此处作为 配置中心。
修改 nacos_client_7100 模块作为演示。
注:
Nacos 作为配置中心时,用法与 Config 很类似。
Config 使用可参考:https://www.cnblogs.com/l-y-h/p/14447473.html#_label1 【参考链接:】
https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example

(2)修改 nacos_client_7100
Step1:
  添加 nacos-config 依赖。

【依赖:】
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

Step2:
  添加 bootstrap.yml 文件,并进行配置中心相关设置。
  修改 application.yml 文件,修改其环境为 dev。
注:
  配置文件默认拼接为:${spring.cloud.nacos.prefix} - ${spring.profiles.active} . ${spring.cloud.nacos.file-extension}
  spring.cloud.nacos.prefix 默认为 spring.application.name。
  spring.profiles.active 为当前环境(比如:dev、test、prod 等)。
  spring.cloud.nacos.file-extension 为配置文件后缀名,一般为:yaml、properties。

下面配置文件,将会读取配置中心中 nacos-client-config-dev.yml 文件。

【bootstrap.yml】
server:
port: 7100 spring:
application:
name: nacos-client-config
cloud:
nacos:
discovery:
# 配置 nacos server 地址(作为注册中心)
server-addr: 120.26.184.41:8848
config:
# 配置 nacos server 地址(作为配置中心)
server-addr: 120.26.184.41:8848
# 设置配置文件前缀,默认为 ${spring.application.name}
prefix: ${spring.application.name}
# 设置配置文件后缀名
file-extension: yml 【application.yml】
spring:
profiles:
active: dev config:
info: ${spring.application.name}-${server.port}-${spring.profiles.active}

Step3:
  编写测试 controller,用于测试。
  需要添加 @RefreshScope 注解,用于自动刷新。

【TestConfigController】
package com.lyh.springcloud.nacos_client_7100.controller; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RequestMapping("/config")
@RestController
@RefreshScope
public class TestConfigController { @Value("${config.info}")
private String configInfo; @GetMapping("/info")
public String getInfo() {
return configInfo;
}
}

Step4:
  访问:http://localhost:7100/config/info
  初始时,若 Nacos Server 中不存在配置文件,则会先加载本地 配置文件中的 config.info。

在配置中心添加配置文件如下:

再次访问,则获取到的是配置中心最新的配置信息。

6、Namespace、Group、DataID

(1)问题与解决:

【问题:】
实际开发中,一个服务可能会对应多个配置环境(dev、test、prod 等)。
那么如何保证服务启动时 能 读取到 正确的 配置文件? 一个大型的微服务系统中,存在很多服务,每个服务都有不同的配置环境,
那么能否统一的、有规划的对配置进行管理呢? 【解决:】
Nacos 中 使用 命名空间(Namespace)、Group(分组)、DataID(配置文件 ID)对配置文件进行管理。

(2)Namespace、Group、DataID

【Namespace:】
Namespace 默认为 public。一般用于实现环境隔离,不同的命名空间是相互隔离的。
比如:
现在有三个环境:开发、测试、生产,则可以创建三个命名空间,每个命名空间对应一个环境。
需要切换环境时,只需切换 命名空间 即可。 【Group:】
Group 默认为 DEFAULT_GROUP。通过一个有意义的字符串对 配置集 或者 服务 进行分组。 【DataID:】
DataID 就是每个配置文件的唯一标识。 【简单理解:】
Namespace、Group、DataID 的关系可以理解成一个 文件系统。
Namespace 为最顶层的目录,其可以包含多个 Group。
Group 为 二级目录,其可以包含多个 DataID。
DataID 为 文件名。
Namespace + Group + DataID 组成配置文件的唯一标识。
注:
Namespace 可以通过 spring.cloud.nacos.config.namespace 指定(在 bootstrap.yml 中指定,且为 命名空间的 id 值)。
Group 可以通过 spring.cloud.nacos.config.group 指定(在 bootstrap.yml 中指定)。

(3)演示
Step1:
  修改 nacos_client_7100 配置文件如下,添加一个分组 TEST_GROUP。

Step2:
  访问配置中心,查看当前配置文件列表。
  如下图所示,仅有一个分组为 DEFAULT_GROUP 的配置文件。

Step3:
  访问:http://localhost:7100/config/info
  由于配置中心中不存在 TEST_GROUP 分组的配置文件,所以加载本地配置文件信息。

Step4:
  在配置中心添加一个 nacos-client-config-dev.yml 且分组为 TEST_GROUP 的文件。

Step5:
  再次访问 http://localhost:7100/config/info
  可以读取到 分组为 TEST_GROUP 的 nacos-client-config-dev.yml 文件中的信息。

7、nacos 获取配置的方式(配置共享)

(1)问题与解决

【问题:】
通过前面的使用,已经知道最基本的 配置文件获取方式为 ${spring.cloud.nacos.prefix} - ${spring.profiles.active} . ${spring.cloud.nacos.file-extension}。
若仅仅这么使用,只能获取到配置中心的一个配置文件。
当服务部署了集群后,每个服务对应一个配置文件,此时这些配置文件可能会出现很多重复的内容。
服务出现变动时,可能需要修改每个配置文件,增加了无用的工作量。 那么能否拆分配置文件,使部分配置文件可以共享?
能否使服务同时获取多个配置文件? 【解决:】
nacos 提供了三种获取配置文件的方式。
方式:
A:通过内部相关规则自动生成相关的 Data Id 配置(${spring.cloud.nacos.prefix} - ${spring.profiles.active} . ${spring.cloud.nacos.file-extension})。
B:通过 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多个扩展 Data Id 的配置。
C:通过 spring.cloud.nacos.config.shared-configs[n].data-id 支持多个共享 Data Id 的配置。
优先级:
A > B > C
注:
extension-configs、shared-configs 支持获取多个配置文件(n 越大优先级越高)。

(2)演示 extension-configs 使用
Step1:
  extension-configs 说明:

【extension-configs 说明:】
extension-configs 类型为 List<NacosConfigProperties.Config>
Config:
dataId String 类型,即配置文件名(比如:config-dev.yml)
group String 类型,即分组名,默认为 DEFAULT_GROUP
refresh boolean 类型,是否具备自动刷新,默认为 false(修改配置文件不自动刷新)

Step2:
  修改 bootstrap.yml 、application.yml如下:

【bootstrap.yml】
server:
port: 7100 spring:
application:
name: nacos-client-config
cloud:
nacos:
discovery:
# 配置 nacos server 地址(作为注册中心)
server-addr: 120.26.184.41:8848
config:
# 配置 nacos server 地址(作为配置中心)
server-addr: 120.26.184.41:8848
# 设置配置文件前缀,默认为 ${spring.application.name}
prefix: ${spring.application.name}
# 设置配置文件后缀名
file-extension: yml
extension-configs[0]:
data-id: test-0-dev.yml
extension-configs[1]:
data-id: test-1-dev.yml
extension-configs[2]:
data-id: test-2-dev.yml
group: DEFAULT_GROUP
refresh: true 【application.yml】
spring:
profiles:
active: dev config:
info: ${spring.application.name}-${server.port}-${spring.profiles.active}
test-0: test-0
test-1: test-1
test-2: test-2

Step3:
  修改 controller,获取配置信息。

package com.lyh.springcloud.nacos_client_7100.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RequestMapping("/config")
@RestController
@RefreshScope
public class TestConfigController { @Value("${config.info}")
private String configInfo; @Value("${config.test-0}")
private String test0; @Value("${config.test-1}")
private String test1; @Value("${config.test-2}")
private String test2; @GetMapping("/test")
public String test() {
return test0 + " ---- " + test1 + " ---- " + test2;
} @GetMapping("/info")
public String getInfo() {
return configInfo;
}
}

Step4:
  访问配置中心,查看当前配置列表。

Step5:
  访问:http://localhost:7100/config/test
  由于配置列表为空,服务仅加载本地配置文件。

Step6:
  添加配置文件。

Step7:
  再次访问:http://localhost:7100/config/test
  由于 test-0-dev.yml 以及 test-1-dev.yml 没有配置自动刷新,所以值没有正常变化。
  而 test-2-dev.yml 配置了自动刷新,其值正常变化。

(3)演示 shared-configs 使用
  shared-configs 用法 与 extension-configs 相同,区别是 extension-configs 优先级高于 shared-configs。(此处省略验证步骤,参考上面 extension-configs 的使用)

【bootstrap.yml】
server:
port: 7100 spring:
application:
name: nacos-client-config
cloud:
nacos:
discovery:
# 配置 nacos server 地址(作为注册中心)
server-addr: 120.26.184.41:8848
config:
# 配置 nacos server 地址(作为配置中心)
server-addr: 120.26.184.41:8848
# 设置配置文件前缀,默认为 ${spring.application.name}
prefix: ${spring.application.name}
# 设置配置文件后缀名
file-extension: yml
shared-configs[0]:
data-id: test-0-dev.yml
shared-configs[1]:
data-id: test-1-dev.yml
shared-configs[2]:
data-id: test-2-dev.yml
group: DEFAULT_GROUP
refresh: true

8、docker-compose 部署 Nacos Server(伪集群版)

(1)说明:
  集群至少得有 3 个 Nacos Server 节点。节点多的话 可以搞个 Nginx 做个代理。
  有条件的可以搞三台 云服务器 玩玩。没条件的可以搞三台 虚拟机 玩玩。
  想偷懒的可以直接在一台服务器上启动 三个 Nacos Server 节点(大坑,不建议做)。
注:
  此处为了偷懒,使用 docker-compose 一次性启动 mysql 以及3 个 Nacos 节点,相关的坑我也做了一些说明,鬼知道我为了解决这些坑经历了什么 (=_=)。

(2)Nacos 常用几个日志文件(此处以 伪集群版日志 进行举例)

【判断 nacos 是否正常启,一般看下面几个日志文件:】
日志文件:
/home/nacos/logs/start.out nacos 是否正常启动
/home/nacos/logs/nacos.log nacos 执行过程中是否报错
/home/nacos/logs/naming-raft.log nacos 选举是否异常 【对于 start.out 文件:】
若 Nacos 一直未启动完成,一般会输出:
Nacos is starting...
比如:
2021-03-25 15:11:05,419 INFO Nacos is starting... 若 Nacos 启动完成,一般会输出:
Nacos started successfully in cluster mode. use external storage
比如:
2021-03-25 15:11:06,811 INFO Nacos started successfully in cluster mode. use external storage 【对于 naming-raft.log 文件:】
若 Nacos 一直未选举成功,其输出日志如下:
ERROR NACOS-RAFT vote failed: 500, url: http://120.26.184.41:8850/nacos/v1/ns/raft/vote
ERROR NACOS-RAFT vote failed: 500, url: http://120.26.184.41:8851/nacos/v1/ns/raft/vote
WARN [IS LEADER] no leader is available now! 若选举成功,输出日志为:
2021-03-26 09:46:49,896 INFO received approve from peer: {"ip":"120.26.184.41:8850","voteFor":"120.26.184.41:8849","term":5,"leaderDueMs":19506,"heartbeatDueMs":3500,"state":"FOLLOWER"}
2021-03-26 09:46:49,897 INFO 120.26.184.41:8849 has become the LEADER
2021-03-26 09:46:49,898 INFO received approve from peer: {"ip":"120.26.184.41:8851","voteFor":"120.26.184.41:8849","term":5,"leaderDueMs":17941,"heartbeatDueMs":3500,"state":"FOLLOWER"} 【对于 nacos.log 文件:】
当 Nacos 选举异常时,可能会输出:
java.lang.IllegalStateException: can not find peer: 172.22.0.5:8848 若没有错误日志输出,输出如下日志,可以认为启动正常:
2021-03-26 09:34:08,199 INFO Started Nacos in 94.251 seconds (JVM running for 100.9)
2021-03-26 09:34:08,199 INFO Nacos Log files: /home/nacos/logs
2021-03-26 09:34:08,208 INFO Nacos Log files: /home/nacos/conf
2021-03-26 09:34:08,208 INFO Nacos Log files: /home/nacos/data
2021-03-26 09:34:08,209 INFO Nacos started successfully in cluster mode. use external storage
2021-03-26 09:34:08,873 INFO Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-03-26 09:34:08,873 INFO Initializing Servlet 'dispatcherServlet'
2021-03-26 09:34:08,954 INFO Completed initialization in 81 ms

(3)伪集群版(大坑)
Step1:编写 docker-compose.yml 文件
  与单机版 docker-compose.yml 类似,
  多了参数 NACOS_SERVERS、NACOS_APPLICATION_PORT。
  修改了 JVM_XMS 、JVM_MMS 等参数。
  修改了 nacos-mysql 版本。
  修改了 nacos-server 版本。
注意:
  添加了 NACOS_APPLICATION_PORT 后,需要修改相应的端口映射 ports。
  添加了 NACOS_SERVERS 后,设置多节点可以使用 逗号 或者 空格 隔开。
  JVM_XMS 等参数设置过小时,Nacos 可能跑不起来(适量调大一点)。
  若你尝试过一切手段,配置均正常时,Nacos 仍然跑不起来 或者 选举失败,而在网上找不到任何解决办法时,这边建议你 更换 镜像版本试一下 (=_=),鬼知道我试了多少次才解决的。

这边使用的都是官方镜像,可以自己手动生成镜像,具体有些什么坑,请自行尝试!!!

【docker-compose.yml】
version: '3.7'
services:
mysql:
container_name: nacos-cluster-mysql
image: nacos/nacos-mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=nacos_devtest
- MYSQL_USER=nacos
- MYSQL_PASSWORD=nacos
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/log:/var/log/mysql
ports:
- "3308:3306"
nacos1:
image: nacos/nacos-server:1.3.0
container_name: nacos1
restart: always
environment:
- NACOS_SERVERS=120.26.184.41:8849 120.26.184.41:8850 120.26.184.41:8851
- NACOS_SERVER_IP=120.26.184.41
- NACOS_APPLICATION_PORT=8849
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=120.26.184.41
- MYSQL_SERVICE_PORT=3308
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=100m
- JVM_MMS=100m
volumes:
- ./cluster-logs/nacos1:/home/nacos/logs
depends_on:
- mysql
ports:
- 8849:8849
nacos2:
image: nacos/nacos-server:1.3.0
container_name: nacos2
restart: always
environment:
- NACOS_SERVERS=120.26.184.41:8849 120.26.184.41:8850 120.26.184.41:8851
- NACOS_SERVER_IP=120.26.184.41
- NACOS_APPLICATION_PORT=8850
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=120.26.184.41
- MYSQL_SERVICE_PORT=3308
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=100m
- JVM_MMS=100m
volumes:
- ./cluster-logs/nacos2:/home/nacos/logs
depends_on:
- mysql
ports:
- 8850:8850
nacos3:
image: nacos/nacos-server:1.3.0
container_name: nacos3
restart: always
environment:
- NACOS_SERVERS=120.26.184.41:8849 120.26.184.41:8850 120.26.184.41:8851
- NACOS_SERVER_IP=120.26.184.41
- NACOS_APPLICATION_PORT=8851
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=120.26.184.41
- MYSQL_SERVICE_PORT=3308
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=100m
- JVM_MMS=100m
volumes:
- ./cluster-logs/nacos3:/home/nacos/logs
depends_on:
- mysql
ports:
- 8851:8851 【巨坑:】
想偷懒,却掉进了个大坑(没解决),特此记录一下,有时间再仔细研究。
情景再现:
在阿里云服务器上,想使用 docker-compose 一次性启动三个 Nacos Server 以及 mysql,结果一直启动失败。
坑一:
nacos-mysql:8.0.16 无法启动,更换成 nacos-mysql:5.7 之后正常启动。
但若 nacos-mysql:8.0.16 是事先就启动好的(即不跟随 Nacos Server 启动),那么连接是正常的。
为什么会出现这种情况,没有仔细研究,此处仅简单记录一下可能存在的问题。 坑二:
访问 http://120.26.184.41:8850/nacos,可以成功登录进去。
但点击 “集群管理” =》 “节点列表”,依次点击 “节点元数据”,可以发现其 "state" 均为 "CANDIDATE"。
即 集群中三个节点 并未选举成功,未成功选举出 LEADER、FOLLOWER。 坑三:
集群节点 "state" 均为 "FOLLOWER",没有 "LEADER"。选举仍然失败。
出现了 8848 端口,貌似其被选择为 LEADER。 坑四:
当前觉得你的配置没有问题了,但是 选举依然失败,这边建议你更换一下镜像看看。
此处 nacos/nacos-server:1.4.1 选举总是失败,更换 nacos/nacos-server:1.3.0 之后没问题了。
具体原因没有深入研究,此处仅记录一下可能存在的问题。

Step2:填坑记录一:
  将 nacos-mysql:8.0.16 镜像替换为 nacos-mysql:5.7 后,重新启动即可。

Step3:填坑记录二:
  若在控制台页面中,看见节点 “state” 均为 “CANDIDATE”。这是选举出问题了,可以查看 nacos 日志(naming-raft.log、nacos.log)。

【nacos.log 错误日志为:(如下两句语句循环出现)】
java.lang.IllegalStateException: can not find peer: 172.22.0.3:8848
java.lang.IllegalStateException: can not find peer: 172.22.0.5:8848 【naming-raft.log 错误日志为:】
ERROR NACOS-RAFT vote failed: 500, url: http://120.26.184.41:8850/nacos/v1/ns/raft/vote
ERROR NACOS-RAFT vote failed: 500, url: http://120.26.184.41:8851/nacos/v1/ns/raft/vote
WARN [IS LEADER] no leader is available now! 【分析:】
通过 nacos.log 的 ip 地址,可以猜测当前 nacos 读取到的是内网 IP。
而集群列表中不存在这个 IP,所以无法连接到其余 节点(无法通信),进而选举失败。 【解决:】
既然获取不到 IP 地址,那就手动输入 IP 地址。
在环境变量中,通过 NACOS_SERVER_IP 指定当前主机的 IP 即可。
比如: - NACOS_SERVER_IP=120.26.184.41

Step4:填坑记录三
  通过上面截图,可以看到三个节点状态均从 "CANDIDATE" 变为了 "FOLLOWER",为什么没有选举出 "LEADER"?

【查看 naming-raft.log 日志如下:】
2021-03-25 17:45:09,770 INFO received approve from peer: {"ip":"120.26.184.41:8848","voteFor":"120.26.184.41:8848","term":9,"leaderDueMs":18440,"heartbeatDueMs":2000,"state":"FOLLOWER"}
2021-03-25 17:45:09,772 INFO received approve from peer: {"ip":"120.26.184.41:8848","voteFor":"120.26.184.41:8848","term":9,"leaderDueMs":18531,"heartbeatDueMs":5000,"state":"CANDIDATE"}
2021-03-25 17:45:09,788 INFO received approve from peer: {"ip":"120.26.184.41:8848","voteFor":"120.26.184.41:8848","term":9,"leaderDueMs":18671,"heartbeatDueMs":5000,"state":"FOLLOWER"}
2021-03-25 17:45:15,001 WARN [IS LEADER] no leader is available now!
2021-03-25 17:45:28,123 INFO vote 120.26.184.41:8848 as leader, term: 10 【分析:】
通过日志可以看到貌似 120.26.184.41:8848 这个东西被选为了 LEADER,
而实际上 我开放的是 8849、8850、8851 三个端口,但是这三个端口并没有被访问。 【解决:】
默认端口号为 8848。既然获取不到端口,那就手动设置端口。
在环境变量中,通过 NACOS_APPLICATION_PORT 指定当前主机需要访问的端口即可。
修改 NACOS_APPLICATION_PORT 后,端口映射 ports 也需要修改。
比如:
environment:
- NACOS_APPLICATION_PORT=8851
ports:
- 8851:8851

(4)连接 Nacos 集群
  在 Nacos Client 配置文件中,直接通过 server-addr 设置节点地址即可,多个节点之间可使用 逗号 隔开。
  若使用 Nginx 做代理,则 server-addr 设置 Nginx 访问地址即可。
  Nginx 在 Nacos 真集群版中使用(请继续往下看)。

【比如:】
spring:
cloud:
nacos:
discovery:
server-addr: 120.26.184.41:8849,120.26.184.41:8850,120.26.184.41:8851
config:
server-addr: 120.26.184.41:8849,120.26.184.41:8850,120.26.184.41:8851 【bootstrap.yml】
server:
port: 7100 spring:
application:
name: nacos-client-config
cloud:
nacos:
discovery:
# 配置 nacos server 地址(作为注册中心)
# server-addr: 120.26.184.41:8848
server-addr: 120.26.184.41:8849,120.26.184.41:8850,120.26.184.41:8851
config:
# 配置 nacos server 地址(作为配置中心)
# server-addr: 120.26.184.41:8848
server-addr: 120.26.184.41:8849,120.26.184.41:8850,120.26.184.41:8851
# 设置配置文件前缀,默认为 ${spring.application.name}
prefix: ${spring.application.name}
# 设置配置文件后缀名
file-extension: yml
shared-configs[0]:
data-id: test-0-dev.yml
shared-configs[1]:
data-id: test-1-dev.yml
shared-configs[2]:
data-id: test-2-dev.yml
group: DEFAULT_GROUP
refresh: true

9、docker-compose 部署 Nacos Server(真集群版)

(1)说明:
  上面使用 docker-compose 演示了伪集群的操作,许多坑已经踩过了。真集群部署就更加简单了,将上面伪集群版的 docker-compose.yml 文件拆分成 几个小的 docker-compose.yml 文件,分别在不同的机器上启动即可(当然 ip 是需要修改的)。
此处创建三个 虚拟机,地址分别为:192.168.157.128、192.168.157.129、192.168.157.130。
  在 192.168.157.128 上启动 mysql、nginx、nacos1。
  在 129、130 上分别启动 nacos2、nacos3。
其中:
  使用 nginx 反向代理 nacos1、nacos2、nacos3。通过 nginx 访问 Nacos Server。
  使用 mysql 做配置信息持久化操作。
注:
  此处可以使用较高版本的 mysql、nacos。比如:nacos-mysql:8.0.16、nacos-server:1.4.1 。

(2)配置 mysql 以及 nginx。
Step1:
  自定义一个 nginx.conf,进行代理配置。
  如下所示,主要关注 server 以及 upstream nacos_server。

【nginx.conf】
user nginx;
worker_processes 1; error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid; events {
worker_connections 1024;
} http {
include /etc/nginx/mime.types;
default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on;
#tcp_nopush on; keepalive_timeout 65; #gzip on; # 负载均衡
upstream nacos_server {
# 默认加权轮询方式执行负载均衡
# weight 表示服务器权重,默认为 1。
# max_fails 表示 nginx 与 服务器通信失败 的最大次数。默认为 1。
# fail_timeout 为失败超时时间。默认 10 秒。在 fail_timeout 时间内,失败次数达到 max_fails,则 Nginx 认为该服务器不可用。
server 192.168.157.128:8849 weight=1 max_fails=2 fail_timeout=10s;
server 192.168.157.129:8850 weight=1 max_fails=2 fail_timeout=10s;
server 192.168.157.130:8851 weight=1 max_fails=2 fail_timeout=10s;
} server {
listen 10080; # 设置监听端口
server_name 192.168.157.128; # 设置 IP 地址
location / {
# 设置代理地址(根据 upstream 的值修改)
# 访问请求 http://192.168.157.128:10080 时,会替换为 http://192.168.157.128:8849/nacos/
proxy_pass http://nacos_server/nacos/; # 下面几个设置默认即可
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
}
}
include /etc/nginx/conf.d/*.conf;
}

Step2:
  编写 docker-compose.yml 文件,配置 mysql、nginx。
  需要将 上面自定义的 conf 文件 挂载到 nginx 的 /etc/nginx/nginx.conf 目录下。

【docker-compose.yml】
version: '3.7'
services:
nginx:
container_name: nginx
image: nginx:latest
restart: always
ports:
- "10080:10080"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx/
mysql:
container_name: nacos-cluster-mysql
image: nacos/nacos-mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=nacos_devtest
- MYSQL_USER=nacos
- MYSQL_PASSWORD=nacos
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/log:/var/log/mysql
ports:
- "3308:3306"

(3)配置 nacos1、nacos2、nacos3

【nacos1:】
version: '3.7'
services:
nacos1:
image: nacos/nacos-server:1.3.0
container_name: nacos1
restart: always
environment:
- NACOS_SERVERS=192.168.157.128:8849 192.168.157.129:8850 192.168.157.130:8851
- NACOS_SERVER_IP=192.168.157.128
- NACOS_APPLICATION_PORT=8849
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=192.168.157.128
- MYSQL_SERVICE_PORT=3308
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=100m
- JVM_MMS=100m
volumes:
- ./nacos1:/home/nacos/logs
ports:
- 8849:8849 【nacos2:】
version: '3.7'
services:
nacos2:
image: nacos/nacos-server:1.3.0
container_name: nacos2
restart: always
environment:
- NACOS_SERVERS=192.168.157.128:8849 192.168.157.129:8850 192.168.157.130:8851
- NACOS_SERVER_IP=192.168.157.129
- NACOS_APPLICATION_PORT=8850
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=192.168.157.128
- MYSQL_SERVICE_PORT=3308
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=100m
- JVM_MMS=100m
volumes:
- ./nacos2:/home/nacos/logs
ports:
- 8850:8850 【nacos3:】
version: '3.7'
services:
nacos3:
image: nacos/nacos-server:1.3.0
container_name: nacos3
restart: always
environment:
- NACOS_SERVERS=192.168.157.128:8849 192.168.157.129:8850 192.168.157.130:8851
- NACOS_SERVER_IP=192.168.157.130
- NACOS_APPLICATION_PORT=8851
- TZ=Asia/Shanghai
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=192.168.157.128
- MYSQL_SERVICE_PORT=3308
- MYSQL_SERVICE_DB_NAME=nacos_devtest
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- JVM_XMS=100m
- JVM_MMS=100m
volumes:
- ./nacos3:/home/nacos/logs
ports:
- 8851:8851

(4)启动
  分别启动 nginx、mysql、nacos1、nacos2、nacos3。
  访问:http://192.168.157.128:10080/ 会自动跳转到相应的 Nacos 控制台界面。

手动关闭 8850 端口的 Nacos,剩余节点会重新进行选举,选举出 LEADER。

(5)连接 nacos 集群
  若未使用 Nginx 代理时,直接在 Nacos Client 配置文件中,直接通过 server-addr 设置节点地址即可,多个节点之间可使用 逗号 隔开。

spring:
cloud:
nacos:
discovery:
# 配置 nacos server 地址(作为注册中心)
server-addr: 192.168.157.128:8849,192.168.157.129:8850,192.168.157.130:8851
config:
# 配置 nacos server 地址(作为配置中心)
server-addr: 192.168.157.128:8849,192.168.157.129:8850,192.168.157.130:8851

  若使用 Nginx 做代理,则 server-addr 设置 Nginx 访问地址即可。
注:
  此处关于 proxy_pass 设置的地址 有个小坑记录一下。

server{
listen 10080; # 设置监听端口
server_name 192.168.157.128; # 设置 IP 地址
location / {
# 设置代理地址(根据 upstream 的值修改)
# 访问请求 http://192.168.157.128:10080 时,会替换为 http://192.168.157.128:8849/nacos/
proxy_pass http://nacos_server/nacos/;
#proxy_pass http://nacos_server/;
}

若使用 proxy_pass http://nacos_server/,则在 Nacos Client 中直接配置 Nginx 地址即可。
此时,客户端正常启动,可以通过 Nginx 访问到 Nacos Server 集群,获取配置文件。
但是直接通过 Nginx 地址(http://192.168.157.128:10080 或者 http://192.168.157.128:10080/nacos),无法跳转到 Nacos 控制台界面。

spring:
cloud:
nacos:
discovery:
server-addr: 192.168.157.128:10080
config:
server-addr: 192.168.157.128:10080

若使用 proxy_pass http://nacos_server/nacos/,则通过 Nginx 地址(http://192.168.157.128:10080) 正常跳转进 Nacos 控制台界面,但若此时 Nacos Client 直接配置 Nginx 地址时,服务启动会异常,启动失败。
报错信息如下:

【错误信息:】
org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is java.lang.reflect.UndeclaredThrowableException Caused by: java.lang.reflect.UndeclaredThrowableException: null Caused by: com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance after all servers([192.168.157.128:10080]) tried: ErrCode:404, ErrMsg:<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Fri Mar 26 20:22:33 CST 2021</div><div>There was an unexpected error (type=Not Found, status=404).</div><div>No message available</div></body></html>

为了使服务正常执行,此处使用 proxy_pass http://nacos_server/ 。
Nginx 跳转不了 Nacos Server 控制台界面问题,有时间再来解决。

三、网关 -- Gateway

1、什么是 Gateway?

(1)什么是 Gateway?

【Gateway:】
Gateway 直译为 网关,是基于 Spring Boot 2.x、Spring WebFlux、Project Reactor 构建的 API 网关服务。
Gateway 目的是 提供一个简单的方式进行 路由转发,用于保护、控制、增强 对于 API 接口的访问。
简单理解:
Gateway 可以理解为 所有流量(请求)的入口,所有浏览器请求 都得通过 Gateway 进行路由,才能访问到服务提供的 API 接口。可以类比于 Nginx 反向代理进行理解。
Gateway 底层使用的是 Netty 框架。
注:
在 Gateway 之前,会使用 Zuul 作为网关,有兴趣的请自行了解。
Zuul 1.x 是基于 Servlet 2.5 的 阻塞 I/O 的 API 网关,不支持长连接。
Zuul 2.x 是基于 Netty 的 非阻塞 I/O 的 API 网关,支持长连接。
SpringCloud 并没有整合 Zuul 2.x,而是自己开发的一个 Gateway。
Gateway 毕竟是亲生的,在微服务中整合起来相对简单,所以使用较多的还是 Gateway。 【官网地址:】
https://spring.io/projects/spring-cloud-gateway
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

(2)网关使用场景?
  如下图所示,网关作为所有流量的 入口,根据路由规则 将请求路由到各个微服务上。

  若网关部署为 集群时,可以使用 Nginx 对其进行负载均衡。

(3)Gateway 功能
Gateway 基于 Spring Framework 5,Project Reactor、Spring Boot 2.0 构建,其基本功能如下:
  能够匹配任何请求属性的路由(支持动态路由,可以根据微服务名进行路由)。
  可以针对特殊路由指定 Predicates 和 Filter 规则,且 Filter、Predicates 易编写。
  集成 断路器 功能(Circuit Breaker)。
  集成 服务发现 功能(Spring Cloud DiscoveryClient)。
  集成 请求限流 功能。
  支持 路径重写 功能。

2、Gateway 核心概念(Route、Filter、Predicate)

(1)Route(路由)
  路由是构建网关的基本模块,基本构成组件: ID、目标 URL、Predicate 集合、Filter 集合 组成。当 Predicate 为 true 时,匹配该路由。

(2)Predicate(断言)
  可以参考 JDK8 中 java.util.function.Predicate 进行理解。
  可以根据 HTTP 请求中的内容(比如:请求头、请求参数 等)进行匹配,如果匹配结果为 true,则匹配该路由。

(3)Filter(过滤)
  指的是 Spring 框架中 GatewayFilter 的实例,根据过滤器,可以在 请求被 路由前 或者 路由后 对请求进行更改。

(4)gateway 工作流程
  Gateway 整个工作流程就是 路由转发 + 过滤器链。
  客户端 向 Gateway 发送请求,在 Gateway Handler Mapping 中找到与 请求匹配的 路由(根据 Predicate 匹配 Route),然后将其转发到 Gateway Web Handler 进行处理。
  Handler 通过指定的过滤器链后,将请求发送到实际的业务逻辑进行处理,处理完成后返回。同时过滤器也可以对返回数据进行处理。
  过滤器 可以在路由处理前 进行 参数校验、权限校验、流量监控、协议转换 等操作。
  过滤器 可以在路由处理后 进行 响应内容修改、日志输出、流量监控等操作。

3、简单使用一下 Gateway

(1)说明
  新建一个子模块 gateway_7200,通过配置文件的方式,简单测试一下 路由匹配规则。此处简单的与 前面使用的 Nacos 一起使用。具体使用规则可参考官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

(2)使用 Gateway
Step1:
  引入 Gateway 依赖。Gateway 注册到 Nacos Server 中。
注:
  添加 Gateway 依赖后,需要排除 spring-boot-starter-web 依赖。若不排除,则启动网关服务时将会出错。

【依赖:】
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency> <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> 【错误:】
若出现如下异常,需要移除 spring-boot-starter-web 依赖,否则会与 webflux 的 jar 包冲突导致启动失败。
Description:
Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.

Step2:
  配置 Gateway。
  如下所示,配置 Nacos 注册中心地址为 120.26.184.41:8848。
  配置路由规则,此处演示 Path 进行匹配。
注:
  若使用 Path 进行路径匹配时,匹配成功后,真实访问路径其实就是 uri + Path。
比如:
  当请求为 http://localhost:7200/nacos/info/ 时,其将跳转到 http://localhost:7101/nacos/info 进行处理。
  当请求为 http://localhost:7200/ 时,其将跳转到 https://www.baidu.com/ 进行处理。

【application.yml】
server:
port: 7200 spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848
# 网关配置
gateway:
# 配置路由规则
routes:
# 路由唯一 id 名
- id: gateway_route
# 断言为 true 时,需执行的 路由地址
uri: http://localhost:7101
# 定义断言,即 地址匹配 规则
predicates:
# 当访问 http://localhost:7200/naocs/info 时,此时断言匹配成功,根据 uri 将跳转到 http://localhost:7101/nacos/info
- Path=/nacos/info
- id: gateway_route2
uri: https://www.baidu.com/
predicates:
- Path=/

Step3:
  启动 nacos_client_7101、gateway_7200。
  访问 http://localhost:7200/nacos/info 时,将跳转到 http://localhost:7101/nacos/info 处理。

  访问 http://localhost:7200/ 时,将跳转到 https://www.baidu.com/ 处理。

(3)根据微服务名进行 路由(动态路由)
  实际开发中,一个服务通常以集群的方式部署,若 uri 配置固定的地址进行跳转,肯定是不合适的。而 微服务注册在 注册中心中,网关 只需要通过 注册中心 提供的 微服务名 找到真实的 微服务,进行路由即可。

修改的配置如下:
  在 uri 中使用 lb://微服务名 进行配置即可。lb 表示负载均衡。
  确保能根据 微服务名 进行路由,需配置 spring.cloud.gateway.discovery.locator.enabled=true
注:
  若注册中心为 nacos,仅需设置 uri 为 lb://微服务名 即可根据 微服务名进行 路由跳转,且实现负载均衡。其他注册中心 Eureka、Zookeeper、Consul 等此处并未进行尝试,若不能路由,则配置一下 discovery.locator.enabled=true 应该就行了。

【application.yml】
server:
port: 7200 spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848
# 网关配置
gateway:
routes:
- id: gateway_route3
uri: lb://nacos-client
predicates:
# 配置多个地址时,可以使用 逗号 隔开
- Path=/nacos/info,/nacos/discovery

启动 nacos_client_7101、nacos_client_7102,微服务名为 nacos-client。
当访问 http://localhost:7200/nacos/info 时,会路由到 http://localhost:7101/nacos/info 与 http://localhost:7102/nacos/info ,并实现负载均衡(此处默认为 轮询)。

4、predicates 规则

  Gateway 内部有许多 Route Predicate factories,可以与 HTTP 请求的不同属性进行匹配,多个 Predicate 规则可以组合使用。请求匹配成功后,则根据 uri 进行路由跳转。

常见的 Predicate 规则如下。
(1)时间相关(Before、After、Between)
  Before 表示在 指定时间(datetime) 前的请求 才能匹配路由。
  After 表示在 指定时间(datetime) 后的请求 才能匹配路由。
  Between 表示在 指定时间段(datetime1,datetime2) 内的请求 才能匹配路由。
注:
  时间参数 datetime 为 ZonedDateTime 的数据。
  可以使用 System.out.println(ZonedDateTime.now()); 输出当前地区、时间。
  比如:2021-03-31T10:35:43.706+08:00[Asia/Shanghai]

【格式:】
predicates:
- After=datetime
- Before=datetime
- Between=datetime1,datetime2 【举例:】
predicates:
- After=2021-03-31T10:35:43.706+08:00[Asia/Shanghai]
- Before=2021-03-31T10:35:43.706+08:00[Asia/Shanghai]
- Between=2021-03-31T10:35:43.706+08:00[Asia/Shanghai],2021-03-31T10:40:43.706+08:00[Asia/Shanghai] 【举例:(如下,在指定时间之后的请求才能 路由。指定时间之前,路由将失败。)】
server:
port: 7200 spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848
# 网关配置
gateway:
routes:
- id: gateway_route3
uri: lb://nacos-client
predicates:
# 配置多个地址时,可以使用 逗号 隔开
- Path=/nacos/info,/nacos/discovery
- After=2021-03-31T10:40:00.706+08:00[Asia/Shanghai]

(2)Cookie 相关
  Cookie 规则具备两个参数,一个是 Cookie 属性名(name),一个是正则表达式(regexp)。
  根据 name 以及 regexp 去匹配路由。带有指定 cookie 信息的请求才能匹配路由。
有两种写法格式。

【格式一:】
predicates:
- Cookie=mycookiename,mycookievalue 【格式二:】
predicates:
- name: Cookie
args:
name: mycookiename
regexp: mycookievalue 【举例:当请求 cookie 中携带 username 为 jarry 时,才能匹配路由】
server:
port: 7200 spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848
# 网关配置
gateway:
routes:
- id: gateway_route3
uri: lb://nacos-client
predicates:
# 配置多个地址时,可以使用 逗号 隔开
- Path=/nacos/info,/nacos/discovery
- After=2021-03-31T10:40:00.706+08:00[Asia/Shanghai]
- Cookie=username,jarry
# - name: Cookie
# args:
# name: username
# regexp: tom 【通过 curl 模拟请求:】
curl http://localhost:7200/nacos/info --cookie "username=jarry" 路由匹配成功
curl http://localhost:7200/nacos/info --cookie "username=jarry1" 路由匹配失败

(3)Header、Host、Method、Path、Query、Weight 等
  可以自行尝试,此处不一一举例。

【Header:】
参数:
name、regexp。 与 Cookie 规则类似。
带有指定请求头的请求才能匹配路由。 格式:
predicates:
- Header=X-Request-Id, \d+ 【Host:】
参数:
patterns
带有指定 host 的请求才能匹配路由。 格式:
predicates:
- Host=**.lyh.com 【Method:】
参数:
methods
带有指定方法的请求才能匹配路由。 格式:
predicates:
- Method=GET,POST 【Path:】
参数:
PathMatcher
带有指定路径的请求才能匹配路由。多路径使用逗号隔开。 格式:
predicates:
- Path=/red/{segment},/blue/{segment} 【Query:】
参数:
param、regexp。 与 Cookie 规则类似。
带有指定参数的请求才能匹配路由。 格式:
predicates:
- Query=green 【Weight:】
参数:
group、weight。
根据权重进行请求分流,80% 请求匹配到 weight_high,20% 请求匹配到 weight_low。 格式:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2 【举例:】
server:
port: 7200 spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848
# 网关配置
gateway:
routes:
- id: gateway_route3
uri: lb://nacos-client
predicates:
# 配置多个地址时,可以使用 逗号 隔开
- Path=/nacos/info,/nacos/discovery
- After=2021-03-31T10:40:00.706+08:00[Asia/Shanghai]
- Query=green,test.
- Method=GET,POST
- Host=**.lyh.com
- Header=X-Request-Id, \d+
- Cookie=username,jarry 【通过 curl 测试:(发送 GET、POST 请求)】
curl -X POST "http://localhost:7200/nacos/info?green=test1" --cookie "username=jarry" --header "X-Request-Id:123" --header "Host:www.lyh.com
curl "http://localhost:7200/nacos/info?green=test1" --cookie "username=jarry" --header "X-Request-Id:123" --header "Host:www.lyh.com

5、Filter 规则

  Gateway 内部有许多 GatewayFilter Factories,可以修改 HTTP 请求 以及 HTTP 响应。
  仅简单介绍几个 Filter,其余请自行参阅官方文档进行使用。
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

(1)添加参数(比如:AddRequestParameter)
  参数为 name、value。用于在请求末尾追加参数(比如: ?name1=value1&name2=value2)
  可以在 predicates 指定变量 segment,并处理。

【格式:】
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route1
uri: https://example.org
predicates:
- Path=/test/{segment}
filters:
- AddRequestParameter=foo, bar-{segment}
- id: add_request_parameter_route2
uri: https://example.org
filters:
- AddRequestParameter=red, blue 【举例:(修改 gateway 配置文件如下)】
server:
port: 7200 spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 配置 nacos 地址
server-addr: 120.26.184.41:8848
# 网关配置
gateway:
routes:
- id: gateway_route4
uri: http://localhost:7101
predicates:
- Path=/nacos/test/{username}
filters:
- AddRequestParameter=password,{username}
- AddRequestParameter=email,jarry@163.com 【在 nacos_client_7101 中添加如下代码:】
@GetMapping("/test/{username}")
public String test(@PathVariable String username,
@RequestParam(required = false) String password,
@RequestParam(required = false) String email,
@RequestParam(required = false) Integer age) {
username = (username != null ? username : "hello");
password = (password != null ? password : "helloworld");
email = (email != null ? email : "tom@163.com");
age = (age != null ? age : 24);
return username + " " + password + " " + email + " " + age;
}

当访问 http://localhost:7200/nacos/test/hello 时,predicates 匹配成功,filters 发挥最用,等同于发送请求 http://localhost:7101/nacos/test/hello?password=hello&email=jarry@163.com

(2)修改路径(比如:PrefixPath、StripPrefix)

【PrefixPath:】
PrefixPath 用于指定 路径前缀。
比如:
gateway:
routes:
- id: gateway_route5
uri: http://localhost:7101
predicates:
- Path=/info
filters:
- PrefixPath=/nacos 当访问 http://localhost:7200/info 时,将跳转到 http://localhost:7101/nacos/info 【StripPrefix:】
StripPrefix 用于跳过 路径前缀(数值型)。
比如:
gateway:
routes:
- id: gateway_route5
uri: http://localhost:7101
predicates:
- Path=/info/**
filters:
- StripPrefix=1 当访问 http://localhost:7200/info/nacos/info 时,将跳转到 http://localhost:7101/nacos/info

其余 Filter 可自行参考文档。也可以自定义 Filter。后续有时间再来补充。。。

学习一下 SpringCloud (六)-- 注册中心与配置中心 Nacos、网关 Gateway的相关教程结束。

《学习一下 SpringCloud (六)-- 注册中心与配置中心 Nacos、网关 Gateway.doc》

下载本文的Word格式文档,以方便收藏与打印。