SpringCloud之AlibabaNacos、Sentinel

目錄
  1. SpringCloudAlibaba Nacos(服務的注冊與發(fā)現(xiàn)少漆、配置的動態(tài)刷新)
  2. SpringCloudAlibaba Sentinel(流量控制)
1. SpringCloudAlibaba Nacos

Nacos(Dynamic Naming and Configuration Service)由Alibaba開發(fā)的Java開源項目:服務注冊中心(實現(xiàn)服務的注冊與發(fā)現(xiàn)战坤, 類似SpringCloudEureka功能)和配置中心(實現(xiàn)配置的動態(tài)刷新弹砚,類似SpringCloudConfig+SpringCloudBus功能)的組合體(以服務為核心)迟蜜。

Nacos作為服務注冊中心經(jīng)歷了十年“雙十一”的洪峰考驗,具有簡單易用荡短、穩(wěn)定可靠丐枉、性能卓越等優(yōu)點,可以幫助用戶更敏捷掘托、容易地構建和管理微服務應用瘦锹。Nacos支持幾乎所有主流類型“服務”的發(fā)現(xiàn)、配置和管理:
  1. Kubernetes Service
  2. gRPC&Dubbo RPC Service
  3. Spring Cloud RESTful Service

Nacos的特性
  1. 服務發(fā)現(xiàn)
    Nacos 支持基于 DNS 和 RPC 的服務發(fā)現(xiàn)。當服務提供者使用原生 SDK沼本、OpenAPI 或一個獨立的 Agent TODO 向 Nacos 注冊服務后噩峦,服務消費者可以在 Nacos 上通過 DNS TODO 或 HTTP&API 查找、發(fā)現(xiàn)服務抽兆。
  2. 服務健康監(jiān)測
    對服務實時健康檢查识补,阻止請求發(fā)送到不健康主機或服務實例上。提供了一個健康檢查儀表盤辫红,能夠幫助我們根據(jù)健康狀態(tài)管理服務的可用性及流量凭涂。
  3. 動態(tài)配置服務
    動態(tài)配置服務可以讓我們以中心化、外部化和動態(tài)化的方式贴妻,管理所有環(huán)境的應用配置和服務配置切油。
    動態(tài)配置消除了配置變更時重新部署應用和服務的需要,讓配置管理變得更加高效名惩、敏捷澎胡。
    配置中心化管理讓實現(xiàn)無狀態(tài)服務變得更簡單,讓服務按需彈性擴展變得更容易娩鹉。
    Nacos 提供了一個簡潔易用的 UI 幫助我們管理所有服務和應用的配置攻谁。Nacos 還提供包括配置版本跟蹤、金絲雀發(fā)布弯予、一鍵回滾配置以及客戶端配置更新狀態(tài)跟蹤在內(nèi)的一系列開箱即用的配置管理特性戚宦,幫助我們更安全地在生產(chǎn)環(huán)境中管理配置變更和降低配置變更帶來的風險。
  4. 動態(tài) DNS 服務
    Nacos 提供了動態(tài) DNS 服務锈嫩,能夠讓我們更容易地實現(xiàn)負載均衡受楼、流量控制以及數(shù)據(jù)中心內(nèi)網(wǎng)的簡單 DNS 解析服務。
    Nacos 提供了一些簡單的 DNS APIs TODO呼寸,可以幫助我們管理服務的關聯(lián)域名和可用的 IP:PORT 列表艳汽。
  5. 服務及其元數(shù)據(jù)管理
    Nacos 能讓我們從微服務平臺建設的視角管理數(shù)據(jù)中心的所有服務及元數(shù)據(jù),包括管理服務的描述对雪、生命周期河狐、服務的靜態(tài)依賴分析、服務的健康狀態(tài)慌植、服務的流量管理甚牲、路由及安全策略义郑、服務的 SLA 以及 metrics 統(tǒng)計數(shù)據(jù)蝶柿。

Nacos的兩大組件
  與Eureka類似,Nacos也采用CS(Client/Server非驮,客戶端/服務器)架構交汤。
  1. NacosServer(Nacos服務端)
    與EurekaServer不同,NacosServer由阿里巴巴團隊使用Java語言開發(fā),只需下載并運行芙扎。
    NacosServer可以作為服務注冊中心星岗,幫助NacosClient實現(xiàn)服務的注冊與發(fā)現(xiàn)。
    NacosServer可以作為配置中心戒洼,幫助NacosClient在不重啟的情況下俏橘,實現(xiàn)配置的動態(tài)刷新。
  2. NacosClient(Nacos 客戶端)
    通常指的是微服務架構中的各個服務圈浇,由開發(fā)者自己搭建寥掐,可以使用多種語言編寫。     
    NacosClient通過添加依賴spring-cloud-starter-alibaba-nacos-discovery磷蜀,在服務注冊中心(Nacos Server)中實現(xiàn)服務的注冊與發(fā)現(xiàn)召耘。
    NacosClient通過添加依賴spring-cloud-starter-alibaba-nacos-config,在配置中心(Nacos Server)中實現(xiàn)配置的動態(tài)刷新褐隆。

從Github下載NacosServer

NacosServer目錄說明:
  1. bin目錄
    用于存放Nacos的可執(zhí)行命令污它。
  2. conf目錄
    用于存放Nacos配置文件。
  3. target目錄
    用于存放Nacos應用的jar包庶弃。

運行NacosServer
  1. 終端執(zhí)行
    cd NacosServer的bin目錄
    Windows:startup.cmd -m standalone 以單機模式啟動NacosServer
    Linux:sh startup.sh -m standalone
  2. 在瀏覽器中訪問http://localhost:8848/nacos登陸頁面衫贬,輸入登錄名和密碼(默認都是nacos)跳轉(zhuǎn)到NacosServer控制臺主頁。
NacosServer登陸頁面

NacosServer控制臺
  1. Nacos服務注冊中心
共涉及3個角色:
  1. 服務注冊中心(Register Service)NacosServer
    為服務提供者和服務消費者提供服務注冊和發(fā)現(xiàn)功能虫埂。
  2. 服務提供者(Provider Service)NacosClient
    對外提供服務(將自己提供的服務注冊到服務注冊中心祥山,以供服務消費者發(fā)現(xiàn)和調(diào)用)。
  3. 服務消費者(Consumer Service)NacosClient
    用于消費服務(從服務注冊中心獲取服務列表掉伏,調(diào)用所需的服務)缝呕。

Nacos實現(xiàn)服務注冊與發(fā)現(xiàn)的流程如下:
    1. 下載NacosServer并運行。
    2. 服務提供者NacosClient啟動時斧散,會把服務以服務名(spring.application.name)的方式注冊到服務注冊中心(NacosServer)供常。
    3. 服務消費者NacosClient啟動時,也會將自己的服務注冊到服務注冊中心鸡捐;并從服務注冊中心獲取一份服務注冊列表信息栈暇,該列表中包含了所有注冊到服務注冊中心上的服務的信息(包括服務提供者和自身的信息);通過HTTP或消息中間件遠程調(diào)用服務提供者提供的服務箍镜。
Nacos 服務注冊與發(fā)現(xiàn)

示例

===》1. 創(chuàng)建spring-cloud-alibaba-demo主項目
1. 修改pom.xml文件
<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>
    <groupId>com.sst.cx</groupId>
    <artifactId>spring-cloud-alibaba-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath /> 
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <spring-cloud.version>2020.0.4</spring-cloud.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--Spring Cloud Alibaba 的版本信息-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--Spring Cloud 的版本信息-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

===》2. 創(chuàng)建spring-cloud-alibaba-provider-8001 子項目(搭建服務提供者)
1. 修改pom.xml文件
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
        <!--Spring Cloud Alibaba Nacos discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
