springcloud Gateway网关的路由、断言、过滤配置

Route(路由)

1、构架pom文件,这里面需要将web包去掉,因为Gateway的底层是webflux,而webflux和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>cloud2020</artifactId>
        <groupId>com.king.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-gateway9527</artifactId>

    <dependencies>
        <!--新增gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client 客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.king.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

2、构建main方法入口,因为gateway也需要注册进注册中心,所以也需要添加注解@EnableEurekaClient

package com.king.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * created by king on 2020/4/23 1:19 下午
 */
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
    public static void main(String[] args) {
            SpringApplication.run(GatewayMain9527.class,args);
        }
}

3、构建yml文件,此处是简单的路由配置,等演示成功后,后边会更改为动态路由配置

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh1 #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由

        - id: payment_routh2 #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由


eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #单机
      defaultZone: http://localhost:7001/eureka
      # 集群
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
  instance:
    instance-id: gateway9527
    prefer-ip-address: true  #访问路径可以显示ip地址
    #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认30s)
    #lease-renewal-interval-in-seconds: 1
    #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认90s,超时将剔除服务
    #lease-expiration-duration-in-seconds: 2

4、此时测试http://localhost:8001/payment/get/1 、http://localhost:8001/payment/lb 和 http://localhost:9527/payment/get/1 、http://localhost:9527/payment/lb得到的结果是一样是,说明我们的9527gateway已经实现了路由功能

 

而Gateway网关实现路由有两种方法:

第一种就是yml配置方式,上述就是这一种

第二种就是在代码中注入RouteLocator的bean,代码如下:

package com.king.springcloud.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * created by king on 2020/4/23 2:21 下午
 * 配置了一个id为myroutebaidu1的路由规则,
 * 当访问地址http://localhost:9527/guone时会自动转发到地址: http://news.baidu.com/guonei
 */
