Sentinel使用教程

一、Sentinel简介

1.sentinel介绍

Sentinel 是由阿里巴巴中间件团队开发的开源项目,是一种面向分布式微服务架构的轻量级高可用流量控制组件。

2.sentinel应用场景

Sentinel 主要以流量为切入点,从流量控制、熔断降级、系统负载保护、实时监控和控制台等多个维度来帮助用户提升服务的稳定性。

3.sentinel与hystrix

Sentinel Hystrix
隔离策略 基于并发数 线程池隔离/信号量隔离
熔断降级策略 基于响应时间或失败比率 基于失败比率
实时指标实现 滑动窗口 滑动窗口(基于 RxJava)
规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件的形式
基于注解的支持 即将发布 支持
调用链路信息 支持同步调用 不支持
限流 基于 QPS / 并发数,支持基于调用关系的限流 不支持
流量整形 支持慢启动、匀速器模式 不支持
系统负载保护 支持 不支持
实时监控 API 各式各样 较为简单
控制台 开箱即用,可配置规则、查看秒级监控、机器发现等 不完善
常见框架的适配 Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet、Spring Cloud Netflix

4.sentinel组件介绍

(1)Sentinel 组成
①Sentinel 核心库:Sentinel 的核心库不依赖任何框架或库,能够运行于 Java 8 及以上的版本的运行时环境中,同时对 Spring Cloud、Dubbo 等微服务框架提供了很好的支持。
②Sentinel 控制台(Dashboard):Sentinel 提供的一个轻量级的开源控制台,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。
(2)基本概念
①资源:资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如由应用程序提供的服务或者是服务里的方法,甚至可以是一段代码。
Sentinel 定义资源的方式有下面几种:适配主流框架自动定义资源、通过 SphU 手动定义资源、通过 SphO 手动定义资源、注解方式定义资源。这个稍后会有使用方法教程。

其中注解方式定义资源@SentinelResource参数介绍如下:

参数 解释
value Sentinel资源的名称,我们不仅可以通过url进行限流,也可以把此值作为资源名配置,一样可以限流。
entryType 条目类型(入站或出站),默认为出站(EntryType.OUT)
resourceType 资源的分类(类型)
blockHandler 块异常函数的名称,默认为空
blockHandlerClass 指定块处理方法所在的类。默认情况下, blockHandler与原始方法位于同一类中。 但是,如果某些方法共享相同的签名并打算设置相同的块处理程序,则用户可以设置存在块处理程序的类。 请注意,块处理程序方法必须是静态的。
fallback 后备函数的名称,默认为空
defaultFallback 默认后备方法的名称,默认为空
defaultFallback用作默认的通用后备方法。 它不应接受任何参数,并且返回类型应与原始方法兼容
fallbackClass fallback方法所在的类(仅单个类)。默认情况下, fallback与原始方法位于同一类中。 但是,如果某些方法共享相同的签名并打算设置相同的后备,则用户可以设置存在后备功能的类。 请注意,共享的后备方法必须是静态的。
exceptionsToTrace 异常类的列表追查,默认 Throwable
exceptionsToIgnore 要忽略的异常类列表,默认情况下为空

②规则:围绕资源而设定的规则。Sentinel 支持流量控制、熔断降级、系统保护、来源访问控制和热点参数等多种规则,所有这些规则都可以动态实时调整。

二、Sentinel使用说明

1.控制台Dashboard

(1)下载地址,选择sentinel-dashboard-1.8.6.jar

下载即可。
(2)打开命令窗口,进入jar包存放目录,使用java -jar sentinel-dashboard-1.8.2.jar进行服务启动即可。
(3)登录网址:http://localhost:8080/,用户名密码为sentinel/sentinel
在这里插入图片描述

2.Sentinel 流量控制和熔断降级

(1)首先我们需要构建一个SpringBoot项目,项目结构如下:
在这里插入图片描述

(2)然后在pom.xml引入sentinel的依赖。

  <!--        sentinel依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2021.1</version>
        </dependency>

(3)application.yml配置

server:
  port: 8800
spring:
  #允许循环依赖
  main:
    allow-circular-references: true
  application:
    name: sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
        #指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer,
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        #port: 8719

#暴露/actuator/sentinel端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

(4)SentinelApplication.java,我这里没有修改

package com.example.sentinel;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SentinelApplication {
    
    

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

}

(5)SentinelServerController.java,其中test()是对springboot的联通测试,resource1()是SphU定义资源测试,resource2()是流量监控测试,resource3()是熔断测试。