2. 創(chuàng)建application.properties配置文件(類路徑resources目錄下)
#端口號
server.port=8001
#服務名
spring.application.name=spring-cloud-alibaba-provider
#Nacos Server 的地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*
3. 創(chuàng)建UserController.java
package com.sst.cx.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class UserController {
    @Value("${server.port}")
    private String serverPort;
    @GetMapping(value = "/user/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id) {
        return "<h2>服務訪問成功源祈!</h2>服務名:spring-cloud-alibaba-provider<br /> 端口號: " + serverPort + "<br /> 傳入的參數(shù):" + id;
    }
}
4. 在主啟動類上,添加@EnableDiscoveryClient注解開啟Nacos服務發(fā)現(xiàn)功能色迂。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient // 開啟服務發(fā)現(xiàn)功能
public class SpringCloudAlibabaProvider8001Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaProvider8001Application.class, args);
    }
}
5. 啟動spring-cloud-alibaba-provider-8001,在瀏覽器中訪問http://localhost:8001/user/nacos/1歇僧、訪問http://localhost:8848/nacos查看“服務管理”下的“服務列表”

===》3. 創(chuàng)建spring-cloud-alibaba-consumer-nacos-8801子項目(搭建服務消費者)
1. 修改pom.xml文件
    <dependencies>
        <!--SpringCloud ailibaba nacos discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--由于 Netflix Ribbon 進入停更維護階段图张,因此新版本的 Nacos discovery 都已經(jīng)移除了 Ribbon ,此時我們需要引入 loadbalancer 代替 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
2. 創(chuàng)建application.yml配置文件(類路徑resources目錄下)
server:
  port: 8801  #端口號
spring:
  application:
    name: spring-cloud-alibaba-consumer #服務名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  #Nacos server 的地址
#以下配置信息并不是默認配置,而是我們自定義的配置祸轮,目的是不在 Controller 內(nèi)硬編碼服務提供者的服務名
service-url:
  nacos-user-service: http://spring-cloud-alibaba-provider #服務提供者的服務名
3. 創(chuàng)建ApplicationContextBean.java配置類兽埃,添加@LoadBalanced注解與Ribbon進行集成開啟負載均衡功能。
package com.sst.cx.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextBean {
    @Bean
    @LoadBalanced // 與Ribbon集成适袜,并開啟負載均衡功能
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
4. 創(chuàng)建UserController_Consumer.java
package com.sst.cx.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class UserController_Consumer {
    @Resource
    private RestTemplate restTemplate;
    @Value("${service-url.nacos-user-service}")
    private String serverURL; //服務提供者的服務名
    @GetMapping("/consumer/user/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Long id) {
        return restTemplate.getForObject(serverURL + "/user/nacos/" + id, String.class);
    }
}
5. 在主啟動類上柄错,添加@EnableDiscoveryClient注解開啟Nacos服務發(fā)現(xiàn)功能。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient // 開啟服務注冊與發(fā)現(xiàn)功能
public class SpringCloudAlibabaConsumerNacos8801Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaConsumerNacos8801Application.class, args);
    }
}
6. 啟動spring-cloud-alibaba-consumer-nacos-8801苦酱,
在瀏覽器中訪問http://localhost:8001/consumer/user/nacos/1鄙陡、訪問http://localhost:8848/nacos查看“服務管理”下的“服務列表。
服務提供者

服務消費者

服務注冊列表
  1. Nacos配置中心
NacosServer還可以作為配置中心躏啰,對SpringCloud應用的外部配置進行統(tǒng)一地集中化管理趁矾。只需要在應用的pom.xml文件中引入spring-cloud-starter-alibaba-nacos-config即可實現(xiàn)配置的獲取與動態(tài)刷新。

示例

創(chuàng)建spring-cloud-alibaba-config-client-3377子項目
1. 修改pom.xml文件
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--SpringCloud2020及以后的版本默認不啟用 bootstrap 配置给僵,我們需要在pom里面顯式地引入:-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</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>
        <!--Spring Cloud Alibaba Config 依賴-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--SpringCloud ailibaba nacos 服務注冊與發(fā)現(xiàn)模塊 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--Spring Boot 監(jiān)控模塊-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
2. 創(chuàng)建bootstrap.yml配置文件(類路徑resources目錄下)
server:
  port: 3377 #端口號
spring:
  application:
    name: config-client #服務名
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 #Nacos服務注冊中心地址
      config:
        server-addr: 127.0.0.1:8848 #Nacos作為配置中心地址
        file-extension: yaml #指定yaml格式的配置
3. 創(chuàng)建application.yml配置文件(類路徑resources目錄下)
spring:
  profiles:
    active: dev #激活 dev 的配置
4. 創(chuàng)建ConfigClientController.java
package com.sst.cx.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ConfigClientController {
    @Value("${config.info}")
    private String ConfigInfo;
    @GetMapping("/config/info")
    public String getConfigInfo(){
        return ConfigInfo;
    }
}
5. 在主啟動類上毫捣,添加@EnableDiscoveryClient注解開啟Nacos服務發(fā)現(xiàn)功能。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudAlibabaNacosConfigClient3377Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaNacosConfigClient3377Application.class, args);
    }
}
6. 啟動NacosServer,并在NacosServer控制臺的“配置管理”下的“配置列表”中,點擊“+”按鈕翩肌,新建如下配置
  Data ID:config-client-dev.yaml
  Group:DEFAULT_GROUP
  配置格式:YAML
  配置內(nèi)容:config:
            info: com.sst.cx
/*
Data ID的完整格式:${prefix}-${spring.profiles.active}.${file-extension}
說明:
    1. ${prefix}:默認取值為微服務的服務名,即配置文件中 spring.application.name 的值斑粱,可以在配置文件中通過配置 spring.cloud.nacos.config.prefix 來指定。
    2. ${spring.profiles.active}:表示當前環(huán)境對應的 Profile脯爪,例如 dev则北、test、prod 等痕慢。當沒有指定環(huán)境的 Profile 時尚揣,其對應的連接符也將不存在, dataId 的格式變成 ${prefix}.${file-extension}掖举。
    3. ${file-extension}:表示配置內(nèi)容的數(shù)據(jù)格式快骗,可以在配置文件中通過配置項 spring.cloud.nacos.config.file-extension 來配置,例如 properties 和 yaml塔次。
*/
7. 啟動spring-cloud-alibaba-config-client-3377方篮,并使用瀏覽器訪問http://localhost:3377/config/info。
在NacosServer中励负,將config-client-dev.yaml 中的配置修改info: hello com.sst.cx藕溅,使用瀏覽器再次訪問http://localhost:3377/config/info,可以看到發(fā)生改變熄守。
  1. NacosServer集群化部署
實際的項目開發(fā)中蜈垮,一個微服務系統(tǒng)往往由十幾,幾十個甚至幾百個微服務組成裕照。 這些服務若全部注冊到同一臺NacosServer攒发,就極有可能導致NacosServer因為不堪重負而崩潰,最終導致整個微服務系統(tǒng)癱瘓晋南。解決這個問題最直接的辦法就是使用NacosServer集群惠猿。

NacosServer的集群化部署有一個十分明顯的優(yōu)點,那就是可以保障系統(tǒng)的高可用性负间。在集群化部署中偶妖,只要不是所有的NacosServer都停止工作,NacosClient就還可以從集群中正常的NacosServer上獲取服務信息及配置政溃,而不會導致系統(tǒng)的整體癱瘓趾访,這就是NacosServer集群化部署的高可用性。
Nacos Server 集群架構

示例

