Spring Cloud Gateway 网关(五)
目录
一 概念引入
二 具体使用
1 首先创建一个网关模块
2 启动类
3 配置类
4 对应方法的修改
5 展示借助81端口进行转发控制
6 断言规则编辑
三 过滤器
1 将前置的请求参数给过滤掉,降低繁琐程度。
2 默认过滤器
3 全局过滤器
4 自定义过滤器工厂
5 全局跨域问题
一 概念引入
Spring Cloud Gateway :: Spring Cloud Gateway
二 具体使用
1 当前主流更多使用的是Reactive Server ,而ServerMVC是老版本的网关。
前景引入:
LoadBalancer:服务内部调用时的负载均衡。
Gateway 负载均衡:系统对外统一入口的负载均衡,内部也会用 LoadBalancer。
<!-- 添加nacos注册中心 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- 添加网关的依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- 请求的负载均衡 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
实现:
1 首先创建一个网关模块
2 启动类
package com.ax.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient
@SpringBootApplication
public class GatewayMainApplication {public static void main(String[] args) {SpringApplication.run(GatewayMainApplication.class, args);}
}
3 配置类
借助的就是81端口的网关进行映射
spring:profiles:include: routeapplication:name: gatewaycloud:nacos:server-addr: 127.0.0.1:8848
server:port: 81
spring:cloud:gateway:routes:- id: order-routeuri: lb://service-orderpredicates:- Path=/api/order/**- id: product-routeuri: lb://service-productpredicates:- Path=/api/product/**- id: last-routeuri: https://cn.bing.com/predicates:- Path=/**
4 对应方法的修改
需要以规范的格式开头(加上api/order或者api/product)
package com.ax.product.controller;import com.ax.product.bean.Product;
import com.ax.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.math.BigDecimal;@Slf4j
@RequestMapping("/api/product")
@RestController
public class ProductController {@Autowiredprivate ProductService productService;// 获取商品信息@GetMapping("/product/{id}")public Product getProduct(@PathVariable("id") Long id) {log.info("getProduct:{}", id);return productService.getProductById(id);}
}
对应的远程调用,这里有一个语法版本兼容问题不能在类的头部写@RequestMapping
package com.ax.order.feign;import com.ax.order.feign.fallback.ProductFeignClientFallback;
import com.ax.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;@FeignClient(name = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {/*** 测试FeignClient** @param id*///mvc注解两套使用逻辑//标注在Controller上,为接收请求//标注在FeignClient上,为发送请求@GetMapping("/api/product/product/{id}")Product getProductById(@PathVariable("id") Long id);//如果调用自己其他服务的api直接将其方法复制过来即可,下面这个就是从product当中复制过来的// @GetMapping("/product/{id}")// Product getProduct(@PathVariable("id") Long id);
}
5 展示借助81端口进行转发控制
6 断言规则
三 过滤器
1 将前置的请求参数给过滤掉,降低繁琐程度。
举例说明:路径重写(此时就可以将原先的@RequestMapping当中的参数给去除掉)
spring:cloud:gateway:routes:- id: order-routeuri: lb://service-orderpredicates:- Path=/api/order/**order: 0filters:- RewritePath=/api/order/(?<segment>.*), /$\{segment}- id: product-routeuri: lb://service-productpredicates:- Path=/api/product/**order: 1filters:- RewritePath=/api/product/(?<segment>.*), /$\{segment}- id: last-routeuri: https://cn.bing.compredicates:- Path=/**order: 2
实现目的,就是说如果请求的前缀固定含有某些内容,我们就可以借助这个过滤器将这些请求前缀给过滤掉,比如说访问路径http://localhost:81/api/product/product/1
但是后端接收就可以按照 http://localhost:81/product/1来进行接收,(暂时感觉)也就简化了一个@RequestMapping的注解
2 默认过滤器
使用 default-filters
可以全局加响应头
spring:cloud:gateway:routes:- id: order-routeuri: lb://service-orderpredicates:- Path=/api/order/**order: 0filters:- RewritePath=/api/order/(?<segment>.*), /$\{segment}- id: product-routeuri: lb://service-productpredicates:- Path=/api/product/**order: 1filters:- RewritePath=/api/product/(?<segment>.*), /$\{segment}- id: last-routeuri: https://cn.bing.compredicates:- Path=/**order: 2default-filters:- AddResponseHeader=X-Response-Abc, 123
携带了一个响应头参数
3 全局过滤器
代码展示:
package com.ax.gateway.filter;import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Slf4j
@Component
public class RtGlobalFilter implements GlobalFilter, Ordered {/*** 全局过滤器** @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String path = request.getPath().toString();long startTime = System.currentTimeMillis(); // 记录开始时间log.info("请求开始: path={}, startTime={}", path, startTime);return chain.filter(exchange).doFinally(signalType -> {long endTime = System.currentTimeMillis(); // 记录结束时间long duration = endTime - startTime; // 计算耗时log.info("请求结束: path={}, endTime={}, 耗时: {}ms", path, endTime, duration);});}/*** 优先级** @return*/@Overridepublic int getOrder() {return 0;}
}
示例:
4 自定义过滤器工厂
自定义过滤器工厂(拦截器的名称也有特殊要求)Spring Cloud Gateway 要求自定义过滤器工厂的类名必须以 GatewayFilterFactory
结尾。
package com.ax.gateway.filter;import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.UUID;@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {@Overridepublic GatewayFilter apply(NameValueConfig config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//每次响应之前添加一个一次性令牌return chain.filter(exchange).then(Mono.fromRunnable(() -> {ServerHttpResponse response = exchange.getResponse();HttpHeaders headers = response.getHeaders();String value = config.getValue();if ("uuid".equalsIgnoreCase(value)) {value = UUID.randomUUID().toString();}if ("jwt".equalsIgnoreCase(value)) {value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY" +"3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NTY1NDA" +"xMTksImFkbWluIjp0cnVlLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iX" +"SwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9.SflKxwRJSMeKKF" +"2QT4fwpMeJf36POk6yJV_adQssw5c";}headers.add(config.getName(), value);}));}};}
}
配置文件(OnceToken....)
spring:cloud:gateway:routes:- id: order-routeuri: lb://service-orderpredicates:- Path=/api/order/**order: 0filters:- RewritePath=/api/order/(?<segment>.*), /$\{segment}- OnceToken=X-Response-Token,uuid- id: product-routeuri: lb://service-productpredicates:- Path=/api/product/**order: 1filters:- RewritePath=/api/product/(?<segment>.*), /$\{segment}- id: last-routeuri: https://cn.bing.compredicates:- Path=/**order: 2default-filters:- AddResponseHeader=X-Response-Abc, 123
结果展示
5 全局跨域问题
全局跨域(CORS)问题 主要是与前端请求不同源的后端接口时,如何解决跨域问题(Cross-Origin Resource Sharing, CORS)。Spring Cloud Gateway 作为网关,它通常会充当微服务之间的流量调度中心,并需要解决跨域请求的问题,确保前端可以安全地与后端进行交互。
代码实现:
spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowed-origin-patterns: '*'allowed-headers: '*'allowed-methods: '*'routes:- id: order-routeuri: lb://service-orderpredicates:- Path=/api/order/**order: 0filters:- RewritePath=/api/order/(?<segment>.*), /$\{segment}- OnceToken=X-Response-Token,uuid- id: product-routeuri: lb://service-productpredicates:- Path=/api/product/**order: 1filters:- RewritePath=/api/product/(?<segment>.*), /$\{segment}- id: last-routeuri: https://cn.bing.compredicates:- Path=/**order: 2default-filters:- AddResponseHeader=X-Response-Abc, 123
结果展示: