Gateway Sentinel 做网关降级/流控,转发header和cookie

大家好,我是烤鸭:

   Springcloud Gateway 使用 Sentinel 流量控制。

环境

springcloud-gateway的网关应用,springboot的服务,nacos作为注册中心

sentinel-dashboard-1.8.2

最新版下载地址:
https://github.com/alibaba/Sentinel/releases

目标

在网关层根据qps对指定路由降级到其他接口。

sentinel 接入的官方wiki:

https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81

网关代码:
https://gitee.com/fireduck_admin/scg-sentinel

业务代码就不贴了,就是普通的springboot服务,集成nacos注册中心。

dashboard

dashboard 需要有访问才能显示,访问几次。

在这里插入图片描述

由于没配置网关类型,sentinel 默认是服务,是没有API管理的。

在这里插入图片描述

网关服务启动参数中添加 -Dcsp.sentinel.app.type=1,这回有了。

在这里插入图片描述

API 管理和流控规则配置

API 添加的地址我这边选的是精确匹配路由,路由就是上图实时监控的地址。

在这里插入图片描述

流程规则选按API分组和QPS进行降级,模式选快速失败。(在网关代码里实现具体快速失败的逻辑,比如调第三方接口降级)

在这里插入图片描述

scg 代码开发

默认流程:

GatewayConfiguration,配置降级时触发异常

package com.maggie.demo.scgsentinel.config;import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.maggie.demo.scgsentinel.handler.SentinelBlockRequestHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;@Order(1)
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class GatewayConfiguration implements ApplicationContextAware {@Autowiredprotected FilteringWebHandler filteringWebHandler;@Value("${spring.application.name}")private String PJ_NAME;public String getPJ_NAME() {return PJ_NAME;}public void setPJ_NAME(String PJ_NAME) {this.PJ_NAME = PJ_NAME;}private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;private final BlockRequestHandler blockRequestHandler;private ApplicationContext applicationContext;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;this.blockRequestHandler = new SentinelBlockRequestHandler();}// 不配置异常处理,采用默认的提示
//    @Bean
//    @Order(Ordered.HIGHEST_PRECEDENCE)
//    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
//        // Register the block exception handler for Spring Cloud Gateway.
//        return new GatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
//    }@Bean@Order(-1)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}@PostConstructpublic void doInit() throws Exception {initBlockStrategy();}private void initBlockStrategy() {String[] beanNamesForType = applicationContext.getBeanNamesForType(BlockRequestHandler.class);if (beanNamesForType != null && beanNamesForType.length > 0) {GatewayCallbackManager.setBlockHandler(applicationContext.getBean(beanNamesForType[0], BlockRequestHandler.class));} else {GatewayCallbackManager.setBlockHandler(blockRequestHandler);}}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}/*** @Description 加这个注解可以直接通过注册中心的application.name直接调用服务* @Date 2021/8/12 14:28**/@Bean@LoadBalancedpublic WebClient.Builder loadBalancedWebClientBuilder() {return WebClient.builder();}
}

下图是触发降级和默认的限流异常提示

在这里插入图片描述

在这里插入图片描述

自定义流程:

增加异常处理和降级逻辑

GatewayBlockExceptionHandler

