Spring Cloud Alibaba Sentinel (Hoxton版) 使用

Spring Cloud Hoxton.SR4 Spring Cloud Alibaba 2.2.2.RELEASE Spring Boot 2.3.0.RELEASE

GitHub:shpunishment/spring-cloud-learning/spring-cloud-sentinel-test

1. 简介

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,Sentinel 作为其核心组件之一,具有熔断与限流等一系列服务保护功能。

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  1. 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  2. 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  3. 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  4. 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

主要特性

2. 安装

GitHub 下载 sentinel-dashboard

启动Sentinel控制台,访问http://ip:8400。默认账号密码为sentinel。

java -Dserver.port=8400 -jar sentinel-dashboard-1.8.0.jar

3. 使用

先用IDEA创建一个Spring Boot的项目,可以随意引用一个Spring Cloud的组件,之后也会删掉。

创建完,删掉除了pom.xml以外的其他文件,再修改pom.xml

<?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 https://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.3.0.RELEASE</version>
    </parent>
    <groupId>com.shpun</groupId>
    <artifactId>spring-cloud-sentinel-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-sentinel-test</name>
    <description>spring cloud sentinel test</description>
    <!--修改打包方式为pom-->
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Hoxton.SR4</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <modules>
        <!--后续添加子模块用-->
    </modules>

    <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>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

3.1 Nacos

官网下载

使用Windows启动时报错,修改startup.cmd的mode从cluster为standalone,还是启动失败。

所以这里使用Linux。

使用单体模式启动Nacos,访问http://ip:8848/nacos。默认账号密码为nacos。

./bin/startup.sh -m standalone

3.2 sentinel-service

创建子模块 sentinel-service

修改pom继承

<parent>
    <groupId>com.shpun</groupId>
    <artifactId>spring-cloud-sentinel-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

再添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

修改application.yml

server:
  port: 8200

spring:
  application:
    name: sentinel-service
  cloud:
    nacos:
      # nacos地址
      server-addr: http://192.168.110.40:8848
    sentinel:
      # 取消Sentinel控制台懒加载
      eager: true
      transport:
        # Sentinel 控制台地址
        dashboard: http://192.168.110.40:8400
        # 当前服务运行的ip
        client-ip: 192.168.4.23
        port: 8719

在启动类上添加@EnableDiscoveryClient注解表明是一个服务发现的客户端。

4. 测试

4.1 限流

Sentinel 默认为所有的 HTTP 服务提供了限流埋点,也可以通过使用@SentinelResource自定义限流行为。

@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {

    /**
     * 按url限流,有默认的限流处理逻辑
     *
     * @return
     */
    @SentinelResource(value = "url")
    @GetMapping("/url")
    public ResultVo<?> url() {
        return ResultVo.ok(200, "/url 请求成功");
    }

    /**
     * 按资源名称限流,注意资源名称要相同。
     * 需要指定限流处理逻辑。
     *
     * @return
     */
    @SentinelResource(value = "resource", blockHandler = "handleException")
    @GetMapping("/resource")
    public ResultVo<?> resource() {
        return ResultVo.ok(200, "/resource 请求成功");
    }

    /**
     * 按资源名称限流处理,方法的访问修饰符需为public
     * @param exception
     * @return
     */
    public ResultVo<?> handleException(BlockException exception) {
        return ResultVo.failure(500, "handleException 资源:" + exception.getRule().getResource() + " 限流成功");
    }

    /**
     * 按资源名称限流,注意资源名称要相同。
     * 自定义限流处理逻辑,指定类CustomBlockHandler和其中的static方法handleException2。
     *
     * @return
     */
    @SentinelResource(value = "resource2", blockHandlerClass = CustomBlockHandler.class, blockHandler = "handleException2")
    @GetMapping("/resource2")
    public ResultVo<?> resource2() {
        return ResultVo.ok(200, "/resource2 请求成功");
    }
}

自定义BlockHandler

public class CustomBlockHandler {
    public static ResultVo<?> handleException2(BlockException exception) {
        return ResultVo.failure(500, "handleException2 资源:" + exception.getRule().getResource() + " 限流成功");
    }
}

测试

根据url限流:
根据url限流
添加url流控规则
添加url流控规则
限流成功,返回默认信息
限流成功

根据资源名称限流:
根据资源名称限流
添加资源名称流控规则
添加资源名称流控规则
资源被限流,返回自定义信息
资源被限流
添加资源名称resource2的流控规则,使用自定义限流处理逻辑。
使用自定义限流处理逻辑

4.2 熔断

Sentinel 支持对服务间调用进行保护,对故障应用进行熔断操作。

这里使用RestTemplate来调用user-service服务所提供的接口。

4.2.1 user-service

创建子模块user-service

修改pom继承

<parent>
    <groupId>com.shpun</groupId>
    <artifactId>spring-cloud-sentinel-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

再添加依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
    <scope>runtime</scope>
</dependency>

修改application.yml

server:
  port: 8101

