spring组件之gateway高级

1. 入门案例

1.1 approve微服务

准备一个微服务approve, 并注册到nacos中
依赖

<dependencies>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>
</dependencies>

配置

server:
  servlet:
    context-path: /approve-service
  port: 8999
spring:
  application:
    name: approve-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.112.77:3306/monitorlog?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
    username: root
    password: pass
  # 用来注册服务
  cloud:
    nacos:
      discovery:
      #这里是nacos本地的虚拟机地址
        server-addr: 192.168.112.77:8848
        namespace: df9a215d-16a2-4a5c-b92f-111fb2598944
        group: DEFAULT_GROUP
#        config:
#          password: nacos
#          username: nacos

controller层

@RestController
@RequestMapping("/approve")
public class AppController {
        @Resource
        private ApproveService approveService;

        @GetMapping("/getUserInfo/{userId}")
        public User getUserInfo(@PathVariable Integer userId){
            return approveService.getUserInfo(userId);
        }
    }

1.2 gateway服务

准备gateway服务
依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!-- Maven整个生命周期内排除内置容器,排除内置容器导出成war包可以让外部容器运行spring-boot项目-->
    <exclusions>
      <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
<!--   gateway 组件依赖 -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gateway</artifactId>
    <version>2.2.0.RC2</version>
  </dependency>
<!--   nacos注册发现 -->
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2.2.5.RC2</version>
  </dependency>
</dependencies>

配置文件

server:
  servlet:
    context-path: /gateway
  port: 10010
spring:
  application:
    name: gateway
  rabbitmq:
    addresses: 192.168.112.77:15672
    username: admin
    password: pass
    virtual-host: / # 虚拟主机
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.112.77:8848
        namespace: df9a215d-16a2-4a5c-b92f-111fb2598944

    #      server-addr: 192.168.112.77:8848
#      namespace: public
#      group: SEATA_GROUP
    gateway:
      routes:
        - id: approve-service  # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://approve-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/approve-service/**  # 这个是按照路径匹配,只要以/user/开头就符合要求
      discovery:
        locator:
          enabled:
          lower-case-service-id: true

1.3 测试访问

检查服务注册
在这里插入图片描述

可以看到服务已经注册成功
请求地址: 127.0.0.1:10010/approve-service/approve/getUserInfo/1
在这里插入图片描述

可以看到请求转发,已经成功

2. 自定义配置

2.1 这里先对配置文件来进行了解

    gateway:
     discovery: # 服务的注册与发现
      locator: 
        enabled: true # 默认的路由转发,一般来说都会关闭,自己指定路由转发,开启后自定义你注解就失效
        lower-case-service-id: true # 大小写转换 一般用于拉取的服务列表转换为小写
      routes:
        - id: approve-service  # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://approve-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/approve-service/**  # 这个是按照路径匹配,只要以/user/开头就符合要求

这里特别需要注意:

  1. 默认的路由转发,会根据请求的端口后面的路径进行和服务名相同的转发,这会造成不必要的路由转发

如:http://ddd/getid/1 如果有拉取到服务列表有ddd的服务就会直接转发.并且自定义的路由转发规则失效

  1. 如果使用的注册中心是eureka,那么拉取的服务是大写的,所以最好开启转换

rounts后面的配置, 是属于快速配置, 代码层面如下图
在这里插入图片描述

其中-id的-代表的是对象的意思,这里就是对RouteDefinition的属性赋值

id不需多讲,只要符合class命名,且不重复即可

2.2 predicates谓语

谓语的作用就是满足你给定的条件就执行指定的路由
我们先来点开一下这个谓语属性,看看有哪些属性
在这里插入图片描述
在这里插入图片描述

可以看出,这个对象中有name和args参数两个属性.并且这里简化的代码书写,多个参数用逗号隔开

-predicates:
	- name= Path
	 args:
	 - pattern=/b/**,/a/**
	# 简化后
	- predicates:
  	 Path=/b/**,/a/**

那么常见的谓语参数有哪些呢,我们可以通过GatewayPredicate这个类来查看所有的谓语工厂
在这里插入图片描述

这里可以看到只要把这个方法的结尾的RoutePredicateFactory去掉就可以得到对应的谓语名称了

2.3 filter过滤器

过滤器分为默认过滤器, 自定义过滤器,全局过滤器

2. 3.1 默认过滤器

在这里插入图片描述

同样的name 和args 并且有简化书写
在这里插入图片描述
我们可以通过GatewayFilter来找到目前已定义好的过滤器
在这里插入图片描述

同样的 把GatewayFilterFactory去掉就得到了我们需要的过滤器 目前已知是24个
如在当前的路由方式下增加一些请求头信息

filters:
  - AddRequestHeader=polo,one patch man!
2.3.1.1 StripPrefix(切割路径)

这里特别说明一个过滤器

在这里插入图片描述

这个过滤器作用就是将我们配置的path链接,从左往右去掉路径个数,
如我们访问通过gateway的地址为

127.0.0.1:10010/approve-service/approve/getUserInfo/4

nacos中注册的服务地址为

192.168.72.1:8999

那么gateway转发后就会变成

192.168.72.1:8999/approve-service/approve/getUserInfo/4

如果我们在approve服务中添加了

server:
  servlet:
    context-path: /approve-service

那么就可以正常访问
如果我们没有配置服务前缀路径

server:
  servlet:
    context-path: /   # '/'杠不能省略,否则会报错

那么我们的地址就会多出前面的服务名称,导致无法路由到对应服务,所以这时候使用StripPrefixgatewayFilter来切割掉最前面的路径,变为

192.168.72.1:8999/approve/getUserInfo/4

从而完成访问

2.3.1.2 RequestRateLimiter

首先,先对常用的三种限制流量算法进行了解

  1. 计算器

一秒钟固定qps,

  1. 漏桶算法

所有请求放入桶中 然后可以匀速处理, 超出桶的容量的就拒绝

  1. 令牌桶算法

匀速产生令牌,放入桶中,每个请求进去后就去拿令牌, 拿不到令牌才会拒绝, 这样就能保证匀速处理请求
我们常用的过滤器为
在这里插入图片描述

这里的主要参数为 keyresolver 和defaultRateLimter

首先对于defaultRateLimter实现
在这里插入图片描述

这里可以使用RedisRateLimiter
在这里插入图片描述
这里可以看到自动装配指定参数为redis-rate-limter
我们可以指定参数令牌数量 桶的容量
接下来对keysolver实现
在这里插入图片描述
这里产生的令牌是远程主机的地址
书写配置文件

      - name : RequestRateLimiter
              args:
                keyResolver: '#{@myKeyResolver}' # springEL表达式  获取容器中的bean
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 5

在这里插入图片描述

因为使用的有redis, 所以需要引入redis依赖

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
spring: 
	  redis:
    host: 192.168.112.77
    port: 6379
    password: 1234

此时 还需要去解决redisRateLimiter中的依赖报错
在这里插入图片描述

去添加上对应的依赖即可

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>23.0.0</version>
</dependency>

这里 估计是版本更新问题 正常来说不需要要我们去特意添加
这样就实现了最高一秒五次, 后续一秒一次,可以用jmeter来进行压测
在这里插入图片描述

在这里插入图片描述

运行查看结果树
在这里插入图片描述

2. 3.1.3 Hystrix容错处理

首先看下它的工厂类 提供的配置
在这里插入图片描述
这里可以看到我们需要设置的三个参数,
name:代表失败返回的策略分组(不可省略 否则会走500或者404原始逻辑)
setter: 代表走默认的505或404逻辑,当name没有设置的时候才会生效(不建议使用)
fallbackUrl: 失败后转发到gateway当前工程中的controller地址

接下来开始测试
首先引入容错降级依赖

<!--降级处理-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

设置filter配置参数

            -name: Hystrix
             args:
               name:fallbackcontroller
               fallbackUri: forward://downgrade #访问失败 去访问gateway工程中的控制器

失败访问的gateway下的控制器

/**
 * @author polo
 * @createTime 2022/12/11 22:43
 * @description 失败降级控制器
 */
@RestController
@RequestMapping("/downGrade")
public class DownGradeController {

    @RequestMapping("/errmessage")
    public String erroMessage(){
        return "<html><body><div style='width:600px;margin:0 auto;text-align:center'>服务器繁忙,请稍后重试</div></body></html>";
    }
}

现在来测试 当approve服务未启动
在这里插入图片描述
可以看出已经走了降级逻辑

2.3 .2 自定义过滤器

  1. 自定义全局过滤器
@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //前置处理
        log.info("执行自定义全局过滤器,记录请求日志信息...");
        //后续执行链
        Mono<Void> result = chain.filter(exchange);
        //后置处理
        log.info("执行自定义全局过滤器,记录响应日志信息");
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        //在同为全局过滤器中,执行前后的控制
        return -1;
    }
}

自定义全局过滤器只需要实现globalFilter 就会在请求是自动调用,
官方推荐还需实现 Ordered接口,来指定在同类型过滤器中执行的先后
在这里插入图片描述
2. 自定义路由过滤器
在这里插入图片描述
官方推荐直接继承 AbstractRoutePredicateFactory
下面实现一个获取请求路径的过滤器

package com.jiang.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.List;

/**
 * @author polo
 * @createTime 2022/12/13 23:48
 * @description
 */
@Slf4j
@Component
public class RequestPathGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestPathGatewayFilterFactory.Config> {
    public RequestPathGatewayFilterFactory() {
        super(Config.class);
    }
    //过滤链实现
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            //写在chain.filter前的为前置过滤
            String path = exchange.getRequest().getPath().toString();
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            log.info("请求路径是:{},请求参数是name:{},path:{}",path,config.getName(),config.getPath());
            return chain.filter(exchange.mutate().request(builder.build()).build());
        };
    }
    //书写简化方案
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name","path");
    }
    //配置参数
    public static class Config {
        private String name;
        private String path;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }
//Put the configuration properties for your filter here
    }
}

配置文件
在这里插入图片描述
注意这里的简化方案需要在自定义路由过滤器中实现 shortcutFieldOrder 方法

到此,常规gateway的用法已经说完,

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值