SpringCloud Gateway路由网关day02


网关的实质就是过滤器的组合

一、微服务网关的好处

在这里插入图片描述

二、SpringCloud Gateway简介

Gateway网关是我们服务的守门神,所有前端访问微服务的统一入口。Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
在SpringCloudGateway之前,SpringCloud并不自己开发网关,而是使用Netflix公司的Zuul框架,不过zuul2.0更新迭代缓慢,难以满足Spring的更新需求。于是就有了SpringCloudGateway。其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

核心功能特性:

路由:gateway加入后,一切请求都必须先经过gateway,因此gateway就必须根据某种规则,把请求转发到某个微服务,这个过程叫做路由。

权限控制:请求经过路由时,我们可以判断请求者是否有请求资格,如果没有则进行拦截。

限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

**网关的实质:**网关中的每一个功能都是由过滤器链完成的。

三、快速入门

首先,我们来研究下Gateway的路由功能,基本步骤如下:

  1. 创建SpringBoot工程gateway_server,引入网关依赖
  2. 编写启动类
  3. 编写基础配置:服务端口,应用名称
  4. 编写路由规则
  5. 启动网关服务进行测试

1)创建一个gateway_server的工程
在工程下添加依赖

<?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.itheima.sh.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2)编写启动类

package com.itheima.sh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @program: eureka-ribbon
 * @description:
 * @author: Mr.Wang
 * @create: 2020-11-28 07:32
 **/
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args){
        SpringApplication.run(GatewayApplication.class, args);
    }
}

3)编写配置

server:
  port: 10010 #服务端口
spring: 
  application:  
    name: gateway-server #指定服务名

4)编写路由规则

server:
  port: 10010 #服务端口

spring:
  application:
    name: gateway-server                         #指定服务名
  cloud:
    gateway:
      routes:
        - id: user-service-route              # 当前路由的唯一标识
          uri: http://127.0.0.1:8081          # 路由的目标微服务地址
          predicates:                      # 断言  判断 前端浏览器发送url和当前path是否匹配
            - Path=/user/**                # 按照路径匹配的规则
#这个的话是如果要拦截多个就在下面配置
#        - id: order-service-route # 当前路由的唯一标识
#          uri: http://127.0.0.1:9081 # 路由的目标微服务地址
#          predicates: # 断言  判断 前端浏览器发送url和当前path是否匹配
#            - Path=/order/** # 按照路径匹配的规则

5)启动测试

当我们访问:http://localhost:10010/user/itcast,符合`/user/**`的规则,
因此请求被代理到http://localhost:8081/user/itcast

四、面向服务的路由

作为客户端放在Eureka的中
1.添加Eureka客户端依赖
2. 添加Eureka配置
3. 修改映射配置
4. 启动测试

1)在Gateway中添加Eureka客户端依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2)添加Eureka配置

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
spring:
  cloud:
    gateway:
      routes:
      - id: user-service # 当前路由的唯一标识
        uri: lb://user-service # 路由的目标微服务,lb:代表负载均衡,user-service:代表服务id
        predicates: # 断言
        - Path=/user/** # 按照路径匹配的规则
  1. 修改映射配置
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
spring:
  cloud:
    gateway:
      routes:
      - id: user-service # 当前路由的唯一标识
        uri: lb://user-service # 路由的目标微服务,lb:代表负载均衡,user-service:代表服务id
        predicates: # 断言
        - Path=/user/** # 按照路径匹配的规则

这里修改了uri的路由方式:

  • lb:负载均衡的协议,将来会使用Ribbon实现负载均衡
  • user-service:服务的id

4) 启动测试
再次访问,也可以达到负载均衡

五、其他路由的方式

可以访问网站 SpringBoot网站网关路由的地址
在这里插入图片描述

六、局部过滤器

GatewayFilter Factories是Gateway中的局部过滤器工厂,作用于某个特定路由,允许以某种方式修改传入的HTTP请求或返回的HTTP响应包括下面的一些过滤工厂:

在这里插入图片描述
在这里插入图片描述

局部过滤器演示

以AddRequestHeader GatewayFilter Factory 这个过滤器举例子
1)在原有的基础上添加请求头

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
spring:
  cloud:
    gateway:
      routes:
      - id: user-service # 当前路由的唯一标识
        uri: lb://user-service # 路由的目标微服务,lb:代表负载均衡,user-service:代表服务id
        predicates: # 断言
          - Path=/user/** # 按照路径匹配的规则
        filters:         # 过滤项
          - AddRequestHeader=name,bocai

其中:

  • filters:就是当前路由规则的所有过滤器配置
  • AddRequestHeader是添加一个头信息 name就是头 bocai是头里的信息
    2)查看结果
    在这里插入图片描述
Hystrix支持(路由的降级)

hystrix支持步骤:

1)引入Hystrix的依赖
2)定义降级的处理函数
3)定义降级处理规则
4)启动测试

1)引入Hystrix的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2)定义降级的处理函数 出现错误跳转的类上

package com.itheima.sh.gateway.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * @program: eureka-ribbon
 * @description: 路由中定义降级的类
 * @author: Mr.Wang
 * @create: 2020-11-28 08:36
 **/
