SpringCloud Alibaba

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6


前言

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。



诞生:2018.10.31,Spring Cloud Alibaba 正式入驻了 Spring Cloud 官方孵化器,并在 Maven 中央库发布了第一个版本。

官网:​​https://spring.io/projects/spring-cloud-alibaba#overview​​​​https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html​​ 中文:​​https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md​

能干什么

  • 服务限流降级:默认支持 WebServlet、WebFlux、OpenFeign、RestTemplate、Spring Cloud Gateway、Zuul、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流 - 降级规则,还支持查看限流降级 Metrics 监控。
  • 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  • 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
  • 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

组件

  • Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
  • Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
  • Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
  • Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
  • Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

​更多组件…​

依赖:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Nacos 服务注册和配置中心

Nacos是什么?

  • 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • Nacos: Dynamic Naming and Configuration Service
  • Nacos就是注册中心 + 配置中心的组合。 Nacos = Eureka+Config +Bus

各注册中心比较

SpringCloud Alibaba_Cloud


SpringCloud Alibaba_spring cloud_02

Nacos 支持AP和CP模式的切换

C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。

何时选择使用何种模式?
一般来说,
如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

curl -X PUT ‘$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP’

SpringCloud Alibaba_Cloud_03

官网文档:​​https://nacos.io/zh-cn/index.html​

​https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery​

使用

环境:Java8 + Maven
下载地址:​​​https://github.com/alibaba/nacos/releases​

解压安装包,直接运行bin目录下的startup.cmd

命令运行成功后直接访问​​http://localhost:8848/nacos​​(8848成功人的标配)

默认账号密码都是nacos

SpringCloud Alibaba_spring cloud_04

1. 新建生产者Module

cloudalibaba-provider-payment9001

父pom

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

本模块pom

<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<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>

yml

server:
port: 9001

spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址

management:
endpoints:
web:
exposure:
include: '*'

主启动类

@EnableDiscoveryClient // 这里就没有什么EurekaServer了,注册中心NacosServer就是上边下载的客户端
@SpringBootApplication
public class PaymentMain9001
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class, args);
}
}

业务类

@RestController
public class PaymentController
{
@Value("${server.port}")
private String serverPort;

@GetMapping(value = "/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id)
{
return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
}
}

启动9001,访问​​http://localhost:9001/payment/nacos/1​​测试

nacos控制台也ok

SpringCloud Alibaba_spring cloud_05

SpringCloud Alibaba_后端_06

2. Nacos的负载均衡

复制上边9001的Module,新建端口为9002,启动9002

SpringCloud Alibaba_spring_07


测试环境不想做重复复制的话,可以根据9001的服务,添加一个9002的虚拟端口映射

SpringCloud Alibaba_Cloud_08


SpringCloud Alibaba_Cloud_09


在Run Dashboard找到9002的服务,右击启动,添加后在Configured下边

SpringCloud Alibaba_spring_10


2. 基于Nacos的服务消费者(负载均衡)Nacos继承了Ribbon,也就是说可以用RestTemplate去调用服务

SpringCloud Alibaba_spring_11

新建cloudalibaba-consumer-nacos-order83

pom

<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<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>

yml

server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider

主启动

@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain83
{
public static void main(String[] args)
{
SpringApplication.run(OrderNacosMain83.class,args);
}
}

config

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

业务类

@RestController
public class OrderNacosController
{
@Resource
private RestTemplate restTemplate;

@Value("${service-url.nacos-user-service}")
private String serverURL;

@GetMapping("/consumer/payment/nacos/{id}")
public String paymentInfo(@PathVariable("id") Long id)
{
return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
}

}

启动83

SpringCloud Alibaba_spring_12


访问:​​http://localhost:83/consumer/payment/nacos/1​​,可以看到已经实现了负载均衡(默认:轮询)

3. Nacos作为配置中心-基础配置

新建Module, cloudalibaba-config-nacos-client3377

pom

<dependencies>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般基础配置-->
<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>

bootstrap.yml

# nacos配置
server:
port: 3377

spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置


# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}

application.yml

spring:
profiles:
active: dev # 表示开发环境

为什么配两个?
- - - Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,
拉取配置之后,才能保证项目的正常启动。

springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application

主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377
{
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class, args);
}
}