spring:
  application:
    name: user-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:4306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        # nacos地址
        server-addr: http://192.168.110.40:8848

mybatis:
  typeAliasesPackage: com.shpun.model
  mapper-locations: classpath:mapper/**.xml

Model,Mapper,Service等省略。

UserController 完成对User的CURD接口。

@RequestMapping("/api/user")
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/add")
    public ResultVo<?> add(@RequestBody User user) {
        userService.insertSelective(user);
        return ResultVo.ok();
    }

    @GetMapping("/delete/{userId}")
    public ResultVo<?> delete(@PathVariable("userId") Integer userId) {
        userService.deleteByPrimaryKey(userId);
        return ResultVo.ok();
    }

    @PostMapping("/update")
    public ResultVo<?> update(@RequestBody User user) {
        userService.updateByPrimaryKeySelective(user);
        return ResultVo.ok();
    }

    @GetMapping("/{userId}")
    public ResultVo<User> get(@PathVariable("userId") Integer userId) {
        User user = userService.selectByPrimaryKey(userId);
        return ResultVo.okData(user);
    }
}

在启动类上添加@EnableDiscoveryClient注解表明是一个服务发现的客户端。

4.2.2 sentinel-service

添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

修改application.yml

service-url:
  user-service: http://user-service

RibbonConfig

@Configuration
public class RibbonConfig {
    @Bean
    // 赋予RestTemplate负载均衡的能力
    @LoadBalanced
    // 添加Sentinel支持
    @SentinelRestTemplate
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

UserRibbonServiceImpl 使用RestTemplate请求user-service

@Service("userRibbonService")
public class UserRibbonServiceImpl implements UserRibbonService {

    @Autowired
    private RestTemplate restTemplate;

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

    @Override
    public ResultVo<?> add(User user) {
        return restTemplate.postForObject(userServiceUrl + "/api/user/add", user, ResultVo.class);
    }

    @Override
    public ResultVo<?> delete(Integer userId) {
        return restTemplate.getForObject(userServiceUrl + "/api/user/delete/{0}", ResultVo.class, userId);
    }

    @Override
    public ResultVo<?> update(User user) {
        return restTemplate.postForObject(userServiceUrl + "/api/user/update", user, ResultVo.class);
    }

    @Override
    public ResultVo<?> get(Integer userId) {
        return restTemplate.getForObject(userServiceUrl + "/api/user/{0}", ResultVo.class, userId);
    }

    @SentinelResource(value = "fallback", fallback = "fallback")
    @Override
    public ResultVo<?> getForTestFallback(Integer userId) {
        return this.get(userId);
    }

    /**
     * fallback 方法访问修饰符需要为 public
     * @param userId
     * @return
     */
    public ResultVo<?> fallback(Integer userId) {
        return ResultVo.failure(500, "fallback(" + userId + ") 服务调用异常");
    }

    @SentinelResource(value = "fallbackException", fallback = "fallback2", exceptionsToIgnore = { NullPointerException.class })
    @Override
    public ResultVo<?> getForTestFallbackWithIgnoreExceptions(Integer userId) {
        if (userId == 1) {
            throw new IndexOutOfBoundsException();
        } else if (userId == 2) {
            throw new NullPointerException();
        }
        return this.get(userId);
    }

    /**
     * fallback 方法访问修饰符需要为 public
     * @param userId
     * @param e
     * @return
     */
    public ResultVo<?> fallback2(Integer userId, Throwable e) {
        return ResultVo.failure(500, "fallback2(" + userId + ") 服务调用异常。异常:" + e.getClass());
    }
}

测试

@RequestMapping("/api/user")
@RestController
public class UserSentinelController {
    @Autowired
    private UserRibbonService userRibbonService;

    @GetMapping("/fallback/{userId}")
    public ResultVo<?> fallback(@PathVariable("userId") Integer userId) {
        return userRibbonService.getForTestFallback(userId);
    }

    @GetMapping("/fallbackWithIgnore/{userId}")
    public ResultVo<?> fallbackWithIgnoreExceptions(@PathVariable("userId") Integer userId) {
        return userRibbonService.getForTestFallbackWithIgnoreExceptions(userId);
    }
}

正常请求
正常请求
关掉user-service,熔断了,走服务降级。
走服务降级
未在忽略异常中,走服务降级。
未在忽略异常中,走服务降级
在忽略异常中,未走服务降级,直接出错。
在忽略异常中,未走服务降级,直接出错

4.3 结合OpenFeign

已下代码在sentinel-service中修改。

添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

修改application.yml

feign:
  sentinel:
    # 开启Sentinel
    enabled: true

UserFeignService 注解@FeignClient中value指定这是对user-service服务的接口调用客户端,fallback指定服务降级实现类

@FeignClient(value = "user-service", fallback = UserFeignServiceFallback.class)
public interface UserFeignService {
    @PostMapping("/api/user/add")
    ResultVo<?> add(@RequestBody User user);

