SpringCloudAlibaba之gateway网关

1. Gateway

1.1. 什么是网关

1.1.1. 网关的功能
  1. 身份认证
  2. 权限校验
  3. 服务路由
  4. 负载均衡
  5. 请求限流
1.1.2. Spring Cloud网关落地方案
  1. Zuul是基于Servlet实现的,属于阻塞式编程
  2. Gateway是基于Spring5中提供的 spring-webflux 实现的,属于响应式编程,性能要由于Zuul

1.2. 搭建网关服务

1.2.1. 创建一个gateway-service服务

引入gateway的依赖:spring-cloud-starter-gateway不需要spring-boot-starter-web;如果他们同时存在则gateway不可用。故我们需要把父工程中的spring-boot-starter-web排除,其它子模块重新引入web包即可。


<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">
    <parent>
        <artifactId>cloud-demoartifactId>
        <groupId>com.acxgroupId>
        <version>1.0.0version>
    parent>
    <modelVersion>4.0.0modelVersion>

    <groupId>com.acxgroupId>
    <artifactId>gateway-serviceartifactId>
    <version>1.0.0version>
    <packaging>jarpackaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
    dependencies>

project>
1.2.2. 编写网关配置
server:
  port: 10010
spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        cluster-name: HZ
    gateway:
      routes:
        - id: order-service # 自定义路由id,必须唯一
          uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
          predicates:
            - Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
        - id: product-service # 商品服务路由id
          uri: lb://product-service
          predicates:
            - Path=/product/**,/product/api/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**,/user/api/**
1.2.3. 测试网关是否生效
  • 访问http://localhost:10010/order/getOne

在这里插入图片描述

2. Gateway原理

2.1. 网关处理流程

SpringCloudAlibaba之gateway网关_第1张图片

2.2. 断言工厂(PathRoutePredicateFactory)

作用:读取断言规则并处理,转变为路由判断的条件,最后规则由PathRoutePredicateFactory断言工厂处理。

11种断言工厂

断言工厂 说明
After 某个时间点之后请求
Before 某个时间点之前请求
Between 某两个时间点之间请求
Cookie 请求必须包含某些cookie
Header 请求必须包含某些header
Host 请求必须是指定方式
Method 请求必须是指定方法:GET、POST
Path(默认) 请求路径必须是指定路由规则
Query 请求必须包含指定参数
RemoteAddr 请求这IP必须为指定范围
Weight 权重处理

11种断言示例:具体的示例可以查询官网文档https://www.springcloud.cc/spring-cloud-greenwich.html#gateway-request-predicates-factories

# After
predicates:
  - After=2022-01-20T17:42:47.789-07:00[America/Denver]    
  
# Before
predicates:       
  - Before=2017-01-20T17:42:47.789-07:00[America/Denver]   
  
# Between
predicates:
  - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] 
  
# Cookie
predicates:
  - Cookie=chocolate, ch.p   # 使用Cookie路由断言工厂,配置cookie,正则表达式(可有可无)
  
# Header
predicates:
  - Header=X-Request-Id, \d+
  
# Host   
predicates:
  - Host=**.somehost.org,**.anotherhost.org
  
# Method
predicates:
  - Method=GET,POST
  
# Query
predicates:
  - Query=green
  
# RemoteAddr
predicates:
  - RemoteAddr=192.168.1.1/24
  
#   Weight
predicates:
  - Weight=group1, 2

2.3. 路由过滤器配置(GatewayFilter)

2.3.1. 请求过滤器流程图

作用:过滤器链对进入网关的请求和微服务返回的响应做处理
SpringCloudAlibaba之gateway网关_第2张图片

2.3.2. 过滤器工厂

Spring Cloud Gateway官网实例:https://www.springcloud.cc/spring-cloud-greenwich.html#_addrequestheader_gatewayfilter_factory

常用的过滤器工厂

过滤器工厂 作用 参数
AddRequestHeader 为原始请求添加Header Header的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加Header Header的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
Hystrix 为路由引入Hystrix的断路器保护 HystrixCommand的名称
2.3.3. 局部配置过滤器

已AddRequestHeader拦截器为例

网关服务做如下配置

server:
  port: 10010
spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        cluster-name: HZ
    gateway:
      routes:
        - id: order-service # 自定义路由id,必须唯一
          uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
          predicates:
            - Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
          filters:
            - AddRequestHeader=abc,hello word # 给order服务配置拦截器
        - id: product-service # 商品服务路由id
          uri: lb://product-service
          predicates:
            - Path=/product/**,/product/api/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**,/user/api/**

Order服务添加获取abc请求头代码

package com.acx.controller;

import com.acx.client.UserClient;
import com.acx.pojo.vo.ActorInfoVO;
import com.acx.pojo.vo.OrderInfoVO;
import com.acx.pojo.vo.StudentVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("order")
public class OrderController {

    private static final Logger logger = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserClient userClient;

    @Autowired
    private StudentVO studentVO;

    @GetMapping("getStudent")
    public StudentVO getStudent() {
        return studentVO;
    }

    @GetMapping("getOne")
    public OrderInfoVO getOne(@RequestHeader("abc") String abc) {
        logger.info("开始查询订单");
        OrderInfoVO orderInfoVO = new OrderInfoVO();
        orderInfoVO.setOrderName("订单123");
        orderInfoVO.setOrderSn("046b399937ad4271bcd5ed275f2b4682");
        orderInfoVO.setProductName("商品123");
        orderInfoVO.setProductNum(23);
        int userId = 1;
//        String getUserUrl = "http://127.0.0.1:8083/user/getUser/" + userId;
//        String getUserUrl = "http://user-service/user/getUser/" + userId;
        //服务发现
//        ActorInfoVO actor = restTemplate.getForObject(getUserUrl, ActorInfoVO.class);
        ActorInfoVO actor = userClient.getUser(userId);
        orderInfoVO.setUser(actor);
        logger.info("测试AddRequestHeader拦截器是否生效,abc={}", abc);
        return orderInfoVO;
    }

}

请求Order接口测试:http://localhost:10010/order/getOne

  • 控制台打印日志如下:说明过滤器生效了
2022-05-09 22:41:58.698  INFO 19452 --- [nio-8081-exec-1] com.acx.controller.OrderController       : 测试AddRequestHeader拦截器是否生效,abc=hello word

2.4. 全局默认过滤器(DefaultFilter)

  • 所有经过此网关路由的服务器请求都会对此过滤器生效
server:
  port: 10010
spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        cluster-name: HZ
    gateway:
      routes:
        - id: order-service # 自定义路由id,必须唯一
          uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
          predicates:
            - Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
        - id: product-service # 商品服务路由id
          uri: lb://product-service
          predicates:
            - Path=/product/**,/product/api/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**,/user/api/**
      default-filters:
        - AddRequestHeader=abc,hello word # 全局配置拦截器

2.5. 全局过滤器(GlobalFilter)

2.5.1. 什么是全局过滤器

作用:处理一切进入网关服务的请求和响应,与GatewayFilter作用一样,区别在于GlobalFilter支持自定义逻辑扩展。

核心方法:

public interface GlobalFilter {

	/**
	 * Process the Web request and (optionally) delegate to the next {@code WebFilter}
	 * through the given {@link GatewayFilterChain}.
	 * @param exchange the current server exchange
	 * exchange: 请求上下文,里面包含了Request、Response等信息
	 * @param chain provides a way to delegate to the next filter
	 * chain: 用来把请求委托给下一个过滤器
	 * @return {@code Mono} to indicate when request processing is complete
	 * MonoZ: 返回时表示当前过滤器逻辑流程结束
	 */
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}
2.5.2. 应用:自定义一个登陆验证拦截器