业务类

@RestController
@RefreshScope //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
public class ConfigClientController
{
@Value("${config.info}")
private String configInfo;

@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}

SpringCloud Alibaba_spring_13


Data ID为下:nacos-config-client-dev.yaml

SpringCloud Alibaba_java_14


启动3377,访问​​http://localhost:3377/config/info​​。ok

在Nacos上编辑配置文件,不用重启3377也可以立即生效

4. Nacos作为配置中心-分类配置

问题1:
dev开发环境
test测试环境
prod生产环境。
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?

问题2:

一个大型分布式微服务系统会有很多微服务子项目,

每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…

那怎么对这些微服务配置进行管理呢?

SpringCloud Alibaba_后端_15


SpringCloud Alibaba_java_16


三者情况

SpringCloud Alibaba_spring cloud_17


默认情况:

Namespace=public,Group=DEFAULT_GROUP, 默认Cluster是DEFAULT

Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。

Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去

Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,
这时就可以给杭州机房的Service微服务起一个集群名称(HZ),
给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。

最后是Instance,就是微服务的实例。


三种方案加载配置:

1. DataID方案

指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置

默认空间+默认分组+新建dev和test两个DataID

SpringCloud Alibaba_spring_18


从而实现配置dev就加载dev,配置test就加载test

2. Group方案

新建两个配置文件,DataID为: nacos-config-client-info.yaml

SpringCloud Alibaba_Cloud_19


SpringCloud Alibaba_spring_20


SpringCloud Alibaba_Cloud_21


通过切换 config.group 为DEV_GROUP或TEST_GROUP,即可实现配置的切换。

3. Namespace方案

SpringCloud Alibaba_spring_22


SpringCloud Alibaba_后端_23


SpringCloud Alibaba_Cloud_24


test命名空间一样,添加两个文件。

bootstrap.yml

# nacos配置
server:
port: 3377

spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEV_GROUP
namespace: eaf79b3e-daa1-4a19-b5f1-7c4839e9b4a9


# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml

加入了一行,namespace: 命名空间Id,测试,修改不同的命名空间Id,就可获取到不同的文件内容。

就好比有一个大型项目,需要部署在上海和杭州,命名空间就叫 HZ(杭州),SH(上海),项目下又有多个微服务,通过Group来命名属于项目中的哪个微服务,每个服务又有开发环境、测试环境、生产环境,最后再通过 application.yml中的spring.profiles.active区分更具体的环境。

获取配置文件的顺序:命名空间(默认public)》Group》Data Id

5. Nacos集群和持久化配置

​https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html​

SpringCloud Alibaba_后端_25


默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。

总不可能集群的每个节点我们都一个个去改吧?

为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。

SpringCloud Alibaba_java_26

SpringCloud Alibaba_后端_27


Nacos默认自带的是嵌入式数据库derby ​​查看​

1. derby到mysql切换配置步骤

  1. nacos-server-1.1.4\nacos\conf目录下找到sql脚本
    nacos-mysql.sql,数据库执行,注意里边是否有创建数据库,没有的话先执行Create DataBase
CREATE DATABASE nacos_config;
USE nacos_config;
  1. nacos-server-1.1.4\nacos\conf目录下找到application.properties
    最下边加入
spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456

ok,重启Nacos,可以看到之前加入的命名空间、服务,都没有了,看到的是个全新的空记录界面,以前是记录进derby。

6. Linux版Nacos+MySQL生产环境配置

SpringCloud Alibaba_spring_28


需要:1个Nginx+3个nacos注册中心+1个mysql

下载nacos:​​https://github.com/alibaba/nacos/releases​

