WARNING
This Project Is Archives,Can't catch up the spring-cloud-version.But the code and the thought is much more useful for who want adapt spring-cloud-gateway to his own project
spring-cloud-gateway-plugin
Spring Cloud Gateway Extra Plugin
Current Version
Gateway Version | Plugin Version |
---|---|
Greenwich.SR1 |
2.1.SR1.3.RELEASE |
Greenwich.SR2 |
2.1.SR2.2.RELEASE |
More Detail See Wiki
This Plugin Support Below Features:
- Cache Request Body And Form Body
- Add Request Log Filter To Log Request And Response
- Add Read Json Response Body And Log Response Body
- Add Global Exception Handler With Json
- Add Custom Exception Handler
- Add Grey Route With Ribbon And Eureka
- Each Route Use different Hystrix CommandKey
- Support Dynamic Predicate With Existing Routes
How To Use This Feature
Note:
- This Dependency Base Spring Cloud Gateway[
Greenwich.SR1
],Suggest To Update To This SpringCloud Version,Official Resolve Some Issues , Fix Some Bugs. - The SpringBoot Version need to update to [
2.1.6.RELEASE
],It fix reactor-netty issues - This Dependency Is Now In Maven Central.
- The Feature To Read Request And Response Json Data Will Loss A Lot Of Performance,It Will Reduce The Gateway Traffic.
Step
-
1 . Include Dependency
Spring Cloud Gateway
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--If you need to use grey route,you should add next dependency ,but grey route only can be used with eureka discover--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Gateway Plugin
<dependency> <groupId>pro.chenggang</groupId> <artifactId>spring-cloud-gateway-plugin</artifactId> <version>2.1.SR1.1.RELEASE</version> </dependency>
-
2 . Add Enable Annotation To You Application
@EnableGatewayPlugin public class SpringCloudGatewayPluginApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudGatewayPluginApplication.class, args); } }
Note: If You User SpringBoot 2.1.0+,You should set
allow-bean-definition-overriding
totrue
spring: main: allow-bean-definition-overriding: true
-
3 . Choose Plugin Feature To Use
By use this annotation
@EnableGatewayPlugin
to enable the plugin,the plugin support switch to choose feature By default,theGatewayContext Filter
is always into systemspring: profiles: active: dev cloud: gateway: plugin: config: log-request: true read-request-data: true # this setting will read all request data read-response-data: true exception-json-handler: true enable-dynamic-route: true grey: enable: false grey-ribbon-rule: weight_response
-
4 . Specific Setting To Enable Read Request Data
spring: cloud: gateway: plugin: config: read-request-data-service-id-list: #set specific serviceId from discover to read request Data - serviceId1 - serviceId2 read-request-data-path-list: #set specific path to read request data - /service/path1/* - /service/path2/** - /service/path3
-
5 . User GatewayContext
You Can Use GatewayContext To Get Cache JsonBody Or Form Body,Just Use
GatewayContext gatewayContext = exchange.getAttribute(GatewayContext.CACHE_GATEWAY_CONTEXT);
-
6 . The Deference Between
GreyRibbonRule.DEFAULT
AndGreyRibbonRule.WeightResponse
The Default GreyRibbonRule Just Use Round Rule As Base Ribbon Rule The WeightResponse GreyRibbonRule Use WeightResponse Rule As Base Ribbon Rule
-
7 . The Grey Route
- Setup Gateway Properties
spring: cloud: gateway: plugin: grey: grey-rule-list: - service-id: privider1 version: 2.0.0 operation: OR rules: - key: key1 value: - value1 - value2 - value3 - key: key2 value: - value4 - value5 - value6 - service-id: provider2 version: 2.0.0 operation: AND rules: - key: keya value: - value1a - value2a - value3a - key: keyb value: - value4b - value5b - value6b
-
Set Up Service MetaInfo
#proiver1 eureka: instance: metadata-map: version: 2.0.0
-
The Properties Active Rule Principle
When Request URL Route To Provider1,When The Request JsonBody Or Form Data Contain The Key ->
Key1
And Match Any Of The Value->[value1
,value2
,value3
] The Route The Request To The Service Which Setup The MetaInfo With Specific Version Which Match The Gateway Grey Setup
-
8 . How To Custom GlobalException Handler With Json
In Order To Handle Other Exception,You Can Define Specific Bean Implements
ExceptionHandlerStrategy
By default,plugin supplyDefaultExceptionHandlerStrategy
In Case Of None Strategy Exist@Slf4j public class DefaultExceptionHandlerStrategy implements ExceptionHandlerStrategy { @Override public Class getHandleClass() { return Throwable.class; } @Override public ExceptionHandlerResult handleException(Throwable throwable) { ResponseResult<String> responseResult = new ResponseResult<>(SystemResponseInfo.GATEWAY_ERROR,throwable.getMessage()); ExceptionHandlerResult result = new ExceptionHandlerResult(HttpStatus.INTERNAL_SERVER_ERROR, JSON.toJSONString(responseResult)); log.debug("[DefaultExceptionHandlerStrategy]Handle Exception:{},Result:{}",throwable.getMessage(),result); log.error("[DefaultExceptionHandlerStrategy]Log Exception In Error Level,Exception Message:{}",throwable.getMessage()); return result; } }
Or You Can Use
@ExceptionHandler
just like below,The
@ResponseStatus
is optional,if you don't add@ResponseStatus
,the default HttpStatus is HttpStatus.BAD_GATEWAY@Component public class DemoExceptionHandler { @ExceptionHandler({NotFoundException.class}) @ResponseStatus(HttpStatus.NOT_FOUND) public Map handlerException(ServerWebExchange exchange,TimeoutException throwable){ LinkedHashSet<URI> originalRequestUris = exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR,null); Map map = Maps.newHashMapWithExpectedSize(2); map.put("URI",originalRequestUris); map.put("ExceptionMessage",throwable.getClass().getSimpleName()); return map; } @ExceptionHandler({ClientException.class, TimeoutException.class}) @ResponseStatus(HttpStatus.BAD_GATEWAY) public Map handlerException(ServerWebExchange exchange,TimeoutException throwable){ LinkedHashSet<URI> originalRequestUris = exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR,null); Map map = Maps.newHashMapWithExpectedSize(2); map.put("URI",originalRequestUris); map.put("ExceptionMessage",throwable.getClass().getSimpleName()); return map; } }
-
9 . How To Use Dynamic Predicate With Existing Routes
- Define A Component Implements
DynamicRouteProcessor
,this processor process serverWebExchange for dynamic route predicate
the
DynamicRouteProcessor
definition/** * Process ServerWebExchange for dynamic route predicate * @author chenggang * @date 2019/07/17 */ public interface DynamicRouteProcessor<T> { /** * preprocess action * @param exchange ServerWebExchange * @return process Result ,if result is Optional.empty(),then dynamic predicate not working */ Optional<PreprocessResult<T>> preprocess(ServerWebExchange exchange); /** * process to unify config for predicate * @param preprocessResult pre process result * @param route current route * @return */ Optional<DynamicRouteConfig> processConfig(PreprocessResult<T> preprocessResult, Route route); /** * target predicate bean 's class * @return RoutePredicateFactory Class */ Optional<Class< ? extends AbstractRoutePredicateFactory>> targetPredicateBeanClass(); }
custom dynamic route processor
@Component public class CustomDynamicRouteProcessor implements DynamicRouteProcessor { @Override public Optional<PreprocessResult> preprocess(ServerWebExchange exchange) { String route = exchange.getRequest().getHeaders().getFirst("route"); if(StringUtils.isBlank(route)){ return Optional.of(PreprocessResult.builder().result(false).build()); } return Optional.of(PreprocessResult.builder().result(true).resultData(route).build()); } @Override public Optional<DynamicRouteConfig> processConfig(PreprocessResult preprocessResult, Route route) { if(!preprocessResult.getResult()){ return Optional.empty(); } Object resultData = preprocessResult.getResultData(); if(!(resultData instanceof String)){ return Optional.empty(); } String data = (String) resultData; DemoDynamicRoutePredicateFactory.Config config = DemoDynamicRoutePredicateFactory.Config.builder().header(data).route(route).build(); return Optional.of(config); } @Override public Optional<Class<? extends AbstractRoutePredicateFactory>> targetPredicateBeanClass() { return Optional.of(DemoDynamicRoutePredicateFactory.class); } }
Define a
AbstractRoutePredicateFactory
,theConfig
Class Must ImplementsDynamicRouteConfig
@Slf4j @Component public class DemoDynamicRoutePredicateFactory extends AbstractRoutePredicateFactory<DemoDynamicRoutePredicateFactory.Config> { public DemoDynamicRoutePredicateFactory() { super(Config.class); } @Override public Predicate<ServerWebExchange> apply(Config config) { return exchange -> { Route route = config.getRoute(); if(Objects.isNull(route.getUri())){ log.debug("Route Uri Is NUll Return False,RouteID:{}",route.getId()); return false; } String routeUriHost = route.getUri().getHost(); String headerData = config.getHeader(); if(StringUtils.isBlank(routeUriHost) || StringUtils.isBlank(headerData)){ log.debug("Route Uri Host Or HeaderData Is Blank Return False,RouteID:{}",route.getId()); return false; } if(routeUriHost.equalsIgnoreCase(headerData)){ log.debug("Route Uri Host Matched Header Data Return True,RouteID:{}",route.getId()); route.getFilters(); return true; } log.debug("Route Uri Not Matched Return False,RouteID:{}",route.getId()); return false; }; } @Getter @Setter @Builder @ToString @AllArgsConstructor public static class Config implements DynamicRouteConfig { private Route route; private String header; } }
This Feature Support dynamic predicate with existing routes,Fox example: You can according the custom header to match the loadbalance route,
- More logical detail to see
DynamicRoutePredicateHandlerMapping
- Define A Component Implements