package com.maggie.demo.scgsentinel.handler;import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.function.Supplier;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;public class GatewayBlockExceptionHandler extends SentinelGatewayBlockExceptionHandler {private List<ViewResolver> viewResolvers;private List<HttpMessageWriter<?>> messageWriters;public GatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {super(viewResolvers, serverCodecConfigurer);this.viewResolvers = viewResolvers;this.messageWriters = serverCodecConfigurer.getWriters();}private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {return response.writeTo(exchange, contextSupplier.get());}@Overridepublic Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {/*** 处理一下504*/if (ex != null && ex instanceof ResponseStatusException) {ResponseStatusException responseStatusException = (ResponseStatusException) ex;//只处理超时的情况if (HttpStatus.GATEWAY_TIMEOUT.equals(responseStatusException.getStatus())) {return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));}}if (exchange.getResponse().isCommitted()) {return Mono.error(ex);}// This exception handler only handles rejection by Sentinel.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);}private final Supplier<ServerResponse.Context> contextSupplier = () -> new ServerResponse.Context() {@Overridepublic List<HttpMessageWriter<?>> messageWriters() {return GatewayBlockExceptionHandler.this.messageWriters;}@Overridepublic List<ViewResolver> viewResolvers() {return GatewayBlockExceptionHandler.this.viewResolvers;}};
}

SentinelBlockRequestHandler

package com.maggie.demo.scgsentinel.handler;import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.HashSet;
import java.util.Set;import static org.springframework.web.reactive.function.BodyInserters.fromValue;@Component
@Slf4j
public class SentinelBlockRequestHandler implements BlockRequestHandler, Ordered {public static final Set<String> FLOW_LIMIT_API = new HashSet<>(32);static {FLOW_LIMIT_API.add("/test/api/tab/test");}public static final String CODE_SYSTEM_BUSY = "11004";public static final String MSG_SYSTEM_BUSY = "网络开小差了,请稍后重试.";@Overridepublic int getOrder() {return LOWEST_PRECEDENCE;}@Autowiredprivate WebClient.Builder webClientBuilder;@Autowiredprivate ApplicationContext applicationContext;@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {String requestUrl = exchange.getRequest().getPath().toString();Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);if (FLOW_LIMIT_API.contains(requestUrl)) {return handleRequestDetail(exchange,ex);}// JSON result by default.return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(fromValue(buildErrorResult(ex)));}private ErrorResult buildErrorResult(Throwable ex) {return new ErrorResult(CODE_SYSTEM_BUSY, MSG_SYSTEM_BUSY);}private static class ErrorResult {private final String status;private final String message;public String getStatus() {return status;}ErrorResult(String status, String message) {this.status = status;this.message = message;}public String getMessage() {return message;}}public Mono<ServerResponse> handleRequestDetail(ServerWebExchange exchange, Throwable ex) {String requestUrl = exchange.getRequest().getURI().getRawPath();System.out.println("requestUrl = " + requestUrl);Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);// post请求的参数需要自己写filter 获取,这里只是获取的get请求参数MultiValueMap<String, String> getParams = exchange.getRequest().getQueryParams();// 转发到第三方接口,这里是配置的时候route-id和注册中心服务的application.name(lb://data-ballast-api) 一致了// 原地址 localhost:8083/test/api/tab/test, 降级地址 http://data-ballast-api/api/tab/test/sentinelStringBuilder newUri = new StringBuilder("http://").append(route.getId()).append(requestUrl+"/sentinel");log.info("限流uri: {}, query参数: {}, 降级至: uri {}", requestUrl, getParams, newUri.toString());//return webClientBuilder.build().post() //.uri(newUri.toString())// 传递 request header.headers(newHeaders -> newHeaders.putAll(exchange.getRequest().getHeaders())).bodyValue(getParams).exchangeToMono(response -> {return response.bodyToMono(String.class).defaultIfEmpty("").flatMap(body -> {return ServerResponse.status(response.statusCode())// 传递 response header,避免cookie在网关层丢失.headers(it -> {it.addAll(response.headers().asHttpHeaders());}).bodyValue(body);});});}
}

自定义三方降级接口:
在这里插入图片描述

降级成功:
在这里插入图片描述

在这里插入图片描述

待优化

上面已经基本实现了网关层面进行流控。

还有几个地方可以优化:

  • 流控规则等配置的持久化:sentinel 存的规则是默认存到内存里的,一旦重启了服务(网关或者普通的业务服务),规则需要重新配置。持久化可以选择 apollo或者 nacos。(一般的配置中心)

  • 针对不同方式的参数获取待完善(POST请求、文件上传 等等)

  • 针对不同的route走不同的降级策略(代码优化,可以使用策略模式)

  • 不同异常的处理,比如通用的 GatewayConfiguration 是处理了所有异常进行的 handleRequest 处理,可能有些请求不适合按降级处理(比如超时之类的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412527.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[css] 说说CSS的优先级是如何计算的?

[css] 说说CSS的优先级是如何计算的&#xff1f; 选择器种类严格来讲&#xff0c;选择器的种类可以分为三种&#xff1a;标签名选择器、类选择器和ID选择器。而所谓的后代选择器和群组选择器只不过是对前三种选择器的扩展应用。而 在标签内写入 style"" 的方式&…

django后台数据管理admin设置代码

新建admin用户 createsuperuser 设定好用户名&#xff0c;邮箱&#xff0c;密码 设置setting LANGUAGE_CODE zh-hansTIME_ZONE Asia/ShanghaiUSE_I18N TrueUSE_L10N TrueUSE_TZ False 在写好的users的app下修改admin.py # -*- coding: utf-8 -*- from __future__ import u…

rocketmq 初探(一)

大家好&#xff0c;我是烤鸭&#xff1a; 今天看下rocketmq。这篇主要是简单介绍下 rocketmq以及idea 本地调试 rocketmq。 项目架构 感兴趣的可以下载源码看下。 https://github.com/apache/rocketmq 项目结构图。 rocketmq-acl: acl 秘钥方式的鉴权&#xff0c;用在bro…

[css] 说说浏览器解析CSS选择器的过程?

[css] 说说浏览器解析CSS选择器的过程&#xff1f; 按照从上到下&#xff0c;从右到左的顺序解析。个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

客户将数据库迁移上云的常用办法

下载网站:www.SyncNavigator.CN 客服QQ1793040---------------------------------------------------------- 关于HKROnline SyncNavigator 注册机价格的问题 HKROnline SyncNavigator 8.4.1 非破解版 注册机 授权激活教程 最近一直在研究数据库同步的问题&#xff0c;在网上…

基于nchan打造百万用户的聊天室

大家好&#xff0c;我是烤鸭&#xff1a; 这次介绍下nchan&#xff0c;nginx的一个module。 nchan 源码: https://github.com/slact/nchan 官网: https://nchan.io/ nginx 配置说明文档: https://nchan.io/documents/nginxconf2016-slides.pdf 测试环境搭建 4 台linux cent…

springboot 获取控制器参数的几种方式

这里介绍springboot 获取控制器参数有四种方式 1、无注解下获取参数 2、使用RequestParam获取参数 3、传递数组 4、通过URL传递参数 无注解下获取参数无注解下获取参数&#xff0c;需要控制器层参数与HTTP请求栏参数名称一致&#xff0c;这样后台就可以获取到请求栏的参数。 /*…

rocketmq 初探(二)

大家好&#xff0c;我是烤鸭&#xff1a; 上一篇简单介绍和rocketmq&#xff0c;这一篇看下源码之注册中心。 namesrv 先看两个初始化方法 NamesrvController.initialize() 和 NettyRemotingServer.start(); public boolean initialize() {// 加载配置文件this.kvConfigMana…

[css] 说说你对line-height是如何理解的?

[css] 说说你对line-height是如何理解的&#xff1f; line-height 行高&#xff0c;就是两行文字之间基线的距离&#xff0c;用来调整文字的行间距。个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 …

2 JVM 运行机制

转载于:https://www.cnblogs.com/likevin/p/10186591.html

[css] 要让Chrome支持小于12px的文字怎么做?

[css] 要让Chrome支持小于12px的文字怎么做&#xff1f; 1, 改用图片 2, 使用 -webkit-text-size-adjust:none; 但是不支持chrome 27.0以上版本 3, 使用 transform: scale( )缩小 暂时不知道更多方法了个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易…

rocketmq 初探(三)

大家好&#xff0c;我是烤鸭&#xff1a; 上一篇介绍了注册中心&#xff0c;这一篇看下broker。基于 rocketmq 4.9 版本。 BrokerStartup#BrokerController 按照代码的先后顺序撸源码&#xff1a; BrokerController.createBrokerController public static BrokerController…

WIN10远程连接时提示内部错误

微软官方的解决方案是重置远程连接设置&#xff0c;步骤如下&#xff1a; 1、以管理员身份运行命令提示符 2、输入以下命令&#xff1a; netsh winsoc reset 随后会提示重启电脑&#xff0c;遂解决。 3、重启后还不行的话&#xff0c;再试试删除掉远程连接保存的凭据&#xff0…

[css] css的属性content有什么作用呢?有哪些场景可以用到?

[css] css的属性content有什么作用呢&#xff1f;有哪些场景可以用到&#xff1f; content属性与 ::before 及 ::after 伪元素配合使用生成文本内容通过attr()将选择器对象的属性作为字符串进行显示&#xff0c;如&#xff1a;a::after{content: attr(href)} <a href"h…

rocketmq 初探(四)

大家好&#xff0c;我是烤鸭&#xff1a; 上一篇简单介绍broker的初始化&#xff0c;这一篇介绍 NettyRequestProcessor 的实现(主要是broker里用到的)。 AdminBrokerProcessor、ClientManageProcessor、ConsumerManageProcessor、EndTransactionProcessor NettyRequestProce…

iOS 去除警告 看我就够了

你是不是看着开发过程中出现的一堆的警告会心情一阵烦躁&#xff0c;别烦躁了&#xff0c;看完此文章&#xff0c;消除警告的小尾巴。 一、SVN 操作导致的警告 1.svn删除文件后报错 ”xx“is missing from working copy 使用命令sudo find 工程项目路径 -name ".svn"…

[css] 什么是FOUC?你是如何避免FOUC的?

[css] 什么是FOUC&#xff1f;你是如何避免FOUC的&#xff1f; FOUC 即 Flash of Unstyled Content&#xff0c;是指页面一开始以样式 A&#xff08;或无样式&#xff09;的渲染&#xff0c;突然变成样式B。 原因是样式表的晚于 HTML 加载导致页面重新进行绘制。通过 import 方…

rocketmq 初探(五)

大家好&#xff0c;我是烤鸭&#xff1a; 上一篇简单介绍部分 NettyRequestProcessor (AdminBrokerProcessor、ClientManageProcessor、ConsumerManageProcessor、EndTransactionProcessor)&#xff0c;这一篇介绍其他的。 PullMessageProcessor、QueryMessageProcessor、Repl…

Python 装饰器初探

Python 装饰器初探 在谈及Python的时候&#xff0c;装饰器一直就是道绕不过去的坎。面试的时候&#xff0c;也经常会被问及装饰器的相关知识。总感觉自己的理解很浅显&#xff0c;不够深刻。是时候做出改变&#xff0c;对Python的装饰器做个全面的了解了。 1. 函数装饰器 直接上…

[css] 解释下 CSS sprites的原理和优缺点分别是什么

[css] 解释下 CSS sprites的原理和优缺点分别是什么 我来说下我的观点 原理&#xff1a; 多张图合并成一张图优点&解决的问题hover效果&#xff0c;如果是多个图片&#xff0c;网络正常的情况下首次会闪烁一下。如果是断网情况下&#xff0c;就没图片了。sprites 就很好的…