首页 > 其他分享 >SpringCloud 集成 Sentinel 和使用小结

SpringCloud 集成 Sentinel 和使用小结

时间:2023-11-09 22:34:54浏览次数:48  
标签:jobs 流控 SpringCloud 接口 com Sentinel import 小结

Sentinel 是阿里的一款微服务请求监控组件,能够通过配置实现流量控制,降级熔断,热点参数限流,授权规则限流,使用非常方便。官方默认只提供了流量控制配置规则持久化代码实现,这也是我们最常用的,上篇博客已经实现并进行了部署。

本篇博客基于上篇博客搭建好的 Sentinel 和 Nacos 环境,介绍 Spring Cloud 如何集成和使用 Sentinel 监控微服务的 http 请求资源和 Feign 请求资源。对于 Sentinel 的监控界面配置非常简单,这里不详细介绍,只介绍总结个别容易出错的细节。

Sentinel 官网地址:https://sentinelguard.io/zh-cn/index.html


一、集成 Sentinel

首先我们先列出父工程的 pom 文件详情,主要是引入 Spring Cloud 依赖和各微服务公共使用的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jobs</groupId>
    <artifactId>springcloud_sentinel</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <modules>
        <module>feign_client</module>
        <module>provider_app</module>
        <module>consumer_app</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <!-- 引入 springCloud 依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--引入 springCloud alibaba 依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

对于各个微服务,我们需要引入 spring-cloud-starter-alibaba-sentinel 依赖:

<!--引入 nacos 依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--引入 sentinel 依赖包-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

微服务的 application.yml 中需要配置 Sentinel Dashboard 的连接信息:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.136.128:8080

默认情况下,Sentinel 只监控 http 接口,也就是 Controller 提供的接口,因此访问任意接口后,就可以在 Sentinel Dashboard 界面中看到请求的资源路径,然后就可以通过其右侧的按钮,添加流控、熔断、热点、授权等限流规则,但是这些规则设置好后,都是存储在相应微服务的内存中的,微服务如果重启,所设置的规则就会丢失。

如果想要对非 Controller 接口进行监控和限流的话,比如 Service 类中的方法,则需要使用 @SentinelResource 注解,比如在 consumer-app 中对 CostService 中的 getSingleCost 方法使用 Sentinel 进行监控并可以设置限流规则,代码如下:

package com.jobs.service;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.jobs.client.EmployeeClient;
import com.jobs.pojo.Cost;
import com.jobs.pojo.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Random;

@Slf4j
@Service
public class CostService {

    //注入 feign 接口
    @Autowired
    private EmployeeClient employeeClient;

    //Sentinel 默认只监控 Controller 对外提供的 http 接口
    //如果想要监控类方法,需要使用 @SentinelResource 注解
    @SentinelResource("SingleCost")
    public Cost getSingleCost(Integer id) {
        Cost cost = new Cost();
        cost.setId(id);

        String[] arr = {"打车费", "团建费", "交通费", "招待费", "电话费", "住宿费"};
        Random rd = new Random();
        int index = rd.nextInt(6);
        cost.setCategory(arr[index]);
        cost.setMoney(rd.nextInt(500));

        Employee emp = employeeClient.getEmployeeById(id);
        cost.setEmployee(emp);

        log.info("返回的报销费用信息:" + cost);
        return cost;
    }
}

二、将流控规则持久化到 Nacos

上篇博客已经通过修改官方提供的源代码,使 Sentinel 具备了将流控规则持久化到 Nacos 的条件,这样所设置的流控规则就不会随着微服务和 Nacos 的重启而丢失。要想实现这种效果,微服务的 application.yml 中还需要配置流控规则的 Nacos 连接信息:

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: 192.168.136.128:8848
            dataId: consumer-app-flow-rules
            groupId: sentinel-group
            rule-type: flow

然后我们在【流控规则Nacos】菜单上进行配置流控规则,才能持久化到 Nacos 中。该菜单有一个缺点:那就是不能自动展示出用户请求的资源,我们只能从【簇点链路】中找到我们需要限制的请求资源路径,复制一下添加到【流控规则Nacos】中。

image

此时在 Nacos 的配置列表中,就能够找到我们配置的流控规则,如下图所示,其内容是 Json 数据

image


三、链路模式支持

配置流控规则时,流控模式设置中,有一种模式是链路模式,其实现效果就是只针对从指定链路访问到具体某种资源的请求做统计,判断是否超过阈值,如果超过阈值,就阻止请求访问。对于 Sentinel 来说默认会给进入微服务的所有请求设置同一个 root 资源,会导致链路模式失效,因此必须在微服务中通过配置关闭对请求资源路径的整合才行,在 application.yml 中进行如下配置即可

spring:
  cloud:
    sentinel:
      # Sentinel 默认会对资源请求链路进行整合,这样会导致流控模式中的链路模式无法使用
      # 因此需要关闭 context 整合,这样 sentinel 监控界面会单独展示出每种链路请求路径
      web-context-unify: false

