Sentinel(笔记)

一、Sentinel与雪崩问题

1.1 雪崩问题与解决方案

  • 服务A依赖服务B
  • 服务B故障,导致A阻塞
  • 导致A不会释放tomcat连接
  • 长期则导致A资源耗尽,A故障
  • 依赖A的服务同理

于是类似滚雪球,故障的范围越来越大。

处理思路:

  1. 超时处理:设定超时时间,请求超过一定时间没有响应则返回错误信息,不会无休止等待。该方式为缓解作用。
  2. 舱壁模式:限定每个业务能使用的线程数目,避免耗尽整个tomcat的资源,因此也就线程隔离。浪费了一些资源。
  3. 熔断降级:有断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
  4. 流量控制:限制业务访问的QPS,避免服务因流量的突增而故障。QPS:每秒钟请求的数量。预防服务故障。

Sentinel来控制QPS。

1.2 Sentinel 与 Hystrix

  • 线程池隔离:给每一个独立的业务都会有个独立的线程池,通过单独的线程池来限制。
  • 信号量隔离:统计当前业务使用了几个线程,并限制业务只能使用几个线程,一但超过就被限制。
  • 基于慢调用比例:依据业务大多数情况下处理得是否很慢,来判断是否熔断。如果该业务大多数情况下都很忙,则认为会拖垮整个服务,因此在请求很多时熔断它。
  • 流量整形:指让突发流量变为匀速流量,让服务处理起来较轻松。
SentinelHystrix
隔离策略信号量隔离线程池隔离/信号量隔离
熔断降级策略基于慢调用比例或异常比例基于失败比率
实时指标实现滑动窗口滑动窗口(基于RxJava)
规则配置支持多种数据源支持多种数据源
扩展性多个扩展点插件的形式
基于注解的支持支持支持
限流基于QPS,支持基于调用关系的限流有限的支持
流量整形支持慢启动、匀速排队模式不支持
系统自适应保护支持不支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等支持查看,不支持动态配置,不完善
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC等Servlet、Spring Cloud Netflix

1.3 安装部署

alibaba/Sentinel
下载如下文件即可。
在这里插入图片描述
下载完成解压后,就可以直接用运行即可
账号密码都是sentinel

java -jar sentinel-dashboard-1.8.1.jar

1.4 整合

在SpringBoot的消费中,导入如下依赖

		<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

设置yml文件

spring:
    sentinel:
      transport:
        dashboard: localhost:8080

如果启动不了或者显示循环依赖,请参照下面版本进行调整
版本说明

如果还是不行,添加如下语句,允许循环依赖

spring:
  main:
    allow-circular-references: true

配置完成后,我们使用消费者多次访问提供者后,再查看Sentinel,可以看到如下结果。
在这里插入图片描述

在这里插入图片描述

1.5 限流规则

  • 簇点链路:项目内的调用链路。比如controller->service->mapper,就是一个链路。链路中被检控的每个接口就是一个资源,比如默认Sentinel会监控SpringMVC中的每一个端点——Controller中的方法。

流量控制、熔断等都是针对簇点链路中的资源拉设置的。

用设置流控,单机阈值为5,然后用Jemeter测试
在这里插入图片描述
在这里插入图片描述
结果:
在这里插入图片描述
在这里插入图片描述

1.5.1 流控模式

  • 直接:统计当前资源的请求,出发阈值时,对当前资源直接限流,默认模式
  • 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
  • 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

1.5.2 关联

一般用于竞争关系,一个优先级较高,一优先级低。但优先级高的触发阈值时,优先级低的被限制。

我们为消费者控制层新增两个方法

 	@GetMapping("/query")
    public String queryOrder(){
        return "查询";
    }

    @GetMapping("/update")
    public String updateOrder(){
        return "更新";
    }
}

访问一下,后
在这里插入图片描述
我们想让update达到一定次数后,query被限流。
我们想要给谁限流,就给谁加规则。
在这里插入图片描述

现在进行测试
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.5.3 链路

  • 当达到阈值时,限制某一条链路。可以理解为,某一个方法被很多模块调用,可以通过限制被哪一个模块调用的数量,来保证他模块顺利调用。
  • 有查询订单和创建订单业务,两者都要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。

在Service层添加一个查询的方法

	/**
     * 手动标记该方法为资源
     * */
    @SentinelResource("goods")
    @Override
    public void queryGoods() {
        System.out.println("查询商品");
    }

为控制层添加

	@GetMapping("/query")
    public String queryOrder(){
        orderService.queryGoods();
        return "查询";
    }

    @GetMapping("/save")
    public String saveOrder(){
        orderService.queryGoods();
        return "新增完成";
    }

修改

    sentinel:
      transport:
        dashboard: localhost:8080
      web-context-unify: false #关闭context整合,Sentinel默认会把Controller的方法做context。导致链路模式的流量失效

