日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/10 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 處理,可能有些請求不適合按降級處理(比如超時之類的)

總結

以上是生活随笔為你收集整理的Gateway Sentinel 做网关降级/流控,转发header和cookie的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。