1. 將NacosServer的conf目錄下的cluster.conf.example文件重命名為cluster.conf董虱,添加
192.168.0.101:3333
192.168.0.101:4444
192.168.0.101:5555
2. 在config目錄下的application.properties中扼鞋,將server.port(端口號)修改為 3333,添加MySQL數(shù)據(jù)庫配置
server.port=3333
###MySQL數(shù)據(jù)庫配置####
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
db.user=root
db.password=12345678
3. 將該NacosServer目錄復制到另外兩臺機器上愤诱,并將它們的端口號分別修改為: 4444 和 5555云头。
4. 下載Nginx,并修改Nginx中conf目錄下的nginx.conf的配置
/*
下載Nginx穩(wěn)定版并解壓
cd跳轉(zhuǎn)到該目錄下,編譯安裝
  ./configure
  make
  sudo make install
啟動
  cd /usr/local/nginx/sbin
  sudo ./nginx 
*/
#user  nobody;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    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;
        }
    }
}
5. 啟動集群中所有的NacosServer淫半、Nginx溃槐,在瀏覽器中訪問http://localhost:1111/nacos/,若成功訪問 NacosServer的控制臺科吭,則說明 Nacos 集群部署成功昏滴。
6. 將主工程spring-cloud-alibaba-demo下所有子模塊配置文件中的 Nacos Server 地址統(tǒng)一修改為:localhost:1111
server-addr: localhost:1111  #集群版 Nacos Server 的地址
7. 重啟spring-cloud-alibaba-consumer-nacos-8801,并使用瀏覽器訪問“http://localhost:1111/nacos”对人,查看“服務管理”下的“服務列表”影涉。

2. SpringCloudAlibaba Sentinel(高可用流量控制組件)

Sentinel由Alibaba開發(fā)的開源項目:面向分布式微服務架構的輕量級高可用流量控制組件(以流量為切入點,從流量控制规伐、熔斷降級、系統(tǒng)負載保護等多個維度幫助用戶保護服務的穩(wěn)定性)吵护。
功能上類似SpringCloudNetfilxHystrix ,但比Hystrix更強大(如:提供了流量控制功能份招、更完善的實時監(jiān)控功能等)切揭。

優(yōu)勢:
  1. 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的“雙十一”大促流量的核心場景,例如秒殺(將突發(fā)流量控制在系統(tǒng)可以承受的范圍)锁摔、消息削峰填谷廓旬、集群流量控制、實時熔斷下游不可用服務等谐腰。
  2. 完備的實時監(jiān)控:Sentinel 提供了實時監(jiān)控功能孕豹。用戶可以在控制臺中看到接入應用的單臺機器的秒級數(shù)據(jù),甚至是 500 臺以下規(guī)模集群的匯總運行情況十气。
  3. 廣泛的開源生態(tài):Sentinel 提供了開箱即用的與其它開源框架或庫(例如 Spring Cloud励背、Apache Dubbo、gRPC砸西、Quarkus)的整合模塊叶眉。只需在項目中引入相應的依賴并進行簡單的配置即可快速地接入 Sentinel。此外芹枷,Sentinel 還提供 Java衅疙、Go 以及 C++ 等多語言的原生實現(xiàn)。
  4. 完善的 SPI 擴展機制:Sentinel 提供簡單易鸳慈、完善的 SPI 擴展接口饱溢,我們可以通過實現(xiàn)這些擴展接口快速地定制邏輯,例如定制規(guī)則管理走芋、適配動態(tài)數(shù)據(jù)源等绩郎。
    SPI(全稱:Service Provider Interface)是一種服務發(fā)現(xiàn)機制潘鲫。它可以在 ClassPath 路徑下的 META-INF/services 文件夾查找文件,并自動加載文件中定義的類肋杖。

Sentinel由2部分組成:
  1. Sentinel核心庫
    不依賴任何框架或庫溉仑,能夠運行于 Java 8 及以上的版本的運行時環(huán)境中,同時對 Spring Cloud兽愤、Dubbo 等微服務框架提供了很好的支持。
    Sentinel 核心庫不依賴 Sentinel Dashboard挪圾,但兩者結合使用可以有效的提高效率浅萧,讓 Sentinel 發(fā)揮它最大的作用。 
  2. Sentinel控制臺(Dashboard)
    1. 機器自發(fā)現(xiàn)(查看機器列表以及健康情況)
      收集Sentinel客戶端發(fā)送的心跳包哲思,判斷機器是否在線洼畅。
    2. 監(jiān)控(單機和集群聚合)
      通過Sentinel客戶端暴露的監(jiān)控API,可以實現(xiàn)秒級的實時監(jiān)控棚赔。
    3. 對規(guī)則(如:流量控制帝簇、熔斷降級)進行配置和管理 
      針對資源定義和推送規(guī)則。
    5. 簇點鏈路自發(fā)現(xiàn)
    6. 鑒權
      從Sentinel 1.6.0起靠益,Sentinel控制臺引入基本的登錄功能丧肴,默認用戶名和密碼都是sentinel。
的重要入口之一胧后。

Sentinel的基本概念
  1. 資源
    可以是Java應用中的任何內(nèi)容(如:由應用提供的服務芋浮、服務里的方法、一段代碼)壳快。
    通過Sentinel提供的API來定義一個資源纸巷,使其能夠被Sentinel保護起來。通常情況下眶痰,我們可以使用方法名瘤旨、URL甚至是服務名來作為資源名來描述某個資源。
  2. 規(guī)則     
    圍繞資源而設定的規(guī)則竖伯。Sentinel支持流量控制存哲、熔斷降級、系統(tǒng)保護七婴、來源訪問控制和熱點參數(shù)等多種規(guī)則宏胯,所有這些規(guī)則都可以動態(tài)實時調(diào)整。

Sentinel的使用步驟
  1. 在項目中【引入Sentinel依賴】:spring-cloud-starter-alibaba-sentinel 本姥。
      Sentinel對大部分的主流框架都進行了適配(如:Web Servlet肩袍、Dubbo、Spring Cloud婚惫、gRPC氛赐、Spring WebFlux魂爪、Reactor等)。
  2. 【定義資源】:
    在項目開發(fā)時艰管,只需要考慮這個服務滓侍、方法或代碼是否需要保護,如果需要保護牲芋,就可以將它定義為一個資源撩笆。
    在Sentinel 控制臺的“簇點鏈路”中,可查看資源的實時統(tǒng)計缸浦。
    4種方式:
      1. 適配主流框架 自動定義資源
        Sentinel 對大部分的主流框架都進行了適配夕冲,只要引入相關的適配模塊(例如 spring-cloud-starter-alibaba-sentinel),Snetinel 就會自動將項目中的服務(包括調(diào)用端和服務端)定義為資源裂逐,【資源名就是服務的請求路徑】歹鱼。只要再對資源定義一些規(guī)則,這些資源就可以享受到Sentinel的保護卜高。
      2. 通過SphU類(try-catch風格)手動定義資源弥姻。
      3. 通過SphO類(if-else風格)手動定義資源。
        返回值為false(發(fā)生限流)時進行限流之后的邏輯處理掺涛。
      4. 注解方式(@SentinelResource注解)手動定義資源庭敦。
  3. 根據(jù)實時統(tǒng)計信息,【對資源定義規(guī)則】(如:流控規(guī)則薪缆、熔斷規(guī)則螺捐、熱點規(guī)則、系統(tǒng)規(guī)則矮燎、授權規(guī)則)定血。
  4. 運行程序,【檢驗規(guī)則】是否生效诞外。
