springcloud gateway网关获取post请求参数,网关路由,网关限流,熔断降级等实际操作

上一篇使用了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 #令牌桶的容量,允许在一秒钟内完成的最大请求数
  • filter 名称必须是 RequestRateLimiter
  • redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求
  • redis-rate-limiter.burstCapacity:令牌桶的容量,允许在一秒钟内完成的最大请求数
  • key-resolver:使用 SpEL 按名称引用 bean

在流量过大时进行服务降级,避免请求长时间的等待,这个是在网关层进行熔断降级,不是在服务层进行熔断降级,服务层也可进行熔断降级,接下来会写,我们先看网关层的熔断降级,先写一个降级的类,返回消息

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秒



项目码云下载地址 

文章参考来自 天涯泪小武 纯洁的微笑 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值