Gateway网关服务:自定义认证拦截器,此拦截器集成GlobalFilter

package com.acx.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Order(1) //拦截器优先级;值越小优先级越高
@Component
public class AuthFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request
        ServerHttpRequest request = exchange.getRequest();
        //2.获取请求参数
        MultiValueMap<String, String> params = request.getQueryParams();
        //3.获取token
        String token = params.getFirst("token");
        if ("login".equals(token)) {
            //4.是、请求通过
            return chain.filter(exchange);
        }
        //5.否、校验未通过
        //5.1.设置校验未通过code 401 认证未通过
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

}

测试结果:认证失败

SpringCloudAlibaba之gateway网关_第3张图片

测试结果:认证成功

在这里插入图片描述

2.5. 过滤器执行顺序


前言:请求到达网关服务后,一共要经过路由过滤器、DefaultFilter、GlobalFilter三种过滤器组成的过滤器链。他们会有一个执行顺序,排序后一次执行每个过滤器。

规则

  1. 当Order值一样时DefaultFilter > 路由过滤器 > GlobalFilter。
  2. Order值越小、Filter优先级越高。

Order值

  1. GlobalFilter的order值可以自己进行指定。
  2. DefaultFilter和路由过滤器的Order值是由Spring决定的,默认按照声明顺序从1递增。

实例如下:

server:
  port: 10010
spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        cluster-name: HZ
    gateway:
      routes:
        - id: order-service # 自定义路由id,必须唯一
          uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
          predicates:
            - Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
          filters:
            - AddRequestHeader=zhangsan,abc123 # order = 1,越往后Order值就越大
        - id: product-service # 商品服务路由id
          uri: lb://product-service
          predicates:
            - Path=/product/**,/product/api/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**,/user/api/**
      default-filters:
        - AddRequestHeader=abc,hello word # 全局配置拦截器;order=1,越往后Order值就越大


2.6. 网关的跨域问题处理

2.6.1. 什么是跨域问题

浏览器禁止请求发送者与服务端发生卡与Ajax请求;请求域名或者端口与后台目标域名地址不一样时就会出现这种问题。

例子:下面的情况就会出现cors跨域问题

  1. 游览器请求地址:127.0.0.1:8080
  2. 目标后台地址:127.0.0.1:10100
2.6.2. 配置文件解救跨域问题
spring:
  cloud:
    gateway:
      # 允许跨域请求配置
      globalcors:
        cors-configurations:
          '[/**]':
            # 允许任何域名使用
            allowedOrigins: "*"
            # 允许任何头
            allowedHeaders: "*"
            # 允许任何方法(post、get等)
            allowedMethods: "*"
            # sessionid 多次访问一致
            allowCredentials: true
        # 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求
        add-to-simple-url-handler-mapping: true   # 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求7

2.6.3. 代码解决跨域问题
@Configuration
public class CorsConfig {
  
  @Bean
  public CorsWebFilter corsFilter() {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
    // 允许任何域名使用
    corsConfiguration.addAllowedOrigin("*");
    // 允许任何头
    corsConfiguration.addAllowedHeader("*");
    // 允许任何方法(post、get等)
    corsConfiguration.addAllowedMethod("*");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
      
    source.registerCorsConfiguration("/**", corsConfiguration);

    return new CorsWebFilter(source);
  }
}

你可能感兴趣的:(微服务,gateway,java,spring)