@SentinelResource注解的常用屬性 說明 必填與否 使用要求
value 用于指定資源的名稱 必填 -
entryType entry 類型 可選項(默認為 EntryType.OUT) -
blockHandler 服務限流后會拋出 BlockException 異常澜沟,而 blockHandler 則是用來指定一個函數(shù)來處理 BlockException 異常的。簡單點說峡谊,該屬性用于指定服務限流后的后續(xù)處理邏輯茫虽。 可選項 1. blockHandler 函數(shù)訪問范圍需要是 public;2. 返回類型需要與原方法相匹配既们;3. 參數(shù)類型需要和原方法相匹配并且最后加一個額外的參數(shù)濒析,類型為 BlockException;4. blockHandler 函數(shù)默認需要和原方法在同一個類中啥纸,若希望使用其他類的函數(shù)号杏,則可以指定 blockHandler 為對應的類的 Class 對象,注意對應的函數(shù)必需為 static 函數(shù)斯棒,否則無法解析盾致。
blockHandlerClass 若 blockHandler 函數(shù)與原方法不在同一個類中主经,則需要使用該屬性指定 blockHandler 函數(shù)所在的類。 可選項 1. 不能單獨使用庭惜,必須與 blockHandler 屬性配合使用罩驻;2. 該屬性指定的類中的 blockHandler 函數(shù)必須為 static 函數(shù),否則無法解析护赊。
fallback 用于在拋出異常(包括 BlockException)時惠遏,提供 fallback 處理邏輯。fallback 函數(shù)可以針對所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進行處理骏啰。 可選項 1. 返回值類型必須與原函數(shù)返回值類型一致节吮;2. 方法參數(shù)列表需要和原函數(shù)一致,或者可以額外多一個 Throwable 類型的參數(shù)用于接收對應的異常再榄;3. fallback 函數(shù)默認需要和原方法在同一個類中下翎,若希望使用其他類的函數(shù),則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數(shù)必需為 static 函數(shù)啄清,否則無法解析。
fallbackClass 若 fallback 函數(shù)與原方法不在同一個類中遗嗽,則需要使用該屬性指定 blockHandler 函數(shù)所在的類宪祥。 可選項 1. 不能單獨使用,必須與 fallback 或 defaultFallback 屬性配合使用瞭亮;2. 該屬性指定的類中的 fallback 函數(shù)必須為 static 函數(shù)方仿,否則無法解析。
defaultFallback 默認的 fallback 函數(shù)名稱统翩,通常用于通用的 fallback 邏輯(即可以用于很多服務或方法)仙蚜。默認 fallback 函數(shù)可以針對所以類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進行處理。 可選項 1. 返回值類型必須與原函數(shù)返回值類型一致厂汗;2. 方法參數(shù)列表需要為空委粉,或者可以額外多一個 Throwable 類型的參數(shù)用于接收對應的異常;3. defaultFallback 函數(shù)默認需要和原方法在同一個類中娶桦。若希望使用其他類的函數(shù)贾节,則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數(shù)必需為 static 函數(shù)衷畦,否則無法解析栗涂。
exceptionsToIgnore 用于指定哪些異常被排除掉,不會計入異常統(tǒng)計中祈争,也不會進入 fallback 邏輯中斤程,而是會原樣拋出。 可選項 -

注:在 Sentinel 1.6.0 之前菩混,fallback 函數(shù)只針對降級異常(DegradeException)進行處理暖释,不能處理業(yè)務異常袭厂。

流量控制規(guī)則

任何系統(tǒng)處理請求的能力都是有限的,但任意時間內(nèi)到達系統(tǒng)的請求量往往是隨機且不可控的球匕,如果在某一個瞬時時刻請求量急劇增纹磺,那么系統(tǒng)就很有可能被瞬時的流量高峰沖垮。為了避免此類情況發(fā)生亮曹,需要根據(jù)系統(tǒng)的處理能力對請求流量進行控制橄杨,即流量控制(簡稱:流控)。

流控規(guī)則
  1. 資源名(流控規(guī)則的作用對象)照卦。
  2. 閾值(流控閾值)式矫。
  3. 閾值類型(流控閾值的類型)
    QPS(默認值,表示并發(fā)請求數(shù)役耕,即每秒鐘最多通過的請求數(shù))采转、并發(fā)線程數(shù)。
  4. 針對來源(流控針對的調(diào)用來源)
    默認值:default(表示不區(qū)分調(diào)用來源)瞬痘。
  5. 流控模式(調(diào)用關系限流策略)
    直接(默認值)故慈、鏈路、關聯(lián)框全。
  6. 流控效果
    直接拒絕(默認值)察绷、Warm Up、勻速排隊津辩。不支持按調(diào)用關系限流拆撼。

對資源定義流控規(guī)則后:
  Sentinel會根據(jù)這些規(guī)則對流量相關的各項指標進行監(jiān)控,當這些指標當達到或超過流控規(guī)則規(guī)定的閾值時喘沿,Sentinel會對請求的流量進行限制(即“限流”)闸度,以避免系統(tǒng)被瞬時的流量高峰沖垮,保障系統(tǒng)的高可用性蚜印。
  觸發(fā)限流時莺禁,資源會拋出BlockException異常,可以捕捉該異常來自定義被限流后的處理邏輯晒哄。
·同一個資源可以創(chuàng)建多條流控規(guī)則睁宰,Sentinel會遍歷這些規(guī)則,直到有規(guī)則觸發(fā)限流或者所有規(guī)則遍歷完畢為止寝凌。

熔斷降級規(guī)則

除了流量控制以外柒傻,對調(diào)用鏈路中不穩(wěn)定資源的熔斷降級,也是保障服務高可用的重要措施之一较木。
在分布式微服務架構中红符,一個系統(tǒng)往往由多個服務組成,不同服務之間相互調(diào)用,組成復雜的調(diào)用鏈路预侯。如果鏈路上的某一個服務出現(xiàn)故障致开,那么故障就會沿著調(diào)用鏈路在系統(tǒng)中蔓延,最終導致整個系統(tǒng)癱瘓萎馅。Sentinel 提供了熔斷降級機制就可以解決這個問題双戳。Sentinel 的熔斷將機制會在調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定狀態(tài)時(例如調(diào)用超時或異常比例升高),暫時切斷對這個資源的調(diào)用糜芳,以避免局部不穩(wěn)定因素導致整個系統(tǒng)的雪崩飒货。
熔斷降級作為服務保護自身的手段,通常在客戶端(調(diào)用端)進行配置峭竣,資源被熔斷降級最直接的表現(xiàn)就是拋出 DegradeException 異常塘辅。

熔斷規(guī)則
  1. 資源名(熔斷規(guī)則的作用對象)     
  2. 熔斷策略(慢調(diào)用比例---默認、異常比例皆撩、異常數(shù)策略)    
  3. 最大RT(請求的最大相應時間扣墩,請求的響應時間大于該值則統(tǒng)計為慢調(diào)用)     
    僅限熔斷策略為慢調(diào)用比例。
  4. 熔斷時長(熔斷開啟狀態(tài)持續(xù)的時間扛吞,超過該時間熔斷器會切換為探測恢復狀態(tài)HALF-OPEN呻惕,單位為s)         
  5. 最小請求數(shù)(熔斷觸發(fā)的最小請求數(shù),默認值:5喻粹,請求數(shù)小于該值時即使異常比率超出閾值也不會熔斷)1.7.0 引入蟆融。 
  6. 統(tǒng)計時長(熔斷觸發(fā)需要統(tǒng)計的時長草巡,默認值:1000ms守呜,單位為ms)1.8.0 引入
  7. 比例閾值(分為慢調(diào)用比例閾值和異常比例閾值,取值范圍[0.0,1.0]山憨,即慢調(diào)用或異常調(diào)用占所有請求的百分比)
    僅限熔斷策略為:慢調(diào)用比例 查乒、異常比例。
  8. 異常數(shù)(請求或調(diào)用發(fā)生的異常的數(shù)量)
    僅限熔斷策略為:異常數(shù)

Sentinel熔斷策略(3種)
  1. 慢調(diào)用比例(SLOW_REQUEST_RATIO)
    選擇以慢調(diào)用比例作為閾值郁竟,需要設置允許的慢調(diào)用RT(即最大響應時間)玛迄,若請求的響應時間大于該值則統(tǒng)計為慢調(diào)用。
    當單位統(tǒng)計時長(statIntervalMs)內(nèi)請求數(shù)目大于設置的最小請求數(shù)目棚亩,且慢調(diào)用的比例大于閾值蓖议,則接下來的熔斷時長內(nèi)請求會自動被熔斷。經(jīng)過熔斷時長后熔斷器會進入探測恢復狀態(tài)(HALF-OPEN 狀態(tài))讥蟆,若接下來的一個請求響應時間小于設置的慢調(diào)用 RT 則結束熔斷勒虾,若大于設置的慢調(diào)用 RT 則再次被熔斷。
  2. 異常比例(ERROR_RATIO)
    當單位統(tǒng)計時長(statIntervalMs)內(nèi)請求數(shù)目大于設置的最小請求數(shù)目且異常的比例大于閾值瘸彤,則在接下來的熔斷時長內(nèi)請求會自動被熔斷修然。經(jīng)過熔斷時長后熔斷器會進入探測恢復狀態(tài)(HALF-OPEN 狀態(tài)),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷愕宋。異常比率的閾值范圍是 [0.0, 1.0]玻靡,代表 0% - 100%。
  3. 異常數(shù)(ERROR_COUNT)
    當單位統(tǒng)計時長內(nèi)的異常數(shù)目超過閾值之后會自動進行熔斷中贝。經(jīng)過熔斷時長后熔斷器會進入探測恢復狀態(tài)(HALF-OPEN 狀態(tài))囤捻,若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷邻寿。

