JAVA微服务_sentinel_java 微服务-程序员宅基地

技术标签: 微服务  sentinel  java  

Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
同hystrix相比,除了服务熔断,还可以进行服务降级、服务流控等。
sentinel有多种使用方式,常见的有单独使用dashboard、同Gateway结合使用、同Spring Cloud/Alibaba结合使用、同RPC组件结合使用等。建议结合项目选择。开源项目Ruoyi-Cloud选择结合Gateway使用。

在服务端用maven引入jar包;
控制面板dashboard可以从https://github.com/alibaba/Sentinel/releases下载sentinel-dashboard-$version.jar包。

Sentinel基本概念

  • 资源 resource:Sentinel中一切抽象成资源,服务、服务中的方法,甚至是一段代码,都可以被定义成资源,然后被Sentinel管理;
  • 规则 rules : 流量规则,熔断规则,负载规则,,,

五大规则

流控规则

降级规则
监控应用中资源调用请求,达到阈值时自动触发熔断降级。
热点规则

系统规则
系统自适应限流
授权规则
黑白名单控制

快速开始

本文以Spring Cloud Alibaba中引入Sentinel为例,其他方式引入请参考官方wiki。
在服务中引入Sentinel,然后在dashboard中配置规则。

<!-- SpringCloud Alibaba Sentinel -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

在配置文件中开启Sentinel

spring.cloud.sentinel.enabled=true
# 指定sentinel dashboard web地址
spring.cloud.sentinel.transport.dashboard=  #dashboard的ip:端口
#  指定snetinel组件和sentinel dashboard组件通信地址(用tcp更高效的通信,端口默认为8719)
spring.cloud.sentinel.transport.port=8719

DashBoard控制台方式

使用java -jar命令启动dashboard控制台:

java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\sentinel\sentinel-dashboard-1.8.0.jar