package com.example.sentinel.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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 org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.text.SimpleDateFormat;
import java.util.Date;

@Controller
public class SentinelServerController {
    
    


    @RequestMapping(value = "/test")
    @ResponseBody
    public String test() {
    
    
        return "Sentinel server";
    }



    @RequestMapping(value = "/resource1")
    @ResponseBody
    public String resource1() {
    
    
        return resource1bySphU();
    }

    /**
     * 通过 SphU 手动定义资源
     * @return
     */
    public String resource1bySphU() {
    
    
        Entry entry = null;
        try {
    
    
            entry = SphU.entry("resource1bySphU");
            //您的业务逻辑 - 开始
            return "resource1";
            //您的业务逻辑 - 结束
        } catch (BlockException e1) {
    
    
            //流控逻辑处理 - 开始
            return "resource1 limit";
            //流控逻辑处理 - 结束
        } finally {
    
    
            if (entry != null) {
    
    
                entry.exit();
            }
        }
    }

    //
    @RequestMapping(value = "/resource2")
    //blockHandler 限流后走的方法
    @SentinelResource(value="resource2byAnnotation",
            blockHandler = "resource2Limit",fallback = "resource2Fallback")
    @ResponseBody
    public String resource2()  {
    
    
        return "resource2";
    }

    public String resource2Limit(BlockException exception){
    
    
        return "您点击太快了,稍后重试!";
    }

    @RequestMapping(value = "/resource3/{id}")
    //抛出异常时,提供 fallback 处理逻辑
    //处于熔断开启状态时,原来的主逻辑则暂时不可用,会走fallback的逻辑。
    //在经过一段时间(熔断时长)后,熔断器会进入探测恢复状态(HALF-OPEN),此时 Sentinel 会允许一个请求对原来的主业务逻辑进行调用
    //若请求调用成功,则熔断器进入熔断关闭状态(CLOSED ),服务原来的主业务逻辑恢复,否则重新进入熔断开启状态(OPEN)
    @SentinelResource(value="resource3byAnnotation",fallback = "resource3Fallback")
    @ResponseBody
    public String resource3(@PathVariable("id") int id)  {
    
    
        monitor();
        System.out.println("主逻辑");
        //这里模拟服务报错
        int defaultId = 5;
        if(id < defaultId){
    
    
            throw new RuntimeException ("服务异常");
        }
        return "resource3";
    }

    //注意fallback返回值类型必须与原函数一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
    //fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
    public String resource3Fallback (int id){
    
    
        return "服务出错,请您先访问这里!";
    }

    /**
     * 自定义事件监听器,监听熔断器状态转换
     */
    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 时会携带触发时的值
                        System.err.println(String.format("%s -> OPEN at %s, 发送请求次数=%.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()))));
                    }
                });
    }


}





(6)测试流程
首先启动springBoot项目,测试resource1方法是否能正常调通。此时在控制台可以看到服务已经监测成功。
在这里插入图片描述

在这里插入图片描述
然后测试resource2方法,先测试resource2方法是否能正常调通。然后在控制台的簇点链路的resource2对应的服务中选择新增流控指标,这里是每秒调用两次就会触发。
在这里插入图片描述
新增完后在快速点击该链接,发现流控实现成功
在这里插入图片描述
最后测试resource3方法,先测试resource3方法是否能正常调通。我这里设置传参5以上服务调用成功,否则失败。
在这里插入图片描述
然后在控制台新增熔断设置,选择策略为异常数,这里代表在一秒内有两个以上请求失败两次以上则会熔断。
在这里插入图片描述
可以看到服务调用失败时成功触发了熔断机制,并且正确的调用和错误的调用重复几次,可以看到熔断状态的变化

在这里插入图片描述

在这里插入图片描述

3.常见报错解决

(1)dashboard控制器启动失败:Web server failed to start. Port 8080 was already in use
找到该端口的id然后杀掉该进程即可

 netstat -aon|findstr "8080"
 taskkill /pid 11596 /f

(2)The dependencies of some of the beans in the application context form a cycle:

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
┌─────┐
| com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration (field private java.util.Optional com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration.sentinelWebInterceptorOptional)
└─────┘

这是循环依赖问题,由于springBoot版本与cloud版本不匹配导致的,可以参考版本说明
实在没有合适的版本可以采取粗暴的方式直接允许循环依赖。

spring:
  #允许循环依赖
  main:
    allow-circular-references: true

猜你喜欢

转载自blog.csdn.net/tttalk/article/details/127884281