Sentinel熔斷狀態(tài)(3種)
  1. 熔斷關閉狀態(tài)(CLOSED)     
    處于關閉狀態(tài)時最蕾,請求可以正常調(diào)用資源。     
    滿足以下任意條件老厌,Sentinel熔斷器進入熔斷關閉狀態(tài):
      1. 全部請求訪問成功瘟则。
      2. 單位統(tǒng)計時長(statIntervalMs)內(nèi)請求數(shù)目小于設置的最小請求數(shù)目。
      3. 未達到熔斷標準枝秤,例如服務超時比例醋拧、異常數(shù)、異常比例未達到閾值淀弹。
      4. 處于探測恢復狀態(tài)時丹壕,下一個請求訪問成功。
  2. 熔斷開啟狀態(tài)(OPEN)   
    處于熔斷開啟狀態(tài)時薇溃,熔斷器會一定的時間(規(guī)定的熔斷時長)內(nèi)菌赖,暫時切斷所有請求對該資源的調(diào)用,并調(diào)用相應的降級邏輯使請求快速失敗避免系統(tǒng)崩潰沐序。  
    滿足以下任意條件琉用,Sentinel 熔斷器進入熔斷開啟狀態(tài):
      1. 單位統(tǒng)計時長內(nèi)請求數(shù)目大于設置的最小請求數(shù)目,且已達到熔斷標準策幼,例如請求超時比例邑时、異常數(shù)、異常比例達到閾值特姐。
      2. 處于探測恢復狀態(tài)時晶丘,下一個請求訪問失敗。
  3. 探測恢復狀態(tài)(HALF-OPEN)  
    處于探測恢復狀態(tài)時唐含,Sentinel 熔斷器會允許一個請求調(diào)用資源浅浮。則若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,熔斷器進入熔斷關閉(CLOSED)狀態(tài)捷枯;否則會再次被熔斷滚秩,熔斷器進入熔斷開啟(OPEN)狀態(tài)。 
    在熔斷開啟一段時間(降級窗口時間或熔斷時長铜靶,單位為 s)后叔遂,Sentinel 熔斷器自動會進入探測恢復狀態(tài)他炊。

Sentinel實現(xiàn)熔斷降級過程
    1. 在項目中,使用@SentinelResource注解的fallback屬性可以為資源指定熔斷降級邏輯(方法)已艰。
    2. 通過Sentinel控制臺或代碼定義熔斷規(guī)則痊末,包括熔斷策略、最小請求數(shù)哩掺、閾值凿叠、熔斷時長以及統(tǒng)計時長等。
    3. 若單位統(tǒng)計時長(statIntervalMs)內(nèi)嚼吞,請求數(shù)目大于設置的最小請求數(shù)目且達到熔斷標準(例如請求超時比例盒件、異常數(shù)、異常比例達到閾值)舱禽,Sentinel 熔斷器進入熔斷開啟狀態(tài)(OPEN)炒刁。
    4. 處于熔斷開啟狀態(tài)時, @SentinelResource 注解的 fallback 屬性指定的降級邏輯會臨時充當主業(yè)務邏輯誊稚,而原來的主邏輯則暫時不可用翔始。當有請求訪問該資源時,會直接調(diào)用降級邏輯使請求快速失敗里伯,而不會調(diào)用原來的主業(yè)務邏輯城瞎。
    5. 在經(jīng)過一段時間(在熔斷規(guī)則中設置的熔斷時長)后,熔斷器會進入探測恢復狀態(tài)(HALF-OPEN)疾瓮,此時 Sentinel 會允許一個請求對原來的主業(yè)務邏輯進行調(diào)用脖镀,并監(jiān)控其調(diào)用結果。
    6. 若請求調(diào)用成功狼电,則熔斷器進入熔斷關閉狀態(tài)(CLOSED )蜒灰,服務原來的主業(yè)務邏輯恢復,否則重新進入熔斷開啟狀態(tài)(OPEN)漫萄。
Sentinel 熔斷狀態(tài)轉(zhuǎn)換

下載Sentinel控制臺

1. 啟動Sentinel控制臺(Dashboard)
  java -jar sentinel-dashboard-xxx.jar
2. 瀏覽器訪問http://localhost:8080/卷员,輸入用戶名和密碼(默認都是 sentinel)

Github下載Sentinel控制臺

Sentinel 控制臺登錄頁

Sentinel 控制臺主頁

示例

  1. 引入Sentinel依賴
創(chuàng)建spring-cloud-alibaba-sentinel-service-8401子項目
1. 修改pom.xml文件
    <dependencies>
        <!--Nacos 服務發(fā)現(xiàn)依賴-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--Snetinel 依賴-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel-datasource-nacos 后續(xù)做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
2. 創(chuàng)建application.yml配置文件(類路徑resources目錄下)
server:
  port: 8401 #端口
spring:
  application:
    name: sentinel-service #服務名
  cloud:
    nacos:
      discovery:
        #Nacos服務注冊中心(集群)地址
        server-addr: localhost:1111
    sentinel:
      transport:
        #配置 Sentinel dashboard 地址
        dashboard: localhost:8080
        #默認8719端口奕枢,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口
        port: 8719
management:
  endpoints:
    web:
      exposure:
        include: '*'
3. 創(chuàng)建SentinelFlowLimitController.java
package com.sst.cx.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class SentinelFlowLimitController {
    @Value("${server.port}")
    private String serverPort;
    @GetMapping("/testA")
    public String testA() {
        return "服務訪問成功------testA";
    }
    @GetMapping("/testB")
    public String testB() {
        return "服務訪問成功------testB";
    }
}
4. 在主啟動類上瘪板,添加@EnableDiscoveryClient注解開啟Nacos服務發(fā)現(xiàn)功能岳链。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudAlibabaSentinelService8401Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaSentinelService8401Application.class, args);
    }
}
5. 依次啟動NacosServer集群、 spring-cloud-alibaba-sentinel-service-8401
在瀏覽器中訪問http://localhost:8401/testA岩瘦。
訪問Sentinel控制臺主頁,在“首頁”下方新增了一個“sentinel-servcie”的菜單窿撬,而這正是 spring-cloud-alibaba-sentinel-service-8401 的服務名(spring.application.name)启昧,說明 Sentinel 已經(jīng)監(jiān)控到這個服務。點擊“實時監(jiān)控”劈伴,查看sentinel-service下各請求的實時監(jiān)控數(shù)據(jù)密末。

  1. 定義資源(通過SphU、SphO、注解 定義資源)
