SpringCloud-Alibaba-Sentinel(1)初探

Sentinel 是什么?

Sentinel 具有以下特征:

丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 的主要特性:

Sentinel 的开源生态:

我们大概叙述后开始整合我们的分布式项目,我这里里为一个简单分布式项目,注册中心为Nacos,一个服务提供者(user-center)和一个服务消费者(content-center)

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareService {
    private final ShareMapper shareMapper;
    private final RestTemplate restTemplate;

    public ShareDTO findById(Integer id) {
        // 获取分享详情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 发布人id
        Integer userId = share.getUserId();

        UserDTO userDTO = this.restTemplate.getForObject(
            "http://user-center/users/{userId}",
            UserDTO.class, userId
        );

        ShareDTO shareDTO = new ShareDTO();
        // 消息的装配
        BeanUtils.copyProperties(share, shareDTO);
//        shareDTO.setWxNickname(userDTO.getWxNickname());
        return shareDTO;
    }

  
}

可以看到我的content-center调用user-center/users服务

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/shares")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareConroller {
    private final ShareService shareService;

    @GetMapping("/{id}")
    public ShareDTO findById(@PathVariable Integer id) {
        return this.shareService.findById(id);
    }
}

我们现在只启动content-center不启动user服务,可想而知肯定报错,我们来看

没错500,找不到user-center服务,我们现在整合Sentinel,首先加入两个依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

actuator配合使用,可通过http://localhost:8010/actuator/sentinel端点进行查看

{
    "blockPage":null,
    "appName":"content-center",
    "consoleServer":"localhost:8333",
    "coldFactor":"3",
    "rules":{
        "systemRules":[
​
        ],
        "authorityRule":[
​
        ],
        "paramFlowRule":[
​
        ],
        "flowRules":[
​
        ],
        "degradeRules":[
​
        ]
    },
    "metricsFileCharset":"UTF-8",
    "filter":{
        "order":-2147483648,
        "urlPatterns":[
            "/*"
        ],
        "enabled":true
    },
    "totalMetricsFileCount":6,
    "datasource":{
​
    },
    "clientIp":"192.168.2.29",
    "clientPort":"8719",
    "logUsePid":false,
    "metricsFileSize":52428800,
    "logDir":"/Users/zhangguowei/logs/csp/",
    "heartbeatIntervalMs":10000
}

我们暂时先不要管这些是什么我们后面再详细阐述,从JSON的形式看不太方便,Sentinel为我们整合有UI界面

整合Sentinel可视化界面

下载 sentinel的jar包然后直接启动https://github.com/alibaba/Sentinel/releases

启动的时候可以自定义端口号

账号密码默认为sentinel,在项目的yml中加入

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/content_center
    hikari:
      username: ***
      password: ******
      driver-class-name: com.mysql.cj.jdbc.Driver
  cloud:
    sentinel:
      transport:
        # 指向sentinel UI界面
        dashboard: localhost:8333
    nacos:
      discovery:
        # 指定nacos server的地址
        server-addr: localhost:8848
  application:
    # 服务名称尽量用-,不要用_,不要用特殊字符
    name: content-center
server:
  port: 8010
management:
  endpoints:
    web:
      exposure:
        include: "*"

这个时候我们继续刚刚的500再执行几次会看到

相对于原来的Hystrix 的UI界面 Sentinel更人性化

他支持QPS的查看,请求时间等等 

那么我们首先来探讨流控规则,我们现在把user服务也启动,我们点击簇点链路

  

可以看到content曾经访问过的路径,我们点击流控按钮,就可以为这路径设置流控规则

我们可以看到资源名默认为我们请求的路径,其实也不一定是路径,他只是做唯一标识,

针对来源:Sentinel可以针对调用者进行限流控制,比如两个服务调用content服务,我可以把针对来源写为A然后设置QPS的阈值为200B300QPS,也就是我当前访问的API接口对不同的服务有不同的限流规则,那么就可以用针对来源来设置,我们暂时用default,标识不区分来源,后面详细说如何区分来源。