@RestController
public class FallbackController {

    @RequestMapping(value = "fallbackTest")
    public Map<String,String> fallbackController(){

        HashMap<String, String> map = new HashMap<>();
        map.put("code", "502");
        map.put("信息", "请重新连接");

        return map;
    }
}

3)定义降级处理规则 可以通过default-filter来配置,会作用于所有的路由规则。

server:
  port: 10010 #服务端口
spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: user-service # 当前路由的唯一标识
          uri: lb://user-service # 路由的目标微服务,lb:代表负载均衡,user-service:代表服务id
#          uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          predicates: # 断言
            - Path=/user/** # 按照路径匹配的规则
          filters: # 过滤项
            - AddRequestHeader=name,itheima
      default-filters: # 默认过滤项
        - name: Hystrix # 指定过滤工厂名称(可以是任意过滤工厂类型)
          args: # 指定过滤的参数
            name: fallbackcmd      # hystrix的指令名   
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径   
hystrix:
  command:
    default:
      execution.isolation.thread.timeoutInMilliseconds: 1000 # 失败超时时长
eureka:           #这里的话是将gateway放在eureka服务中
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka   

default-filters:默认过滤项,作用于所有的路由规则

  • name:过滤工厂名称,这里指定Hystrix,意思是配置Hystrix类型
  • args:配置过滤工厂的配置
  • name:Hystrix的指令名称,用于配置例如超时时长等信息
  • fallbackUri:失败降级时的跳转路径 fallbackTest 是自己定义的错误跳转页面

4)启动测试
再UserController的业务中打断点,让请求超时,查看页面的效果:
在这里插入图片描述

七、路由前缀

我们之前每次访问的时候 每次都会拦截 /user/**的路径但是我此时有另外一个业务的话不包含user这个路径的话,此时就不会被访问到
此时我们只需要在GateWay_server端加个配置: - StripPrefix=1

spring:
  application:
    name: gateway-server #指定服务名
  redis:
    host: 192.168.200.150
  cloud:
    gateway:
      routes:
        - id: user-service-route # 当前路由的唯一标识
       #   uri: http://127.0.0.1:8081 # 路由的目标微服务地址    这个是一个服务至于一个实例的时候配置的
          uri: lb://user-service #   路由的目标微服务,   lb:代表负载均衡,user-service:代表服务id    可以把user-service 中的所有实例映射上去
          predicates: # 断言  判断 前端浏览器发送url和当前path是否匹配
            - Path=/user/** # 按照路径匹配的规则  通过user访问的都会被拦截i
          filters:    #局部过滤工厂  下面匹配过滤项
            - AddRequestHeader=name,itheima  #添加请求头的过滤信息
            - StripPrefix=1           # 此时会无视user-bocai   这个主要是可以路由到多个微服务上,地址不会被写死

- StripPrefix=1 他可以无视我定义的user,但是user有能被检测到,后面就可以跟我们需要访问的路径了

八、全局过滤器

全局过滤器的顶级接口是:GlobalFilter

public interface GlobalFilter {
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}
//     参数:  GatewayFilterChain   过滤器链,用于放行请求到下一个过滤器

实现接口,就要实现其中的filter方法,在方法内部完成过滤逻辑,其中的参数包括:

  • ServerWebExchange:一个类似于Context的域对象,封装了Request、Response等服务相关的属性
  • 过滤器顺序

通过添加@Order注解,可以控制过滤器的优先级,从而决定了过滤器的执行顺序。

另外,一个过滤器的执行包括"pre""post"两个过程。在GlobalFilter.filter()方法中编写的逻辑属于pre阶段,在使用GatewayFilterChain.filter().then()的阶段,属于Post阶段。

优先级最高的过滤器,会在pre过程的第一个执行,在post过程的最后一个执行,如图:

在这里插入图片描述
我们可以在pre阶段做很多事情,诸如:

  • 登录状态判断
  • 权限校验
  • 请求限流等
自定义全局过滤器

定义过滤器只需要实现GlobalFilter即可,不过我们有多种方式来完成:

  • 方式一:定义过滤器类,实现接口
  • 方式二:通过@Configuration类结合lambda表达式
登录拦截案例

现在,我们通过自定义过滤器,模拟一个登录校验功能,逻辑非常简单:

  • 获取用户请求参数中的 token 参数
  • 判断是否为"admin"
    1)写一个类实现 GlobFilter
package com.itheima.sh.filter;

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

/**
 * @Description:
 * @Version: V1.0
 */
