目录
一 概念引入
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: route
application:
name: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848
server:
port: 81
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order
predicates:
- Path=/api/order/**
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
- id: last-route
uri: 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 {
@Autowired
private 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-route
uri: lb://service-order
predicates:
- Path=/api/order/**
order: 0
filters:
- RewritePath=/api/order/(?<segment>.*), /$\{segment}
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
order: 1
filters:
- RewritePath=/api/product/(?<segment>.*), /$\{segment}
- id: last-route
uri: https://cn.bing.com
predicates:
- 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-route
uri: lb://service-order
predicates:
- Path=/api/order/**
order: 0
filters:
- RewritePath=/api/order/(?<segment>.*), /$\{segment}
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
order: 1
filters:
- RewritePath=/api/product/(?<segment>.*), /$\{segment}
- id: last-route
uri: https://cn.bing.com
predicates:
- Path=/**
order: 2
default-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
*/
@Override
public 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
*/
@Override
public 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 {
@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
@Override
public 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-route
uri: lb://service-order
predicates:
- Path=/api/order/**
order: 0
filters:
- RewritePath=/api/order/(?<segment>.*), /$\{segment}
- OnceToken=X-Response-Token,uuid
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
order: 1
filters:
- RewritePath=/api/product/(?<segment>.*), /$\{segment}
- id: last-route
uri: https://cn.bing.com
predicates:
- Path=/**
order: 2
default-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-route
uri: lb://service-order
predicates:
- Path=/api/order/**
order: 0
filters:
- RewritePath=/api/order/(?<segment>.*), /$\{segment}
- OnceToken=X-Response-Token,uuid
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
order: 1
filters:
- RewritePath=/api/product/(?<segment>.*), /$\{segment}
- id: last-route
uri: https://cn.bing.com
predicates:
- Path=/**
order: 2
default-filters:
- AddResponseHeader=X-Response-Abc, 123
结果展示:
补充:
如何理解网关的负载均衡与微服务之间的负载均衡
第一个是客户端与网关之间的请求
第二个是各个微服务应用之间的请求
可以梳理为 两层负载均衡:
第一层:网关层 LB(入口层)
作用于所有外部请求
只管把流量合理分配到下游微服务实例
第二层:服务调用层 LB(内部层)
作用于服务之间的 RPC 调用
解决微服务内部调用的流量分配问题
微服务之间的调用可以不过网关,但是如果微服务之间的调用也想借助网关,那么可以在远程调用的微服务名称的指定时,将名称修改为对应的网关服务名称。