【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【29】Sentinel


持续学习&持续更新中...

守破离


【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【29】Sentinel

简介

熔断降级

什么是熔断

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

如果这样子的次数太多。我们就可以直接将 B 断路了(A 不再请求 B 接口),凡是调用 B 的直接返回降级数据,不必等待 B 的超长执行。 这样 B 的故障问题,就不会级联影响到 A。

什么是降级

整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级:停止服务,所有的调用直接返回降级数据。

缓解服务器资源的的压力,保证核心业务的正常运行,保持了大部分客户得到正确的响应。

相同点

  1. 为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我,成就全局
  2. 用户最终都是体验到某个功能不可用

不同点

  1. 熔断是被调用方故障,触发的系统主动规则
  2. 降级是基于全局考虑,停止一些正常服务,释放资源

整合Sentinel

https://sentinelguard.io/zh-cn/docs/quick-start.html

导入依赖 spring-cloud-starter-alibaba-sentinel

xml 复制代码
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  </dependency>

下载sentinel的控制台,直接在命令行启动sentinel-dashboard-1.6.3.jar,这是Sentinel的控制台,用户名密码是sentinel,默认是8080端口,如果该端口被占用,使用java -jar sentinel-dashboard-1.6.3.jar --server.port=xxxx启动

配置sentinel控制台地址信息

java 复制代码
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.transport.port=8719 【默认,可以不用配】

Sentinel默认对SpringCloud进行了适配,每个请求默认就是一个资源,访问Controller的请求接口后,就可以在Sentinel的控制台看到簇点链路了

建议给每一个微服务都导入spring-boot-starter-actuator ,并配合management.endpoints.web.exposure.include=*,就可以在控制台看到Sentinel的实时监控了

自定义sentinel流控返回数据

java 复制代码
@Configuration
public class MySentinelConfig {
    public MySentinelConfig() {
        WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
            @Override
            public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
                R error = R.error(BizCodeEnume.TOO_MANY_REQUEST.getCode(), BizCodeEnume.TOO_MANY_REQUEST.getMsg());
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                response.getWriter().write(JSON.toJSONString(error));
            }
        });
    }
}

使用Sentinel来保护feign远程调用

调用方:

java 复制代码
feign.sentinel.enabled=true
java 复制代码
@Slf4j
@Component
public class SeckillFeignServiceFallBack implements SeckillFeignService {
    @Override
    public R getSkuSeckillInfo(Long skuId) {
        log.info("熔断方法调用...getSkuSeckillInfo");
        return R.error(BizCodeEnume.TOO_MANY_REQUEST.getCode(),BizCodeEnume.TOO_MANY_REQUEST.getMsg());
    }
}
java 复制代码
@FeignClient(value = "gulimall-seckill", fallback = SeckillFeignServiceFallBack.class)
public interface SeckillFeignService {
    @GetMapping("/sku/seckill/{skuId}")
    R getSkuSeckillInfo(@PathVariable("skuId") Long skuId);
}

远程服务:当然也可以自己配置自己的流控/降级规则

自定义资源

无论是方式1还是2一定要配置好被限流以后的返回内容

方式1:

方式2:


java 复制代码
    public List<SecKillSkuRedisTo> blockHandler(BlockException e) {
        log.error("getCurrentSeckillSkusResource被限流了..");
        return null;
    }
java 复制代码
    //返回当前时间可以参与的秒杀商品信息

    /**
     * blockHandler 函数会在原方法被限流/降级/系统保护的时候调用,而 fallback 函数会针对所有类型的异常。
     */
    @SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler"/*, fallback = "getCurrentSeckillSkusFallback"*/)
    @Override
    public List<SecKillSkuRedisTo> getCurrentSeckillSkus() {
        //1、确定当前时间属于哪个秒杀场次。
        long time = new Date().getTime();

//        try (Entry entry = SphU.entry("resourceName")) {
            Set<String> keys = redisTemplate.keys(SESSIONS_CACHE_PREFIX + "*");
            for (String key : keys) {
                //seckill:sessions:1582250400000_1582254000000
                String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
                String[] s = replace.split("_");
                Long start = Long.parseLong(s[0]);
                Long end = Long.parseLong(s[1]);
                if (time >= start && time <= end) {
                    //2、获取这个秒杀场次需要的所有商品信息
                    List<String> range = redisTemplate.opsForList().range(key, -1000, 1000);
                    BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
                    List<String> list = hashOps.multiGet(range);
                    if (list != null) {
                        List<SecKillSkuRedisTo> collect = list.stream().map(item -> {
                            SecKillSkuRedisTo redis = JSON.parseObject((String) item, SecKillSkuRedisTo.class);
//                        redis.setRandomCode(null); 当前秒杀开始就需要随机码
                            return redis;
                        }).collect(Collectors.toList());
                        return collect;
                    }
                    break;
                }
            }
//        } catch (BlockException e) {
//            log.error("资源被限流,{}", e.getMessage());
//        }

        return null;
    }

给网关整合Sentinel

使用sentinel-dashboard-1.7.1.jar这个控制台

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-spring-cloud-gateway-adapter -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
java 复制代码
spring.cloud.sentinel.transport.dashboard=localhost:8080
management.endpoints.web.exposure.include=*

spring.cloud.sentinel.scg.fallback.content-type=application/json
java 复制代码
@Configuration
public class SentinelGatewayConfig {

    //TODO 响应式编程
    //GatewayCallbackManager
    public SentinelGatewayConfig(){
        GatewayCallbackManager.setBlockHandler(new BlockRequestHandler(){
            //网关限流了请求,就会调用此回调  Mono Flux
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
                R error = R.error(BizCodeEnume.TOO_MANY_REQUEST.getCode(), BizCodeEnume.TOO_MANY_REQUEST.getMsg());
                String errJson = JSON.toJSONString(error);
//                Mono<String> aaa = Mono.just("aaa");
                Mono<ServerResponse> body = ServerResponse.ok().body(Mono.just(errJson), String.class);
                return body;
            }
        });

//        FlowRule flowRule = new FlowRule();
//        flowRule.setRefResource("gulimall_seckill_route");
        flowRule.set
//        FlowRuleManager.loadRules(Arrays.asList(flowRule));
    }
}

参考

雷丰阳: Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目.


本文完,感谢您的关注支持!


相关推荐
ShareBeHappy_Qin20 分钟前
ZooKeeper 中的 ZAB 一致性协议与 Zookeeper 设计目的、使用场景、相关概念(数据模型、myid、事务 ID、版本、监听器、ACL、角色)
分布式·zookeeper·云原生
fanstuck1 小时前
从构思到上线的全栈开发指南:全栈开发中的技术选型和架构
架构
md_10088 小时前
架构优化指南:五大场景下如何发现隐藏的耦合?
架构
黄名富9 小时前
Kafka 日志存储 — 日志索引
java·分布式·微服务·kafka
Ase5gqe9 小时前
大数据-259 离线数仓 - Griffin架构 修改配置 pom.xml sparkProperties 编译启动
xml·大数据·架构
DM很小众10 小时前
Kafka 和 MQ 的区别
分布式·kafka
小白的一叶扁舟10 小时前
深入剖析 JVM 内存模型
java·jvm·spring boot·架构
sjsjsbbsbsn10 小时前
基于注解实现去重表消息防止重复消费
java·spring boot·分布式·spring cloud·java-rocketmq·java-rabbitmq
重生之Java再爱我一次11 小时前
Hadoop集群搭建
大数据·hadoop·分布式
中东大鹅12 小时前
MongoDB的索引与聚合
数据库·hadoop·分布式·mongodb