上传至 opt/,解压移动到mynacos下

cp -r nacos /mynacos/

1.Linux服务器上mysql数据库配置

根据 nacos/conf/nacos-mysql.sql文件,导表

mynacos/nacos/conf:cp application.properties.example application.properties
vim application.properties

application.properties文件最后加入:

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456

2.Linux服务器上nacos的集群配置cluster.conf

复制出:cluster.conf

cluster.conf.example cluster.conf

内容:

SpringCloud Alibaba_spring_29


这个IP不能写127.0.0.1,必须是Linux命令hostname -i能够识别的IP

3.编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端口

/mynacos/nacos/bin 目录下有startup.sh

SpringCloud Alibaba_Cloud_30


最下边是: -Dserver.port=${PORT}

SpringCloud Alibaba_spring cloud_31


依次启动3333,4444,5555

4. Nginx的配置,由它作为负载均衡器

修改 nginx/conf/nginx.conf

upstream cluster{
server 127.0.0.1:3333;
server 127.0.0.1:4444;
server 127.0.0.1:5555;
}
server {
listen 1111;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
#root html;
#index index.html index.htm;
proxy_pass http://cluster;
}
./nginx -c /usr/local/nginx/conf/nginx.conf

访问:​​http://192.168.111.144:1111/nacos/#/login​​,新建一个配置,然后查看config_info表,里边有数据就ok.

7. SpringCloud Alibaba Sentinel实现熔断与限流

官网:​​https://github.com/alibaba/Sentinel​

下载:​​https://github.com/alibaba/Sentinel/releases​

SpringCloud Alibaba_spring_32


官方使用手册:​​https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel​

解决服务使用中的各种问题:服务雪崩、服务降级、服务熔断、服务限流 …

SpringCloud Alibaba_spring cloud_33

1. 安装Sentinel控制台

下载:

SpringCloud Alibaba_java_34


运行

环境:java8环境,8080端口不能被占用

java -jar sentinel-dashboard-1.7.0.jar

SpringCloud Alibaba_spring_35


访问:​​http://localhost:8080​​,登录账号密码均为 sentinel

SpringCloud Alibaba_spring cloud_36

2. 初始化演示工程

新建Module, cloudalibaba-sentinel-service8401

pom

<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</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>

yml

server:
port: 8401

spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719

management:
endpoints:
web:
exposure:
include: '*'

主启动

@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}

业务类

@RestController
public class FlowLimitController
{

@GetMapping("/testA")
public String testA()
{
return "------testA";
}

@GetMapping("/testB")
public String testB()
{
return "------testB";
}
}

启动,查看Sentinel控制台,什么也没有,Sentinel采用的懒加载,需要先执行一次访问即可
​http://localhost:8401/testA​​

SpringCloud Alibaba_Cloud_37

可以看到sentinel8080正在监控微服务8401

3. 流控规则

SpringCloud Alibaba_Cloud_38

4. 流控模式

  1. 直接(默认)
    1.QBS(每秒的请求数量)

    表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误

访问:​​http://localhost:8401//testB​​​,

SpringCloud Alibaba_Cloud_39

  1. 线程数

SpringCloud Alibaba_后端_40


请求:​​http://localhost:8401/testB​​,一直刷,都没问题,这是因为方法执行时间太快,毫秒级别就响应了。

修改testB方法,记得IDEA启用热部署,否则重启后,限流规则会重置(目前还没有做持久化配置)