接下来访问一下,
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

分别请求
在这里插入图片描述

可以看到,query被限制,但是update没有被限制
在这里插入图片描述
query
在这里插入图片描述

update
在这里插入图片描述

1.5.4 控制效果

  • 快速失败:达到阈值后,请求被立即拒绝,并抛出FlowException异常,为默认处理方式。
  • 预热模式 warm up:对超出阈值的请求,同样是拒绝跑出异常。但它的阈值会动态变化,从一个较小值逐渐增加到最大阈值。
    • 请求初始阈值 = threshold(最大阈值)/coldFacotor(冷启动因子),持续指定时长后,逐渐提高到threshold值。coldFacotor默认是3,即刚开始阈值为1/3*threshold,在设定的预热时间过后,会变为threshold。
  • 排队等待:让所有的请求进入一个队列中,然后按照阈值允许的时间间隔依次执行,后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
    • 比如:QPS = 5(每秒最多处理5个),意味着每200ms处理队列中的一个请求。当timeout=2000,即最大等待时间为2000ms,超过求拒绝并抛出异常。
    • 当前面有10个请求,且第一个刚开始处理时,第11个会直接被拒绝。
    • 流量整形,让到达的流量稳定。

1.5.5 热点参数限流

分别统计参数值相同的请求,判断是否超过阈值。

  • 参数索引:统计第几个参数
  • 单机阈值:就是最多多少个(集群的话就是均摊阈值,分摊到每个多少)
  • 统计窗口时长:每秒内单机阈值不能超过
    在这里插入图片描述

参数例外项,也即是会有一些参数需要单独配置,可能它们需要高于或者低于其他参数。
如果没有高级选项,从左侧的热点规则一栏进去新增。
在这里插入图片描述

  • 注意:热点参数限流对默认的SpringMVC资源无效,我们需要手动标记。方式同链路一样,打一个注解即可。

1.6 隔离与降级

1.6.1 Feign整合Sentinel

首先在配置中打开feign的sentinel功能。

feign:
  client:
    config:
      user-service:
        logger-level: BASIC
  sentinel:
    enabled: true

随后编辑要降级的逻辑,有两种方式

  • 方式一:FallbackClass,无法对远程调用做异常处理
  • 方式一:FallbackFactory,可以对远程调用的异常做处理
package com.config;

import com.model.User;
import com.service.UserClient;
import org.springframework.cloud.openfeign.FallbackFactory;


public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable cause) {
        return new UserClient() {
            @Override
            public User getUserById(Long id) {
                System.err.println("查询失败:" + cause);
                return new User();
            }
        };
    }
}

package com.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {
    @Bean
    public UserClientFallbackFactory userClientFallbackFactory(){
        return new UserClientFallbackFactory();
    }
}

重启后,运行,访问,就可以看到

接下来就可以进行配置了
在这里插入图片描述

1.6.2 线程隔离

如下图设置即可,就完成了舱壁模式。
在这里插入图片描述

1.6.3 熔断降级

success
达到失败阈值
快速失败
熔断时间结束
失败则打开断路器
成功则关闭断路器
Closed
Open
Half-Open
尝试放行一次请求

1.6.3.1 熔断策略

有三种:

  • 慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数目超过设定的最小数量,慢调用比例大于设定阈值,则出发熔断。

    • RT:最大响应时长,超过就算慢调用
    • 比例阈值:慢调用比例超过了0.5,就算熔断
    • 熔断时长:每次熔断持续5s
    • 最小请求数:每次统计最小要有5个
    • 统计时长:统计每1000ms内的请求
    • 即在统计时长内且超过最小请求次数的所有请求,会被用来计算比例阈值。
      在这里插入图片描述
  • 异常比例:统计指定时间内的调用,如果调用次数超过指定请求数目,并且出现异常比例达到比例阈值,则触发熔断。
    在这里插入图片描述

  • 异常数:统计指定时间内的调用,如果调用次数超过指定请求数目,并且出现异常数目超过指定异常数目,则触发熔断。
    在这里插入图片描述

二、Sentinel 授权规则

为什么需要?避免服务地址暴露,被访问者绕过网关直接访问。
白名单:来源(origin)在白名单的调用者允许访问
黑名单:来源(origin)在黑名单的调用者不允许访问
在这里插入图片描述

2.1 授权规则

Setinel 通过 RequestOriginParser 这个接口的parseOrigin来获取请求来源,但无论是走网关还是直接访问默认都是default,无法区分。

因此我们需要自己去实现它。

public interface RequestOriginParser{
	/**
	* 从请求request对象中获取origin,获取方式自定义
	*/
	String parseOrigin(HttpServletRequest request);

}

为消费者实现该方法。

