当前位置: 首页 > news >正文

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包即可。

<?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">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>com.acx</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.acx</groupId>
    <artifactId>gateway-service</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </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. 网关处理流程

在这里插入图片描述

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. 请求过滤器流程图

作用:过滤器链对进入网关的请求和微服务返回的响应做处理
在这里插入图片描述

2.3.2. 过滤器工厂

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

常用的过滤器工厂

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
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<Void>} 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();
    }

}

测试结果:认证失败

在这里插入图片描述

测试结果:认证成功

在这里插入图片描述

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);
  }
}

相关文章:

  • 七牛云配置自定义域名
  • Spring/IoC、DI、Bean
  • 信息管理java毕业设计项目分享【含源码+论文】
  • MASA Stack 第五期社区例会
  • JavaScript 30. JSON
  • CAS:26915-72-0,mPEG-Methacrylate,mPEG-MAC,甲氧基-聚乙二醇-甲基丙烯酸酯
  • 分布式事务最经典的八种解决方案
  • 2.ROS编程学习:话题通信c++
  • 【python】计算偏度和峰度
  • 单片机原理指令系统习题超详细讲解
  • 使用Python,Keras和TensorFlow训练第一个CNN
  • Flutter: Dart 参数,以及 @required 与 required
  • 基于JAVA网上商城系统演示录像计算机毕业设计源码+数据库+lw文档+系统+部署
  • 【复杂网络】关于复杂网络中的动力学系统重构的文献资料整理
  • 应对数据安全典型薄弱点,这家医院“外防内控”筑牢屏障
  • DataBase in Android
  • ES6 ...操作符
  • ES6系统学习----从Apollo Client看解构赋值
  • java多线程
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • npx命令介绍
  • Sass 快速入门教程
  • Spring Cloud中负载均衡器概览
  • Unix命令
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • vue自定义指令实现v-tap插件
  • 程序员该如何有效的找工作?
  • - 概述 - 《设计模式(极简c++版)》
  • 技术胖1-4季视频复习— (看视频笔记)
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 写给高年级小学生看的《Bash 指南》
  • 追踪解析 FutureTask 源码
  • 进程与线程(三)——进程/线程间通信
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • #if 1...#endif
  • #includecmath
  • (1)STL算法之遍历容器
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (javascript)再说document.body.scrollTop的使用问题
  • (Matlab)使用竞争神经网络实现数据聚类
  • (八)c52学习之旅-中断实验
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (多级缓存)缓存同步
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (转)重识new
  • .bat文件调用java类的main方法
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .net core Swagger 过滤部分Api
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .Net Core与存储过程(一)
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?