集成OpenFeign和Sentinel实现自定义服务降级Fallback及网关流量控制

  • Sentinel 和 Gateway 集成实现网关限流。
  • 通过 OpenFeign和 Sentinel 集成实现自定义 fallback 服务降级,减轻服务负担。

sentinel下载安装

image.png

启动sentinel

  • 打开命令窗CD到下载放置 sentinel-dashboard-1.8.8.jar 的文件夹
  • 执行**java -jar sentinel-dashboard-1.8.8.jar**就可以启动了

image.png

  • 如果出现端口被占用, 就将端口对应的进程kill掉即可
  • KILL进程 :
  1. Windows
netstat -ano | findstr port
taskkill /pid PID -f       # 第二个PID填写端口对应的PID

  1. Mac
lsof -i tcp:port
sudo kill -9 PID

访问sentinel

  • 访问 http://localhost:8080/`
  • 初始账号密码均为 : sentinel
  • 忽略左边的**pmhub-gateway**哈哈哈, 第一次启动是没有的

image.png

pmhub-gateway 整合 sentinel

  • Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
  • 在 PmHub 中主要是用的 route 维度,也即对应的微服务名

引入依赖
<!-- SpringCloud Alibaba Sentinel 核心依赖-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!-- SpringCloud Alibaba Sentinel Gateway,如果不是gateway可以不用引入 -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<!-- Sentinel Datasource Nacos 用来做持久化存储-->
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

YML配置文件
spring: 
  cloud:
    sentinel:
      # 取消控制台懒加载
      eager: true
      transport:
        # 配置控制台服务地址
        dashboard: 127.0.0.1:8080
        # 默认 8719 端口,假如被占用会自动从 8719 开始依次 +1 扫描,直至找到未被占用的端口
        port: 8719
      # nacos配置持久化
      datasource:
        ds1:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: sentinel-pmhub-gateway
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: gw-flow

Nacos持久化配置

Sentinel 的配置默认是放在内存中,如果服务一旦重启,原先配置好的规则就都不见了,为了防止这样,所以需要配置数据持久化,因为在 PmHub 中,nacos 本身配置了持久化,也就是 nacos 的数据都会最终持久化到 MySQL 数据库。所以我们只需要将 Sentinel 的配置放到 nacos 配置统一管理起来就好了。

  • 上一步已经配置了持久化的配置,可以看到文件是 sentinel-pmhub-gateway。

image.png

启动pmhub-gateway, 查看sentinel控制台

启动结果如图

image.png
image.png

相关名词解释
参数名称参数值示例解释
API类型Route ID / API分组指定限流规则应用的API类型, 可以是路由ID或API分组
API名称pmhub-system被限流的具体API的名称
针对请求属性复选框是否根据请求属性, (如IP地址, 用户身份等), 进行限流
阈值类型QPS / 线程数限流的依据类型, 可以是美妙查询数( QPS ), 或并发线程数
QPS阈值1000指定每秒允许通过的最大请求数, 当超过此值时, 将触发限流
间隔1配合QPS阈值使用, 指定限流的时间窗口大小, 如1秒, 1分钟等
流控方式快速失败 / 匀速排队当达到限流阈值时, 采取的策略. 快速失败表示直接拒绝, 匀速排队表示排队等待
Burst size0突发请求数, 表示允许瞬间通过的请求数, 0表示不允许突发流量

OpenFeign和 Sentinel 集成实现自定义 fallback 服务降级

  • SentinelResource 注解是 Sentinel 官方提供的流量防卫防护组件注解,
  • 用于指定防护资源,对配置的资源进行流量控制,熔断降级等功能。

  • 在接口上加上这个注解,就能够实现自定义限流提示和程序异常返回 fallback 服务降级
/**
 * 根据用户名获取当前用户信息
 */
@InnerAuth
@GetMapping("/info/{username}")
@SentinelResource(value = "infoSentinelResource",blockHandler = "handlerBlockHandler", fallback = "doActionFallback") // 演示SentinelResource细粒度管控服务流控和降级
public R<LoginUser> info(@PathVariable("username") String username) {
    SysUser sysUser = userService.selectUserByUserName(username);
    if (StringUtils.isNull(sysUser)) {return R.fail("用户名或密码错误");}
    // 角色集合
    Set<String> roles = permissionService.getRolePermission(sysUser);
    // 权限集合
    Set<String> permissions = permissionService.getMenuPermission(sysUser);
    LoginUser loginUser = new LoginUser();
    loginUser.setUser(sysUser);
    loginUser.setRoles(roles);
    loginUser.setPermissions(permissions);
    loginUser.setUserId(sysUser.getUserId());
    loginUser.setDeptId(sysUser.getDeptId());
    loginUser.setNickName(sysUser.getNickName());
    return R.ok(loginUser);
}
  • 所谓 fallback 服务降级是指,调用一个微服务出现异常时候,访问者要有fallback 服务降级的情况,也就是我们常见的一些提醒如:“服务繁忙,请稍后再试!”不要持续访问9001加大微服务负担,

  • 直接在接口的 @SentinelResource 方法直接加 fallback 降级,但是通过 feign 接口调用的方法各自不同,如果每个不同方法都加一个fallback 配对方法,会导致代码膨胀不好管理,工程埋雷…

  • 在 UserFeignService 中的 @FeignClient 注解上
  • 添加自定义 fallbackFactory 就能实现我们想要的功能,下面是具体的代码。
/**
 * @author canghe
 * @description 用户服务
 * @create 2024-04-24-22:38
 */
@FeignClient(contextId = "userFeignService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = UserFeginFallbackFactory.class)
public interface UserFeignService {
    
    /**
     * 根据用户名获取当前用户信息
     */
    @GetMapping("/system/user/info/{username}")
    R<LoginUser> info(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
     * 根据 userId 获取用户信息
     */
    @GetMapping("/system/user/getInfoByUserId/{userId}")
    R<LoginUser> getInfoByUserId(@PathVariable("userId") Long userId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
     * 根据条件获取用户列表
     */
    @PostMapping("/system/user/listOfInner")
    R<List<SysUserVO>> listOfInner(@RequestBody SysUserDTO sysUserDTO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
     * 注册用户信息
     *
     * @param sysUser 用户信息
     * @param source 请求来源
     * @return 结果
     */
    @PostMapping("/system/user/register")
    R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

  • UserFeginFallbackFactory 降级处理
/**
 * 用户服务降级处理
 *
 * @author canghe
 */
@Component
public class UserFeginFallbackFactory implements FallbackFactory<UserFeignService>
{
    private static final Logger log = LoggerFactory.getLogger(UserFeginFallbackFactory.class);
    @Override
    public UserFeignService create(Throwable throwable)
    {
        log.error("用户服务调用失败:{}", throwable.getMessage());
        return new UserFeignService()
        {
            @Override
            public R<LoginUser> info(String username, String source) {
                return R.fail("根据用户名获取用户失败:" + throwable.getMessage());
            }
            @Override
            public R<LoginUser> getInfoByUserId(Long userId, String source) {
                return R.fail("根据userId获取用户失败:" + throwable.getMessage());
            }
            @Override
            public R<List<SysUserVO>> listOfInner(SysUserDTO sysUserDTO, String source) {
                return R.fail("根据调教获取用户列表失败:" + throwable.getMessage());
            }
            @Override
            public R<Boolean> registerUserInfo(SysUser sysUser, String source) {
                return R.fail("注册用户失败:" + throwable.getMessage());
            }
        };
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值