微服务04(网关gateway)

1、gateway简介

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代 Netflflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。

优点:
性能强劲:是第一代网关Zuul的1.6倍
功能强大:内置了很多实用的功能,例如转发、监控、限流等
设计优雅,容易扩展
缺点:
其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
需要Spring Boot 2.0及以上的版本,才支持

路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:
id,路由标识符,区别于其他 Route。
uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
filter,过滤器用于修改请求和响应信息。

1、初始

创建新的服务

shop-apiGateway-hdw
       <!--加入gateway的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
server:
  port: 7000
spring:
  application:
    name: api-gateway
  # 配置api
  cloud:
    gateway:
      routes:
        - id: product_route   # 路由的唯一标识,只要不重复都可以,如果不写默认会通过UUID产生,一般写成被路由的服务名称
          uri: http://localhost:8082/  # 被路由的地址
          order: 1                     #表示优先级  数字越小优先级越高
          predicates:                  #断言: 执行路由的判断条件
            - Path= /product_serv/**
          filters:                     # 过滤器: 可以在请求前或请求后作一些手脚
            - StripPrefix=1

启动shop-product和shop-apigateway,访问

http://localhost:7000/product_serv/product/productById/2

2、简单

加入nacos+gateway

        <!--加入gateway的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
        </dependency>

application.yml

#简写版
server:
  port: 7000
spring:
  application:
    name: api-gateway
  # 配置api
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true

启动类

@SpringBootApplication
@EnableDiscoveryClient //nacos服务发现的注解
public class ApiGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class,args);
    }
}

通过网关访问

http://localhost:7000/shop-product/product/productById/3

在这里插入图片描述
这时候,就发现只要按照网关地址/微服务/接口的格式去访问,就可以得到成功响应

3、断言

Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。
断言就是说: 在 什么条件下 才能进行路由转发

基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]

基于远程地址的断言工厂 RemoteAddrRoutePredicateFactory:
接收一个IP地址段,判断请求主机地址是否在地址段中
-RemoteAddr=192.168.1.1/24
基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate, ch.
基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否
具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+
基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。

-Host=**.testhost.org  

基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET
基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具
有给定名称且值与正则表达式匹配。
-Query=baz, ba.
基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/**
-Weight= group3, 9

4、自定义断言

写一个年龄大于18小于60的断言

server:
  port: 7000
spring:
  application:
    name: api-gateway

  # 配置gateway路由规则
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 运行从注册中心拉去服务清单。

      # 你的请求地址必须: http://localhost:7000/shop-product/product/findById/1
      routes:   # 路由: 把客户的符合断言的请求 转发到指定的微服务。 在到达路由前可以适当修改一下请求地址
        - id: product-router  #路由的唯一标识: 如果没有赋值默认会通过UUID 生成一个唯一标识
          uri: lb://shop-product # 路由转发的地址《目的地微服务的地址》
          order: 0 # 路由的优先级 值越小优先级越高
          predicates:  # 断言: 条件  把符合断言的请求 才可以被转发
            - Path=/product-serv/**
            - Age=18,60
            #- After=2021-12-31T23:59:59.789+08:00[Asia/Shanghai]
          filters:    # 过滤器: 在到达路由地址前可以对请求地址进行修改
            - StripPrefix=1  # 截取请求地址的第一层
#            - SetStatus=250            - 
    nacos:
      discovery:
        server-addr: http://localhost:8848

import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

@Component
//AfterRoutePredicateFactory
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
    public static final String DATETIME_KEY = "datetime";

    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }
    // 读取配置文件中内容,把他赋值给Config类的属性
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("minAge","maxAge");
    }
    // 断言的业务逻辑
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                //获取请求地址的age参数值.
                //localhost:8001/findbyid?age=18&name=zs&age=18
                String age = serverWebExchange.getRequest().getQueryParams().getFirst("age");
                if(StringUtils.isNotEmpty(age)){
                    if(Integer.parseInt(age)>=config.getMinAge()&&Integer.parseInt(age)<=config.getMaxAge()){
                        return true;
                    }
                }
                return false;
            }
        };
    }

    @Data
    public static class Config {
        private Integer minAge;
        private Integer maxAge;

    }
}

访问

http://localhost:7000/product-serv/product/productById/2?age=30

api网关开启,直接访问controller中RequestMapping路径

server:
  port: 7000
spring:
  application:
    name: api-gateway

  # 配置gateway路由规则
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 运行从注册中心拉去服务清单。

      # 你的请求地址必须: http://localhost:7000/shop-product/product/findById/1
      routes:   # 路由: 把客户的符合断言的请求 转发到指定的微服务。 在到达路由前可以适当修改一下请求地址
        - id: product-router  #路由的唯一标识: 如果没有赋值默认会通过UUID 生成一个唯一标识
          uri: lb://shop-product # 路由转发的地址《目的地微服务的地址》
          order: 0 # 路由的优先级 值越小优先级越高
          predicates:  # 断言: 条件  把符合断言的请求 才可以被转发
#            - Path=/product-serv/**
            - Age=18,60
            #- After=2021-12-31T23:59:59.789+08:00[Asia/Shanghai]
#          filters:    # 过滤器: 在到达路由地址前可以对请求地址进行修改
#            - StripPrefix=1  # 截取请求地址的第一层
    #            - SetStatus=250            -
    nacos:
      discovery:
        server-addr: http://localhost:8848

5、网关限流

依赖导入

 <!--引入spring-cloud-gateway和sentinel整合的jar  网关限流-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
        </dependency>

修改yml配置文件


server:
  port: 7000
spring:
  application:
    name: api-gateway

  # 配置gateway路由规则
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 运行从注册中心拉去服务清单。

      # 你的请求地址必须: http://localhost:7000/shop-product/product/findById/1
      routes:   # 路由: 把客户的符合断言的请求 转发到指定的微服务。 在到达路由前可以适当修改一下请求地址
        - id: product-router  #路由的唯一标识: 如果没有赋值默认会通过UUID 生成一个唯一标识
          uri: lb://shop-product # 路由转发的地址《目的地微服务的地址》
          order: 0 # 路由的优先级 值越小优先级越高
          predicates:  # 断言: 条件  把符合断言的请求 才可以被转发
            - Path=/product-serv/**
#            - Age=18,60
            #- After=2021-12-31T23:59:59.789+08:00[Asia/Shanghai]
          filters:    # 过滤器: 在到达路由地址前可以对请求地址进行修改
            - StripPrefix=1  # 截取请求地址的第一层
    #            - SetStatus=250            -
    nacos:
      discovery:
        server-addr: http://localhost:8848

写网关配置类

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 配置限流的异常处理器:SentinelGatewayBlockExceptionHandler
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * 配置限流过滤器
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 配置初始化的限流参数
     */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_service") //资源名称
                .setCount(1) // 限流阈值
                .setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
        );
        GatewayRuleManager.loadRules(rules);
    }
}

基于Sentinel 的Gateway限流是通过其提供的Filter来完成的,使用时只需注入对应的SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。
@PostConstruct定义初始化的加载方法,用于指定资源的限流规则。这里资源的名称为 order-service ,统计时间是1秒内,限流阈值是1。表示每秒只能访问一个请求。

多次访问http://localhost:7000/product_service/product/findById/2,发现被限流

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值