赞
踩
上一篇使用了springcloud 一代网关 zullfilter 的拦截和路由,这一篇将使用springcloud新一代网关 gateway 来进行参数拦截,路由,限流,熔断等操作。
首先我们看下pom.xml ,不同版本,可能会有不同的坑,操作基于以下版本
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.0.4.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.hqk</groupId>
- <artifactId>gtwayservice</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>gtwayservice</name>
- <description>Demo project for Spring Boot</description>
-
- <properties>
- <java.version>1.8</java.version>
- <spring-cloud.version>Finchley.SR2</spring-cloud.version>
- </properties>
-
- <dependencies>
- <!-- 熔断、降级 -->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
- </dependency>
-
- <!--reids 限流-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>${spring-cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-
- </project>
首先我们进行网关拦截,验证token session 等操作,如下图代码,
- package com.hqk.gtwayservice.filter;
-
- import io.netty.buffer.ByteBufAllocator;
- import org.springframework.cloud.gateway.filter.GatewayFilterChain;
- import org.springframework.cloud.gateway.filter.GlobalFilter;
- import org.springframework.core.Ordered;
- import org.springframework.core.io.buffer.DataBuffer;
- import org.springframework.core.io.buffer.DataBufferUtils;
- import org.springframework.core.io.buffer.NettyDataBufferFactory;
- import org.springframework.http.server.reactive.ServerHttpRequest;
- import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
-
- import java.net.URI;
- import java.nio.CharBuffer;
- import java.nio.charset.StandardCharsets;
- import java.util.Map;
- import java.util.concurrent.atomic.AtomicReference;
-
- @Component
- public class TokenFilter implements GlobalFilter, Ordered {
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
-
- ServerHttpRequest serverHttpRequest = exchange.getRequest();
- String method = serverHttpRequest.getMethodValue();
- if ("POST".equals(method)) {
- //从请求里获取Post请求体
- String bodyStr = resolveBodyFromRequest(serverHttpRequest);
- System.out.println("bodyStr:"+bodyStr);
- //TODO 这里对你获取的参数进行操作,比如 token session 的验证
-
- //下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值
- URI uri = serverHttpRequest.getURI();
- ServerHttpRequest request = serverHttpRequest.mutate().uri(uri).build();
- DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
- Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
- request = new ServerHttpRequestDecorator(request) {
- @Override
- public Flux<DataBuffer> getBody() {
- return bodyFlux;
- }
- };
- //封装request,传给下一级
- return chain.filter(exchange.mutate().request(request).build());
-
- } else if ("GET".equals(method)) {
- Map requestQueryParams = serverHttpRequest.getQueryParams();
- //TODO 得到Get请求的请求参数后,做你想做的事
-
- return chain.filter(exchange);
- }
-
- return chain.filter(exchange);
- }
-
- /**
- * 从Flux<DataBuffer>中获取字符串的方法
- */
- private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
- Flux<DataBuffer> body = serverHttpRequest.getBody();
- AtomicReference<String> bodyRef = new AtomicReference<>();
- body.subscribe(buffer -> {
- CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
- DataBufferUtils.release(buffer);
- bodyRef.set(charBuffer.toString());
- });
- return bodyRef.get();
- }
-
- private DataBuffer stringBuffer(String value) {
- byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
-
- NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
- DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
- buffer.write(bytes);
- return buffer;
- }
-
- @Override
- public int getOrder() {
- return 0;
- }
- }
然后我们postman进行测试,如下图所示
这里我们完成第一步的参数拦截
接下来我们将对请求进行路由转发,这里没有注册到注册中心,直接转发服务,直接看yml配置
- server:
- port: 9999
-
- spring:
- cloud:
- gateway:
- routes:
- - id: limit_route
- uri: http://127.0.0.1:8801 # 已负载均衡方式转发
- predicates:
- - Path=/**/** #只漏油转发
这里只改变IP进行转发,不会改变请求地址,如需改变请求地址,则改变 Path 路径,可参考
如果项目存在高并发场景,我们可以对请求地址 或 IP 等进行限流,限流配合redis进行使用,首先加入redis配置
- redis:
- host: localhost
- port: 6379
- database: 0
然后新建一个类
- package com.hqk.gtwayservice.resolver;
-
- import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
- import org.springframework.context.annotation.Bean;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
-
- import java.net.InetAddress;
-
- @Component
- public class IpResolver implements KeyResolver{
-
-
- @Override
- public Mono<String> resolve(ServerWebExchange exchange) {
-
- String ip=exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
-
- // exchange.getRequest().getURI().toString() //获取请求地址
- //可以对 ip 地址 等进行过滤
-
- System.out.println("ip:"+ip);
-
- return Mono.just(ip);
- }
- }
然后在yml中配置,这样就可以根据 ip 进行限流
- spring:
- cloud:
- gateway:
- routes:
- - id: limit_route
- uri: http://47.102.192.50:8081 # http://47.102.192.50:8081 # lb://consumer-service lb 代表从注册中心获取服务,且已负载均衡方式转发
- predicates:
- - Path=/**/** #只漏油转发
- filters:
- - name: RequestRateLimiter #名称必须是RequestRateLimiter
- args:
- key-resolver: "#{@ipResolver}" #使用SpEL按名称引用bean
- redis-rate-limiter.replenishRate: 2 #允许用户每秒处理多少个请求
- redis-rate-limiter.burstCapacity: 3 #令牌桶的容量,允许在一秒钟内完成的最大请求数
在流量过大时进行服务降级,避免请求长时间的等待,这个是在网关层进行熔断降级,不是在服务层进行熔断降级,服务层也可进行熔断降级,接下来会写,我们先看网关层的熔断降级,先写一个降级的类,返回消息
- package com.hqk.gtwayservice.controller;
-
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.HashMap;
- import java.util.Map;
-
- @RestController
- public class DefaultHystrixController {
-
- @RequestMapping("/defaultfallback")
- public Map<String,String> defaultfallback(){
- //System.out.println("降级操作...");
- Map<String,String> map = new HashMap<>();
- map.put("resultCode","false");
- map.put("resultMessage","服务异常");
- map.put("resultObj","这里测试网关服务熔断");
- return map;
- }
-
- }
看下整个yml的配置
- server:
- port: 9999
-
- spring:
- cloud:
- gateway:
- routes:
- - id: limit_route
- uri: http://47.102.192.50:8081 # http://47.102.192.50:8081 # lb://consumer-service lb 代表从注册中心获取服务,且已负载均衡方式转发
- predicates:
- - Path=/**/** #只漏油转发
- filters:
- - StripPrefix=1 #表示路由时会去除一位 如 api/app/login app/login
- - name: RequestRateLimiter #名称必须是RequestRateLimiter
- args:
- key-resolver: "#{@ipResolver}" #使用SpEL按名称引用bean
- redis-rate-limiter.replenishRate: 2 #允许用户每秒处理多少个请求
- redis-rate-limiter.burstCapacity: 3 #令牌桶的容量,允许在一秒钟内完成的最大请求数
- - name: Hystrix
- args:
- name: fallbackcmd
- fallbackUri: forward:/defaultfallback #这里是服务降级,调用的方法
-
- redis:
- host: localhost
- port: 6379
- database: 0 #限流redis 端口地址 这里没有设置密码
-
-
- hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 3000 #超时时间 3秒
-
-
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。