阈值类型有两个选项,分别是QPS和线程数,如果选择QPS然后设置阈值,就是当这个API达到这个QPS阈值时会进行限流,同理也可以使用线程数限流,当达到一定的线程数的时候进行限流,是否集群我们在后面会详细探讨。

流控模式,sentinel提供了三种流控模式,我们先来看直接我们设置直接并保存

可以看到访问一次直接再次访问就直接限流了。那关联是什么意思,也就是说当关联的资源达到阈值,就限流自己,我们来尝试,刚刚上面所说我们可以用http://localhost:8010/actuator/sentinel 来查看信息,我们用/shares/1关联actuator/sentinel

 

它表示actuator/sentinel这个端点达到这个阈值那么就限流/shares/1这个路径,

我们来写段测试代码

import org.springframework.web.client.RestTemplate;

public class SentinelTest {

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        for (int i = 0; i <10000 ; i++) {
            String forObject = restTemplate.getForObject("http://localhost:8010/actuator/sentinel", String.class);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
}

理论上这样调用的话QPS肯定大于1,那么此时/shares/1会被限流

此时/shares/1会一直处于限流状态,他适用于什么场景:比如一个请求查询shares一个来修改shares处于高并发状态,查询过快的话会影响修改的性能,修改快的话会影响查询性能,所以这个时候可以根据业务场景,是希望优先读还是优先改,如果我们希望优先改,我们可以把关联的资源写成修改的API,资源名设置为查询API,这样修改超过阈值的话查询就会被限流。总的来说关联就是保护关联资源的设计。

我们继续来说第三个模式

点击链路会变成入口资源,链路:只记录指定链路上的流量,指定资源从入口资源进来的流量达到阈值就会限流,我们继续写代码来测试

这里用到@SentinelResource注解,后续我们会说到

我们访问后点击簇点链路

会发现有testatestb

以及common

我们在common上面点击流控  

 

我们把入口写testa

第一次请求正常请求

请求多次后直接500被限流而testb无论怎么请求都会正常请求,

这其实就是细粒度的针对来源,我们上面所说的针对来源是微服务级别而链路是API级别的。那现在我们三种流控模式都尝试完毕,我们恢复成直接状态,最后我们来探讨流控效果

快速失败很好理解,就是直接抛异常,在测试当中我们也看到了,他的源码在com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController 目录中

第二种叫Warm Up 

什么意思呢?就是说sentinel也有个默认值为3,从阈值/3,经过预热时长,才到达设置的QPS阈值。也就是说,我把阈值设置为100我把预热时长设置为10

那么他就会用1003作为最初的阈值,然后经过10秒之后才去达到这个100限流。比如有一个秒杀微服务,平常的并发并不是很高,但是在某一个瞬间,某一场秒杀活动开始,在开始的瞬间流量会激增,如果不做任何处理,可能服务一下就宕了,所以Sentinel设计Warm Up这种模式,可以让允许通过的流量缓慢的增加,在经过10秒之后才达到设定的阈值,这样就会更好的保护我们的微服务

相关源码在:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

我们来看最后一种效果

 

这个是排队等待,这个也很好理解就是 匀速排队,让请求以均匀的速度通过,但是阈值的类型必须设置为QPS,否则都无效了

也就说比如我阈值10060只允许阈值为100,其他的请求在后面排队直到设置的超时时间,如果过了超时时间求丢弃这次请求,这里我就不再做实验,他比较适用于突发流量场景,比如你的系统突然来了好多流量,一会就比较少,你希望应用在空闲的时候处理请求而不直接拒绝就可以使用排队等待。这个模式的源码路径为:

com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

暂时先说到这里 sentinel提供很多非常灵活,而且还有强大的限流规则。

原文地址:https://www.cnblogs.com/zgwjava/p/11211525.html