1. 在spring-cloud-alibaba-sentinel-service-8401的SentinelFlowLimitController中严里,創(chuàng)建
testAbySphU()方法新啼、testBbySphO()方法、testC()方法刹碾,分別定義資源:testAbySphU燥撞、testBbySphO、testCbyAnnotation迷帜。
    // 通過 SphU 手動定義資源
    public String testAbySphU() {
        Entry entry = null;
        try {
            entry = SphU.entry("testAbySphU");
            // 您的業(yè)務邏輯 - 開始
            log.info("服務訪問成功------testA:"+serverPort);
            return "服務訪問成功------testA:"+serverPort;
            // 您的業(yè)務邏輯 - 結束
        } catch (BlockException e1) {
            // 流控邏輯處理 - 開始
            log.info("testA 服務被限流");
            return "testA 服務被限流";
            // 流控邏輯處理 - 結束
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }
    // 通過 SphO 手動定義資源
    public String testBbySphO() {
        if (SphO.entry("testBbySphO")) {
            // 務必保證finally會被執(zhí)行
            try {
                log.info("服務訪問成功------testB:" + serverPort);
                return "服務訪問成功------testB:" + serverPort;
            } finally {
                SphO.exit();
            }
        } else {
            // 資源訪問阻止物舒,被限流或被降級
            // 流控邏輯處理 - 開始
            log.info("testB 服務被限流");
            return "testB 服務被限流";
            // 流控邏輯處理 - 結束
        }
    }
    // 通過注解定義資源
    @GetMapping("/testC")
    @SentinelResource(value = "testCbyAnnotation") 
    public String testC() {
        log.info("服務訪問成功------testC:" + serverPort);
        return "服務訪問成功------testC:" + serverPort;
    }
    在testA()方法中直接返回testAbySphU();、在testB()方法中直接返回testBbySphO();
2. 重啟 spring-cloud-alibaba-sentinel-service-8401戏锹,
在瀏覽器中訪問http://localhost:8401/testA冠胯、http://localhost:8401/testB、http://localhost:8401/testC锦针,訪問Sentinel控制臺主頁涵叮,點擊sentinel-service下的“簇點鏈路”。
  1. 流量控制
===》1. 通過Sentinel控制臺定義流控規(guī)則
1. 在 spring-cloud-alibaba-sentinel-service-8401 下的 SentinelFlowLimitController 中伞插,新增
    // 通過 Sentinel 控制臺定義流控規(guī)則
    @GetMapping("/testD")
    public String testD() {
        log.info("服務訪問成功------testD:" + serverPort);
        return "服務訪問成功------testD:" + serverPort;
    }
2. 重啟 spring-cloud-alibaba-sentinel-service-8401割粮,
在瀏覽器中訪問http://localhost:8401/testD,點擊 sentinel-sevice 下的“簇點鏈路”,點擊“/testD”右側(cè)的“+流控”按鈕媚污,在彈出的“新增流控規(guī)則”窗口中定義流控規(guī)則(單機閥值添2舀瓢,其他默認),點擊新增按鈕耗美,跳轉(zhuǎn)到“流控規(guī)則”列表京髓。
3. 快速連續(xù)(頻率大于每秒鐘 2 次)訪問http://localhost:8401/testD,頁面會顯示Blocked by Sentinel (flow limiting)商架,說明該服務已被限流堰怨。這種提示是Sentinel系統(tǒng)自動生成的,用戶體驗不好蛇摸。
4. 在服務代碼SentinelFlowLimitController中使用 @SentinelResource注解定義資源名稱备图,并在 blockHandler屬性指定一個限流函數(shù),來自定義服務限流信息(展示給用戶):
    // 通過 Sentinel 控制臺定義流控規(guī)則
    @GetMapping("/testD")
    @SentinelResource(value = "testD-resource", blockHandler = "blockHandlerTestD")
    public String testD() {
        log.info("服務訪問成功------testD:" + serverPort);
        return "服務訪問成功------testD:" + serverPort;
    }
    // 限流之后的邏輯
    public String blockHandlerTestD(BlockException exception) {
        log.info(Thread.currentThread().getName() + "TestD服務訪問失敗! 您已被限流赶袄,請稍后重試");
        return "TestD服務訪問失敗! 您已被限流揽涮,請稍后重試";
    }
 使用@SentinelResource注解的blockHandler屬性時,需要注意以下事項:
    1. blockHandler 函數(shù)訪問范圍需要是 public饿肺;
    2. 返回類型需要與原方法相匹配蒋困;
    3. 參數(shù)類型需要和原方法相匹配并且最后加一個額外的參數(shù),類型為 BlockException敬辣;
    4. blockHandler 函數(shù)默認需要和原方法在同一個類中雪标,若希望使用其他類的函數(shù)零院,則可以指定 blockHandler 為對應的類的 Class 對象,注意對應的函數(shù)必需為 static 函數(shù)村刨,否則無法解析门粪。
    5. 請務必添加 blockHandler 屬性來指定自定義的限流處理方法,若不指定烹困,則會跳轉(zhuǎn)到錯誤頁(用戶體驗不好)玄妈。
5. 重啟 spring-cloud-alibaba-sentinel-service-8401,在瀏覽器中訪問Sentinel控制臺主頁髓梅,點擊 sentinel-sevice 下的“簇點鏈路拟蜻,點擊資源“testD-resource”右側(cè)的“+流控”按鈕,并在彈出的“新增流控規(guī)則”窗口中為這個資源定義流控規(guī)則枯饿,流控規(guī)則內(nèi)容為 :QPS 的閾值為 2(即每秒最多通過 2 個請求)
6. 快速連續(xù)(頻率大于每秒鐘 2 次)訪問http://localhost:8401/testD酝锅,結果如下:
  TestD服務訪問失敗! 您已被限流,請稍后重試


限流

限流
===》2. 通過代碼定義流控規(guī)則
在SentinelFlowLimitController中奢方,首先通過FlowRule的以下屬性來定義流控規(guī)則搔扁,然后調(diào)用FlowRuleManager類的loadRules(List<FlowRule>)方法。
  1. resource(資源名蟋字,即流控規(guī)則的作用對象)   
  2. count(限流的閾值)
  3. grade(流控閾值的類型:QPS---默認值稿蹲、并發(fā)線程數(shù))
  4. limitApp(流控針對的調(diào)用來源。默認值:default鹊奖,表示不區(qū)分調(diào)用來源)
  5. strategy(調(diào)用關系限流策略:直接--默認值苛聘、鏈路、關聯(lián)) 
  6. controlBehavior(流控效果:直接拒絕--默認值忠聚、Warm Up设哗、勻速排隊)不支持按調(diào)用關系限流。    

1. SentinelFlowLimitController 中添加一個 initFlowRules() 方法两蟀,為名為 testD-resource 的資源定義流控規(guī)則:每秒最多只能通過 2 個請求(即:QPS的閾值為 2)
    // 通過代碼定義流量控制規(guī)則
    private static void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        // 定義一個限流規(guī)則對象
        FlowRule rule = new FlowRule();
        // 資源名稱
        rule.setResource("testD-resource");
        // 限流閾值的類型
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 設置 QPS 的閾值為 2
        rule.setCount(2);
        rules.add(rule);
        // 定義限流規(guī)則
        FlowRuleManager.loadRules(rules);
    }
2. 在testD()方法中調(diào)用initFlowRules()方法网梢,初始化流控規(guī)則:
    @GetMapping("/testD")
    @SentinelResource(value = "testD-resource", blockHandler = "blockHandlerTestD")
    public String testD() {
        initFlowRules(); // 調(diào)用初始化流控規(guī)則的方法
        log.info("服務訪問成功------testD:" + serverPort);
        return "服務訪問成功------testD:" + serverPort;
    }
3. 重啟spring-cloud-alibaba-sentinel-service-8401,
使用瀏覽器訪問“http://localhost:8401/testD
  服務訪問成功------testD:8401
快速連續(xù)(頻率大于每秒鐘 2 次)訪問http://localhost:8401/testD
  TestD服務訪問失敗! 您已被限流赂毯,請稍后重試
命令查看資源的實時統(tǒng)計信息
  curl http://localhost:8719/cnode?id=testD-resource
  idx id thread    pass      blocked   success    total    aRt   1m-pass   1m-block   1m-all   exception
說明:
  1. thread: 代表當前處理該資源的并發(fā)數(shù)战虏;
  2. pass: 代表一秒內(nèi)到來到的請求;
  3. blocked: 代表一秒內(nèi)被流量控制的請求數(shù)量欢瞪;
  4. success: 代表一秒內(nèi)成功處理完的請求活烙;
  5. total: 代表到一秒內(nèi)到來的請求以及被阻止的請求總和;
  6. RT: 代表一秒內(nèi)該資源的平均響應時間遣鼓;
  7. 1m-pass: 則是一分鐘內(nèi)到來的請求;
  8. 1m-block: 則是一分鐘內(nèi)被阻止的請求重贺;
  9. 1m-all: 則是一分鐘內(nèi)到來的請求和被阻止的請求的總和骑祟;
  10. exception: 則是一秒內(nèi)業(yè)務本身異常的總和回懦。
  1. 熔斷降級