package com.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class HeaderOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        /**
         * 如果浏览器或者网关获取的origin头不一样,那么就可以区分
         * */
        String origin = httpServletRequest.getHeader("origin");
        if(StringUtils.isEmpty(origin)){
            return "blank";
        }
        return origin;
    }
}

为网关添加过滤器,将我们上面的来源加上

server:
  port: 10010
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      routes: # 网关路由配置
        - id: user-service #路由id,自定义,但需要唯一
          uri: lb://user-service #路由目标地址,lb即负载均衡,后面跟着服务名称
          # uri: http://127.0.0.1:8001 # 也可以这样写,但是地址固定,不推荐
          predicates: # 路由断言,即判断是否符合要求
            - Path=/user/** # 这个规则是,只要以/user/开头,就算符合要求
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
      default-filters:
        - AddRequestHeader=origin,gateway # , 是等于的意思

在这里插入图片描述
重启后
在这里插入图片描述
在这里插入图片描述

2.2 自定义异常

所有异常默认的都是限流异常,我们接下来自定义异常,避免抛异常时不知道是哪里出错

public interface BlockExceptionhanlder{
	/**
	* 处理请求被限流、降级、授权拦截时抛出的异常
	*/
	void handle(HttpServletRequest request,HttpServletResponse response,BlockException e) throws Exception;
}

BlockException包含很多子类

异常说明
FlowException限流异常
ParamFlowExcetion热点参数限流异常
DegradeException降级异常
AuthorityException授权规则异常
SystemBlockException系统规则异常
package com.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class SentinelBolckhandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;
        if(e instanceof FlowException){
            msg = "请求被限流了!";
        }
        else if(e instanceof DegradeException){
            msg = "请求被降级了!";
        }
        else if(e instanceof ParamFlowException){
            msg = "热点参数被限流了!";
        }
        else if(e instanceof AuthorityException){
            msg = "请求没有权限!";
            status = 401;
        }
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(status);
        httpServletResponse.getWriter().println("{\"message\":\""+msg+"\",\"status\":"+status+"}");
    }
}

在这里插入图片描述

2.3 规则持久化

有三种模式:

  • 原始模式: 默认模式,规则保存在内存之中,重启服务会丢失
  • pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或者数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。缺点就是不是实时的,导致数据不一致。
1.推送规则
2.内存中更新规则
3.将规则更新至文件
SentinelDashboard
Sentinel客户端
规则缓存
本地数据库
  • push模式:控制台将配置规则推送到远程配置中心(nacos)。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。该方式实时更新,推荐该模式。
    但是该模式最为复杂,因为需要修改Sentinel控制台源代码。

我们为消费者引入如下依赖

		<dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
spring:
  application:
    name: order-service
  profiles: # 环境
    active: dev
  cloud:
    nacos:
      config:
        enabled: true
        server-addr: localhost:8848 #nacos地址
        file-extension: yaml #文件后缀名
    sentinel:
      transport:
        dashboard: localhost:8080
      web-context-unify: false #关闭context整合,Sentinel默认会把Controller的方法做context。导致链路模式的流量失效
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848
            data-id: orderservice-flow-rules
            group-id: SENTINEL_GROUP
            rule-type: flow #此处我们选择限流,也可以选:degrade、authority、param-flow

随后重启服务
下载源码
alibaba/Sentinel
接下来我们修改源码:
在源码中找到dashboard
在这里插入图片描述
在其中如下图进行注释
在这里插入图片描述
将该类的test中的nacos,复制到main中对应的位置
在这里插入图片描述

在这里插入图片描述
在main中,修改nacosConfig类

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.csp.sentinel.dashboard.rule.nacos;

import java.util.List;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Eric Zhao
 * @since 1.4.0
 */
@Configuration
@ConfigurationProperties(prefix = "nacos")
public class NacosConfig {
    private String addr;
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        return ConfigFactory.createConfigService(addr);
    }
    public String getAddr(){
        return addr;
    }
    public void setAddr(String addr){
        this.addr = addr;
    }
}

随后在Properties文件中,添加Nacos地址

nacos.addr=localhost:8848

接着修改数据源在这里插入图片描述
修改如下


    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

接着修改前端页面
在这里插入图片描述
取消该部分注释
在这里插入图片描述

<li ui-sref-active="active" ng-if="entry.appType==0">
            <a ui-sref="dashboard.flow({app: entry.app})">
              <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则-NACOS</a>
          </li>

随后打包
在这里插入图片描述
启动后如下:
在这里插入图片描述

点击流控规则Nacos,并添加一个规则
在这里插入图片描述
可以看到多出来了一个规则
在这里插入图片描述

同理,如果要改其他的,也是需要把页面进行修改。

参考文献

[1]黑马程序员Java微服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值