Sentinel提供了一个可视化的操作平台,安装好之后,在浏览器中输入(http://localhost:8718 (opens new window))就可以访问了,默认的用户名和密码都是sentinel

dashboard在有一次调用发生后,才能在web界面看到服务(这算懒加载么?)
调用后sentinel才会产生资源调用日志,有日志生成后才会dashboard出现服务,多调用几次才会出现qps等信息。

启动参数说明:
-Dserver.port:指定启动的端口,默认8080
-Dproject.name:指定本服务的名称
-Dcsp.sentinel.dashboard.server:指定sentinel控制台的地址,用于将自己注册进入实现监控自己
-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 “sentinel”,默认值为 “sentinel”
-Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 “123456”,默认值为 “sentinel”
-Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟,需要注意的是,部署多台控制台时,session 默认不会在各实例之间共享,这一块需要自行改造。

熔断 降级规则

用来避免服务雪崩。触发熔断后对该服务的调用不可用。
在dashboard中选择“降级规则”

Sentinel熔断策略

触发熔断(断路器打开)的三种策略
RT 根据请求响应时间熔断
异常比例
抛出的异常的比例。如设置为0.1,则比例超过10%时触发熔断
异常数

时间窗口(单位:秒)结束后,断路器关闭。

热点规则

也成为热点参数限流,对携带了指定参数的资源进行限流。
https://editor.csdn.net/md/?articleId=131038775,参数为articleId
只能使用QPS模式。

不能使用资源路径的方式配置,只能使用资源别名。
资源别名:在处理请求的方法上使用@SentinelResource(value="别名")注解指定别名。mvc模式的话就放在controller层方法上。

在[统计窗口时长]内,对带有第[参数索引]个参数的请求,超过阈值时进行限流。
参数索引从0开始计数,代表请求中的第几个参数。

@SentinelResource定义资源

@SentinelResource(value=“别名”, fallback=“”, blcokHandler=“限流处理method名”)
fallback处理业务异常,blockHandler处理限流。两参数的值都是指定方法名。
限流处理method例子:
注:该例子中controller层方法带Integer id参数

@Controller
@SentinelResource(value="别名", fallback="", blcokHandler="限流处理method名"public String controllerMethod(Integer id){
    
	//Method body...
}

private String blockHandler(Integer id, BlockException e){
    
	if(e instanceof FlowException){
    
		return "当前请求过于火爆,已被降级";
	}
	if(e instanceof DegradeException){
    
		return "当前请求过于火爆,已被降级";
	}
	if(e instanceof ParaFlowException){
    
		return "当前请求过于火爆,已被参数热点限流";
	}
	return "服务器过于火爆,请稍后再试捏~(づ ̄3 ̄)づ╭~kira";
}

@SentinelResource 注解不支持 private 方法。默认情况下,Sentinel 对控制资源的保护处理是直接抛出异常,这样对用户不友好,所以我们需要通过可选的异常处理 blockHandler 和 fallback 配置项处理一下异常信息。

  • blockHandler/blockHandlerClass: blockHandler 指定函数负责处理 BlockException 异常,可选项。blockHandler 函数默认需要和原方法在同一个类中,通过指定 blockHandlerClass 为对应类的 Class 对象,则可以指定其他类中的函数,但注意对应的函数必需为 static 函数,否则无法解析。

    blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型必须和原方法一致并且最后加一个类型为 BlockException 的异常参数。

    可以通过这种方式拿到请求发来的参数。
    @SentinelResource和blockHandler都要求public,是因为反射吗?不过blockerHandler应该不用反射吧,,直接调用似乎可以,,
    
  • fallback /fallbackClass: fallback 指定的函数负责处理业务运行的异常,可选项,fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数默认需要和原方法在同一个类中,通过指定 fallbackClass 为对应类的 Class 对象,则可以指定指定为其他类的函数,但注意对应的函数必需为 static 函数,否则无法解析。

fallback 函数的返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
@SentinelResource 的 fallback 负责业务运行的异常,blockHandler 负责 sentinel 配置的违规。

流量控制(限流)

原理是监控应用流量的 QPS 或 并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被顺势的流量高峰冲垮,从而保障应用的高可用性;

  • Q: 怎么控制
    • A: dashboard设置规则时选择高级选项-流控模式
      • 直接 返回Blocked by Sentinel
      • 关联
      • 链路

流控模式 三种

配置资源:在编辑规则时设置的资源
关联资源:和配置资源相关联的资源

直接

当对配置资源的请求超过阈值时,对配置资源的请求的处理(三种处理方式在本文下一小节)

关联

当所关联的资源的请求超过阈值后,对当前资源进行的处理
如在以下例子中,当关联的"/aa"资源访问次数超过阈值,则"/demo"资源要受到流控处理
在这里插入图片描述

链路

对链路上的资源的处理

流控效果/处理方式 三种

注意:只适用于QPS限流

  • 快速失败
    • 直接拒绝请求并抛出异常FlowExcrption
  • Warm up
    • 冷启动,预热
    • 在一定时间(预热时长)内将系统逐渐拉至高水位
  • 排队等待 Rate Limiter
    • 同Warm up相比,特点为始终匀速处理
      注意:这一效果只针对QPS流控,并发线程数流控不支持。

排队等待的方式会以匀速排队方式严格控制请求通过的间隔时间,也就是让请求以均匀的速度通过,其余的排队等待,它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。

对应的是漏桶算法,源码对应得类:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。该方式的作用如下图:

图片该方式主要用于处理间隔性突发的流量。假设某时刻来了大流量的请求,如果此时要处理所有请求,很可能会导致系统负载过高,影响稳定性。

但其实接下来几秒可能系统处于空闲状态,若直接把多余的请求丢弃则没有充分利用系统的处理能力,所以我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

Sentinel 的 Rate Limiter 模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性。

Sentinel会以固定的间隔时间让请求通过, 访问资源,当请求到来时,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间大于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过;反之,则马上抛出阻塞异常。

下图能很形象的展示这种场景的削峰填谷的作用:X轴代表时间,Y轴代表系统处理的请求

图片

结合Gateway

Sentinel 支持对 Spring Cloud Gateway、Netflix Zuul 等主流的 API Gateway 进行限流。

在Gateway模块中添加依赖:

<!-- SpringCloud Alibaba Sentinel -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
		
<!-- SpringCloud Alibaba Sentinel Gateway -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

限流

Gateway配置类中添加限流规则,注册全局SentinelGatewayFilter()过滤器,
以一分钟访问超过三次则限流为例:

import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;

/**
 * 网关限流配置
 * 
 */
@Configuration
public class GatewayConfig
{
    
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter()
    {
    
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit()
    {
    
        // 加载网关限流规则
        initGatewayRules();
    }

    /**
     * 网关限流规则   
     */
    private void initGatewayRules()
    {
    
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("ruoyi-system")
                .setCount(3) // 限流阈值
                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
        // 加载网关限流规则
        GatewayRuleManager.loadRules(rules);
    }
}
  • 这种小事就不能交给配置文件吗?难道因为不是gateway自己的?
  • 为什么这里不用控制面板了
  • 那个异常处理可以在配置文件,详见“自定义异常”

分组限流

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.ruoyi.gateway.handler.SentinelFallbackHandler;

/**
 * 网关限流配置
 * 
 */
@Configuration
public class GatewayConfig
{
    
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelFallbackHandler sentinelGatewayExceptionHandler()
    {
    
        return new SentinelFallbackHandler();
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter()
    {
    
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit()
    {
    
        // 加载网关限流规则
        initGatewayRules();
    }

    /**
     * 网关限流规则   
     */
    private void initGatewayRules()
    {
    
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("system-api")
                .setCount(3) // 限流阈值
                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
        rules.add(new GatewayFlowRule("code-api")
                .setCount(5) // 限流阈值
                .setIntervalSec(60));
        // 加载网关限流规则
        GatewayRuleManager.loadRules(rules);
        // 加载限流分组
        initCustomizedApis();
    }

    /**
     * 限流分组   
     */
    private void initCustomizedApis()
    {
    
        Set<ApiDefinition> definitions = new HashSet<>();
        // ruoyi-system 组
        ApiDefinition api1 = new ApiDefinition("system-api").setPredicateItems(new HashSet<ApiPredicateItem>()
        {
    
            private static final long serialVersionUID = 1L;
            {
    
                // 匹配 /user 以及其子路径的所有请求
                add(new ApiPathPredicateItem().setPattern("/system/user/**")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }
        });
        // ruoyi-gen 组
        ApiDefinition api2 = new ApiDefinition("code-api").setPredicateItems(new HashSet<ApiPredicateItem>()
        {
    
            private static final long serialVersionUID = 1L;
            {
    
                // 只匹配 /job/list
                add(new ApiPathPredicateItem().setPattern("/code/gen/list"));
            }
        });
        definitions.add(api1);
        definitions.add(api2);
        // 加载限流分组
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}
  • 不过这样的话,,路径就写死在代码里了

注解方式

sentinel 不仅支持通过硬代码方式进行资源的申明,还能通过注解方式进行声明,为了让注解生效,还需要配置切面类SentinelResourceAspect

   @Bean
  public SentinelResourceAspect sentinelResourceAspect() {
    
    return new SentinelResourceAspect();
  }

自定义异常

为了展示更加友好的限流提示, Sentinel支持自定义异常处理。

方案一:yml配置

# Spring
spring: 
  cloud:
    sentinel:
      scg:
        fallback:
          mode: response
          response-body: '{"code":403,"msg":"请求超过最大数,请稍后再试"}'

方案二:GatewayConfig注入Bean

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
    
	return new SentinelFallbackHandler();
}

SentinelFallbackHandler.java

import java.nio.charset.StandardCharsets;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

/**
 * 自定义限流异常处理
 *
 */
public class SentinelFallbackHandler implements WebExceptionHandler
{
    
    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
    {
    
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] datas = "{\"code\":429,\"msg\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
    {
    
        if (exchange.getResponse().isCommitted())
        {
    
            return Mono.error(ex);
        }
        if (!BlockException.isBlockException(ex))
        {
    
            return Mono.error(ex);
        }
        return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
    }

    private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
    {
    
        return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
    }
}

实现原理

在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)

这些功能插槽有些类似游戏或是组装电脑里“升级插槽”的概念,在原有基础上添加功能。可以自定义插槽

插槽介绍

NodeSelectorSlot

这个 slot 主要负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级。
资源确实是树状结构的,就像树形目录。结构上资源和url很像,也应当是树形结构

熔断降级设计理念
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对依赖(在我们的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。
缺点:

  • 增加了线程切换的成本
  • 需要预先给各个资源做线程池大小的分配,容易导致碎片化
  • 怎么线程池隔离?不如说实际上隔离时怎么做到的?内存地址拒绝访问?

核心类解析

Sentinel 对这个问题采取了两种手段:
通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统负载保护
Sentinel 同时对系统的维度提供保护。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

进一步学习

相关概念:

SPI:SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议,比如AT91RM9200。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5

附录

@SentinelResource注解参数

① value: 资源名称,必需项
② entryType: entry 类型,可选项(默认为 EntryType.OUT)

⑤ defaultFallback(since 1.6.0): 默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑。defaultFallback 函数默认需要和原方法在同一个类中,通过指定 fallbackClass 为对应类的 Class 对象,则可以指定指定为其他类的函数,但注意对应的函数必需为 static 函数,否则无法解析。defaultFallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback和 defaultFallback,则只有 fallback会生效。

defaultFallback函数的返回值类型必须与原函数返回值类型一致;方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。

⑥ exceptionsToIgnore(since 1.6.0): 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)

流控规则

一条限流规则由以下属性组成:

① resource: 资源名,即限流规则的作用对象,默认请求路径。

② limitApp: 流控针对的调用来源,若为 default 则不区分调用来源,默认值default

③ count: 限流阈值

④ grade: 限流阈值类型(1代表 QPS,0 代表并发线程数),默认值QPS

⑤ strategy: 流控模式

直接拒绝(默认): 接口达到限流条件时,直接限流
关联: 当关联的资源达到阈值时,就限流自己(适合做应用让步)
链路: 只记录指定链路上的流量,指定资源从入口资源进来的流量,如果达到阈值,就可以限流
⑥ controlBehavior: 流控效果

快速失败(默认): 当 QPS 超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
排队等待: 这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
Warm Up: 该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。预热底层是根据令牌桶算法实现的。
以上几种属性在 sentinel-dashboard 控制台对应的规则如下图:

图片

Spring事务失效

在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是 public,则TransactionAttribute返回 null,即不支持事务。

    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    
          return null;
        }
     
        // The method may be on an interface, but we need attributes from the target class.
        // If the target class is null, the method will be unchanged.
        Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
     
        // First try is the method in the target class.
        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
        if (txAttr != null) {
    
          return txAttr;
        }
     
        // Second try is the transaction attribute on the target class.
        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
    
          return txAttr;
        }
     
        if (specificMethod != method) {
    
          // Fallback is to look at the original method.
          txAttr = findTransactionAttribute(method);
          if (txAttr != null) {
    
            return txAttr;
          }
          // Last fallback is the class of the original method.
          txAttr = findTransactionAttribute(method.getDeclaringClass());
          if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
    
            return txAttr;
          }
        }
        return null;
      }

也就是说,如果我们自定义的事务方法(即目标方法),它的访问权限不是public,而是 private、default 或 protected 的话,spring 则不会提供事务功能。
2. 方法用 final 修饰
spring 事务底层使用了 aop,也就是通过 jdk 动态代理或者 cglib,帮我们生成了代理类,在代理类中实现的事务功能。

但如果某个方法用 final 修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。

    注意:如果某个方法是 static 的,同样无法通过动态代理,变成事务方法。
  • 但是sentinel却没提static和final,,如果只是私有方法,那么可能是其他类判断方法类型的,,为什么?是为了在反射的情况下仍然能保证访问权限吗?

控制台模式存在的问题

优点是方便。缺点是:
通过 Sentinel-Dashboard 控制台配置的规则依然要推送到微服务应用中 Sentinel 客户端本身才能生效,而微服务之间的调用链路等指标信息也需要推送给Sentinel控制台,才能比较方便地使用Sentinel提供的一些能力,因此在开源的架构版本中需要微服务应用本身开启独立端口与 sentinel-dashboard 进行通信,从而获取配置规则以及上送微服务应用的各类指标信息。

而这显然也会占用微服务额外的资源,并且由于 sentinel-dashboard 在此条件下并不具备集群部署能力,因此也会形成一个单节点问题。

在开源版本架构中,通过 sentinel-dashboard 控制台配置的限流、熔断降级等规则都是存储于 Sentinel-Dashboard 控制台服务的内存之中,如果控制台服务重启或者微服务应用重启都会导致规则丢失,而这在生产环境下显然是不能接受的,因此 Sentinel 官方推荐在生产架构中使用第三方数据源作为永久存储中心,比如 nacos、apollo、zookeeper,这样各个微服务的限流、降级规则都可以永久存储。

sentinel 整合 openFeign:

上文介绍的都是在单个模块间的进行 fallback 和 blockhandler 测试,但在实际的 SpringCloud 微服务开发场景中肯定会遇到服务间远程服务调用的问题,而目前最主流的远程调用组件就是 openFeign 了,那接下来我们看看 sentinel 如何整合 openFeign 进行熔断降级。

1、引入 openFeign 的依赖:

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

2、配置文件中开启 openFeign 对 sentinel 熔断降级的支持:

feign.sentinel.enabled=true

3、消费者主启动类添加注解 @EnableFeignClients 激活 openFeign

4、消费者声明接口,标识要调用提供方模块的哪个方法,并在 @FeignClient 注解中使用 fallback 属性指定熔断降级类:

@FeignClient(value = "payment-provider", fallback = PaymentServiceImpl.class)
public interface PaymentService {
    
 
    @GetMapping("/payment/get/{id}")
    public CommonResult paymentSql(@PathVariable("id")Long id);
}

5、添加降级类:实现第4步的定义的接口,并添加降级逻辑:

@Component
public class PaymentServiceFallback implements PaymentService {
    
 
    @Override
    public CommonResult paymentSql(Long id) {
    
        return new CommonResult(414, "open-feign 整合 sentinel 实现的全局服务降级策略",null);
    }
}

PaymentServiceFallback 是 PaymentService 的降级回调类,一旦PaymentService 中对应得接口出现了异常,则会调用PaymentServiceFallback类中对应得方法进行降级处理。

  • 为什么fallback指定的是ServiceImpl的类?
  • 没看出和Sentinel有什么关系?
  • feign不是还默认集成了Hystrix吗
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_44915801/article/details/131038775

智能推荐

EasyDarwin开源流媒体云平台之EasyRMS录播服务器功能设计_开源录播系统-程序员宅基地

文章浏览阅读3.6k次。需求背景EasyDarwin开发团队维护EasyDarwin开源流媒体服务器也已经很多年了,之前也陆陆续续尝试过很多种服务端录像的方案,有:在EasyDarwin中直接解析收到的RTP包,重新组包录像;也有:在EasyDarwin中新增一个RecordModule,再以RTSPClient的方式请求127.0.0.1自己的直播流录像,但这些始终都没有成气候;我们的想法是能够让整套EasyDarwin_开源录播系统

oracle Plsql 执行update或者delete时卡死问题解决办法_oracle delete update 锁表问题-程序员宅基地

文章浏览阅读1.1w次。今天碰到一个执行语句等了半天没有执行:delete table XXX where ......,但是在select 的时候没问题。后来发现是在执行select * from XXX for update 的时候没有commit,oracle将该记录锁住了。可以通过以下办法解决: 先查询锁定记录 Sql代码 SELECT s.sid, s.seri_oracle delete update 锁表问题

Xcode Undefined symbols 错误_xcode undefined symbols:-程序员宅基地

文章浏览阅读3.4k次。报错信息error:Undefined symbol: typeinfo for sdk::IConfigUndefined symbol: vtable for sdk::IConfig具体信息:Undefined symbols for architecture x86_64: "typeinfo for sdk::IConfig", referenced from: typeinfo for sdk::ConfigImpl in sdk.a(config_impl.o) _xcode undefined symbols:

项目05(Mysql升级07Mysql5.7.32升级到Mysql8.0.22)_mysql8.0.26 升级32-程序员宅基地

文章浏览阅读249次。背景《承接上文,项目05(Mysql升级06Mysql5.6.51升级到Mysql5.7.32)》,写在前面需要(考虑)检查和测试的层面很多,不限于以下内容。参考文档https://dev.mysql.com/doc/refman/8.0/en/upgrade-prerequisites.htmllink推荐阅读以上链接,因为对应以下问题,有详细的建议。官方文档:不得存在以下问题:0.不得有使用过时数据类型或功能的表。不支持就地升级到MySQL 8.0,如果表包含在预5.6.4格_mysql8.0.26 升级32

高通编译8155源码环境搭建_高通8155 qnx 源码-程序员宅基地

文章浏览阅读3.7k次。一.安装基本环境工具:1.安装git工具sudo apt install wget g++ git2.检查并安装java等环境工具2.1、执行下面安装命令#!/bin/bashsudoapt-get-yinstall--upgraderarunrarsudoapt-get-yinstall--upgradepython-pippython3-pip#aliyunsudoapt-get-yinstall--upgradeopenjdk..._高通8155 qnx 源码

firebase 与谷歌_Firebase的好与不好-程序员宅基地

文章浏览阅读461次。firebase 与谷歌 大多数开发人员都听说过Google的Firebase产品。 这就是Google所说的“ 移动平台,可帮助您快速开发高质量的应用程序并发展业务。 ”。 它基本上是大多数开发人员在构建应用程序时所需的一组工具。 在本文中,我将介绍这些工具,并指出您选择使用Firebase时需要了解的所有内容。 在开始之前,我需要说的是,我不会详细介绍Firebase提供的所有工具。 我..._firsebase 与 google

随便推点

k8s挂载目录_kubernetes(k8s)的pod使用统一的配置文件configmap挂载-程序员宅基地

文章浏览阅读1.2k次。在容器化应用中,每个环境都要独立的打一个镜像再给镜像一个特有的tag,这很麻烦,这就要用到k8s原生的配置中心configMap就是用解决这个问题的。使用configMap部署应用。这里使用nginx来做示例,简单粗暴。直接用vim常见nginx的配置文件,用命令导入进去kubectl create cm nginx.conf --from-file=/home/nginx.conf然后查看kub..._pod mount目录会自动创建吗

java计算机毕业设计springcloud+vue基于微服务的分布式新生报到系统_关于spring cloud的参考文献有啥-程序员宅基地

文章浏览阅读169次。随着互联网技术的发发展,计算机技术广泛应用在人们的生活中,逐渐成为日常工作、生活不可或缺的工具,高校各种管理系统层出不穷。高校作为学习知识和技术的高等学府,信息技术更加的成熟,为新生报到管理开发必要的系统,能够有效的提升管理效率。一直以来,新生报到一直没有进行系统化的管理,学生无法准确查询学院信息,高校也无法记录新生报名情况,由此提出开发基于微服务的分布式新生报到系统,管理报名信息,学生可以在线查询报名状态,节省时间,提高效率。_关于spring cloud的参考文献有啥

VB.net学习笔记(十五)继承与多接口练习_vb.net 继承多个接口-程序员宅基地

文章浏览阅读3.2k次。Public MustInherit Class Contact '只能作基类且不能实例化 Private mID As Guid = Guid.NewGuid Private mName As String Public Property ID() As Guid Get Return mID End Get_vb.net 继承多个接口

【Nexus3】使用-Nexus3批量上传jar包 artifact upload_nexus3 批量上传jar包 java代码-程序员宅基地

文章浏览阅读1.7k次。1.美图# 2.概述因为要上传我的所有仓库的包,希望nexus中已有的包,我不覆盖,没有的添加。所以想批量上传jar。3.方案1-脚本批量上传PS:nexus3.x版本只能通过脚本上传3.1 批量放入jar在mac目录下,新建一个文件夹repo,批量放入我们需要的本地库文件夹,并对文件夹授权(base) lcc@lcc nexus-3.22.0-02$ mkdir repo2..._nexus3 批量上传jar包 java代码

关于去隔行的一些概念_mipi去隔行-程序员宅基地

文章浏览阅读6.6k次,点赞6次,收藏30次。本文转自http://blog.csdn.net/charleslei/article/details/486519531、什么是场在介绍Deinterlacer去隔行处理的方法之前,我们有必要提一下关于交错场和去隔行处理的基本知识。那么什么是场呢,场存在于隔行扫描记录的视频中,隔行扫描视频的每帧画面均包含两个场,每一个场又分别含有该帧画面的奇数行扫描线或偶数行扫描线信息,_mipi去隔行

ABAP自定义Search help_abap 自定义 search help-程序员宅基地

文章浏览阅读1.7k次。DATA L_ENDDA TYPE SY-DATUM. IF P_DATE IS INITIAL. CONCATENATE SY-DATUM(4) '1231' INTO L_ENDDA. ELSE. CONCATENATE P_DATE(4) '1231' INTO L_ENDDA. ENDIF. DATA: LV_RESET(1) TY_abap 自定义 search help