四、开启 Feign 对 Sentinel 的支持

对于 SpringCloud 来说,绝大部分情况下微服务之间的调用是通过 Feign 进行的,Sentinel 默认情况下不会监控 Feign 的调用请求,要想启动则必须在微服务的 application.yml 中进行如下配置:

feign:
  sentinel:
    enabled: true

微服务之间的调用,难免会因为各种原因导致调用失败,使用 Feign 的好处在于可以为 Feign 的每个调用接口设置失败处理方法,也就是服务降级处理方案,一般采用实现 FallbackFactory 接口的实现方案,因为该方案能够对远程调用的异常做处理。

以 consumer-app 为例,其需要通过 feign 调用 provider-app 的接口,为 feign 接口提供降级方法的具体步骤为:

首先编写一个实现 FallbackFactory 的自定义类,代码如下:

package com.jobs.client.fallback;

import com.jobs.client.EmployeeClient;
import com.jobs.pojo.Employee;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class EmployeeClientFallback implements FallbackFactory<EmployeeClient> {

    @Override
    public EmployeeClient create(Throwable cause) {
        //由于 feign 都是调用远程接口的,很有可能会因为某种原因调用失败。
        //这里实现 EmployeeClient 中的每个方法,调用远程接口失败时的默认返回结果
        return new EmployeeClient() {
            @Override
            public Employee getEmployeeById(Integer id) {
                log.error("远程接口调用失败:根据 id 获取员工信息");
                //这里实例化一个默认的员工实例并返回
                Employee employee =
                        new Employee().setId(0).setName("未知员工").setDepart("未知部门");
                return employee;
            }
        };
    }
}

然后将该类的实例注册为 Bean

package com.jobs.client.config;

import com.jobs.client.fallback.EmployeeClientFallback;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    //将 feign 接口的 FallbackFactory 实例注册到 Spring 容器
    @Bean
    public EmployeeClientFallback getEmployeeClientFallback() {
        return new EmployeeClientFallback();
    }
}

最后在 Feign 接口上面的 @FeignClient 注解中,设置 fallbackFactory 属性即可

package com.jobs.client;

import com.jobs.client.config.FeignConfig;
import com.jobs.client.fallback.EmployeeClientFallback;
import com.jobs.pojo.Employee;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

//指定【服务提供者】的【服务名称】
@FeignClient(value = "provider-app", fallbackFactory = EmployeeClientFallback.class)
public interface EmployeeClient {

    @GetMapping("/emp/get/data/{id}")
    Employee getEmployeeById(@PathVariable("id") Integer id);
}

五、授权规则的使用

授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。

  • 白名单:来源(origin)在白名单内的调用者允许访问

  • 黑名单:来源(origin)在黑名单内的调用者不允许访问

image

这里重点要说的是【流控应用】如何进行设置,比如设置白名单,只允许 Feign 调用接口,不需要浏览器调用接口,这里的流控应用该如何填写呢?其实填写的并不是应用的名称,而是我们自定义的值。比如我们在 Feign 请求时,让其携带一个 Header,Header 的名称是 ReqSource,值是 feignRequest,这里的 feignRequest 就是我们自定义的值,具体实现如下:

首先我们在自定义的 FeignConfig 类中,拦截 Feign 的所有请求,为其增加 Header

package com.jobs.client.config;

import com.jobs.client.fallback.EmployeeClientFallback;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    //拦截 feign 的请求,为其增加一个 header
    @Bean
    public RequestInterceptor getRequestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("ReqSource", "feignRequest");
        };
    }
}

然后在 Feign 接口上的 @FeignClient 注解上,配置 configuration 属性即可

//指定【服务提供者】的【服务名称】
@FeignClient(value = "provider-app", configuration = FeignConfig.class)
public interface EmployeeClient {

    @GetMapping("/emp/get/data/{id}")
    Employee getEmployeeById(@PathVariable("id") Integer id);
}

最后在 provider-app 中编写一个自定义类,实现 RequestOriginParser 接口:

package com.jobs.config;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

//用于 sentinel 监控获取自定义的 header ,从而判断请求来源。
//这样就可以在授权规则中,通过请求来源进行限制
@Component
public class HeadSourceParse implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {

        //我们在 FeignConfig 中配置 feign 客户都调用接口时会携带 ReqSource 头
        //因此 feign 请求 provider-app 的接口时,会携带 ReqSource 头,且值为 feignRequest
        //如果是浏览器请求 provider-app 的接口时,则不会携带 ReqSource 头,我们这里设置其值为 null
        //此时可以通过 sentinel 设置授权规则,比如设置来源应用为 feignRequest
        //此时只允许 feign 调用 provider-app 的接口,浏览器则无法访问。
        String source = httpServletRequest.getHeader("ReqSource");
        if (StringUtils.isEmpty(source)) {
            source = "null";
        }
        return source;
    }
}