@GetMapping("/testB")
public String testB()
{
try {
TimeUnit.MILLISECONDS.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "------testB";
}

访问:​​http://localhost:8401/testB​​,快速请求两次(F5两次),当线程数达到阈值时,直接限流。

SpringCloud Alibaba_spring cloud_41


2. 关联

当关联资源/testA的qps阀值超过1时,就限流/testB的Rest访问地址,当关联资源到阈值后限制配置好的资源名

SpringCloud Alibaba_spring cloud_42


postman模拟并发密集访问 关联资源 testA

SpringCloud Alibaba_java_43


运行之后发现 testB挂了,也就是说当关联资源达到阈值后,当前资源就限流,例如支付服务达到阈值后,我们就可以对订单系统进行限流,关联模式的阈值类型 线程数,同理

SpringCloud Alibaba_spring cloud_44


3.链路

SpringCloud Alibaba的版本为2.1.1RELEASE,不同版本配置可能不同

需求:两个业务接口调用了相同的service,可以对单独的接口进行service链路的限流,保证服务的高可用。

  1. 新建service
    SentinelService
@Service
public class SentinelService {
/**
* @SentinelResource: 可以理解就是一个资源名,通过url + SentinelResource对链路限流
*/
@SentinelResource("myresource")
public String sentinelChain() {
return "调用该资源成功!!!!!";
}
}

FlowLimitLinkController

@RestController
public class FlowLimitLinkController {
@Autowired
private SentinelService sentinelService;

@GetMapping("/testC")
public String testC() {
return "testC"+sentinelService.sentinelChain();
}

@GetMapping("/testD")
public String testD() {
return "testD"+sentinelService.sentinelChain();
}
}

配置文件中关闭sentinel官方的CommonFilter实例化

spring:
cloud:
sentinel:
filter:
enabled: false

配置类

package cn.jack.config;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterContextConfig {

@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CommonFilter());
registrationBean.addUrlPatterns("/*");
// 入口资源关闭聚合
registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registrationBean.setName("sentinelFilter");
registrationBean.setOrder(1);

return registrationBean;
}
}

SpringCloud Alibaba_后端_45


快速刷新访问​​http://localhost:8401/testD​

SpringCloud Alibaba_后端_46


快速刷新访问​​http://localhost:8401/testC​​没有出现限流

也就是说上边的流控规则,只对入口资源(访问的路径)的具体实现类@SentinelResource(“myresource”)生效

  1. 流控效果
  1. 快速失败(默认的流控处理)

直接失败,抛出异常

  1. 预热

限流 冷启动:​​https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8​

流量控制:​​https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6​

如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统直接打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。

公式:阈值除以coldFactor(冷却因子,默认为3,表示倍数,即系统最"冷"),经过预热时长后才会达到阈值

SpringCloud Alibaba_spring cloud_47

系统初始化的阀值为10 / 3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10

访问:​​http://localhost:8401/testA​​,刷新刷新刷新,可以看到刚开始阈值为3,超过就报错,5秒后阈值=10

  1. 排队等待

匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。

设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

SpringCloud Alibaba_spring_48

SpringCloud Alibaba_spring cloud_49

SpringCloud Alibaba_Cloud_50

5. 降级规则

官网:​​https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7​

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断

Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,
让请求快速失败,避免影响到其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

Sentinel的断路器是没有半开状态的,半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix

SpringCloud Alibaba_Cloud_51


RT测试

@GetMapping("/testD")
public String testD()
{
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("testD 测试RT");
return "------testD";
}

SpringCloud Alibaba_java_52

启动jmeter压测

SpringCloud Alibaba_Cloud_53


访问 ​​http://localhost:8401/testD​

SpringCloud Alibaba_spring_54


按照上述配置,

永远一秒钟进来10个线程大于5个了)调用testD,我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,

则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断若大于设置的慢调用 RT 则会再次被熔断

后续停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK

异常比例

当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

SpringCloud Alibaba_Cloud_55

@GetMapping("/testD")
public String testD()
{
log.info("testD 测试RT");
int age = 10/0;
return "------testD";
}

SpringCloud Alibaba_spring_56


SpringCloud Alibaba_spring cloud_57


开压,访问 ​​http://localhost:8401/testD​​,没有出现异常信息,而是熔断了

SpringCloud Alibaba_spring_58

停止jmeter,访问​​http://localhost:8401/testD​​,正常异常,因为我们写的int age = 10/0;