===》1. 通過Sentinel控制臺定義熔斷降級規(guī)則
創(chuàng)建spring-cloud-alibaba-api子項目
1. 修改pom.xml文件
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
2. 創(chuàng)建User.java
package com.sst.cx.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor // 無參構造函數(shù)
@Data // 提供類的get、set次企、equals怯晕、hashCode、canEqual缸棵、toString 方法
@Accessors(chain = true)
public class User implements Serializable {
    private Integer id;
    private String name;
    private String password;
}
3. 創(chuàng)建CommonResult.java
package com.sst.cx.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;
    public CommonResult(Integer code, String message) {
        this(code, message, null);
    }
}

創(chuàng)建spring-cloud-alibaba-provider-mysql-8003子項目
1. 修改pom.xml文件
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>net.biancheng.c</groupId>
            <artifactId>spring-cloud-alibaba-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--添加 Spring Boot 的監(jiān)控模塊-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
2. 創(chuàng)建application.yml配置文件(類路徑resources目錄下)
server:
  port: 8003 #端口
spring:
  application:
    name: spring-cloud-alibaba-provider-mysql
  cloud:
    nacos:
      discovery:
        server-addr: localhost:1111
  ####### 數(shù)據(jù)庫連接 #######
  datasource:
    username: root        #數(shù)據(jù)庫登陸用戶名
    password: 12345678        #數(shù)據(jù)庫登陸密碼
    url: jdbc:mysql://127.0.0.1:3306/test       #數(shù)據(jù)庫url
    driver-class-name: com.mysql.cj.jdbc.Driver
management:
  endpoints:
    web:
      exposure:
        include: "*"   # * 在yaml 文件屬于關鍵字舟茶,所以需要加引號