在 parseOrigin 方法中,获取请求的 Header 中是否存在 ReqSource 的 Header,Sentinel 会从该方法中获取【流控应用】的值,因此将流控应用配置为 feignRequest,并设置为白名单,便实现了只允许 feign 接口访问所配置的资源,因为浏览器访问所配置的资源时,该方法由于无法获取到 ReqSource 这个 Header 的值,返回的流控应用名称为 null。当然这里设置的是获取 Header 的值,你也可以通过 Cookie 或者 Session 等其它方式进行控制,绝大多数情况下都是采用 Header 的控制方式。


以上就是 Sentinel 比较常用但容易出错的问题小结,这里再把源代码发出来,可以下载后运行验证效果。

本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/springcloud_sentinel.zip

标签:jobs,流控,SpringCloud,接口,com,Sentinel,import,小结
From: https://www.cnblogs.com/studyjobs/p/17823048.html

相关文章

  • 概率期望小结论
    对于一个概率\(p\),设它能提供的期望值为命中此概率的次数。那么保持这个概率直至命中此概率的期望值为\(\frac{1}{p}\)证明:\[\begin{aligned}\sum\limits_{i=1}^{\infty}(1-p)^{i-1}*p*i&=p\sum\limits_{i=1}^{\infty}(1-p)^{i-1}*i\\\end{......
  • Spring mvc中@RequestMapping 6个基本用法小结
    小结下springmvc中的@RequestMapping的用法。 1)最基本的,方法级别上应用,例如:    Java代码  @RequestMapping(value="/departments")public"simplePatternmethodwascalled");return"someResult";}  则访问http://loc......
  • 【Java基础】Java容器相关知识小结
    目录0.前言1.Collection接口1.1.List接口1.1.1.ArrayList1.1.2.LinkedList1.1.3.Vector1.1.4.Stack1.2.Set接口1.2.1.HashSet1.2.2.LinkedHashSet1.2.3.TreeSet1.3.Queue接口1.3.1.PriorityQueue1.3.2.LinkedList2.Map接口2.1.HashMap2.2.TreeMap2.3.LinkedHash......
  • SpringCloudAlibaba引入Gateway统一网关
    一、概述网关是我们服务的守门神,是所有微服务的统一入口,一切请求都要先到网关,再到微服务,它可以帮助我们统一的进行一种操作,处理一些问题。网关的核心功能特性:1.请求路由、负载均衡一切请求都必须先经过gateway网关,但网关不处理业务,而是根据某种规则把请求转发到某个微服务,这个过程......
  • 51单片机 小结
    总共耗时14天,把51单片机彻底搞定。耗时13天,把江科大51单片机入门教程视频学完了,就是代码不怎么打。这个另说以后有没有心情手打。以后会不会学习STM32不知道,虽然说现在对下一周的学习也没有什么特别的安排就是了。第十四天做出总结,弄一张思维导图。......
  • SpringCloud 基础
    SpringCloud基础微服务基础注意:此阶段学习推荐的电脑配置,至少配备4核心CPU(主频3.0Ghz以上)+16GB内存,否则卡到你怀疑人生。前面我们讲解了SpringBoot框架,通过使用SpringBoot框架,我们的项目开发速度可以说是得到了质的提升。同时,我们对于项目的维护和理解,也会更加的轻松。可见,Spr......
  • Redis 哨兵模式(Sentinel)配置
    哨兵是Redis的一种运行模式,它专注于对Redis实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个Redis系统的可用性。集群演示本次测试为“1主2从”的模式,即一个master两个从节点slave。如下图条件限制,......
  • ==springCloud(一)==
    为什么选择SpringCloud作为微服务架构选型依据整体解决方案和框架成熟度社区热度可维护性学习曲线当前各大IT公司用的微服务架构有那些?阿里:dubbo+HFS京东:JFS新浪:Motan当当网:DubboX…SpringCloud概念Spring官网:https://spring.io/什么是微服务架构微服务有什么优点微服务就是将单......
  • Redis【Sentinel 哨兵机制】
    一、简介        二、作用    哨兵是Redis集群架构中一个非常重要的组件,主要功能如下:集群监控。即时刻监控着redis的master和slave进程是否是在正常工作。消息通知。就是说当它发现有redis实例有故障的话,就会发送消息给管理员。自动故障转移。如果redi......
  • 11月1日 小结
    目录小结小结1.进程与线程​ CPU最小的执行单位是线程,而进程则是一个存储资源单位2.操作系统、进程、线程的关系比喻:​ 操作系统就是工厂,进程就是车间,线程就是车间里面的流水线,CPU就是电源3.进程与线程在内存方面的区别​ 进程的内存空间是彼此隔离​ 线程的内存空间是共......