@Component
//@Order(1)
public class LoginFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1 获取Request对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 2 获取请求参数中是否包含token
        String token = request.getQueryParams().getFirst("token");
        // 3 不包含拦截
        if (StringUtils.isEmpty(token)) {
            // 未授权
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        // 4 判断当前的token值是否为 admin
        if (!"admin".equals(token)) {
            // 4.1 不是 拦截
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        // 放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

2)访问

localhost:10010/user/itcast?access-token=namin
如果token写错的话就访问不到

九、网关限流

网关除了请求路由、身份验证,还有一个非常重要的作用:请求限流。当系统面对高并发请求时,为了减少对业务处理服务的压力,需要在网关中对请求限流,按照一定的速率放行请求。

通过RateLimitFilter限流过滤器进行限流

常见的限流算法包括:

  • 计数器算法
  • 漏桶算法
  • 令牌桶算法
Gateway中限流实现(令牌通算法)

SpringCloudGateway是采用令牌桶算法,其令牌相关信息记录在redis中,因此我们需要安装redis,并引入Redis相关依赖。

Gateway会在Redis中记录令牌相关信息,我们可以自己定义令牌桶的规则,例如:

  • 给不同的请求URI路径设置不同令牌桶
  • 给不同的登录用户设置不同令牌桶
  • 给不同的请求IP地址设置不同令牌桶

Redis中的一个Key和Value对就是一个令牌桶。因此Key的生成规则就是桶的定义规则。SpringCloudGateway中key的生成规则定义在KeyResolver接口中:
KeyResolver结构

public interface KeyResolver {

	Mono<String> resolve(ServerWebExchange exchange);

}

参数:

  • Mono:是一个单元素容器,用来存放令牌桶的key
  • ServerWebExchange:上下文对象,可以理解为ServletContext,可以从中获取request、response、cookie等信息
KeyResolver实现类中三种令牌桶生成key的方式

给不同的请求URI路径设置不同令牌桶,示例代码:

return Mono.just(exchange.getRequest().getURI().getPath());// 获取请求URI

给不同的登录用户设置不同令牌桶

return exchange.getPrincipal().map(Principal::getName);// 获取用户

给不同的请求IP地址设置不同令牌桶

return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());// 获取请求者IP
实现步骤:

1)导入redis有关的依赖 (不是普通的redis的依赖)
2)配置redis的地址yml文件
3)配置过滤条件key 这里我们选择最后一种,使用IP地址的令牌桶key。
4)配置桶的参数
5)测试

1)导入redis有关的依赖 (不是普通的redis的依赖)

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

注意:这里不是普通的redis依赖,而是响应式的Redis依赖,因为SpringGateway是基于WebFlux的响应式项目。

2)配置redis的地址yml文件

spring:
  redis:
    host: 192.168.200.129

3)配置过滤条件key (这里我们选择最后一种,使用IP地址的令牌桶key。)

package com.itheima.sh.ratelimit;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Description:
 * @Version: V1.0
 */
@Component
public class IpKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // IP
            return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}

4)配置桶的参数

server:
  port: 10010 #服务端口
spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: user-service # 当前路由的唯一标识
          uri: lb://user-service # 路由的目标微服务,lb:代表负载均衡,user-service:代表服务id
#          uri: http://127.0.0.1:8081 # 路由的目标微服务地址
          predicates: # 断言
            - Path=/user-api/** # 按照路径匹配的规则
          filters: # 过滤项
            - AddRequestHeader=name,itheima
            - StripPrefix=1
      default-filters: # 默认过滤项
        - name: Hystrix # 指定过滤工厂名称(可以是任意过滤工厂类型)
          args: # 指定过滤的参数
            name: fallbackcmd  # hystrix的指令名
            fallbackUri: forward:/fallbackTest # 失败后的跳转路径

        - name: RequestRateLimiter                                     #请求数限流 名字不能随便写
          args:
            key-resolver: "#{@ipKeyResolver}"                               # 指定一个key生成器  ipKeyResolver是自己定义的key生成类的首字母小写
            redis-rate-limiter.replenishRate: 2                                # 生成令牌的速率
            redis-rate-limiter.burstCapacity: 2                                # 桶的容量
  redis:
    host: 192.168.200.129
hystrix:
  command:
    default:
      execution.isolation.thread.timeoutInMilliseconds: 1000 # 失败超时时长
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

这里配置了一个过滤器:RequestRateLimiter,并设置了三个参数:

  • key-resolver"#{@ipKeyResolver}"是SpEL表达式,写法是#{@bean的名称},ipKeyResolver就是我们定义的Bean名称
  • redis-rate-limiter.replenishRate:每秒钟生成令牌的速率
  • redis-rate-limiter.burstCapacity:令牌桶的容量

这样的限流配置可以达成的效果:

  • 每一个IP地址,每秒钟最多发起2次请求
  • 每秒钟超过2次请求,则返回429的异常状态码

5)测试

我们快速在浏览器多次访问http://localhost:10010/user/user/itcast,就会得到一个错误:429错误

十、解决跨域问题

有时候,我们需要对所有微服务跨域请求进行处理,则可以在gateway中进行跨域支持。修改application.yml,添加如下代码:

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]': # 匹配所有请求
              allowedOrigins: "*" #跨域处理 允许所有的域
              allowedMethods: # 支持的方法
                - GET
                - POST
                - PUT
                - DELETE
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值