    @GetMapping("/api/user/delete/{userId}")
    ResultVo<?> delete(@PathVariable("userId") Integer userId);

    @PostMapping("/api/user/update")
    ResultVo<?> update(@RequestBody User user);

    @GetMapping("/api/user/{userId}")
    ResultVo<User> get(@PathVariable("userId") Integer userId);
}

UserFeignServiceFallback 服务降级实现类

@Component
public class UserFeignServiceFallback implements UserFeignService {
    @Override
    public ResultVo<?> add(User user) {
        return ResultVo.failure(500, "add 服务调用异常");
    }

    @Override
    public ResultVo<?> delete(Integer userId) {
        return ResultVo.failure(500, "delete 服务调用异常");
    }

    @Override
    public ResultVo<?> update(User user) {
        return ResultVo.failure(500, "update 服务调用异常");
    }

    @Override
    public ResultVo<User> get(Integer userId) {
        return ResultVo.build(500, null,"get 服务调用异常");
    }
}

在启动类上添加@EnableFeignClients注解启用Feign的客户端功能。

测试

@RequestMapping("/api/user")
@RestController
public class UserFeignController {

    @Autowired
    private UserFeignService userFeignService;

    @PostMapping("/add")
    public ResultVo<?> add(@RequestBody User user) {
        return userFeignService.add(user);
    }

    @GetMapping("/delete/{userId}")
    public ResultVo<?> delete(@PathVariable("userId") Integer userId) {
        return userFeignService.delete(userId);
    }

    @PostMapping("/update")
    public ResultVo<?> update(@RequestBody User user) {
        return userFeignService.update(user);
    }

    @GetMapping("/{userId}")
    public ResultVo<User> get(@PathVariable("userId") Integer userId) {
        return userFeignService.get(userId);
    }
}

关掉user-service,走服务降级。
走服务降级

4.4 使用Nacos存储规则

默认情况下,Sentinel推送为原始模式。在Sentinel控制台中配置的规则是通过API将规则推送到客户端的内存中,只要重启服务就会消失。
原始模式
使用Push模式,将配置配置在配置中心中,由控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是:配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel

可使用Nacos将规则持久化,在配置中心创建规则,配置中心将规则推送到客户端。Sentinel控制台也从配置中心去获取配置信息。
push模式

已下代码在sentinel-service中修改。

添加依赖

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

修改application.yml

spring:
  application:
    name: sentinel-service
  cloud:
    nacos:
      # nacos地址
      server-addr: http://192.168.110.40:8848
    sentinel:
      # 取消Sentinel控制台懒加载
      eager: true
      transport:
        # Sentinel 控制台地址
        dashboard: http://192.168.110.40:8400
        # 当前服务运行的ip
        client-ip: 192.168.4.23
        port: 8719
      # 添加Nacos数据源配置
      datasource:
        ds1-nacos:
          nacos:
            server-addr: 192.168.110.40:8848
            username: nacos
            password: nacos
            namespace: public
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

在Nacos中添加配置
在Nacos中添加配置

[
    {
        "resource": "resource",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

参数含义:
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。

重启sentinel-service,打开Sentinel控制台。Sentinel控制台已经有了限流规则:
Sentinel控制台已经有了限流规则
对资源resource的限流也生效。
对资源resource的限流也生效

参考:
Spring Cloud Alibaba Sentinel 官方文档
Spring Cloud入门-Sentinel实现服务限流、熔断与降级(Hoxton版本)
sentinel持久化到nacos报错FactoryBean threw exception on object creation; nested exception is null

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud Alibaba Sentinel 是一个流量控制、熔断降级框架,主要用于保护微服务应用程序的稳定性和可靠性。下面是 Sentinel 使用的简单步骤: 1. 在项目的 pom.xml 文件中添加 Sentinel 的依赖: ```xml <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.2.2.RELEASE</version> </dependency> ``` 2. 在启动类上添加 @EnableDiscoveryClient、@EnableFeignClients、@EnableCircuitBreaker、@EnableHystrixDashboard、@EnableSentinelDashboard 注解。 ```java @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker @EnableHystrixDashboard @EnableSentinelDashboard public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. 在 application.yml 中配置 Sentinel: ```yaml spring: cloud: sentinel: transport: dashboard: localhost:8080 ``` 其中 `localhost:8080` 是 Sentinel Dashboard 的地址。 4. 在 Controller 或 Service 的方法上添加 Sentinel 注解,例如: ```java @GetMapping("/hello") @SentinelResource(value = "hello", blockHandler = "blockHandler") public String hello() { return "Hello World"; } public String blockHandler(BlockException ex) { return "Blocked by Sentinel"; } ``` 其中 `@SentinelResource` 注解用于定义资源名称,并且可以指定 blockHandler 方法,当资源被限流或降级时,将会调用该方法。 5. 启动 Sentinel Dashboard,使用浏览器访问 `http://localhost:8080` 即可查看 Sentinel 监控面板。 以上是 Spring Cloud Alibaba Sentinel 的简单使用步骤,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值