@Configuration
public class GatewayConfiguration {
    @Bean
    public RouteLocator customerRouterLocator1(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("myroutebaidu1", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
    @Bean
    public RouteLocator customerRouterLocator2(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("myroutebaidu2", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
    //注意:此处的path需要和uri的最后路径保持一致,不一致会报错找不到的,这个game就是反例~~
    @Bean
    public RouteLocator customerRouterLocator3(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("myroutebaidu3", r -> r.path("/mygame").uri("http://news.baidu.com/game")).build();
        return routes.build();
    }
}

可以实现路由到百度新闻的某个标签模块

上面是gateway实现路由的简单配置,默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。下面给出了动态路由创建配置:

1、动态路由配置yml,修改部分:

①是添加了discovery.locator.enabled:true开启了动态路由功能

②是修改uri为 lb://开头 + 注册中心微服务名 (注意服务名小写) (lb代表注从注册中心获取微服务,有LoadBalance功能)

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh1 #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service  #uri以lb://开头(lb代表从注册中心获取服务)
#          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由

        - id: payment_routh2 #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service  #uri以lb://开头(lb代表从注册中心获取服务)
#          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由

2、启动eureka7001、provider8001、provider8002和 gateway9527服务,然后测试访问http://localhost:9527/payment/get/1,根据响应的结果端口号8001和8002轮训出现,即可确定实现了动态路由功能。

 

Predicates(断言)

Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。

官网例子地址:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/3.0.0.M1/reference/html/#gateway-request-predicates-factories

下面举例一下 predicates断言的几种类型:

  • Loaded RoutePredicateFactory [After]  :时间点之后服务可访问
  • Loaded RoutePredicateFactory [Before] :时间点之前服务可访问
  • Loaded RoutePredicateFactory [Between] :两个之间点之间服务可访问,datetime2参数必须是后于datetime1
  • Loaded RoutePredicateFactory [Cookie] :请求服务需要带cookie
    • 此处可使用curl来进行测试 curl http://localhost:9527/payment/lb --Cookie "username=king"
  • Loaded RoutePredicateFactory [Header] :请求服务需要带header
    • 请求头要有X-Request-Id属性并且值为整数的正则表达式 curl http://localhost:9527/payment/lb -H "X-Request-Id:1234"
  • Loaded RoutePredicateFactory [Host] :请求头中的header中需要包含host的内容为somehost.org等这些
    • curl http://localhost:9527/payment/lb -H "host:www.anotherhost.org"  
    • curl http://localhost:9527/payment/lb -H "host:www.somehost.org"
  • Loaded RoutePredicateFactory [Method] :请求为post请求
    • curl http://localhost:9527/payment/lb -X POST 
  • Loaded RoutePredicateFactory [Path] : 路径相匹配的进行路由
  • Loaded RoutePredicateFactory [Query] :要有参数名称并且是正整数才能路由 没演示成功
  • Loaded RoutePredicateFactory [ReadBodyPredicateFactory] :官网没看见,不做解释了
  • Loaded RoutePredicateFactory [RemoteAddr] :需要ip在1-24区间的可以访问通过,比如 192.168.1.10
  • Loaded RoutePredicateFactory [Weight] :这条路线会将此百分比的流量转发到这里,与其他配合服务使用。
  • Loaded RoutePredicateFactory [CloudFoundryRouteService] :官网没找到,不做解释了

这里官网给出的时间点是美国时区的,所以我们需要工具转换一下为中国/上海时区

import java.time.ZonedDateTime;

/**
 * created by king on 2020/4/23 3:44 下午
 */
public class T1 {
    public static void main(String[] args) {
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        System.out.println(zonedDateTime);
        //输出为 2020-04-23T15:45:46.581+08:00[Asia/Shanghai]
    }
}

yml的例子我就直接全粘贴了,不再一个个去操作了


spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh1 #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service  #uri以lb://开头(lb代表从注册中心获取服务)
#          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由
#            - Weight=group2, 8

        - id: payment_routh2 #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service  #uri以lb://开头(lb代表从注册中心获取服务)
#          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由
#            - After=2020-04-23T15:45:46.581+08:00[Asia/Shanghai]  #此时间点后才能访问
#            - Before=2020-04-23T18:45:46.581+08:00[Asia/Shanghai]  #此时间点前才能访问
#            - Between=2020-04-23T15:45:46.581+08:00[Asia/Shanghai],2020-04-24T15:45:46.581+08:00[Asia/Shanghai]  #datetime1和datetime2区间请求可访问。datetime2参数必须是后于datetime1
#            - Cookie=username,king #请求服务需要带cookie,此处可使用curl来进行测试 curl http://localhost:9527/payment/lb --Cookie "username=king"
#            - Header=X-Request-Id, \d+ #请求服务需要带header,请求头要有X-Request-Id属性并且值为整数的正则表达式 curl http://localhost:9527/payment/lb -H "X-Request-Id:1234"
#            - Host=**.somehost.org,**.anotherhost.org #请求头中的header中需要包含host的内容为somehost.org等这些 curl http://localhost:9527/payment/lb -H "host:www.anotherhost.org" 或 curl http://localhost:9527/payment/lb -H "host:www.somehost.org"
#            - Method=POST  #请求为post请求 curl http://localhost:9527/payment/lb  -X POST
#            - Query=username, \d+ #要有参数名称并且是正整数才能路由  没演示成功 curl http://localhost:9527/payment/lb?username=1,但是浏览器输入请求这个地址成功测试完成
#            - RemoteAddr=192.168.1.1/24   #需要ip在1-24区间的可以访问通过,比如 192.168.1.10
#            - Weight=group1, 2 #这条路线会将大约80%的流量转发到上边儿,将大约20%的流量转发到这里,与其他配合服务使用。

 

Filter(过滤)

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂 类来产生。

springcloud gateway的Filter的 :

一般我们是通过实现自定义全局GlobalFilter过滤器,来完成操作的

自定义全局GlobalFilter过滤器搭建演示:

1、新建MyLogGlobalFilter,在类上加注解@Component ,并实现 GlobalFilter, Ordered两个接口,并自定义拦截规则

  • 先通过exchange获取参数
  • 验证参数是否合法
    • 参数不合法:通过exchange的response设置状态码,然后响应出去
  • 完成操作后通过 chain.filter(exchange)过滤链 传递下去
package com.king.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
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.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * created by king on 2020/4/23 5:05 下午
 */
@Component
@Slf4j
public class MyLogGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 1.先通过exchange获取参数
     * 2.验证参数是否合法
     *      参数不合法:通过exchange的response设置状态码,然后响应出去
     * 3.完成操作后通过 chain.filter(exchange)过滤链 传递下去
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("*********come in MyLogGlobalFilter: "+new Date());
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname ==null){
            log.info("uname 为空,非法用户,请出去~~~~");
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return  response.setComplete();
        }
        return chain.filter(exchange);
    }

    //加载过滤器的顺序,一般数字越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

2、通过url请求测试Filter是否成功 http://localhost:9527/payment/lb?uname=asga、http://localhost:9527/payment/lb?u=asga

通过uname有值的得到放行,而没有uname参数的直接返回错误~

到此,springcloud 的Gateway 新网关的演示结束了~后续有新的会继续补充~~~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章