熔断降级限流 Sentinel 服务链路追踪

什么是熔断

  • A 服务调用 B 服务的某个功能,由于网络不稳定问题,或者 B 服务卡机,导致功能时

间超长。如果这样子的次数太多。我们就可以直接将 B 断路了(

A 不再请求 B 接口),凡是

调用 B 的直接返回降级数据,不必等待 B 的超长执行。 这样 B 的故障问题,就不会级联影

响到 A。

什么是降级

  • 整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和

页面进行有策略的降级[停止服务,所有的调用直接返回降级数据]。以此缓解服务器资源的

的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应。

相同点:

1、为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我

2、用户最终都是体验到某个功能不可用

不同点:

1、熔断是被调用方故障,触发的系统主动规则

2、降级是基于全局考虑,停止一些正常服务,释放资源

什么是限流

对打入服务的请求流量进行控制,使服务能够承担不超过自己能力的流量压力

使用Sentinel实现熔断降级限流

 

依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.3</version>
</dependency>


<!--启用监控:-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.3</version>
</dependency>

在idea左侧栏里搜一下 sentinel 用的是哪个版本,然后在 https://github.com/alibaba/Sentinel/releases 下载对应版本 控制台jar包

在cmd运行命令

java -Dserver.port=8083 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar (jar包名)

控制台默认用户和密码都是 sentinel

spring配置:微服务和控制台的通信端口为 8719 ,控制台的地址

spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080

EndPoint暴露 配置文件
management.endpoints.web.exposure.include=*

打开 Sentinel 对 Feign 的支持(调用方)
feign.sentinel.enabled=true

配置类:设置url限流后,页面返回的数据【注解和trycatch要定义限流后要返回的资源】

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.xunqi.common.exception.BizCodeEnum;
import com.xunqi.common.utils.R;
import org.springframework.context.annotation.Configuration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
public class GulimallSeckillSentinelConfig {

    public GulimallSeckillSentinelConfig() {

        WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
            @Override
            public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
                R error = R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(), BizCodeEnum.TO_MANY_REQUEST.getMessage());
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                response.getWriter().write(JSON.toJSONString(error));


            }
        });
    }
}

定义资源:

资源默认是servlet的资源,

try catch定义资源 【要定义限流后要返回的资源】

// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
  // 被保护的业务逻辑
  // do something here...
} catch (BlockException ex) {
  // 资源访问阻止,被限流或被降级
  // 在此处进行相应的处理操作
}

注解定义资源【被限流熔断保护时,可以用blockHandler 函数 , 而 fallback 函数会针对所有类型的异常】

  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
  • 返回值类型必须与原函数返回值类型一致;
  • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
  • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
// 原本的业务方法.
@SentinelResource(value = "资源名", blockHandler = "blockHandlerForGetUser", fallback =)
public User getUserById(String id) {
    throw new RuntimeException("getUserById command failed");
}


// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
    return new User("admin");
}

可以直接在控制台上设置规则,或者在代码加注解【所有流控规则默认保存在内存,重启失效】

 

使用Sentinel来保护feign远程调用,熔断;

* 1)、调用方的熔断保护:feign.sentinel.enable=true

  • 2)、调用方手动指定远程服务的降级策略。远程服务被降级处理。触发调用方的熔断回调方法

* 3)、超大流量的时候,必须牺牲一些远程服务。在服务的提供方(远程服务)指定降级策略;

* 提供方是在运行,但是不运行自己的业务逻辑,返回的是默认的降级数据**(限流的数据)**

Feign的调用方:@FeignClient加一个fallback

@FeignClient(value = "gulimall-seckill",fallback = SeckillFeignServiceFallBack.class)
public interface SeckillFeignService {

    @GetMapping(value = "/sku/seckill/{skuId}")
    R getSkuSeckilInfo(@PathVariable("skuId") Long skuId);

}

Feign的调用方:熔断时调用此方法

@Component
public class SeckillFeignServiceFallBack implements SeckillFeignService {
    @Override
    public R getSkuSeckilInfo(Long skuId) {
        return R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(),BizCodeEnum.TO_MANY_REQUEST.getMessage());
    }
}