###### MyBatis 配置 #######
mybatis:
  # 指定 mapper.xml 的位置
  mapper-locations: classpath:mybatis/mapper/*.xml
  #掃描實體類的位置,在此處指明掃描實體類的包,在 mapper.xml 中就可以不寫實體類的全路徑名
  type-aliases-package: com.sst.cx.domain
  configuration:
    #默認開啟駝峰命名法堵第,可以不用設置該屬性
    map-underscore-to-camel-case: true
3. 創(chuàng)建UserMapper.java
package com.sst.cx.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.sst.cx.domain.User;
@Mapper
public interface UserMapper {
    // 根據(jù)主鍵獲取數(shù)據(jù)
    User selectByPrimaryKey(Integer id);
    // 獲取表中的全部數(shù)據(jù)
    List<User> getAll();
}
4. 創(chuàng)建UserMapper.xml(resources/mybatis/mapper/目錄下)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sst.cx.mapper.UserMapper">
    <sql id="Base_Column_List">
        id, name, password
    </sql>
    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultType="User">
        select
        <include refid="Base_Column_List"/>
        from users
        where id = #{id,jdbcType=INTEGER}
    </select>
    <select id="getAll" resultType="User">
        select *
        from users;
    </select>
</mapper>
7. 創(chuàng)建UserService.java
package com.sst.cx.service;
import java.util.List;
import com.sst.cx.domain.User;
public interface UserService {
    User get(Integer id);
    List<User> selectAll();
}
5. 創(chuàng)建UserServiceImpl.java
package com.sst.cx.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sst.cx.domain.User;
import com.sst.cx.mapper.UserMapper;
import com.sst.cx.service.UserService;
import java.util.List;
@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public User get(Integer id) {
        return userMapper.selectByPrimaryKey(id);
    }
    @Override
    public List<User> selectAll() {
        return userMapper.getAll();
    }
}
6. 創(chuàng)建UserController.java
package com.sst.cx.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.sst.cx.domain.*;
import com.sst.cx.service.UserService;
@RestController
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;
    @Value("${server.port}")
    private String serverPort;
    @RequestMapping(value = "/user/get/{id}", method = RequestMethod.GET)
    public CommonResult<User> get(@PathVariable("id") int id) {
        log.info("端口:" + serverPort + "\t+ user/get/");
        try {
            TimeUnit.SECONDS.sleep(1);
            log.info("休眠 1秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user = userService.get(id);
        CommonResult<User> result = new CommonResult(200, "from mysql,serverPort:  " + serverPort, user);
        return result;
    }
    @RequestMapping(value = "/user/list", method = RequestMethod.GET)
    public CommonResult<List<User>> list() {
        log.info("端口:" + serverPort + "\t+ user/list/");
        List<User> userList = userService.selectAll();
        CommonResult<List<User>> result = new CommonResult(200, "from mysql,serverPort:  " + serverPort, userList);
        return result;
    }
}
7. 在主啟動類上吧凉,添加@EnableDiscoveryClient注解開啟Nacos服務發(fā)現(xiàn)功能。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudAlibabaProviderMysql8003Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaProviderMysql8003Application.class, args);
    }
}

創(chuàng)建spring-cloud-alibaba-consumer-mysql-8803子項目
1. 修改pom.xml文件
    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入 OpenFeign 的依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</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>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.sst.cx</groupId>
            <artifactId>spring-cloud-alibaba-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
2. 創(chuàng)建application.yml配置文件(類路徑resources目錄下)
server:
  port: 8803
spring:
  application:
    name: spring-cloud-alibaba-consumer-mysql-feign
  cloud:
    nacos:
      discovery:
        server-addr: localhost:1111
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
# 以下配置信息并不是默認配置踏志,而是我們自定義的配置阀捅,目的是不在 Controller 內(nèi)硬編碼 服務提供者的服務名
service-url:
  nacos-user-service: http://spring-cloud-alibaba-provider-mysql #消費者要方位的微服務名稱
# 激活Sentinel對Feign的支持
feign:
  sentinel:
    enabled: true
3. 創(chuàng)建UserFeignService.java
package com.sst.cx.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.sst.cx.domain.*;
import java.util.List;
@Component
@FeignClient(value = "spring-cloud-alibaba-provider-mysql", fallback = UserFallbackService.class)
public interface UserFeignService {
    @RequestMapping(value = "/user/get/{id}", method = RequestMethod.GET)
    public CommonResult<User> get(@PathVariable("id") int id);
    @RequestMapping(value = "/user/list", method = RequestMethod.GET)
    public CommonResult<List<User>> list();
}
4. 創(chuàng)建UserFallbackService.java
package com.sst.cx.service;
import java.util.List;
import org.springframework.stereotype.Component;
import com.sst.cx.domain.CommonResult;
import com.sst.cx.domain.User;
@Component
public class UserFallbackService implements UserFeignService{
    @Override
    public CommonResult<User> get(int id) {
        System.err.println("--------->>>>服務降級邏輯");
        User user = new User(id, "null", "null");
        return new CommonResult(444, "服務被降級" , user);
    }
    @Override
    public CommonResult<List<User>> list() {
        System.err.println("--------->>>>服務降級邏輯");
        return null;
    }
}
5. 創(chuàng)建UserFeignController.java
package com.sst.cx.service.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.alibaba.csp.sentinel.util.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.sst.cx.domain.*;
import com.sst.cx.service.UserFeignService;
@RestController
@Slf4j
public class UserFeignController {
    @Resource
    UserFeignService userFeignService;
    // 使用@SentinelResource注解的fallback屬性指定了一個fallback函數(shù),進行熔斷降級的后續(xù)處理针余。
    @RequestMapping(value = "consumer/feign/user/get/{id}", method = RequestMethod.GET)
    @SentinelResource(value = "fallback", fallback = "handlerFallback")
    public CommonResult<User> get(@PathVariable("id") int id) {
        monitor();
        System.out.println("--------->>>>主業(yè)務邏輯");
        CommonResult<User> result = userFeignService.get(id);
        if (id == 6) {
            System.err.println("--------->>>>主業(yè)務邏輯饲鄙,拋出非法參數(shù)異常");
            throw new IllegalArgumentException("IllegalArgumentException,非法參數(shù)異常....");
        } else if (result.getData() == null) {
            System.err.println("--------->>>>主業(yè)務邏輯圆雁,拋出空指針異常");
            throw new NullPointerException("NullPointerException忍级,該ID沒有對應記錄,空指針異常");
        }
        return result;
    }
    @RequestMapping(value = "consumer/feign/user/list", method = RequestMethod.GET)
    public CommonResult<List<User>> list() {
        return userFeignService.list();
    }
    // 處理異常的回退方法(服務降級)
    public CommonResult handlerFallback(@PathVariable int id, Throwable e) {
        System.err.println("--------->>>>服務降級邏輯");
        User user = new User(id, "null", "null");
        return new CommonResult(444, "服務被降級!異常信息為:" + e.getMessage(), user);
    }
    // 自定義事件監(jiān)聽器伪朽,監(jiān)聽熔斷器狀態(tài)轉(zhuǎn)換
    public void monitor() {
        EventObserverRegistry.getInstance().addStateChangeObserver("logging",
                (prevState, newState, rule, snapshotValue) -> {
                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    if (newState == CircuitBreaker.State.OPEN) {
                        // 變換至 OPEN state 時會攜帶觸發(fā)時的值
                        System.err.println(String.format("%s -> OPEN at %s, 發(fā)送請求次數(shù)=%.2f", prevState.name(),
                                format.format(new Date(TimeUtil.currentTimeMillis())), snapshotValue));
                    } else {
                        System.err.println(String.format("%s -> %s at %s", prevState.name(), newState.name(),
                                format.format(new Date(TimeUtil.currentTimeMillis()))));
                    }
                });
    }
}
5. 在主啟動類上颤练,添加@EnableDiscoveryClient注解開啟Nacos服務發(fā)現(xiàn)功能、添加@EnableFeignClients注解開啟驱负。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SpringCloudAlibabaConsumerMysql8803Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaConsumerMysql8803Application.class, args);
    }
}
6. 依次啟動 NacosServer集群嗦玖、Sentinel控制臺、spring-cloud-alibaba-provider-mysql-8003 和 spring-cloud-alibaba-consumer-mysql-8803跃脊,
在瀏覽器中訪問http://localhost:8803/consumer/feign/user/get/1宇挫,結果如下
  {"code":200,"message":"from mysql,serverPort:  8003","data":{"id":1,"name":"zhansan","password":"123456"}}
在瀏覽器訪問“http://localhost:8803/consumer/feign/user/get/7”,結果如下
  {"code":444,"message":"服務被降級酪术!異常信息為:NullPointerException器瘪,該ID沒有對應記錄,空指針異常","data":{"id":7,"name":"null","password":"null"}}
7. 訪問Sentinel控制臺,在“簇點鏈路”列表中绘雁,點擊fallback資源的“+降級”按鈕橡疼,選擇異常數(shù),并設置異常數(shù)為1庐舟,熔斷時常(時間窗口)為10s欣除。
當熔斷器處于熔斷開啟狀態(tài)時,所有的請求都直接交給降級邏輯處理挪略。
熔斷器在經(jīng)歷了10秒的熔斷時長后历帚,自動切換到了探測恢復狀態(tài)(HALF-OPEN)滔岳,并在下一個請求成功的情況下,結束了熔斷開啟狀態(tài)挽牢,切換到了熔斷關閉狀態(tài)(CLOSED)谱煤。

【存疑:無法設置最小請求數(shù)、統(tǒng)計時長禽拔,1.70版本沒有這兩選項刘离,未進入熔斷狀態(tài)】
===》2. 通過代碼定義熔斷規(guī)則
首先通過DegradeRule類的以下屬性來定義熔斷降級規(guī)則,然后調(diào)用DegradeRuleManager類(Sentinel核心庫提供)的loadRules(List<DegradeRule> rules) 方法睹栖。
  1. resource(資源名硫惕,即規(guī)則的作用對象)      
  2. grade(熔斷策略,慢調(diào)用比例---默認磨淌、異常比例疲憋、異常數(shù)策略)  
  3. count(慢調(diào)用比例模式下為慢調(diào)用臨界RT,超出該值計為慢調(diào)用梁只;異常比例/異常數(shù)模式下為對應的閾值)     
  4. timeWindow(熔斷時長缚柳,單位為s)   
  5. minRequestAmount(熔斷觸發(fā)的最小請求數(shù),默認5搪锣,請求數(shù)小于該值時即使異常比率超出閾值也不會熔斷)1.7.0 引入【注意該屬性】
  6. statIntervalMs(統(tǒng)計時長秋忙,單位為ms,默認1000ms)1.8.0 引入
  7. slowRatioThreshold(慢調(diào)用比例閾值构舟,僅慢調(diào)用比例模式有效)1.8.0 引入

1. 在 spring-cloud-alibaba-consumer-mysql-8803的UserFeignController 中灰追,添加initDegradeRule方法定義熔斷規(guī)則(30s內(nèi)當請求數(shù)>=100且異常率大于0.7時會進入熔斷狀態(tài)10s),在UserFeignController的get()方法的開始處調(diào)用initDegradeRule()方法初始化熔斷規(guī)則狗超。
    // 初始化熔斷策略
    private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule("fallback");
        // 熔斷策略為異常比例
        rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());
        // 異常比例閾值
        rule.setCount(0.7);
        // 最小請求數(shù)
        rule.setMinRequestAmount(100);
        // 統(tǒng)計時長弹澎,單位毫秒
        rule.setStatIntervalMs(30000);
        // 熔斷時長,單位秒
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }
2. 重啟spring-cloud-alibaba-consumer-mysql-8803努咐,
在瀏覽器中訪問http://localhost:8803/consumer/feign/user/get/1苦蒿,結果如下
  {"code":200,"message":"from mysql,serverPort:  8003","data":{"id":1,"name":"zhansan","password":"123456"}}
訪問Sentinel控制主頁,點擊“降級規(guī)則”查看熔斷降級規(guī)則列表渗稍。
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佩迟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子竿屹,更是在濱河造成了極大的恐慌报强,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拱燃,死亡現(xiàn)場離奇詭異秉溉,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門坚嗜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夯膀,“玉大人诗充,你說我怎么就攤上這事苍蔬。” “怎么了蝴蜓?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵碟绑,是天一觀的道長。 經(jīng)常有香客問我茎匠,道長格仲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任诵冒,我火速辦了婚禮凯肋,結果婚禮上,老公的妹妹穿的比我還像新娘汽馋。我一直安慰自己侮东,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布豹芯。 她就那樣靜靜地躺著悄雅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铁蹈。 梳的紋絲不亂的頭發(fā)上宽闲,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音握牧,去河邊找鬼容诬。 笑死,一個胖子當著我的面吹牛沿腰,可吹牛的內(nèi)容都是我干的览徒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼矫俺,長吁一口氣:“原來是場噩夢啊……” “哼虫蝶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扣泊,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤流炕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铅匹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體押赊,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了流礁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涕俗。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖神帅,靈堂內(nèi)的尸體忽然破棺而出再姑,到底是詐尸還是另有隱情,我是刑警寧澤找御,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布元镀,位于F島的核電站,受9級特大地震影響霎桅,放射性物質(zhì)發(fā)生泄漏栖疑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一滔驶、第九天 我趴在偏房一處隱蔽的房頂上張望遇革。 院中可真熱鬧,春花似錦揭糕、人聲如沸萝快。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杠巡。三九已至,卻和暖如春雇寇,著一層夾襖步出監(jiān)牢的瞬間氢拥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工锨侯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嫩海,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓囚痴,卻偏偏與公主長得像叁怪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子深滚,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容