前两篇博客已经介绍了十一种路由过滤器工厂:
- Spring Cloud Alibaba:Gateway之路由过滤器工厂(一)
- Spring Cloud Alibaba:Gateway之路由过滤器工厂(二)
随着Gateway的不断更新,Gateway提供的路由过滤器工厂种类一直在增加,目前已经有三十多种了(不同版本可能不一样)。
搭建工程一个父module和两个子module(server module提供服务,gateway module实现网关)。
父module的pom.xml:
server module4.0.0 com.kaven alibabapom 1.0-SNAPSHOT Spring Cloud Alibaba gateway server 8 8 Hoxton.SR9 2.2.6.RELEASE org.springframework.boot spring-boot-starter-parent2.3.2.RELEASE org.springframework.cloud spring-cloud-dependencies${spring-cloud-version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies${spring-cloud-alibaba-version} pom import
pom.xml:
com.kaven alibaba1.0-SNAPSHOT 4.0.0 server8 8 org.springframework.boot spring-boot-starter-web
application.yml:
server: port: 8085
接口定义:
package com.kaven.alibaba.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MessageController { @GetMapping("/message") public String getMessage() { return "hello kaven, this is spring cloud alibaba"; } }
启动类:
package com.kaven.alibaba; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }gateway module
pom.xml:
com.kaven alibaba1.0-SNAPSHOT 4.0.0 gateway8 8 org.springframework.cloud spring-cloud-starter-gateway
application.yml:
server: port: 8086 spring: cloud: gateway: routes: - id: server uri: http://localhost:8085 predicates: - Path=/message
启动类:
package com.kaven.alibaba; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }StripPrefix
StripPrefix路由过滤器工厂接受一个参数(parts),parts参数表示在将请求发送到下游服务之前要从请求中剥离的路径数量。
源码相关部分:
增加路由过滤器的配置(全写):
predicates: - Path=/** filters: - name: StripPrefix args: parts: 2
或(简写):
predicates: - Path=/** filters: - StripPrefix=2
访问http://127.0.0.1:8086/1/2/message,会被路由到http://127.0.0.1:8085/message。
Retry路由过滤器工厂接受以下参数:
-
retries:应该尝试的重试次数。
-
series:要重试的一系列状态码,用HttpStatus.Series表示。
-
statuses:应该重试的HTTP状态码,用HttpStatus表示。
-
methods:应该重试的HTTP方法,用HttpMethod表示。
-
exceptions:应该重试的抛出异常列表。
-
backoff:为重试配置的指数退避,在退避间隔firstBackoff * (factor ^ n)之后执行重试,其中n是重试次数减一(即n的取值范围为[0,retries-1])。如果maxBackoff已配置,则应用的最大退避限制为maxBackoff。如果basedOnPreviousValue为真,则使用prevBackoff * factor计算退避(也不能超过maxBackoff)。
修改路由过滤器的配置:
predicates: - Path=/message filters: - name: Retry args: retries: 5 statuses: Internal_Server_Error methods: GET,POST backoff: firstBackoff: 300ms maxBackoff: 1300ms factor: 2 basedOnPreviousValue: false
Internal_Server_Error对应500状态码(详见HttpStatus类)。
修改接口:
AtomicLong time = new AtomicLong(0); @GetMapping("/message") public String getMessage(HttpServletResponse response) { if(time.get() != 0) { System.out.println(System.currentTimeMillis() - time.get()); } time.set(System.currentTimeMillis()); response.setStatus(500); return "hello kaven, this is spring cloud alibaba"; }
访问http://127.0.0.1:8086/message,在server module中会输出如下所示的重试间隔毫秒数(接近网关设置的重试间隔时间):
327 623 1216 1308 1307
是符合预期的。
HTTP系列状态码的枚举类:
public enum Series { INFORMATIONAL(1), SUCCESSFUL(2), REDIRECTION(3), CLIENT_ERROR(4), SERVER_ERROR(5); ... }
如下所示的配置可以对5XX系列状态码的响应进行请求重试。
predicates: - Path=/message filters: - name: Retry args: retries: 5 series: SERVER_ERROR # 默认值 backoff: firstBackoff: 300ms maxBackoff: 1300ms factor: 2 basedOnPreviousValue: true
访问http://127.0.0.1:8086/message,结果是类似的。
修改路由过滤器的配置:
predicates: - Path=/message filters: - name: Retry args: retries: 5 exceptions: java.util.concurrent.TimeoutException,java.lang.ClassNotFoundException backoff: firstBackoff: 300ms maxBackoff: 1300ms factor: 2 basedOnPreviousValue: true
修改接口:
AtomicLong time = new AtomicLong(0); @GetMapping("/message") public String getMessage(@RequestParam("isThrow") boolean isThrow) throws ClassNotFoundException { if(time.get() != 0) { System.out.println(System.currentTimeMillis() - time.get()); } time.set(System.currentTimeMillis()); if(isThrow) throw new ClassNotFoundException(); return "hello kaven, this is spring cloud alibaba"; }
访问http://127.0.0.1:8086/message,在server module中的输出如下图所示:
也是符合预期的。
当请求大小大于允许的限制时,RequestSize路由过滤器工厂可以限制请求到达下游服务。过滤器接受一个maxSize参数,maxSize是一个DataSize类型,所以值可以被定义为一个数字,后跟一个可选的DataUnit后缀,例如KB或MB,字节的默认值为B。它是以字节为单位定义的请求的允许大小限制。
BYTES("B", DataSize.ofBytes(1L)), KILOBYTES("KB", DataSize.ofKilobytes(1L)), MEGABYTES("MB", DataSize.ofMegabytes(1L)), GIGABYTES("GB", DataSize.ofGigabytes(1L)), TERABYTES("TB", DataSize.ofTerabytes(1L));
源码相关部分:
修改路由过滤器的配置:
predicates: - Path=/message filters: - name: RequestSize args: maxSize: 5KB
请求负载限制为5KB。
修改接口:
@GetMapping("/message") public String getMessage() { return "hello kaven, this is spring cloud alibaba"; }
访问http://127.0.0.1:8086/message成功(请求负载没有超过限制)。
访问http://127.0.0.1:8086/message失败(请求负载超过限制)。
PAYLOAD_TOO_LARGE和REQUEST_ENTITY_TOO_LARGE(即将被抛弃)表示同一个状态码,而网关响应的是PAYLOAD_TOO_LARGE(上面的源码截图),Postman显示的是REQUEST_ENTITY_TOO_LARGE,可能是博主的Postman版本的原因。
响应头里面的错误信息显示请求负载限制是5.1KB,很显然是换算不一致的问题(过滤器使用1000,而DataSize使用1024):
private static String getReadableByteCount(long bytes) { int unit = 1000; if (bytes < unit) { return bytes + " B"; } int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = Character.toString(PREFIX.charAt(exp - 1)); return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); }
private static final long BYTES_PER_KB = 1024L; private static final long BYTES_PER_MB = 1048576L; private static final long BYTES_PER_GB = 1073741824L; private static final long BYTES_PER_TB = 1099511627776L;SetRequestHostHeader
在某些情况下,可能需要覆盖请求的主机头。在这种情况下,SetRequestHostHeader路由过滤器工厂可以用指定的值替换现有的主机头。过滤器接受一个host参数。
源码相关部分:
由源码可知,请求没有主机头时,也会给请求添加指定值的主机头。
修改路由过滤器的配置:
predicates: - Path=/message filters: - name: SetRequestHostHeader args: host: kaven.top
修改接口:
@GetMapping("/message") public String getMessage(HttpServletRequest httpServletRequest) { StringBuilder result = new StringBuilder("hello kaven, this is spring cloud alibaban"); result.append(getKeyAndValue(httpServletRequest)); return result.toString(); } // 获取header中key和value组成的StringBuilder private StringBuilder getKeyAndValue(HttpServletRequest httpServletRequest) { StringBuilder result = new StringBuilder(); EnumerationheaderNames = httpServletRequest.getHeaderNames(); while (headerNames.hasMoreElements()) { String key = headerNames.nextElement(); String value = httpServletRequest.getHeader(key); result.append(key).append(" : ").append(value).append("n"); } return result; }
访问http://127.0.0.1:8086/message,请求的主机头就会被修改成指定的值。
请求没有主机头时,也会给请求添加指定值的主机头。
注释掉路由过滤器,请求的主机头是本地的套接字。
使用ModifyRequestBody过滤器在网关向下游服务发送请求Body之前修改请求Body。此过滤器目前只能通过使用Java DSL进行配置。
在gateway module中添加一个bean:
@Bean public RouteLocator routes(RouteLocatorBuilder builder) { String uri = "http://127.0.0.1:8085"; return builder.routes() .route("server", r -> r.path("/message") .filters(f -> f.modifyRequestBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> Mono.just(s.toUpperCase()))) .uri(uri)) .build(); }
其实就是用代码的形式来定义路由的处理流程,因此,之前介绍过的路由过滤器都可以通过这种形式添加到路由中。server是路由的id,path方法是给路由添加Path断言,modifyRequestBody方法是给路由添加ModifyRequestBody路由过滤器,这些lambda表达式应该容易看懂,modifyRequestBody方法的第一个参数表示请求Body输入时需要转换的类型,第二个参数表示请求Body输出时需要转换的类型,而第三个参数就是请求发送到下游服务时的新Content-Type 请求头,最后一个参数就是用于修改请求Body的方法(这里就是将字符串全部变成大写),uri方法是给路由指定路由匹配成功后的请求转发路径。
- Spring Cloud Alibaba:Gateway网关 & 路由断言工厂
修改接口:
@GetMapping("/message") public String getMessage(@RequestBody String message) { return message + "nhello kaven, this is spring cloud alibaban"; }ModifyResponseBody
使用ModifyResponseBody过滤器在响应Body发送回客户端之前对其进行修改。此过滤器目前也只能通过使用Java DSL进行配置。
在gateway module中添加一个bean(和之前那个bean类似):
@Bean public RouteLocator routes(RouteLocatorBuilder builder) { String uri = "http://127.0.0.1:8085"; return builder.routes() .route("server", r -> r.path("/message") .filters(f -> f.modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> Mono.just(s.toUpperCase()))) .uri(uri)) .build(); }
通过modifyResponseBody方法给路由添加ModifyResponseBody过滤器。
其他的路由过滤器工厂在以后的博客中介绍,如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)