SpringCloud Alibaba_后端_59


异常数异常数是按照分钟统计的

SpringCloud Alibaba_spring_60

@GetMapping("/testE")
public String testE()
{
log.info("testE 测试异常数");
int age = 10/0;
return "------testE 测试异常数";
}

SpringCloud Alibaba_spring cloud_61


​​http://localhost:8401/testE​​,第一次访问绝对报错,因为除数不能为零,

我们看到error窗口,但是达到5次报错后,进入熔断后降级。

6. 热点key限流

何为热点

热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作

比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

从HystrixCommand 到@SentinelResource,Sentinel也可以设置降级处理的方法

// 方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1,String p2,BlockException exception)
{
return "-----dealHandler_testHotKey";
}

SpringCloud Alibaba_Cloud_62

SpringCloud Alibaba_spring cloud_63


访问:​​http://localhost:8401/testHotKey?p1=1&p2=2​​,一秒内访问超过设置的阈值(1)次后,违反了配置的热点规则;就会走降级处理,程序运行时异常是不会走降级的。

总接:当 p1=1这个参数,QBS(每秒访问次数) > 设定的阈值后,就会走blockHandler 降级处理,窗口时间过后才会正常走testHotKey方法

7. 系统规则

​https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81​

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

8. 自定义限流处理逻辑

myhandler.CustomerBlockHandler.java

// 公共的处理降级方法
public class CustomerBlockHandler {
public String handleException(BlockException blockException){
return "handleException 被限流了";
}
}
@GetMapping("/testD")
@SentinelResource(value = "testD",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handleException")
public String testD() {
return "testD"+sentinelService.sentinelChain();
}

可以通过url和SentinelResource绑定限流规则,测试

9. 服务熔断

消费者

@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";

@Resource
private RestTemplate restTemplate;

@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "consumer-fallback", fallback = "handlerFallback", blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class})
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}

return result;
}

public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"fallback,无此流水,exception "+e.getMessage(),payment);
}
public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}

}

@SentinelResource:

  • value:Sentinel流控的资源名
  • fallback:熔断执行的方法
  • blockHandler:违反流控规则后执行的方法
  • exceptionsToIgnore:忽略属性,把某些异常抛出来不触发降级

注意:同时配置的话,先判断流控规则 blockHandler 》

10. 集成openfeign

服务调用用Ribbon和 feign都可以

新建Module,cloudalibaba-consumer-nacos-order84
pom

<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<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>

yml 激活Sentinel对Feign的支持

server:
port: 84


spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719

management:
endpoints:
web:
exposure:
include: '*'
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true

业务接口

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)//调用中关闭9003服务提供者
public interface PaymentService
{
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

fallback降级

@Component
public class PaymentFallbackService implements PaymentService
{
@Override
public CommonResult<Payment> paymentSQL(Long id)
{
return new CommonResult<>(444,"服务降级返回,没有该流水信息",new Payment(id, "errorSerial......"));
}
}

controller

//==================OpenFeign
@Resource
private PaymentService paymentService;

@GetMapping(value = "/consumer/openfeign/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
{
if(id == 4)
{
throw new RuntimeException("没有该id");
}
return paymentService.paymentSQL(id);
}

主启动

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84
{
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}

启动访问:​​http://localhost:84/consumer/paymentSQL/1​​ 关闭服务调用者再访问84,84消费侧自动降级,不会被超时耗死,自动返回降级的内容

SpringCloud Alibaba_spring_64


熔断框架对比:

SpringCloud Alibaba_spring_65

11. sentinel规则持久化

一旦重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化

解决:将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台
的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效

pom

<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

yml

spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow

SpringCloud Alibaba_java_66


SpringCloud Alibaba_java_67


启动8401后刷新sentinel发现业务规则有了,第一次启动后sentinel是空白的,调几次接口,配置就出现了

完整代码已上传:​​gitee​


阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring