微服务Springcloud——Gateway

微服务Springcloud——Gateway

在这里插入图片描述


前言

现网上gateway多跟eurake或者数据库连用配置动态路由,但我只用到了gateway的转发功能,并不需要进行注册接口服务,所以在Filter里面进行了逻辑处理,例子比较少,记录一下,也方便小伙伴一起码,直接上代码喽;


提示:数据库中直接保存了要调用接口的整个url,无法配置动态路由。

一、gateway是什么?

pring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是 Netflix Zuul。网关通常在项目中为了简化前端的调用逻辑,同时也简化内部服务之间互相调用的复杂度;具体作用就是转发服务,接收并转发所有内外部的客户端调用;其他常见的功能还有权限认证,限流控制等等。
简而言之,就是统一转发的框架 ;

二、基础gateway

1.引入pom

pom代码如下:

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.4.RELEASE</version>
		<relativePath />
	</parent>
	  </dependency>
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-security</artifactId>
		</dependency>
	<!-- gateway -->
	<dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-starter-gateway</artifactId>
	    <version>2.2.2.RELEASE</version>
	</dependency>
	<!-- 热部署 -->
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-devtools</artifactId>
	    <version>2.0.6.RELEASE</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis-reactive -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>
<!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>
	<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.0.6</version>
		</dependency>
	<dependency>
			<groupId>oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>5.1.38</version>
		    <scope>runtime</scope>
		</dependency>
		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
     <!-- https://mvnrepository.com/artifact/log4j/log4j -->
		<dependency>
		    <groupId>log4j</groupId>
		    <artifactId>log4j</artifactId>
		    <version>1.2.17</version>
		</dependency>
 	<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
		<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-io</artifactId>
		    <version>1.3.2</version>
		</dependency>
 	
  </dependencies>
  <dependencyManagement>
  	<dependencies>
  		<dependency>
  			<groupId>org.springframework.cloud</groupId>
	  		<artifactId>spring-cloud-dependencies</artifactId>
	  		<version>Finchley.SR2</version>
	  		<type>pom</type>
	  		<scope>import</scope>
  		</dependency>
  	</dependencies>
  </dependencyManagement>

特别声明,springcloud的版本需要跟springboot匹配;

2.配置信息

代码如下:

server:
  port: 80
spring:
  application:
    name: api-gateway
  security:
    user:
      name: cmd
      password: 123456 
  datasource:
      driver-class-name: oracle.jdbc.OracleDriver
      url: jdbc:oracle:thin:@ip:1526/user
      username: root
      password: 123456   
  redis:
      host: ip
      port: 6379
      password: 123456
      jedis:
        pool:
          max-active: 8
          max-wait: -1
          max-idle: 500
          min-idle: 0
      lettuce:
        shutdown-timeout: 0
  cloud:
    gateway:
      discovery:
        locator:
          enable:true
      httpclient:
        websocket:
          max-frame-payload-length: 1024*1024*10
      routes:
        - id: gateway-service    
          uri: http://ip:8080
          predicates:
          - After=2018-12-25T14:33:47.789+08:00
mybatis-plus:
   configuration:
     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
   mapper-locations: classpath:mapper/*.xml                   

3.启动类

@EnableDiscoveryClient
@SpringBootApplication
@MapperScan(basePackages= {"com.cmd.mapper"})
@EnableConfigurationProperties({EnvironmentPro.class})
public class GatewayApplication
{
    public static void main( String[] args )
    {
    	SpringApplication.run(GatewayApplication.class, args);
    }
   
}

操作完就可启动查看gateway配置是否成功

三、晋级gateway

1.与数据库连用实现动态路由

1.1 定义路由对象

package com.cmd.sys;

import java.util.ArrayList;
import java.util.List;

/**
 * @author cmd
 *路由对象
 */
public class GatewayRouteDefinition {
	/*** 路由的Id ***/
    private String id;
    /*** 路由断言集合配置 ***/
    private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
    /*** 路由过滤器集合配置 ***/
    private List<GatewayFilterDefinition> filters = new ArrayList<>();
    /*** 路由规则转发的目标uri ***/
    private String uri;
    /*** 路由执行的顺序 ***/
    
    private int order = 0;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public List<GatewayPredicateDefinition> getPredicates() {
		return predicates;
	}
	public void setPredicates(List<GatewayPredicateDefinition> predicates) {
		this.predicates = predicates;
	}
	public List<GatewayFilterDefinition> getFilters() {
		return filters;
	}
	public void setFilters(List<GatewayFilterDefinition> filters) {
		this.filters = filters;
	}
	public String getUri() {
		return uri;
	}
	public void setUri(String uri) {
		this.uri = uri;
	}
	public int getOrder() {
		return order;
	}
	public void setOrder(int order) {
		this.order = order;
	}
}

1.2 定义断言对象

@Data
public class GatewayPredicateDefinition {
	//断言对应的Name
    private String name;
    //配置的断言规则
    private Map<String, String> args = new LinkedHashMap<>();
}

1.3 定义路由模型

@Data
public class GatewayFilterDefinition {
	//Filter Name
    private String name;
    //对应的路由规则
    private Map<String, String> args = new LinkedHashMap<>();
    
}

1.4 业务处理

package com.cmd.sys;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import reactor.core.publisher.Mono;

@Service
public class DynamicRouteService implements ApplicationEventPublisherAware{
	 @Autowired
	    private RouteDefinitionWriter routeDefinitionWriter;
	    private ApplicationEventPublisher publisher;
	 
	    @Override
	    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
	        this.publisher = applicationEventPublisher;
	    }
	 
	    /**
	     * 增加路由
	     *
	     */
	    public String add(RouteDefinition definition) {
	        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
	        this.publisher.publishEvent(new RefreshRoutesEvent(this));
	        return "添加成功!";
	    }
	 
	    /**
	     * 更新路由
	     *
	     */
	    public String update(RouteDefinition definition) {
	        try {
//	            delete(definition.getId());
	        } catch (Exception e) {
	            return "update fail,not find route  routeId: "+definition.getId();
	        }
	        try {
	            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
	            this.publisher.publishEvent(new RefreshRoutesEvent(this));
	            return "路由修改成功!";
	        } catch (Exception e) {
	            return "更新失败!";
	        }
	    }
	 
	    /**
	     * 删除路由
	     *
	     */
	    public Mono<ResponseEntity<Object>> delete(String id) {
	        return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
	            return Mono.just(ResponseEntity.ok().build());
	        })).onErrorResume((t) -> {
	            return t instanceof NotFoundException;
	        }, (t) -> {
	            return Mono.just(ResponseEntity.notFound().build());
	        });
	    }
}

1.4 控制层

package com.cmd.sys;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;


@RestController
@RequestMapping("/route")
public class DynamicRouteController {
	@Autowired
    private RouteDefinitionLocator routeDefinitionLocator;//读取路由的配置信息
 
    @Autowired
    private DynamicRouteService dynamicRouteService;
 
    /**
     * 获取网关所有的路由信息
     * 
     */
    @GetMapping("/routes")
    public Flux<RouteDefinition> getRouteDefinitions(){
    	
        return routeDefinitionLocator.getRouteDefinitions();
    }
 
    //增加路由
    @GetMapping("/add")
    public String add() {
    	 RouteDefinition definition = new RouteDefinition();
    	System.out.println("111");
    	definition.setId("111");
    	 URI uri = null;
             uri = URI.create("url");
         definition.setUri(uri);
    	 List<PredicateDefinition> pdList=new ArrayList<PredicateDefinition>();
    	 PredicateDefinition pre=new PredicateDefinition();
         pre.setName("After");
         Map<String, String> map=new HashMap<String, String>();
         map.put("args", "2018-12-25T14:33:47.789+08:00");
         pre.setArgs(map);
         pdList.add(pre);
         definition.setPredicates(pdList);
        String flag = "fail";
        try {
           
            flag = this.dynamicRouteService.add(definition);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }
 
    //更新路由
    @PostMapping("/update")
    public String update(@RequestBody GatewayRouteDefinition gwdefinition) {
        RouteDefinition definition = assembleRouteDefinition(gwdefinition);
        return this.dynamicRouteService.update(definition);
    }
    //删除路由
    @DeleteMapping("/routes/{id}")
    public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
        try {
            return this.dynamicRouteService.delete(id);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
 
 
 
 
 
    //把前端传递的参数转换成路由对象
    private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) {
        RouteDefinition definition = new RouteDefinition();
        definition.setId(gwdefinition.getId());
        definition.setOrder(gwdefinition.getOrder());
 
        //设置断言
        List<PredicateDefinition> pdList=new ArrayList<PredicateDefinition>();
        List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates();
        for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) {
            PredicateDefinition predicate = new PredicateDefinition();
            predicate.setArgs(gpDefinition.getArgs());
            predicate.setName(gpDefinition.getName());
            pdList.add(predicate);
        }
        definition.setPredicates(pdList);
 
        //设置过滤器
        List<FilterDefinition> filters = new ArrayList();
        List<GatewayFilterDefinition> gatewayFilters = gwdefinition.getFilters();
        for(GatewayFilterDefinition filterDefinition : gatewayFilters){
            FilterDefinition filter = new FilterDefinition();
            filter.setName(filterDefinition.getName());
            filter.setArgs(filterDefinition.getArgs());
            filters.add(filter);
        }
        definition.setFilters(filters);
 
        URI uri = null;
        if(gwdefinition.getUri().startsWith("http")){
            uri = UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri();
        }else{
            uri = URI.create(gwdefinition.getUri());
        }
        definition.setUri(uri);
        return definition;
    }

}

2.禁用Security

禁用网关的登录认证,添加如下代码即可

package com.cmd.sys;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

/**
 * @author cmd
 *gateway登录认证
 */
@Configuration
@EnableWebFluxSecurity
public class WebSecurityConfig {
 
    @Bean
    SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception {
 
        http.authorizeExchange()
                .anyExchange()
                .permitAll();
 
        // 一些配置
        http
                .csrf().disable()
                .httpBasic().disable()
                .logout().disable()
                .formLogin().disable();
        return http.build();
    }
 
}


3.拦截器

抛弃动态路由,在拦截器中实现接口url的跳转,即强制更改要跳转的url请求,代码如下:

package com.cmd.sys;

import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;

import com.cmd.bean.Interface;
import com.cmd.service.InterfaceService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Slf4j
@Component("requestGlobalFilter")
public class RequestGlobalFilter implements GlobalFilter,Ordered {
	 @Resource
	    private DiscoveryClient disClient;
	 
	   @Autowired
	   private InterfaceService interfaceService;
	   @Autowired
	   private ObjectMapper objectMapper;
	   @Autowired
		private EnvironmentPro environmentPro;
	   private static final List<HttpMessageReader<?>> MESSAGE_READERS = HandlerStrategies.withDefaults().messageReaders();
	 
	    @Override
	    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	    	ServerHttpRequest req = exchange.getRequest();
	    	ServerHttpResponse res = exchange.getResponse();
	    	//自定义配置文件中添加url,为测试使用
	    	String enurl=environmentPro.getUrl();
//	    	  String bodyStr = resolveBodyFromRequest(req);
	    	  HttpHeaders headers = req.getHeaders();
//	    	  logtrace(exchange, bodyStr);
	    	  //獲取請求頭中id信息
	    	  String id = headers.getFirst("id");
	            //随便写了个规则来改变路由地址
	            if(Objects.equals(id,"1")){
	                //获取域名后的path
	                String rawPath = req.getURI().getRawPath();
	                URI uri = UriComponentsBuilder.fromHttpUrl(enurl+"/test/getId").build().toUri();
	                //重新封装request对象
	                ServerHttpRequest request = req.mutate().uri(uri).build();
	                Route oldroute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	                //从新设置Route地址
	                Route route =
	                    Route.async().asyncPredicate(oldroute.getPredicate()).filters(oldroute.getFilters()).id(oldroute.getId())
	                        .order(oldroute.getOrder()).uri(uri).build();
	                URI mergedUrl = UriComponentsBuilder.fromUri(uri)
	                        .scheme(uri.getScheme())
	                        .host(uri.getHost())
	                        .port("8080")
	                        .build()
	                        .toUri();
	                //NettyRoutingFilter 最终会从GATEWAY_REQUEST_URL_ATTR 取出uri对象进行http请求,所以这里需要将新的对象覆盖进去
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,route);
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,route);
	                return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
	                    //请求完成回调方法 可以再此完成计算请求耗时等操作
	                }));
	            }
	            else if(Objects.equals(id,"3")) {
	            	//从数据库拉去url
	            	Interface inter = interfaceService.getInterface("ffff-1618987519204-10772443-0157");
	  	    	  	String url = inter.getUrl();
	  	    	  //获取域名后的path
	  	            String rawPath = req.getURI().getRawPath();
	  	            URI uri = UriComponentsBuilder.fromHttpUrl(url).build().toUri();
//	  	            //重新封装request对象
//	  	            ServerHttpRequest request = req.mutate().uri(uri).build();
	  	            Route oldroute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	                //从新设置Route地址
	                Route route =
	                    Route.async().asyncPredicate(oldroute.getPredicate()).filters(oldroute.getFilters()).id(oldroute.getId())
	                        .order(oldroute.getOrder()).uri(uri).build();
	  	            URI mergedUrl = UriComponentsBuilder.fromUri(uri)
	  	                    .scheme(uri.getScheme())
	  	                    .host(uri.getHost())
	  	                    .port("8080")
	  	                    .build()
	  	                    .toUri();
	  	          String bodyStr = resolveBodyFromRequest(req);
	  	            DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
	  	            byte[] bytes = new byte[bodyDataBuffer.readableByteCount()];
	  	            bodyDataBuffer.read(bytes);
	  				String bodyString = new String(bytes, StandardCharsets.UTF_8);
	  				logtrace(exchange, bodyString);
	  				exchange.getAttributes().put("POST_BODY", bodyString);
	  				DataBufferUtils.release(bodyDataBuffer);
	  				Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
	  					DataBuffer buffer = exchange.getResponse().bufferFactory()
	  							.wrap(bytes);
	  					return Mono.just(buffer);
	  				});
	  				ServerHttpRequest request = new ServerHttpRequestDecorator(req) {
	  	                @Override
	  	                public Flux<DataBuffer> getBody() {
	  	                    return cachedFlux;
	  	                }
	  	                @Override
	  	            	public URI getURI() {
	  	            		return uri;
	  	            	}
	  	            };
	  	            
	  	            //NettyRoutingFilter 最终会从GATEWAY_REQUEST_URL_ATTR 取出uri对象进行http请求,所以这里需要将新的对象覆盖进去
	  	          exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,route);
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,route);
	  	            return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
	  	                //请求完成回调方法 可以再此完成计算请求耗时等操作
	  	            }));
	            }else if(Objects.equals(id,"4")) {
	            	//从redis中获取url数据
	            	String url = interfaceService.getRedisInter("Url");
	            	 //获取域名后的path
	                String rawPath = req.getURI().getRawPath();
	                URI uri = UriComponentsBuilder.fromHttpUrl(url).build().toUri();
//	                //重新封装request对象
//	                ServerHttpRequest request = req.mutate().uri(uri).build();
	                Route oldroute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	                //从新设置Route地址
	                Route route =
	                    Route.async().asyncPredicate(oldroute.getPredicate()).filters(oldroute.getFilters()).id(oldroute.getId())
	                        .order(oldroute.getOrder()).uri(uri).build();
	                URI mergedUrl = UriComponentsBuilder.fromUri(uri)
	                        .scheme(uri.getScheme())
	                        .host(uri.getHost())
	                        .port("8080")
	                        .build()
	                        .toUri();
	                String bodyStr = resolveBodyFromRequest(req);
	                DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
	                byte[] bytes = new byte[bodyDataBuffer.readableByteCount()];
	                bodyDataBuffer.read(bytes);
	    			String bodyString = new String(bytes, StandardCharsets.UTF_8);
	    			logtrace(exchange, bodyString);
	    			exchange.getAttributes().put("POST_BODY", bodyString);
	    			DataBufferUtils.release(bodyDataBuffer);
	    			Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
	    				DataBuffer buffer = exchange.getResponse().bufferFactory()
	    						.wrap(bytes);
	    				return Mono.just(buffer);
	    			});
	    			ServerHttpRequest request = new ServerHttpRequestDecorator(req) {
	                    @Override
	                    public Flux<DataBuffer> getBody() {
	                        return cachedFlux;
	                    }
	                    @Override
	                	public URI getURI() {
	                		return uri;
	                	}
	                };
	                
	                //NettyRoutingFilter 最终会从GATEWAY_REQUEST_URL_ATTR 取出uri对象进行http请求,所以这里需要将新的对象覆盖进去
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,uri);
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,route);
	                return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
	                    //请求完成回调方法 可以再此完成计算请求耗时等操作
	                }));
	            }else if(Objects.equals(id,"5")) {
	            	//base64
	            	String url = enurl+"/test/gets";
	            	//获取域名后的path
	                String rawPath = req.getURI().getRawPath();
	                URI uri = UriComponentsBuilder.fromHttpUrl(url).build().toUri();
//	                //重新封装request对象
//	                ServerHttpRequest request = req.mutate().uri(uri).build();
	                Route oldroute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	                //从新设置Route地址
	                Route route =
	                    Route.async().asyncPredicate(oldroute.getPredicate()).filters(oldroute.getFilters()).id(oldroute.getId())
	                        .order(oldroute.getOrder()).uri(uri).build();
	                URI routeUri = route.getUri();
	                URI mergedUrl = UriComponentsBuilder.fromUri(uri)
	                        .scheme(routeUri.getScheme())
	                        .host(routeUri.getHost())
	                        .port("8080")
	                        .build()
	                        .toUri();
	                String bodyStr = resolveBodyFromRequest(req);
	                 DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
	                byte[] bytes = new byte[bodyDataBuffer.readableByteCount()];
	                bodyDataBuffer.read(bytes);
	    			String bodyString = new String(bytes, StandardCharsets.UTF_8);
	    			logtrace(exchange, bodyString);
	    			exchange.getAttributes().put("POST_BODY", bodyString);
	    			DataBufferUtils.release(bodyDataBuffer);
	    			Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
	    				DataBuffer buffer = exchange.getResponse().bufferFactory()
	    						.wrap(bytes);
	    				return Mono.just(buffer);
	    			});
	    			ServerHttpRequest request = new ServerHttpRequestDecorator(req) {
	                    @Override
	                    public Flux<DataBuffer> getBody() {
	                        return cachedFlux;
	                    }
	                    @Override
	                	public URI getURI() {
	                		return uri;
	                	}
	                };
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,uri);
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,route);
	                return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
	                    //请求完成回调方法 可以再此完成计算请求耗时等操作
	                }));
	            }else if(Objects.equals(id,"6")) {
	            	//从redis中获取url数据
	            	String url = enurl+"/test/download";
	            	 //获取域名后的path
	                String rawPath = req.getURI().getRawPath();
	                URI uri = UriComponentsBuilder.fromHttpUrl(url).build().toUri();
//	                //重新封装request对象
//	                ServerHttpRequest request = req.mutate().uri(uri).build();
	                Route oldroute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	                //从新设置Route地址
	                Route route =
	                    Route.async().asyncPredicate(oldroute.getPredicate()).filters(oldroute.getFilters()).id(oldroute.getId())
	                        .order(oldroute.getOrder()).uri(uri).build();
	                URI mergedUrl = UriComponentsBuilder.fromUri(uri)
	                        .scheme(uri.getScheme())
	                        .host(uri.getHost())
	                        .port("8080")
	                        .build()
	                        .toUri();

	    			ServerHttpRequest request = new ServerHttpRequestDecorator(req) {

	                    @Override
	                	public URI getURI() {
	                		return uri;
	                	}
	                };
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,uri);
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,route);
	                return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
	                    //请求完成回调方法 可以再此完成计算请求耗时等操作
	                }));
	            }
	            else if(Objects.equals(id,"7")){
	            	//MultipartFile
	            	String url = enurl+"/test/upload";
	            	 //获取域名后的path
	                String rawPath = req.getURI().getRawPath();
	                URI uri = UriComponentsBuilder.fromHttpUrl(url).build().toUri();
//	                //重新封装request对象
//	                ServerHttpRequest request = req.mutate().uri(uri).build();
	                Route oldroute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	                //从新设置Route地址
	                Route route =
	                    Route.async().asyncPredicate(oldroute.getPredicate()).filters(oldroute.getFilters()).id(oldroute.getId())
	                        .order(oldroute.getOrder()).uri(uri).build();
	                URI mergedUrl = UriComponentsBuilder.fromUri(uri)
	                        .scheme(uri.getScheme())
	                        .host(uri.getHost())
	                        .port("8080")
	                        .build()
	                        .toUri();

	    			ServerHttpRequest request = new ServerHttpRequestDecorator(req) {

	                    @Override
	                	public URI getURI() {
	                		return uri;
	                	}
	                };
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,uri);
	                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,route);
	                return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(() -> {
	                    //请求完成回调方法 可以再此完成计算请求耗时等操作
	                }));
	            }
//	            else{
//	                //模拟调用B服务 B服务无法掉通
	                System.out.println("模拟调用B服务 B服务无法调通");
//	            }
	         return chain.filter(exchange);
	 
	    }

	            @SuppressWarnings("unchecked")
	            private Mono<Void> cacheBody(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) {
	                final HttpHeaders headers = exchange.getRequest().getHeaders();
	                if (headers.getContentLength() == 0) {
	                    return chain.filter(exchange);
	                }
	                final ResolvableType resolvableType;
	                if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(headers.getContentType())) {
	                    resolvableType = ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, Part.class);
	                } else {
	                    resolvableType = ResolvableType.forClass(String.class);
	                }
	                
	                return MESSAGE_READERS.stream().filter(reader -> reader.canRead(resolvableType, exchange.getRequest().getHeaders().getContentType())).findFirst()
	                    .orElseThrow(() -> new IllegalStateException("no suitable HttpMessageReader.")).readMono(resolvableType, exchange.getRequest(), Collections.emptyMap()).flatMap(resolvedBody -> {
	                        if (resolvedBody instanceof MultiValueMap) {
	                            final Part partInfo = (Part) ((MultiValueMap) resolvedBody).getFirst("info");
	                            if (partInfo instanceof FormFieldPart) {
	                                gatewayContext.setRequestBody(((FormFieldPart) partInfo).value());
	                            }
	                        } else {
	                            gatewayContext.setRequestBody((String) resolvedBody);
	                        }
	                        return chain.filter(exchange);
	                    });
	            }
	    
	    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());
	        });
	        //获取request body
	        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() {
	        //WRITE_RESPONSE_FILTER 之前执行
	        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
	    }
	    /**
		 * 日志信息
		 *
		 * @param exchange
		 * @param param    请求参数
		 */
		private void logtrace(ServerWebExchange exchange, String param) {
			ServerHttpRequest serverHttpRequest = exchange.getRequest();
			String path = serverHttpRequest.getURI().getPath();
			String method = serverHttpRequest.getMethodValue();
			String headers = serverHttpRequest.getHeaders().entrySet()
					.stream()
					.map(entry -> "            " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
					.collect(Collectors.joining("\n"));
			log.info("\n" + "----------------             ----------------             ---------------->>\n" +
							"HttpMethod : {}\n" +
							"Uri        : {}\n" +
							"Param      : {}\n" +
							"Headers    : \n" +
							"{}\n" +
							"\"<<----------------             ----------------             ----------------"
					, method, path, param, headers);
		}

	  
		private Mono<Void> fileRequest(ServerWebExchange exchange, GatewayFilterChain chain){
	         return DataBufferUtils.join(exchange.getRequest().getBody())
	                 .flatMap(dataBuffer -> {
	                     byte[] bytes = new byte[dataBuffer.readableByteCount()];
	                     dataBuffer.read(bytes);
	                     DataBufferUtils.release(dataBuffer);
	                     Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
	                         DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
	                         DataBufferUtils.retain(buffer);
	                         return Mono.just(buffer);
	                     });

	                     ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
	                         @Override
	                         public Flux<DataBuffer> getBody() {
	                             return cachedFlux;
	                         }
	                     };
	                     ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
	                     return ServerRequest.create(mutatedExchange, MESSAGE_READERS)
	                             .bodyToMono(byte[].class)
	                             .then(chain.filter(mutatedExchange));
	                 });
	    }
	    private Mono<Void> processRequest(ServerWebExchange exchange, GatewayFilterChain chain) {
	        ServerRequest serverRequest =ServerRequest.create(exchange, MESSAGE_READERS);
	        DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
	        Mono<String> rawBody = serverRequest.bodyToMono(String.class).map(s -> s);
	        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(rawBody, String.class);
	        HttpHeaders tempHeaders = new HttpHeaders();
	        tempHeaders.putAll(exchange.getRequest().getHeaders());
	        tempHeaders.remove(HttpHeaders.CONTENT_LENGTH);
	        MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, tempHeaders);
	        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
	            Flux<DataBuffer> body = outputMessage.getBody();
	            DataBufferHolder holder = new DataBufferHolder();
	            body.subscribe(dataBuffer -> {
	                int len = dataBuffer.readableByteCount();
	                holder.length = len;
	                byte[] bytes = new byte[len];
	                dataBuffer.read(bytes);
	                DataBufferUtils.release(dataBuffer);
	                String text = new String(bytes, StandardCharsets.UTF_8);
	                //读取jsonc串
	                JsonNode jsonNode = readNode(text);
	                //直接内存
	                DataBuffer data = bufferFactory.allocateBuffer();
	                //把数据写到直接内存中
	                data.write(jsonNode.toString().getBytes(StandardCharsets.UTF_8));
	                holder.dataBuffer = data;
	            });
	            ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {

	                @Override
	                public HttpHeaders getHeaders() {
	                    long contentLength = tempHeaders.getContentLength();
	                    HttpHeaders httpHeaders = new HttpHeaders();
	                    httpHeaders.putAll(super.getHeaders());
	                    if (contentLength > 0) {
	                        httpHeaders.setContentLength(contentLength);
	                    } else {
	                        httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
	                    }
	                    return httpHeaders;
	                }

	                @Override
	                public Flux<DataBuffer> getBody() {
	                    return Flux.just(holder.dataBuffer);
	                }
	            };
	            return chain.filter(exchange.mutate().request(requestDecorator).build());
	        }));
	    }

	    //json读取格式
	    private JsonNode readNode(String in) {
	        try {
	            return objectMapper.readTree(in);
	        } catch (Exception e) {
	            throw new IllegalStateException(e);
	        }
	    }
	    class DataBufferHolder {

	        DataBuffer dataBuffer;
	        int length;
	    }
		
}

上述代码多重复,自行优化

四.常见错误

1.Only one connection receive subscriber allowed
这种错误,一般pom中版本不对,另一种就是拦截器中代码出错,可以详细检查就可解决,特别注意exchage中的attributes。


2.如果调用的接口为https的,直接更改拦截器中ServerHttpRequest为ServerHttpsRequest。

总结

常用的springcloud gateway 还是跟注册中心连用,将接口服务注册到注册中心,gateway直接调用注册服务接口。有空可以看看gateway源码,毕竟源码才是进阶的根本,一起搬砖哟。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值