# AlibabaNacos

返回:springCloud | 返回:Spring

nacos

关于 Nacos 名字:前四个字母分别为 NamingConfiguration 的前两个字母,最后的 sService
gitHub | 官网

nacos地图

p

全景图

p

架构及概念

p

# 基本概念及用途

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos主要用于 服务发现和服务健康监测,动态配置服务,动态 DNS 服务等场景。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
服务(Service)是 Nacos 世界的一等公民。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理: Kubernetes ServicegRPC & Dubbo RPC ServiceSpring Cloud RESTful Service
简单来说 Nacos 就是注册中心 + 配置中心的组合,提供简单易用的特性集,帮助我们解决微服务开发必会涉及到的服务注册与发现服务配置服务管理等问题。Nacos 还是 Spring Cloud Alibaba 组件之一,负责服务注册与发现。

  • 服务 (Service)

服务是指一个或一组软件功能(例如特定信息的检索或一组操作的执行),其目的是不同的客户端可以为不同的目的重用(例如通过跨进程的网络调用)。Nacos 支持主流的服务生态,如 Kubernetes Service、gRPC|Dubbo RPC Service 或者 Spring Cloud RESTful Service.

  • 服务注册中心 (Service Registry)

服务注册中心,它是服务,其实例及元数据的数据库。服务实例在启动时注册到服务注册表,并在关闭时注销。服务和路由器的客户端查询服务注册表以查找服务的可用实例。服务注册中心可能会调用服务实例的健康检查 API 来验证它是否能够处理请求。

  • 服务元数据 (Service Metadata)

服务元数据是指包括服务端点(endpoints)、服务标签、服务版本号、服务实例权重、路由规则、安全策略等描述服务的数据

  • 服务提供方 (Service Provider)

是指提供可复用和可调用服务的应用方

  • 服务消费方 (Service Consumer)

是指会发起对某个服务调用的应用方

  • 配置 (Configuration)

在系统开发过程中通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成这个步骤。配置变更是调整系统运行时的行为的有效手段之一。

  • 配置管理 (Configuration Management)

在数据中心中,系统中所有配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动统称为配置管理。

  • 名字服务 (Naming Service)

提供分布式系统中所有对象(Object)、实体(Entity)的“名字”到关联的元数据之间的映射管理服务,例如 ServiceName -> Endpoints Info, Distributed Lock Name -> Lock Owner/Status Info, DNS Domain Name -> IP List, 服务发现和 DNS 就是名字服务的2大场景。

  • 配置服务 (Configuration Service)

在服务或者应用运行过程中,提供动态配置或者元数据以及配置管理的服务提供者。

返回顶部

# Nacos vs Spring Cloud

相对于 Spring Cloud Eureka 来说,Nacos 更强大。

Nacos = Spring Cloud Eureka + Spring Cloud Config

Nacos 可以与 Spring, Spring Boot, Spring Cloud 集成,并能代替 Spring Cloud Eureka, Spring Cloud Config

通过 Nacos Serverspring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。 通过 Nacos Serverspring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。

# why-nacos

返回顶部

现在的微服务生态中,已经有很多服务注册与发现的开源组件,如 EurkaZooKeeperConsul,为什么还要用 Nacos 呢,我们看下这些框架的简单对比:

服务注册与发现框架 CAP 模型 控制台管理 社区活跃度
Eureka AP 支持 低 (2.x 版本闭源)
Zookeeper CP 不支持
Consul CP 支持
Nacos AP 支持

相比之下,目前的 Nacos 无论是部署,还是使用上都简单上手,更重要的是文档资料齐全,社区活跃度高。

并且 Nacos 与目前主流的开源生态都提供了很好的支持:

  • Nacos 是专为 Dubbo 而生的注册中心与配置中心
  • Nacos 会完全兼容 Spring Cloud
  • Nacos 支持 Service Mesh 集成,Kubernetes 集成

除此之外,阿里巴巴正在通过 Dubbo + Nacos 以及一系列开源项目打造服务发现、服务及流量管理、服务共享平台,未来还在不断地发展和演进,相信未来会有更多的地方上使用 Nacos。

# 开始入手

返回顶部

Windows上下载zip包就可以了,linux上下载tar包。
以Windows为例,解压之后进入bin目录,直接运行startup.cmd脚本即可启动Nacos服务。Linux/Unix/Mac上执行sh startup.sh -m standalone启动服务,standalone代表着单机模式运行,非集群模式。

当然也可以通过下载源码自己编译打包文件,步骤如下:

git clone https://github.com/alibaba/nacos.git alibaba-nacos
cd alibab-nacos
mvn -Prelease-nacos -DskipTests clean install -U
1
2
3

当前最新的 Nacos 版本为 1.0.2-SNAPSHOT(最新release版为1.01),Maven 方式打包后会在当前目录 distribution/target 下生成两个压缩包 nacos-server-1.0.2-SNAPSHOT.tar.gznacos-server-1.0.2-SNAPSHOT.zip,任意解压一个使用即可。

# 单机模式

./startup.sh -m standalone
1

这里 Nacos 单机部署方式使用命令 -m standalone ,如果是 Windows 命令,可以直接双击 startup.cmd 文件即可。

控制台上也直接给出了当前可访问的 Nacos 控制台地址 http://{ip}:8848/nacos/index.html,点击就进入了 Nacos 的可视化管理界面,需要账号密码登录访问,默认都为 nacos

p

服务管理下目前只有一个名为服务列表的子菜单,展示的内容也比较简单,包含服务名称,组别,集群数目,总的实例数,运行中的实例数。

当有新的服务通过 Nacos 客户端注册到 Nacos 上时列表项就会增加,我们还可以通过操作栏的按钮进行对服务的详情查看和编辑。

# 集群模式

相比 Nacos 简单的单机部署,集群部署方式稍微麻烦一些,跟着官方文档走还是有点小坑,还需要自己额外的调整。 为了用于生产环境,必须确保 Nacos 的高可用,所以还是有必要实践下集群部署的操作。
准备环境跟单机部署相同,额外的要求就是 Nacos 需要 3 个或 3 个以上 Nacos 节点构成集群,并且使用 MySQL 作为数据源,主要用于服务配置的数据持久化
我们先看下官方推荐的集群部署架构图,通过域名方式反向代理如 Nginx 来负载多个 Nacos 节点 IP,外部客户端直接通过域名访问就可,不仅可读性好,而且更换 IP 方便,最为推荐采用。

# 添加集群配置文件

在每个 Nacos 节点的conf目录下,添加配置文件 cluster.conf,可以参考相同目录下的 cluster.conf.example 文件,每行配置一个节点的 IP 和端口,如 ip:port

192.168.0.112:8848
192.168.0.111:8848
192.168.0.102:8847
1
2
3

注意:配置文件中不能使用 127.0.0.1 或者localhost ,需要真实 IP 或者域名,否则启动后服务无法注册到该集群节点上

# 配置MySQL数据库

返回顶部

Nacos 推荐生产环境中数据库使用建议至少主备模式,或者采用高可用数据库。

  • 安装MySQL数据库

生产环境需使用MySQL作为后端存储,因此需要搭建MySQL。生产中,MySQL建议至少主备模式,高可用MySQL更佳。
高可用MySQL的搭建可参考
注意:Nacos支持的MySQL版本为:5.6.5+

这里为了简化只采用了一个数据库。首先新建一个名为 nacos_config 的数据库,使用提供的 sql(nacos-mysql.sql) 语句源文件 导入初始数据。
然后在每个 Nacos 节点的配置文件 conf/application.properties 里添加数据库连接配置:

# spring

server.contextPath=/nacos
server.servlet.contextPath=/nacos
server.port=8848

db.num=2
db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.url.1=jdbc:mysql://11.163.152.9:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=nacos_devtest
db.password=nacos
1
2
3
4
5
6
7
8
9
10
11

最后以集群模式分别启动每个节点,并且默认为后台启动,启动信息需要从 logs/logs/start.out日志文件中获取。

sh startup.sh
1

当日志文件最后出现 Nacos started successfully in cluster mode. 一行时,即说明集群模式下 Nacos 启动成功。这时,我们也可以通过登录任一个 Nacos 控制台的集群管理界面看到节点的信息。

可以从上面看到,集群下的 Nacos 节点状态分为 FOLLOWER ,LEADER 两种,跟我们熟悉的主从架构相似。
到这里,我们集群方式的搭建也完成了。接下我们就来看下如何使用 Nacos 进行服务注册和发现吧。

# 多集群模式

返回顶部

# 服务注册与发现

返回顶部

# Rest服务的注册与发现

# 服务提供者创建

  • 版本依赖关系
Release Train Boot Version Alibaba
Greenwich 2.1.x 0.9.X
Finchley 2.0.x 0.2.X
Edgware 1.5.x 0.1.X
Dalston 1.5.x 0.1.X

此处采用Alibaba最新版0.9.0.RELEASE版,Cloud最新版Greenwich.SR2

  • 构建父工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.clm</groupId>
    <artifactId>ht-nacos</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ht-nacos</name>
    <description>Demo project for Spring Boot</description>

    <modules>
        <module>nacos-server</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
        <spring-cloud.alibaba.version>0.9.0.RELEASE</spring-cloud.alibaba.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  • 接下来构建服务提供者
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.clm</groupId>
        <artifactId>ht-nacos</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>nacos-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-server</name>
    <packaging>jar</packaging>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
<!--        由于在dependencyManagement中已经引入了版本,所以这里就不用指定具体版本了。-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<!--            <version>0.9.0.RELEASE</version>-->
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  • 启动类修改

加上注解@EnableDiscoveryClient,开启Spring Cloud的服务注册与发现,由于这里引入了spring-cloud-starter-alibaba-nacos-discovery模块,所以Spring Cloud Common中定义的那些与服务治理相关的接口将使用Nacos的实现。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class NacosServerApplication {

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 配置服务名称和nacos地址
spring.application.name=ht-nacos-server
server.port=8061

spring.cloud.nacos.discovery.server-addr=localhost:8848
1
2
3
4
  • 启动工程
2019-06-29 01:19:48.605  INFO 10204 --- [           main] o.s.c.a.n.registry.NacosServiceRegistry  : nacos registry, ht-nacos-server 192.168.0.101:8061 register finished
1

表示注册成功,此时启动多个实例,可以看到nacos上就会出现多个服务实例

启动多实例,要关掉热部署功能

p

# 服务消费者

消费方式RestTemplatefeign

返回顶部

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.clm</groupId>
        <artifactId>ht-nacos</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>nacos-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
        <!--        由于在dependencyManagement中已经引入了版本,所以这里就不用指定具体版本了。-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <!--            <version>0.9.0.RELEASE</version>-->
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

启动类修改:

@EnableDiscoveryClient
@SpringBootApplication
public class NacosConsumerApplication {

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

}
1
2
3
4
5
6
7
8
9

# RestTemplate

返回顶部

  • 消费类RestTemplate方式进行消费,实现方式一:
package com.clm.consumer.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @author HTRING
 * @version 1.0.0
 * @date 2019/6/29
 */
@RestController
@Slf4j
@RequestMapping("/first")
public class FirstController {

    private final LoadBalancerClient loadBalancerClient;

    @Autowired
    public FirstController(LoadBalancerClient loadBalancerClient) {
        this.loadBalancerClient = loadBalancerClient;
    }

    @GetMapping("/hi")
    public String sayHi(){
        ServiceInstance serviceInstance = loadBalancerClient.choose("ht-nacos-server");
        String url = serviceInstance.getUri()+"/first/hi?name="+"clm";
        RestTemplate restTemplate = new RestTemplate();
        String result = restTemplate.getForObject(url,String.class);
        return "执行:"+url+",获得结果:"+result;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  • 消费类RestTemplate方式进行消费,实现方式二:

在定义RestTemplate的时候,增加了@LoadBalanced注解,直接采用服务名的时候来写请求路径即可。在真正调用的时候,Spring Cloud会将请求拦截下来,然后通过负载均衡器选出节点,并替换服务名部分为具体的ip和端口,从而实现基于服务名的负载均衡调用。

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class NacosConsumerApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

调用方法

    private final LoadBalancerClient loadBalancerClient;
    private final RestTemplate restTemplate;
    /**
     * 服务提供者名称
     */
    @Value("${cloud.server.name}")
    private String nacosServerName;

    @Autowired
    public FirstController(LoadBalancerClient loadBalancerClient,RestTemplate restTemplate) {
        this.loadBalancerClient = loadBalancerClient;
        this.restTemplate = restTemplate;
    }

@GetMapping("/hi2")
    public String sayHi2(){
        return Optional.ofNullable(this.restTemplate.getForObject("http://ht-nacos-server/first/hi?name=CHLM",String.class))
                .orElse("调用错误");
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

启动后可以发现已实现了负载均衡(ribbon)

执行:http://192.168.0.101:8062/first/hi?name=clm,获得结果:Hiclm

执行:http://192.168.0.101:8061/first/hi?name=clm,获得结果:Hiclm
1
2
3

每次请求到的服务可能不一样

# feign

返回顶部

可以看到这样比较繁琐来调用服务,我们采用feign方式进行消费
调整如下:

pom增加依赖

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
1
2
3
4
5
6

启动类增加注解@EnableFeignClients

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients//这个注解要放在最后面,否则找不到服务提供者
public class NacosConsumerApplication {
1
2
3
4

新增接口

@FeignClient("ht-nacos-server")
public interface FirstService {
    @RequestMapping(value = "/first/hi",method = RequestMethod.GET)
    String sayHi(@RequestParam("name") String name);
}

@FeignClient("ht-nacos-server")
public interface FirstService {
    @RequestMapping(value = "/first/hi")
    String sayHi(@RequestParam("name") String name);
}
1
2
3
4
5
6
7
8
9
10
11

新增调用

@RestController
@RequestMapping("/feign")
@Slf4j
public class FirstFeignController {
    private final FirstService firstService;

    public FirstFeignController(FirstService firstService) {
        this.firstService = firstService;
    }

    @GetMapping("/hi")
    public String sayHi(){
        return this.firstService.sayHi("From Feign");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Get 请求方式的接口方法参数必须使用 @RequestParam 参数指定请求参数名称,否则 Feign 都会通过 POST 方式调用,得到状态码 405 的错误。

# WebClient

返回顶部

WebClient是Spring 5中最新引入的,可以将其理解为reactive版的RestTemplate。

# 熔断sentinel

目录 目录 目录 目录
整合sentinel 配置限流规则 使用Nacos存储限流规则 使用Apollo存储限流规则
sentinel-dashboard中修改规则同步到Nacos sentinel-dashboard中修改规则同步到Apollo @SentinelResource注解使用详解 问题收集

返回顶部

下载sentinel的release包GitHub

sentinel-dashboard不像Nacos的服务端那样提供了外置的配置文件,比较容易修改参数。不过不要紧,由于sentinel-dashboard是一个标准的spring boot应用,所以如果要自定义端口号等内容的话,可以通过在启动命令中增加参数来调整,比如:-Dserver.port=8888

默认情况下,sentinel-dashboard以8080端口启动,所以可以通过访问:localhost:8080来验证是否已经启动成功,如果一切顺利的话,可以看到如下页面:

注意:只有1.6.0及以上版本,才有这个简单的登录页面。默认用户名和密码都是sentinel。对于用户登录的相关配置可以在启动命令中增加下面的参数来进行配置:

-Dsentinel.dashboard.auth.username=sentinel:
用于指定控制台的登录用户名为 sentinel;

-Dsentinel.dashboard.auth.password=123456:
用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel

-Dserver.servlet.session.timeout=7200:
用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;

# 整合sentinel

1.添加依赖2.增加sentinel-dashboard地址3.feign添加支持

返回

# 1.添加依赖

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-alibaba-nacos-config -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<!--            <artifactId>spring-cloud-alibaba-nacos-config</artifactId>-->
<!--            <version>0.9.0.RELEASE</version>-->
        </dependency>
1
2
3
4
5
6
7

# 2.增加sentinel-dashboard地址

#spring.application.name=com.clm.nacos.config.dev
#server.port=9100
#spring.cloud.nacos.config.server-addr=localhost:8848
spring.cloud.nacos.config.server-addr=localhost:8848
spring.application.name=ht-nacos-config
spring.cloud.nacos.config.file-extension=properties
spring.profiles.active=dev
#spring.cloud.nacos.config.namespace=a447cbd0-c37d-4cb1-bacf-eb6e6622999f

# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8849
1
2
3
4
5
6
7
8
9
10
11

# 3.feign添加支持

feign的依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
  <version>${latest.version}</version>
</dependency>
1
2
3
4
5

配置文件新增feign.sentinel.enabled=true

# 配置限流规则

返回

在完成了上面的两节之后,我们在ht-nacos-config服务下,点击簇点链路菜单,可以看到如下界面:

其中 /cloud/hi接口,就是我们上一节中实现并调用过的接口。通过点击流控按钮,来为该接口设置限流规则,比如:
这里做一个最简单的配置:
阈值类型选择:QPS
单机阈值:2

综合起来的配置效果就是,该接口的限流策略是每秒最多允许2个请求进入。

点击新增按钮之后,可以看到如下界面:

# 一、流控规则

Field 说明 默认值
resource 资源名,资源名是限流规则的作用对象
count 限流阈值
grade 限流阈值类型,QPS 或线程数模式 QPS 模式
limitApp 流控针对的调用来源 default,代表不区分调用来源
strategy 判断的根据是资源自身,还是根据其它关联资源 (refResource),还是根据链路入口 根据资源本身
controlBehavior 流控效果(直接拒绝 / 排队等待 / 慢启动模式) 直接拒绝

在Spring Cloud Alibaba的整合封装之下,接口限流这件事情可以非常轻易的整合到我们的Spring Cloud应用中。
但是,通过上篇的整合,依然还不能完美的满足我们日常的生产需求。其中,非常重要的一点就是限流规则的持久化问题。 Dashboard中设置的限流规则在应用重启之后就丢失了的问题。那么,接下来我们就来说说Sentinel的规则持久化如何实现。

# 使用Nacos存储限流规则

返回

Sentinel自身就支持了多种不同的数据源来持久化规则配置,目前包括以下几种方式:

# 准备工作

下面我们将同时使用到NacosSentinel Dashboard,所以可以先把Nacos和Sentinel Dashboard启动起来。

# 应用配置

第一步:在Spring Cloud应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块和Nacos存储扩展:

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-sentinel -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<!--            <version>0.9.0.RELEASE</version>-->
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-nacos -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
            <version>1.6.2</version>
            <scope>test</scope>
        </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13

第二步:添加配置信息

# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8849
spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel
spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow
1
2
3
4
5
6

spring.cloud.sentinel.transport.dashboard:sentinel dashboard的访问地址,根据上面准备工作中启动的实例配置
spring.cloud.sentinel.datasource.ds.nacos.server-addr:nacos的访问地址,,根据上面准备工作中启动的实例配置
spring.cloud.sentinel.datasource.ds.nacos.groupId:nacos中存储规则的groupId
spring.cloud.sentinel.datasource.ds.nacos.dataId:nacos中存储规则的dataId
spring.cloud.sentinel.datasource.ds.nacos.rule-type:该参数是spring cloud alibaba升级到0.2.2之后增加的配置,用来定义存储的规则类型。所有的规则类型可查看枚举类: org.springframework.cloud.alibaba.sentinel.datasource.RuleType,每种规则的定义格式可以通过各枚举值中定义的规则对象来查看,比如限流规则可查看:
com.alibaba.csp.sentinel.slots.block.flow.FlowRule

注意:由于版本迭代关系,Github Wiki中的文档信息不一定适用所有版本。比如:在这里适用的0.2.1版本中,并没有spring.cloud.sentinel.datasource.ds2.nacos.rule-type这个参数。
所以,读者在使用的时候,可以通过查看org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfigurationorg.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties两个类来分析具体的配置内容,会更为准确。

第四步:Nacos中创建限流规则的配置,比如:参照配置管理
其中:Data ID、Group就是上面第二步中配置的内容。配置格式选择JSON,并在配置内容中填入下面的内容:

[
    {
        "resource": "/cloud/hi",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
1
2
3
4
5
6
7
8
9
10
11

可以看到上面配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:

resource:资源名,即限流规则的作用对象
limitApp:流控针对的调用来源,若为 default 则不区分调用来源
grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
count:限流阈值
strategy:调用关系限流策略
controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
clusterMode:是否为集群模式

第五步:启动应用。如果一些顺利,可以看到类似下面的日志,代表已经成功从Nacos加载了一条限流规则:

此时,在Sentinel Dashboard中就可以看到当前我们启动的ht-nacos-config服务。点击左侧菜单中的流控规则,可以看到已经存在一条记录了,具体如下

在完成了上面的整合之后,对于接口流控规则的修改就存在两个地方了:Sentinel控制台Nacos控制台

这个时候,需要注意当前版本的Sentinel控制台不具备同步修改Nacos配置的能力,而Nacos由于可以通过在客户端中使用Listener来实现自动更新。所以,在整合了Nacos做规则存储之后,需要知道在下面两个地方修改存在不同的效果:

  • Sentinel控制台中修改规则:仅存在于服务的内存中,不会修改Nacos中的配置值,重启后恢复原来的值。
  • Nacos控制台中修改规则:服务的内存中规则会更新,Nacos中持久化规则也会更新,重启后依然保持。

# 使用Apollo存储限流规则

返回

Sentinel自身就支持了多种不同的数据源来持久化规则配置,目前包括以下几种方式:

# 准备工作Apollo

下面我们将同时使用到ApolloSentinel Dashboard,所以可以先把Apollo和Sentinel Dashboard启动起来。

如果还没入门Sentinel Dashboard可以通过文末的系列目录先学习之前的内容。Apollo的话相对复杂一些,这里不做详细介绍了
如果还没有接触过Apollo的读者可以查看其官方文档进一步学习。

# 应用配置Apollo

第一步:在Spring Cloud应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块和Apollo存储扩展:

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-sentinel -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<!--            <version>0.9.0.RELEASE</version>-->
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-nacos -->
<!--        <dependency>-->
<!--            <groupId>com.alibaba.csp</groupId>-->
<!--            <artifactId>sentinel-datasource-nacos</artifactId>-->
<!--            <version>1.6.2</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
        <!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-apollo -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-apollo</artifactId>
            <version>1.6.2</version>
        </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

第二步:在Spring Cloud应用中配置的服务信息,在resource目录下,创建apollo-env.properties文件,内容样例:

local.meta=http://192.168.0.201:8080
dev.meta=http://192.168.0.202:8080
1
2

第三步:在Spring Cloud应用中添加配置信息:

# apollo config
app.id=${spring.application.name}

# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8849
# sentinel datasource apollo
spring.cloud.sentinel.datasource.ds.apollo.namespaceName=application
spring.cloud.sentinel.datasource.ds.apollo.flowRulesKey=sentinel.flowRules
spring.cloud.sentinel.datasource.ds.apollo.rule-type=flow
1
2
3
4
5
6
7
8
9

app.id:Apollo中的创建的项目名称,这里采用spring.application.name参数的引用,从而达到服务名与配置项目名一致的效果
spring.cloud.sentinel.transport.dashboard:sentinel dashboard的访问地址,根据上面准备工作中启动的实例配置
spring.cloud.sentinel.datasource.ds.apollo.namespaceName:Apollo的空间名 spring.cloud.sentinel.datasource.ds.apollo.flowRulesKey:配置规则的key名称 spring.cloud.sentinel.datasource.ds.apollo.rule-type:该参数是spring cloud alibaba升级到0.2.2之后增加的配置,用来定义存储的规则类型。所有的规则类型可查看枚举类:org.springframework.cloud.alibaba.sentinel.datasource.RuleType,每种规则的定义格式可以通过各枚举值中定义的规则对象来查看,比如限流规则可查看:com.alibaba.csp.sentinel.slots.block.flow.FlowRule

启动类加上@EnableApolloConfig注解是开启Apollo的配置加载功能。

第五步:Apollo中配置限流规则,具体可见第三步的截图中的样子。其中,key值的内容是下面的json

[
    {
        "resource": "/cloud/hi",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
1
2
3
4
5
6
7
8
9
10
11

可以看到上面配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:

resource:资源名,即限流规则的作用对象
limitApp:流控针对的调用来源,若为 default 则不区分调用来源
grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
count:限流阈值
strategy:调用关系限流策略
controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
clusterMode:是否为集群模式

在使用Apollo存储规则配置的时候与Nacos存储一样,对于Sentinel控制台这些数据是只读的,也就是说:

Sentinel控制台中修改规则:仅存在于服务的内存中,不会修改Apollo中的配置值,重启后恢复原来的值。
Nacos控制台中修改规则:服务的内存中规则会更新,Apollo中持久化规则也会更新,重启后依然保持。

# sentinel-dashboard中修改规则同步到Nacos

返回

# 代码实现

下面直接来看看如何实现的具体改造步骤,这里参考了Sentinel Dashboard源码中关于Nacos实现的测试用例。但是由于考虑到与Spring Cloud Alibaba的结合使用,略作修改。

第一步:修改pom.xml中的sentinel-datasource-nacos的依赖,将<scope>test</scope>注释掉,这样才能在主程序中使用。

<!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-nacos -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
<!--            <version>1.6.2</version>-->
<!--            <scope>test</scope>-->
        </dependency>
1
2
3
4
5
6
7

第二步:找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码:

<li ui-sref-active="active">
    <a ui-sref="dashboard.flowV1({app: entry.app})">
        <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则
    </a>
</li>
1
2
3
4
5

修改为:

<li ui-sref-active="active">
    <a ui-sref="dashboard.flow({app: entry.app})">
        <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则
    </a>
</li>
1
2
3
4
5

第三步:在com.alibaba.csp.sentinel.dashboard.rule包下新建一个nacos包,用来编写针对Nacos的扩展实现。

第四步:创建Nacos的配置类,具体代码如下:

@Configuration
public class NacosConfig {

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "localhost");
        return ConfigFactory.createConfigService(properties);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

如果用到了namespace隔离环境,可以在nacosConfigService方法中再加入配置,比如:properties.put(PropertyKeyConst.NAMESPACE, "130e71fa-97fe-467d-ad77-967456f2c16d");

第五步:实现Nacos的配置拉取。

@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    public static final String FLOW_DATA_ID_POSTFIX = "-sentinel";
    public static final String GROUP_ID = "DEFAULT_GROUP";

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + FLOW_DATA_ID_POSTFIX, GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

getRules方法中的appName参数是Sentinel中的服务名称
configService.getConfig方法是从Nacos中获取配置信息的具体操作。其中,DataIdGroupId分别对应客户端使用时候的对应配置。比如这里的例子对应了之前我们在《Sentinel使用Nacos存储规则》一文中的配置,具体如下:

spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel
1
2

注意:两边的DataId和GroupId必须对应上。

第六步:实现Nacos的配置推送。

@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    public static final String FLOW_DATA_ID_POSTFIX = "-sentinel";
    public static final String GROUP_ID = "DEFAULT_GROUP";

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + FLOW_DATA_ID_POSTFIX, GROUP_ID, converter.convert(rules));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这里的大部分内容与上一步中的实现一致。主要就是Nacos中存储配置的DataId和GroupId不要弄错

第七步:修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2DynamicRuleProviderDynamicRulePublisher注入的Bean,改为上面我们编写的针对Apollo的实现:

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
1
2
3
4
5
6

最后,读者可以使用本文改造后的sentinel-dashboard联合之前《Sentinel使用Nacos存储规则》一文的例子来验证本文内容。

# sentinel-dashboard中修改规则同步到Apollo

返回

# 代码实现Apollp

下面继续说说具体的代码实现,这里参考了Sentinel Dashboard源码中关于Apollo实现的测试用例。但是由于考虑到与Spring Cloud Alibaba的结合使用,略作修改。

第一步:修改pom.xml中的Apollo OpenAPi的依赖,将<scope>test</scope>注释掉,这样才能在主程序中使用。

<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-openapi</artifactId>
    <version>1.2.0</version>
    <!--<scope>test</scope>-->
</dependency>
1
2
3
4
5
6

第二步:找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码:

<li ui-sref-active="active">
    <a ui-sref="dashboard.flowV1({app: entry.app})">
        <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则
    </a>
</li>
1
2
3
4
5

修改为:

<li ui-sref-active="active">
    <a ui-sref="dashboard.flow({app: entry.app})">
        <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则
    </a>
</li>
1
2
3
4
5

第三步:在com.alibaba.csp.sentinel.dashboard.rule包下新建一个apollo包,用来编写针对Apollo的扩展实现。

第四步:创建Apollo的配置类,定义Apollo的portal访问地址以及第三方应用访问的授权Token(通过Apollo管理员账户登录,在“开放平台授权管理”功能中创建),具体代码如下:

@Configuration
public class ApolloConfig {

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ApolloOpenApiClient apolloOpenApiClient() {
        ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
            .withPortalUrl("https://apollo.xxx.com")  // TODO 根据实际情况修改
            .withToken("open api token") // TODO 根据实际情况修改
            .build();
        return client;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

第五步:实现Apollo的配置拉取实现。

@Component("flowRuleApolloProvider")
public class FlowRuleApolloProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private ApolloOpenApiClient apolloOpenApiClient;
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Value("${env:DEV}")
    private String env;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        // flowDataId对应
        String flowDataId = "sentinel.flowRules";
        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appName, env, "default", "application");
        String rules = openNamespaceDTO
            .getItems()
            .stream()
            .filter(p -> p.getKey().equals(flowDataId))
            .map(OpenItemDTO::getValue)
            .findFirst()
            .orElse("");

        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

getRules方法中的appName参数是Sentinel中的服务名称,这里直接通过这个名字获取Apollo配置是由于Apollo中的项目AppId与之一致,如果存在不一致的情况,则需要自己做转换。
这里注入了一个env属性,主要由于我们在使用Apollo的时候,通过启动参数来控制不同环境。所以这样就能在不同环境区分不同的限流配置了。
这里的flowDataId对应各个微服务应用中定义的spring.cloud.sentinel.datasource.ds.apollo.flowRulesKey配置,即:Apollo中使用了什么key来存储限流配置。
其他如Cluster、Namepsace都采用了默认值:default和application,这个读者有特殊需求可以做对应的修改。

第六步:实现Apollo的配置推送实现。

@Component("flowRuleApolloPublisher")
public class FlowRuleApolloPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private ApolloOpenApiClient apolloOpenApiClient;
    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Value("${env:DEV}")
    private String env;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        String flowDataId = "sentinel.flowRules";

        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }

        OpenItemDTO openItemDTO = new OpenItemDTO();
        openItemDTO.setKey(flowDataId);
        openItemDTO.setValue(converter.convert(rules));
        openItemDTO.setComment("modify by sentinel-dashboard");
        openItemDTO.setDataChangeCreatedBy("apollo");
        apolloOpenApiClient.createOrUpdateItem(app, env, "default", "application", openItemDTO);

        // Release configuration
        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
        namespaceReleaseDTO.setEmergencyPublish(true);
        namespaceReleaseDTO.setReleaseComment("release by sentinel-dashboard");
        namespaceReleaseDTO.setReleasedBy("apollo");
        namespaceReleaseDTO.setReleaseTitle("release by sentinel-dashboard");
        apolloOpenApiClient.publishNamespace(app, env, "default", "application", namespaceReleaseDTO);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

这里的大部分内容,如:env、flowDataId、app说明与上一步中的实现一致
openItemDTO.setDataChangeCreatedBy("apollo");namespaceReleaseDTO.setReleasedBy("apollo");这两句需要注意一下,必须设置存在并且有权限的用户,不然会更新失败。

第七步:修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2DynamicRuleProviderDynamicRulePublisher注入的Bean,改为上面我们编写的针对Apollo的实现:

@Autowired
@Qualifier("flowRuleApolloProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleApolloPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
1
2
3
4
5
6

# @SentinelResource注解使用详解

返回

属性 作用 是否必须
value 资源名称
entryType entry类型,标记流量的方向,取值IN/OUT,默认是OUT
blockHandler 处理BlockException的函数名称。函数要求:1. 必须是 public。2.返回类型与原方法一致。3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。
blockHandlerClass 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。
fallback 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。
fallbackClass【1.6】 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
defaultFallback【1.6】 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:1. 返回类型与原方法一致2. 方法参数列表为空,或者有一个 Throwable 类型的参数。3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
exceptionsToIgnore【1.6】 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
exceptionsToTrace 需要trace的异常 Throwable

我们仅依靠引入Spring Cloud AlibabaSentinel的整合封装spring-cloud-starter-alibaba-sentinel,就完成了对所有Spring MVC接口的限流控制。
然而,在实际应用过程中,我们可能需要限流的层面不仅限于接口。可能对于某个方法的调用限流,对于某个外部资源的调用限流等都希望做到控制。那么,这个时候我们就不得不手工定义需要限流的资源点,并配置相关的限流策略等内容了。

今天这篇我们就来一起学习一下,如何使用@SentinelResource注解灵活的定义控制资源以及如何配置控制策略。

# 自定义资源点

第一步:在应用主类中增加注解支持的配置:

@SpringBootApplication
public class TestApplication {

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

    // 注解支持的配置Bean
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

第二步:在需要通过Sentinel来控制流量的地方使用@SentinelResource注解,比如下面以控制Service逻辑层的某个方法为例:

@Slf4j
@Service
public class TestService {

    @SentinelResource(value = "doSomeThing")
    public void doSomeThing(String str) {
        log.info(str);
    }

}
1
2
3
4
5
6
7
8
9
10

到这里一个需要被保护的方法就定义完成了。下面我们分别说说,定义了资源点之后,我们如何实现不同的保护策略,包括:限流、降级等。

# 如何实现限流与熔断降级

在定义了资源点之后,我们就可以通过Dashboard来设置限流和降级策略来对资源点进行保护了。同时,也可以通过@SentinelResource来指定出现限流和降级时候的异常处理策略。下面,就来一起分别看看限流和降级都是如何实现的。

# 实现限流控制

第一步:在Web层调用这个被保护的方法:

@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/hello")
    public String hello() {
        testService.doSomeThing("hello " + new Date());
        return "didispace.com";
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13

可以看到,除了如之前入门实例中那样有/hello资源点之外,多了一个doSomeThing资源点。可以通过界面为这个资源点设置限流规则,比如将其QPS设置为2。由于/hello资源不设置限流规则,所以只要请求/hello接口,就可以直接模拟调用doSomeThing资源,来观察限流规则是否生效

# 实现限流的异常处理

默认情况下,Sentinel对控制资源的限流处理是直接抛出异常,也就是上一节中贴出的日志内容。在没有合理的业务承接或者前端对接情况下可以这样,但是正常情况为了更好的用户业务,都会实现一些被限流之后的特殊处理,我们不希望展示一个生硬的报错。那么只需要基于上面的例子做一些加工,比如:

@Slf4j
@Service
public class TestService {

    @SentinelResource(value = "doSomeThing", blockHandler = "exceptionHandler")
    public void doSomeThing(String str) {
        log.info(str);
    }

    // 限流与阻塞处理
    public void exceptionHandler(String str, BlockException ex) {
        log.error( "blockHandler:" + str, ex);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

主要做了两件事:

通过@SentinelResource注解的blockHandler属性制定具体的处理函数
实现处理函数,该函数的传参必须与资源点的传参一样,并且最后加上BlockException异常参数;同时,返回类型也必须一样

# 实现熔断降级

@SentinelResource注解除了可以用来做限流控制之外,还能实现与Hystrix类似的熔断降级策略。下面就来具体看看如何使用吧。

第一步:与限流控制一样,使用@SentinelResource注解标记资源点,比如:

@Slf4j
@Service
public class TestService {

    @SentinelResource(value = "doSomeThing2")
    public void doSomeThing2(String str) {
        log.info(str);
        throw new RuntimeException("发生异常");
    }

}
1
2
3
4
5
6
7
8
9
10
11

这里在TestService类中创建了一个新的方法,并使用@SentinelResource将该资源命名为doSomeThing2。该方法会抛出异常,以配合后续制定基于异常比例的降级策略(类似Hystrix)。Sentinel相比Hystrix更丰富,还有基于响应时间和异常数的降级策略。

第二步:在Web层调用这个被保护的方法:

@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/hello2")
    public String hello2() {
        testService.doSomeThing2("hello2 " + new Date());
        return "didispace.com";
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13

第三步:启动测试应用,启动Sentinel-Dashboard。发一个请求到/hello2接口上,使得Sentinel-Dashboard上可以看到名为doSomeThing2的资源点。然后点击”降级“按钮,为该资源设置降级规则。这里使用异常比例策略,比例设置为0.5(即:50%的异常率),时间窗口设置为2(秒)。

第四步:验证熔断降级,根据上面的降级策略配置,当doSomeThing2方法的调用QPS >= 5,如果异常率超过50%,那么后续2秒内的调用将直接出发熔断降级,默认情况会直接抛出DegradeException异常,比如:

# 熔断的降级处理

在Sentinel中定义熔断的降级处理方法非常简单,与Hystrix非常相似。只需要使用@SentinelResource注解的fallback属性来指定具体的方法名即可。这里也需要注意传参与返回必须一致。比如:

@Slf4j
@Service
public class TestService {

    // 熔断与降级处理
    @SentinelResource(value = "doSomeThing2", fallback = "fallbackHandler")
    public void doSomeThing2(String str) {
        log.info(str);
        throw new RuntimeException("发生异常");
    }

    public void fallbackHandler(String str) {
        log.error("fallbackHandler:" + str);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

完成上面的改造之后,重启应用,并设置doSomeThing2资源的熔断降级策略(使用异常百分比),然后频繁的请求/hello2接口。在QPS>=5之后,由于这个接口一直在抛出异常,所以一定会满足熔断降级条件,这时候就会执行fallbackHandler方法,不断的打印如下日志:

# 更多注解属性说明

关于@SentinelResource注解最主要的两个用法:限流控制熔断降级的具体使用案例介绍完了。另外,该注解还有一些其他更精细化的配置,比如忽略某些异常的配置、默认降级函数等等,具体可见如下说明:

value:资源名称,必需项(不能为空)
entryType:entry 类型,可选项(默认为 EntryType.OUT)
blockHandler / blockHandlerClass: blockHandler对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。

fallback 函数签名和位置要求:

  • 返回值类型必须与原函数返回值类型一致;
  • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
  • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。

defaultFallback 函数签名要求:

  • 返回值类型必须与原函数返回值类型一致;
  • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。 特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出。

# 部署收集

返回

配置相关
Q: 配置参数如何指定? A: 可通过 JVM -D 参数指定。除 project.name 之外,其余参数还可通过 properties 文件指定,路径为 ${home}/logs/csp/${project.name}.properties。详见 启动项配置文档。
Q: 可否自定义 properties 文件的位置或日志文件的位置? A: 1.3.0 版本开始支持配置日志文件的目录,可以参考 启动项配置文档;若希望自定义 properties 文件的位置或使用其它的加载方式,可以结合 Spring Cloud Alibaba Sentinel 使用。
Sentinel 核心功能相关
Q: Sentinel 可否用于生产环境? A: 当然可以!Sentinel 可用于生产环境,但若要在生产环境中使用 Sentinel 控制台则还需要进行一些改造,可以参考:在生产环境中使用 Sentinel 控制台。

Q: 想从 Hystrix 迁移到 Sentinel,有没有相关指南? A: 可以参考 Guideline: 从 Hystrix 迁移到 Sentinel。

Q: 我的服务是基于 Spring Boot 和 Dubbo 编写的,可以直接用 Sentinel 吗?

A: 当然啦,Sentinel 针对常用框架和库进行了适配,包括 Web Servlet、Dubbo、Spring Boot / Spring Cloud、gRPC 等,可以参见 主流框架的适配。

Q: Sentinel 的性能如何?只进行资源定义不配置规则,仅统计指标信息会有多少性能损耗?

A: 引入 Sentinel 带来的性能损耗非常小。只有在业务单机量级超过 25W QPS 的时候才会有一些显著的影响(10% 左右),单机 QPS 不太大的时候损耗几乎可以忽略不计。性能测试报告见 Benchmark。

Q: 是否需要在初始化时手工调用 InitExecutor.doInit() 方法进行相应的初始化?

A: 不需要,sentinel-core 的 Env 类通过 static 块调用了此方法进行初始化,在首次调用资源时即可触发。一些 demo 中手动调用此方法是为了不让进程退出或提前进行初始化。

Q: 规则生效后,除了抛异常之外还有没有其它的处理方式? A: 有以下几种情况:

默认资源被流控降级后会抛出 BlockException 异常的子类(比如限流会抛 FlowException 异常,降级会抛出降级异常 DegradeException)。您可以通过以下方法判断是否为流控降级异常:BlockException.isBlockException(Throwable t)。 对于 Sentinel 注解模式,可以在 @SentinelResource 注解上配置 blockHandler 函数(注意对应函数的签名需要符合要求),在被限流降级的时候进行相应的处理。 对于 Dubbo 服务,我们支持注册全局的 fallback 函数,可参考 Dubbo 适配文档。 Web Servlet Filter:默认情况下,当请求被限流时会返回默认的提示页面,提示信息为:Blocked by Sentinel (flow limiting)。您也可以通过 WebServletConfig.setBlockPage(blockPage) 方法设定自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL。同样也可以实现 UrlBlockHandler 接口并编写定制化的限流处理逻辑,然后将其注册至 WebCallbackManager 中。详情可参考 Web Servlet Filter 扩展文档。 Q: 怎么针对特定调用端限流?比如我想针对某个 IP 或者来源应用进行限流?规则里面 limitApp(流控应用)的作用? A: Sentinel 支持按来源限流,可以参考 基于调用关系的限流。注意 origin 数量不能太多,否则会导致内存暴涨,并且目前不支持模式匹配。

若 Web 应用使用了 Sentinel Web Filter,并希望对 HTTP 请求按照来源限流,则可以自己实现 RequestOriginParser 接口从 HTTP 请求中解析 origin 并注册至 WebCallbackManager 中。示例代码:

WebCallbackManager.setRequestOriginParser(ServletRequest::getRemoteAddr); 特别地,流控应用如果使用了 Sentinel Dubbo Adapter,同时 Dubbo 的消费者也引入了 Sentinel Dubbo Adapter(用于透传 dubboApplication 这个参数),则填 Dubbo 调用方的 application name 就好(注意是 Dubbo 里配置的应用名而不是 Sentinel 的应用名),消费端引入 adapter 就会自动透传,否则需要自己传来源应用名。

注意来源信息(origin)一般是在入口处传入的(如 HTTP 入口或 Dubbo 服务入口),因此在链路中间再通过 ContextUtil.enter(xxx, origin) 传入可能不会生效。

Q: 很多开发通过错误码来处理流程,而非通过异常。这种写法,导致 Sentinel 不能拦截到异常,无法触发降级。对于这种情况,有没有什么好的处理方法?

A: 实际上 Sentinel 是通过 Tracer.trace(e) 来统计业务异常的,因此可以收到错误码就调用此函数来统计业务异常。

Q: 入口资源和出口资源是什么意思?EntryType.IN 有什么作用? A: 入口流量指的是进入应用的流量(EntryType.IN),通常作为 provider。比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。而出口流量指的是从本资源调其它资源的流量(EntryType.OUT),通常作为 consumer。

部分规则的判断会区分流量类型,比如系统规则只对入口流量生效。

Q: 手动通过 SphU.entry(xxx) 进行埋点具有侵入性,有没有低侵入性的方案?

A: 可以利用 Sentinel 注解支持 或者 各种框架的适配。

Q: Sentinel 未来会考虑支持分布式链路调用跟踪吗?

A: Sentinel 的核心功能是流量控制和限流降级。分布式链路调用跟踪不是 Sentinel 关注的功能,可以参考其它分布式链路调用跟踪系统。

Q: Sentinel 支持集群限流吗?

A: 1.4.0 版本开始已经支持,可以参考 集群流控文档。

Q: Sentinel 支持 reactive 应用吗(比如用了 Spring WebFlux 或者 Reactor 之类的)?

A: Sentinel 1.5.0 开始提供 Reactor 的适配模块,可以参考 Sentinel Reactor Adapter 文档。

Q: Web 端的资源目前都是根据某个特定的 URL 限流,可不可以根据前缀匹配限流?

A: 对于 Sentinel Web Servlet Filter,可以借助 UrlCleaner 处理对应的 URL(如提取前缀的操作),这样对应的资源都会归到处理后的资源(如 /foo/1 和 /foo/2 都归到 /foo/* 资源里面)。UrlCleaner 实现 URL 前缀匹配只是个 trick,它会把对应的资源也给归一掉,直接在资源名粒度上实现模式匹配还是有很多顾虑的问题的。

Sentinel 集群限流相关 Q: 集群限流没有生效? A: 请参考以下步骤排查:

请确保接入端引入了 Sentinel 集群流控相关的依赖; 检查 ~/logs/csp/sentinel-record.log 查看 Token Server / Client 是否加载成功,是否成功启动;动态规则源是否注册成功,接入端规则和 Token Server 集群规则是否都接收成功; 在 Token Client 端检查 ~/logs/csp/sentinel-cluster-client.log 日志,看一下是否是 Token Server 未启动或者规则未接收到的问题; 若从开源 Sentinel 控制台推送集群规则,请务必确保按照 Sentinel 集群流控控制台文档 进行了相应改造,并且接入端按照 集群规则配置文档 配置了相关动态规则源; Q: 集群限流有时候会超过设定的总体阈值? A: 偶尔的、少量的超出阈值是正常的。Sentinel 是非严格限流,兼顾性能与准确性。

Q: 通过 Sentinel 控制台或 API 来分配管理 Token Server 时,返回错误:token client/server mode not available: no SPI found

A: 请确保接入端引入了 Sentinel 集群流控相关的依赖(嵌入模式两者都需要):

Token Client: sentinel-cluster-client-default Token Server: sentinel-cluster-server-default 更多信息可参见 集群流控文档。

Q: 集群流控规则中“单机均摊”阈值模式是什么意思?

A: 单机均摊模式下配置的阈值等同于单机能够承受的平均限额。Token Server 会根据客户端对应的 namespace(默认为 project.name 定义的应用名)下的连接数来计算总的阈值(比如独立模式下有 3 个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为 30)。单机均摊阈值仅用于计算总体阈值,不是说每台机器一定要控制在均摊阈值上。配置方式:若希望某个资源限制集群总量为 Q,服务实例为 N,则可以配置单机均摊阈值为 Q / N。

Q: 集群流控 Token Server 中的 namespace set 有什么用处?

A: namespace set 即 Token Server 服务的作用域(命名空间集合),用于指定该 Token Server 可以为哪些应用提供集群流控服务,一般设置为接入端应用名的集合。Token Client 在连接到 token server 后会上报自己的命名空间(默认为 project.name 配置的应用名),token server 会根据上报的命名空间名称统计连接数。

嵌入模式下一般仅服务自身,默认嵌入模式的 namespace set 为该应用本身 所有 Token Server 都自带一个默认的命名空间 default,一般情况下不会用到 Sentinel Transport/Dashboard 相关 Q:Sentinel Transport 同一台机器起相同的端口不报错?如果几个应用都没配 csp.sentinel.api.port 会出错吗?

A: 对于 sentinel-transport-simple-http,默认 8719 端口被占用以后会自动选择下一个端口,直到有可用的端口为止。选择端口时间会比较长,本地如果起多个应用建议自己通过 csp.sentinel.api.port 设置端口。

Q: 为什么 Sentinel Transport 模块里自己用原生 Socket 或 Netty 自己实现的 HTTP Server,为什么不用 Web 容器(如 Tomcat)?

A: 使用 Web 容器作为一个组件来说太过于重量级了,因此我们自己实现了简单的 sentinel-transport-simple-http 和 sentinel-transport-netty-http 模块,两个模块的功能是相同的,一般用 sentinel-transport-simple-http 即可,而 sentinel-transport-netty-http 是为了后续的可扩展性和兼容性而设计的。

Q: 目前 Sentinel Transport 里面可否自己扩展添加 command?

A: 可以的,用户可以自行实现 CommandHandler 接口,并在实现类上加上 @CommandMapping 注解(代表 command name)。接着在资源目录下的 META-INF/services 目录下添加 com.alibaba.csp.sentinel.command.CommandHandler 文件(如果没有的话),在文件里加上自己的实现类的全名即可。比如:

package io.test;

@CommandMapping(name = "test")
public class TestCommandHandler implements CommandHandler<String> {

    @Override
    public CommandResponse<String> handle(CommandRequest request) {
        return CommandResponse.ofSuccess("666");
    }
}
1
2
3
4
5
6
7
8
9
10

在自己项目目录下的 resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler 文件中添加:

io.test.TestCommandHandler 这样初始化 Sentinel Transport Server 的时候,对应的 command 就会自动注册,可以通过 curl host:ip/test 来验证是否成功。

Q: 想要在生产环境中使用 Sentinel 控制台,还需要进行哪些改造? A: 在生产环境中使用 Sentinel 控制台需要考虑下面的问题:

监控数据持久化 规则推送支持应用维度,并且整合配置中心以便可以直接推送至远程配置中心 权限控制(1.6.0 引入了基本的登录功能) 详细指南请参考此 blog: 在生产环境中使用 Sentinel 控制台。

Q: 已经引入了适配模块(比如 sentinel-dubbo-adapter),然而在 Sentinel 控制台上没有找到我的应用? A: 请确保对应的依赖和参数配置正确。接入控制台需要上报的客户端依赖,如 sentinel-transport-simple-http,不引入相关依赖则无法将相关信息上报到控制台。参数配置详情请参考 Sentinel 控制台文档。

详细的排查步骤请见下面。

Q: Sentinel 控制台没有显示我的应用,或者没有监控展示,如何排查? A: Sentinel Dashboard 是一个单独启动的控制台,应用若想上报监控信息给 Sentinel 控制台,需要引入 Sentinel 上报信息的 transport 模块。它们各自有自己的通信端口,其中控制台的端口可通过启动参数 -Dserver.port=xxxx 进行配置,而 Sentinel transport 模块的端口可以通过启动参数 -Dcsp.sentinel.api.port 进行配置(默认是 8719)。两者都启动之后,Sentinel 客户端在首次访问资源时会初始化并给控制台发送心跳,之后控制台会通过接入端提供的端口对 Sentinel 进行访问来拉取相关信息。基于此,接入控制台需要的步骤如下:

接入 Sentinel 的应用应该引入 Sentinel 客户端通信的基础 jar 包,如 sentinel-transport-simple-http 客户端启动时添加相应的 JVM 参数,包括: 应用名称:-Dproject.name=xxxx(会显示在控制台) 控制台地址:-Dcsp.sentinel.dashboard.server=ip:port 本地的 Sentinel 客户端端口:-Dcsp.sentinel.api.port=xxxx(可选,默认是 8719,有冲突会尝试向后探测) 详细参数配置详情请参考 Sentinel 控制台文档。 启动控制台,运行应用,当首次访问对应的资源后 等待一段时间即可在控制台上看到对应的应用以及相应的监控信息。可以通过 curl http://ip:port/tree 命令查看调用链,正常情况下会显示出已访问资源的调用链。 注意:Sentinel 会在客户端首次调用时候进行初始化,开始向控制台发送心跳包。因此需要确保客户端有访问量,才能在控制台上看到监控数据。另外,还是期待大家养成看日志的好习惯,详见 日志文档。

控制台推送规则的日志默认位于控制台机器的 ${user.home}/logs/csp/sentinel-dashboard.log 接入端接收规则日志默认位于接入端机器的 ${user.home}/logs/csp/sentinel-record.log.xxx 接入端 transport server 日志默认位于接入端机器的 ${user.home}/logs/csp/command-center.log.xxx 常用排查问题列表:

确认 Dashboard 已经正常启动并可以正常访问 检查网络配置、防火墙配置,确认控制台与接入端服务双向的网络是否连通 若是 Spring Boot / Dubbo 等应用,请务必检查是否引入了整合依赖(如 Dubbo 对应 sentinel-dubbo-adapter)并进行了相应配置 检查接入端的启动参数配置是否正确(如控制台地址是否配置正确) 通过 ~/logs/csp/sentinel-record.log 日志排查客户端发送心跳包是否正常,是否正常上报给 Dashboard 确保 fastjson 的版本和 Sentinel 的依赖版本保持一致 通过 curl IP:port/getRules?type=flow 等命令查看结果,查看规则是否推送成功 发送到客户端的规则格式是否正确 若控制台已正常显示应用,但看不到监控、请求链路,机器列表页面显示的 IP 不正确,可以参考 此 issue。

  • Q: 客户端和控制台不在一台机器上,客户端成功接入控制台后,控制台无法显示实时的监控数据?但簇点链路页面有实时请求数据(不为 0)?

A: 请确保 Sentinel 控制台所在的机器时间与自己应用的机器时间保持一致(通过 NTP 等同步)。Sentinel 控制台是通过控制台所在机器的时间戳拉取监控数据的,因此时间不一致会导致拉不到实时的监控数据。

Q: 控制台正确的规则推送后看不到? 客户端成功接入控制台后,控制台配置规则准确无误,但是客户端收不到规则或者报错?比如配置的资源名正确但 sentinel-record.log 日志里面却报 resourceName 为空的错误?

A: 排查客户端是否使用了低版本的 fastjson,低版本的 fastjson 可能会有此问题,建议使用和 Sentinel 相关组件一致版本的 fastjson。

Q: Sentinel 监控数据能保留多久?控制台聚合的统计数据可否进行持久化?

A: Sentinel 对监控数据的做法是定时落盘在客户端,然后 Sentinel 提供接口去拉取日志文件。所以 Sentinel 在监控数据上理论上是最少存储 1 天以上的数据;然而作为控制台展示,则仅在内存中聚合 5 分钟以内的统计数据,不进行持久化。

我们鼓励大家对 Dashboard 进行改造实现指标信息的持久化,并从其它的存储中(如 RDBMS、时序数据库等)拉取的监控信息,包括实时的和历史的数据。

Q: Sentinel 控制台有登录或者细粒度的权限控制功能吗?

A: 目前 Sentinel 控制台提供权限控制接口(AuthService),但具体的权限控制实现需要自己去定制。

Q: Sentinel Dashboard 中簇点链路页面里面资源名称为什么会有重复的?

A: 最顶层的是 Context 名,用于区分不同调用链路(入口)。

Q: 应用已经退出了,Sentinel 控制台的应用列表里还有显示?

A: 若应用已退出,过 1 分钟没有收到心跳,控制台就会标记对应机器为失联状态,但不会将对应应用从列表中移除。

Q: 为什么说 Sentinel 控制台集群资源汇总仅支持 500 台以下的应用集群?

A: 因为 Sentinel 控制台仅作为示范,其监控聚合能力非常有限,但是应用端是没有限制的。若希望支持更多的机器,可以使用动态规则数据源并改造控制台;同时改造实现监控数据持久化。

Q: Sentinel 控制台规则配置里面“流控应用”是什么?

A: 对应规则中的 limitApp,即请求来源,通过入口处 ContextUtil.enter(contextName, origin) 中的 origin 传入。参见上面的“按调用方进行限流”。

规则存储与动态规则数据源(DataSource) Q: 动态规则数据源分为哪几种?各自的使用场景?

A: 数据源分为只读的数据源和可写入的数据源。

可写入的数据源一般对应 pull 模式的数据源(如本地文件、RDBMS),从控制台推送规则时可先经 Sentinel 客户端更新到内存中,然后经注册的 WritableDataSource 写入到本地。 只读的数据源一般对应 push 模式的数据源(如配置中心等)。对于配置中心,写入的操作不应由数据源进行,数据源仅负责获取配置中心推送的配置并更新到本地。从控制台推送规则的次序应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。注意由于不同的生产环境可能使用不同的数据源,从 Sentinel 控制台推送至配置中心的实现需要用户自行改造。 Q: 在控制台配置的规则存在哪里?为什么应用重启规则就消失了? A: 在控制台配置的规则是存在内存里面的,没有进行持久化,因此应用重启后规则就消失了。

可以参考 动态规则文档 持久化存储规则。

Q: 我在客户端处配置了动态规则数据源(如基于 ZooKeeper / Nacos / Apollo 的数据源),然后在控制台处向客户端推送了规则,但是规则并没有写入到对应的数据源中?

A: push 模式的数据源(如配置中心)都是只读的。对于配置中心类型的数据源(如 ZooKeeper),我们推荐在推送规则时直接推送至配置中心,然后配置中心再自动推送至所有的客户端(即 Dashboard -> Config Center -> Sentinel DataSource -> Sentinel),目前需要自行改造控制台。可以参见:在生产环境中使用 Sentinel 控制台。

对于 pull 模式的数据源(如本地文件),则可以向 transport-common 模块的 WritableDataSourceRegistry 注册写入数据源,在推送规则时会一并推送至本地数据源中。

其它 Q: 引了 Spring Cloud Alibaba 的相关依赖以后,再引高版本的 Sentinel 依赖会报错? A: spring-cloud-alibaba-dependencies 里面会限定 Sentinel 的依赖版本,导致不同 Sentinel 模块之间的版本不一致,可能会有兼容问题。可以去掉 dependencyManagement 里面的 spring-cloud-alibaba-dependencies,或者在自己项目的 pom 文件中强制统一相关依赖的版本:

<dependencyManagement>
    <dependencies>
        <!-- 保持着几个依赖版本统一 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-common</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-extension</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-parameter-flow-control</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-api-gateway-adapter-common</artifactId>
            <version>1.6.2</version>
        </dependency>
    </dependencies>
</dependencyManagement>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Q: 编译打包的时候 Maven 报错?

A: 请参考以下解决方案:

如果是很诡异的错误,请将 Maven 版本升级至较新版本(3.5.x)再进行构建,看看是否有问题。 Q: 编译时或者 CI 跑测试用例会出现偶尔无法通过的情况?

A:有少数的基于 sleep 的测试用例可能会有不稳定的情况,这时候只需要重试几次即可成功。

# 配置管理

返回顶部

Nacos除了实现了服务的注册发现之外,还将配置中心功能整合在了一起。通过Nacos的配置管理功能,我们可以将整个架构体系内的所有配置都集中在Nacos中存储。
Nacos 提供了动态配置服务,能让我们可以实时进行服务应用的配置变更,让配置管理变得更加高效和快捷。它基于 key/value 方式存储应用配置和其他元数据信息,为分布式系统中的外部化配置提供服务器端和客户端支持。

  • 分离的多环境配置,可以更灵活的管理权限,安全性更高
  • 应用程序的打包更为纯粹,以实现一次打包,多处运行的特点

Nacos的配置管理模型与淘宝开源的配置中心Diamond类似,基础层面都通过DataIdGroup来定位配置内容,除此之外还增加了很多其他的管理功能。

首先了解下 Nacos 在配置管理模块上的几个重要概念,能帮助我们更好的理解和正确的使用 Nacos 进行配置管理。

  • 命名空间(Namespace)

用于进行租户粒度的配置隔离,可用于对不同环境配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

  • 配置项 (Configuration Item)

一个具体的可配置的参数与其值,通常以 param-key=param-value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。

  • 配置集 (Configuration Set)

一组相关或者不相关的配置项的集合。一个配置文件通常就是一个配置集,它可能包含了数据源、线程池、日志级别等配置项。

  • 配置集 ID(Data ID)

某个配置集的标识 ID,用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集。官方推荐采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则定义 Data ID 来保证全局唯一性

  • 配置分组(Group) 对配置集进行分组,用于区分 Data ID 相同的配置集。默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 消息队列 Topic 配置等

# 快速入手

返回顶部

springCloud方式Spring方式springboot方式

多环境配置管理数据持久化配置监听命名空间

Sentinel降级熔断

集群部署

p

# springCloud方式

返回

pom文件

<modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.clm</groupId>
        <artifactId>ht-nacos</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>nacos-config</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-config</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-alibaba-nacos-config -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
<!--            <version>0.9.0.RELEASE</version>-->
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-spring-context -->
<!--        <dependency>-->
<!--            <groupId>com.alibaba.nacos</groupId>-->
<!--            <artifactId>nacos-spring-context</artifactId>-->
<!--            <version>0.2.3-RC1</version>-->
<!--        </dependency>-->

    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

也可以换成以下,其实一样

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<!--            <artifactId>spring-cloud-alibaba-nacos-config</artifactId>-->
<!--            <version>0.9.0.RELEASE</version>-->
        </dependency>
1
2
3
4
5
6

调用类

/**
 * 配置接收
 * @author huting
 */
@Slf4j
@RequestMapping("/first")
@RestController
@RefreshScope
public class FirstConfigController {
    @Value("${ht.nacos.config.title:}")
    //@Value("${ht.nacos.config.title}")
    private String title;

    @GetMapping("/hi")
    public String sayHi(){
        return title;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

两种"${ht.nacos.config.title:}","${ht.nacos.config.title}"都可以
@RefreshScope,主要用来让这个类下的配置内容支持动态刷新,也就是当我们的应用启动之后,修改了Nacos中的配置内容之后,这里也会马上生效。

新增配置文件bootstrap.properties
配置 Nacos server 的地址和应用名如下,在 Nacos Spring Cloud 中,dataId 默认为 spring.application.name 加上 properties 文件后缀,所以,为了能正确读取配置,我们需要将 Nacos 上配置集 ID 为 com.one.learn.nacos.config 调整为 com.one.learn.nacos.config.properties

为什么用bootstrap.properties
这里使用 bootstrap.properties 作为配置 Nacos 地方,是因为 Spring Boot 配置文件的加载顺序,依次为 bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml ,在 application 层级上配置 Nacos 时无法生效。
注意
注意:当使用域名的方式来访问 Nacos 时,spring.cloud.nacos.config.server-addr 配置的方式必须为 域名:port。 即使使用域名,端口不能省略。例如 Nacos 的域名为 abc.com.nacos,监听的端口为 80,则 spring.cloud.nacos.config.server-addr=abc.com.nacos:80

# Spring方式

返回

pom修改

<dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-spring-context</artifactId>
            <version>0.2.3-RC1</version>
        </dependency>
1
2
3
4
5

新增配置类

@Configuration
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "localhost:8848"))
@NacosPropertySource(dataId = "com.clm.nacos.config.dev.properties",autoRefreshed = true)
public class NacosConfiguration {
}
1
2
3
4
5

dataId为全。
通过 Nacos 的 @NacosValue 注解将配置项与属性进行绑定。

/**
 * @author huting
 */
@RequestMapping("/spring")
@RestController
public class FirstSpringConfigController {
    @NacosValue(value = "${ht.nacos.config.title}",autoRefreshed = true)
    private String title;

    @GetMapping("/hi")
    public String sayHi(){
        return "hi,"+title;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

通过注解@NacosValue
启动程序后访问:http://192.168.0.66:8080/spring/hi,即可拿到配置项的值。且会动态更新。

# springboot方式

返回

pom文件修改

<dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-starter</artifactId>
            <version>0.2.1</version>
        </dependency>
1
2
3
4
5

application.properties 中配置 Nacos Server 的地址:

nacos.config.server-addr=localhost:8848
1

调整启动类

@SpringBootApplication
@NacosPropertySource(dataId = "com.clm.nacos.config.dev.properties",autoRefreshed = true)
public class NacosConfigApplication {

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

}
1
2
3
4
5
6
7
8
9

后续步骤同spring方式

# 多环境配置管理

通过group实现多环境

返回

上面实现了Nacos基本的配置读取和更新后,接下来我们看下 Nacos 如何进行多环境配置的管理需要注意的是此特性仅针对于 Spring Cloud 应用

假设我们程序有两个环境:测试,生产环境需要管理配置,在 Spring Boot 程序中,默认的配置文件为 application.properties,传统的方式是,利用 Spring Profile 特性,在项目里存放多个环境对应的配置文件,文件格式为 application-${env}-properties,并且需要用 spring.profile.active 指定启动时应用哪个环境的配置。

Nacos Config 主要通过 dataIdgroup 来唯一确定一条配置,在 Nacos Spring Cloud 中,dataId 的完整格式如下:

${prefix}-${spring.profile.active}.${file-extension}
1

prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。

spring.profile.active 即为当前环境对应的 profile,

注意:当 spring.profile.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 {prefix}.{file-extension}

file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 propertiesyaml 类型。

group 默认为 DEFAULT_GROUP,可以通过 spring.cloud.nacos.config.group 自定义指定名称。

在采用默认值的应用要加载的配置规则就是:Data ID=${spring.application.name}.properties,Group=DEFAULT_GROUP。

基于上面的配置规则描述,我们可以先在 Nacos 控制台上新建两个不同环境的配置集,如下

p

server.port=8031
title=nacos-config-dev

server.port=8038
title=nacos-config-prod
1
2
3
4
5

修改程序的bootstrap.properties

spring.cloud.nacos.config.server-addr=localhost:8848
spring.application.name=ht-nacos-config
spring.cloud.nacos.config.file-extension=properties
spring.profiles.active=prod
1
2
3
4

都是修改这个顶级的配置文件,才可生效,如果是配置在application.properties中,则会报错,NacosException

2019-06-29 16:22:26.677  INFO 12284 --- [-localhost_8848] o.s.c.a.n.c.NacosPropertySourceBuilder   : Loading nacos data, dataId: 'ht-nacos-config-dev.properties', group: 'DEFAULT_GROUP'
1

启动成功,多环境配置成功

# 通过group实现多环境

spring.cloud.nacos.config.group=DEV_GROUP

# 总结

  • 第一种:通过Data ID与profile实现。

优点:这种方式与Spring Cloud Config的实现非常像,用过Spring Cloud Config的用户,可以毫无违和感的过渡过来,由于命名规则类似,所以要从Spring Cloud Config中做迁移也非常简单。
缺点:这种方式在项目与环境多的时候,配置内容就会显得非常混乱。配置列表中会看到各种不同应用,不同环境的配置交织在一起,非常不利于管理。
建议:项目不多时使用,或者可以结合Group对项目根据业务或者组织架构做一些拆分规划。

  • 第二种:通过Group实现。

优点:通过Group按环境讲各个应用的配置隔离开。可以非常方便的利用Data ID和Group的搜索功能,分别从应用纬度和环境纬度来查看配置。
缺点:由于会占用Group纬度,所以需要对Group的使用做好规划,毕竟与业务上的一些配置分组起冲突等问题。
建议:这种方式虽然结构上比上一种更好一些,但是依然可能会有一些混乱,主要是在Group的管理上要做好规划和控制。

  • 第三种:通过Namespace实现。

优点:官方建议的方式,通过Namespace来区分不同的环境,释放了Group的自由度,这样可以让Group的使用专注于做业务层面的分组管理。同时,Nacos控制页面上对于Namespace也做了分组展示,不需要搜索,就可以隔离开不同的环境配置,非常易用。
缺点:没有啥缺点,可能就是多引入一个概念,需要用户去理解吧。 建议:直接用这种方式长远上来说会比较省心。虽然可能对小团队而言,项目不多,第一第二方式也够了,但是万一后面做大了呢?

注意:不论用哪一种方式实现。对于指定环境的配置(spring.profiles.active=DEV、spring.cloud.nacos.config.group=DEV_GROUP、spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a),都不要配置在应用的bootstrap.properties中。而是在发布脚本的启动命令中,用-Dspring.profiles.active=DEV的方式来动态指定,会更加灵活!

# 共享配置

有时候我们会对应用的配置根据具体作用做一些拆分,存储在不同的配置文件中,除了归类不同的配置之外,也可以便于共享配置给不同的应用。对于这样的需求,Nacos也可以很好的支持,下面就来具体介绍一下,当使用Nacos时,我们如何加载多个配置,以及如何共享配置。

#### 加载多个配置

通过之前的学习,我们已经知道Spring应用对Nacos中配置内容的对应关系是通过下面三个参数控制的:

spring.cloud.nacos.config.prefix
spring.cloud.nacos.config.file-extension
spring.cloud.nacos.config.group
1
2
3

默认情况下,会加载Data ID=${spring.application.name}.properties,Group=DEFAULT_GROUP的配置。

假设现在有这样的一个需求:
我们想要对所有应用的Actuator模块以及日志输出做统一的配置管理。所以,我们希望可以将Actuator模块的配置放在独立的配置文件actuator.properties文件中,而对于日志输出的配置放在独立的配置文件log.properties文件中。通过拆分这两类配置内容,希望可以做到配置的共享加载与统一管理。

第一步:在Nacos中创建Data ID=actuator.propertiesGroup=DEFAULT_GROUPData ID=log.propertiesGroup=DEFAULT_GROUP的配置内容。

第二步:在Spring Cloud应用中通过使用spring.cloud.nacos.config.ext-config参数来配置要加载的这两个配置内容,比如:

spring.cloud.nacos.config.ext-config[0].data-id=actuator.properties
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[1].data-id=log.properties
spring.cloud.nacos.config.ext-config[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[1].refresh=true
1
2
3
4
5
6

可以看到,spring.cloud.nacos.config.ext-config配置是一个数组List类型。
每个配置中包含三个参数:data-id、group,refresh;前两个不做赘述,与Nacos中创建的配置相互对应,refresh参数控制这个配置文件中的内容时候支持自动刷新,默认情况下,只有默认加载的配置才会自动刷新,对于这些扩展的配置加载内容需要配置该设置时候才会实现自动刷新。

# 共享配置2

通过上面加载多个配置的实现,实际上我们已经可以实现不同应用共享配置了。但是Nacos中还提供了另外一个便捷的配置方式,比如下面的设置与上面使用的配置内容是等价的:

spring.cloud.nacos.config.shared-dataids=actuator.properties,log.properties
spring.cloud.nacos.config.refreshable-dataids=actuator.properties,log.properties
1
2
  • spring.cloud.nacos.config.shared-dataids参数用来配置多个共享配置的Data Id,多个的时候用用逗号分隔
  • spring.cloud.nacos.config.refreshable-dataids参数用来定义哪些共享配置的Data Id在配置变化时,应用中可以动态刷新,多个Data Id之间用逗号隔开。如果没有明确配置,默认情况下所有共享配置都不支持动态刷新

# 配置加载的优先级

当我们加载多个配置的时候,如果存在相同的key时,我们需要深入了解配置加载的优先级关系。

在使用Nacos配置的时候,主要有以下三类配置:

  • A: 通过spring.cloud.nacos.config.shared-dataids定义的共享配置
  • B: 通过spring.cloud.nacos.config.ext-config[n]定义的加载配置
  • C: 通过内部规则(spring.cloud.nacos.config.prefix、spring.cloud.nacos.config.file-extension、spring.cloud.nacos.config.group这几个参数)拼接出来的配置
A < B < C
1

# 数据持久化

返回

Nacos采用了集中式存储的方式来支持集群化部署,目前只要支持MySQL的存储。

nacos-mysql.sql
1

# 配置监听

返回

除了主动检验配置生效之外,我们可以通过添加监听器的方式来监听配置的变化,实现很简单,使用 @NacosConfigListener 即可,一旦指定的配置集 ID 对应配置发生了变化,监听器就会受到回调,将所有的配置信息以字符串形式返回。

# 命名空间

返回

Nacos 里 Namespace 作为租户颗粒度细分而存在,主要为了解决多环境以及多租户数据(配置和服务)隔离的问题。

如果只是一个租户(用户),不同的 namespce 可以对应不同的环境,以此实现环境配置的隔离,效果就跟上节内容:Nacos 多环境配置管理 相似。

如果存在多个租户,为每个租户分配不同的 namespace,这样每个租户(用户)的配置数据以及注册的服务数据都会归属到自己的 namespace 下,就可以实现多租户配置数据隔离作用。

注意:Nacos 目前还没实现账号权限的分配和隔离,无法让各租户自己管理自己的配置,这一块功能仍在规划中。

简单介绍之后,再来看下 namespace 相关的最佳实践: 如何来获取 namespace 的值, namespace 参数初始化方式。

# 创建命名空间

在 nacos 的控制台左边功能侧看到有一个 命名空间 的功能,点击就可以看到 新建命名空间 的按钮,那么这个时候就可以创建自己的命名空间了。创建成功之后,会生成一个命名空间ID,主要是用来避免命名空间名称有可能会出现重名的情况。因此当您在应用中需要配置指定的 namespace 时,填入的是命名空间ID。

# 关联命令空间

在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:

spring.cloud.nacos.config.namespace=a447cbd0-c37d-4cb1-bacf-eb6e6622999f
1

该配置必须放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace 的值是 namespace 对应的 id。

其余处理类似,namespace仅作租户区分

# Sentinel降级熔断

返回

Sentinel的官方标题是:分布式系统的流量防卫兵。从名字上来看,很容易就能猜到它是用来作服务稳定性保障的。对于服务稳定性保障组件,如果熟悉Spring Cloud的用户,第一反应应该就是Hystrix。但是比较可惜的是Netflix已经宣布对Hystrix停止更新。那么,在未来我们还有什么更好的选择呢?除了Spring Cloud官方推荐的resilience4j之外,目前Spring Cloud Alibaba下整合的Sentinel也是用户可以重点考察和选型的目标。

Sentinel的功能和细节比较多,一篇内容很难介绍完整。所以下面我会分多篇来一一介绍Sentinel的重要功能。本文就先从限流入手,说说如何把Sentinel整合到Spring Cloud应用中,以及如何使用Sentinel Dashboard来配置限流规则。通过这个简单的例子,先将这一套基础配置搭建起来。

# 使用Sentinel实现接口限流

Sentinel的使用分为两部分:

  • sentinel-dashboard:与hystrix-dashboard类似,但是它更为强大一些。除了与hystrix-dashboard一样提供实时监控之外,还提供了流控规则、熔断规则的在线维护等功能。
  • 客户端整合:每个微服务客户端都需要整合sentinel的客户端封装与配置,才能将监控信息上报给dashboard展示以及实时的更改限流或熔断规则等。

下面我们就分两部分来看看,如何使用Sentienl来实现接口限流。
0.9.0.RELEASE使用1.5.2版

# 集群部署

返回

# 集群配置

在Nacos的conf目录下有一个cluster.conf.example,可以直接把example扩展名去掉来使用,也可以单独创建一个cluster.conf文件,然后打开将后续要部署的Nacos实例地址配置在这里。

本文以在本地不同端点启动3个Nacos服务端为例,可以如下配置:

127.0.0.1:8841
127.0.0.1:8842
127.0.0.1:8843
1
2
3

注意:这里的例子仅用于本地学习测试使用,实际生产环境必须部署在不同的节点上,才能起到高可用的效果。另外,Nacos的集群需要3个或3个以上的节点,并且确保这三个节点之间是可以互相访问的。

# 启动实例

在完成了上面的配置之后,我们就可以开始在各个节点上启动Nacos实例,以组建Nacos集群来使用了。

由于本文中我们测试学习采用了本地启动多实例的情况,与真正生产部署会有一些差异,所以下面分两种情况说一下,如何启动各个Nacos实例

本地测试

本文中,在集群配置的时候,我们设定了3个Nacos的实例都在本地,只是以不同的端口区分,所以我们在启动Nacos的时候,需要修改不同的端口号。

下面介绍一种方法来方便地启动Nacos的三个本地实例,我们可以将bin目录下的startup.sh脚本复制三份,分别用来启动三个不同端口的Nacos实例,为了可以方便区分不同实例的启动脚本,我们可以把端口号加入到脚本的命名中,比如:

startup-8841.sh
startup-8842.sh
startup-8843.sh
1
2
3

新增如下代码:

set "JAVA_OPT=%JAVA_OPT% -Dserver.port=8841"
1

# 生产环境

在实际生产环境部署的时候,由于每个实例分布在不同的节点上,我们可以直接使用默认的启动脚本(除非要调整一些JVM参数等才需要修改)。只需要在各个节点的Nacos的bin目录下执行sh startup.sh命令即可

# Proxy配置

在Nacos的集群启动完毕之后,根据架构图所示,我们还需要提供一个统一的入口给我们用来维护以及给Spring Coud应用访问。简单地说,就是我们需要为上面启动的的三个Nacos实例做一个可以为它们实现负载均衡的访问点。这个实现的方式非常多,这里就举个用Nginx来实现的简单例子吧。

在Nginx配置文件的http段中,我们可以加入下面的配置内容:

upstream nacosserver{
        server 127.0.0.1:8848;
        server 127.0.0.1:8841;
        server 127.0.0.1:8842;
    }

location /nacos/{
            proxy_pass http://nacosserver/nacos/;
        }
1
2
3
4
5
6
7
8
9

# 部署问题汇总

  • 问题一:Ubuntu下启动Nacos报错

使用命令sh startup.sh -m standalone启动报错:

./startup.sh: 78: ./startup.sh: [[: not found
./startup.sh: 88: ./startup.sh: [[: not found
解决方法
改用命令bash -f ./startup.sh -m standalone启动

  • 问题二:Failed to create database ‘/data/soft/nacos/data/derby-data’

使用命令sh startup.sh -m standalone启动的时候,出现如下报错:

解决方法
删除报错信息中的’/data/nacos/data/derby-data’目录,重新启动。