SpringCloudAlibaba系列微服务搭建笔记六_Gateway

目录儿

  • 九、服务网关-SpirngCloudGateway
    • 9.1 SpringCloudGateway简介
    • 9.2 集成SpringCloudGateway
    • 9.3 网关整合Nacos
      • 9.3.1 先把服务模块注册到`Nacos`上
      • 9.3.2 网关服务配置nacos
    • 9.4 Predicate断言的实现
      • 9.4.1 内置断言工厂实现
    • 9.4.2 自定义断言工厂实现
    • 9.5 过滤器
      • 9.5.1 内置局部过滤器
      • 9.5.2 自定义局部过滤器

九、服务网关-SpirngCloudGateway

随着微服务多个模块的部署,每个服务所在的服务器可能不同,产生多个地址,为了方便维护这些地址,需要一个网关来统一服务地址,同时也可以通过网关统一认证鉴权。

9.1 SpringCloudGateway简介

网关挡在众多微服务前面,做路由转发、监控、限流、鉴权等功能。
SpringCloudGateway是基于WebFlux框架实现的,而WebFlux底层使用了Netty通信框架。
SpringCloudGateway核心的概念是路由Predicate断言、Filter过滤器

SpringCloudGateway需要使用SpringBoot2.0以上版本,并且不能再TomcatJettyServlet容器中运行,只能是jar包运行。

9.2 集成SpringCloudGateway

复制一个项目,修改后项目包含三个模块,一个网关,一个用户,一个商品服务。
SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第1张图片

  1. 网关模块添加依赖
<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>

注意:不要引入spring-boot-starter-web依赖,否则会报错

  1. application.yml配置文件配置网关路由
server:
  port: 8051

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      routes:                         # 路由,可配置多个
        - id: module_one              # 路由id,唯一即可,默认是uuid
          uri: http://localhost:8041  # 真实服务地址
          order: 1                    # 路由优先级,数值越小优先级越高,默认为 0
          predicates:                 # 断言
            - Path=/moduleone/**     # 路径匹配
        - id: module_two
          uri: http://localhost:8031
          order: 1
          predicates:
            - Path=/moduletwo/**

注意predicates:- Path=/module8031/**Path是大写开头

如此配置效果:地址以/moduletwo开头就路由到http://localhost:8031服务,
/moduleone开头就路由到http://localhost:8041服务。

其实就是把协议ip端口給替换掉,去访问真实的服务

  1. 在服务模块创建对应的服务接口
@RestController
@RequestMapping("/moduleone")
public class TestController {
    @Value("${server.port}")
    private String port;

    @RequestMapping("/test")
    public String test() {
        return "return from module test,服务端口:" + port;
    }
}
@RestController
@RequestMapping("/moduletwo")
public class TestController {
    @Value("${server.port}")
    private String port;

    @RequestMapping("/test")
    public String test() {
        return "return from module test,服务端口:" + port;
    }
}
  1. 访问测试接口
    SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第2张图片SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第3张图片
    可以看到,通过8051这个端口(网关服务),通过请求路径的不同成功路由到了两个不同的服务模块。

9.3 网关整合Nacos

现在网关是能够正常使用了,但是路由配置都写在配置文件上,如果后续要修改或新增的话,需要修改配置文件然后重启项目,这样非常不方便。于是整合Nacos非常有必要,这样gateway服务可以直接通过应用名称拉取到服务的地址。

9.3.1 先把服务模块注册到Nacos

怎么注册服务到nacos参考:https://blog.csdn.net/qq_31856061/article/details/126420140

注意:应用名不能带_,原因在下面

9.3.2 网关服务配置nacos

  1. 添加依赖
		
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
  1. 启动类贴上注解
    @EnableDiscoveryClient
  2. 增加bootstrap.yml配置文件
    application.yml里面的配置剪切到bootstrap.yml中,再修改一下
server:
  port: 8051

spring:
  application:
    name: gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: 124.221.89.209:8848 # Nacos 注册中心地址
    gateway:
      discovery:
        locator:
          enabled: true # 让网关服务可以发现 nacos 中的服务,自动拉取数据
      routes:
        - id: moduleOne
          uri: lb://moduleOne  # 从注册中心拉取 moduleOne 的地址
          order: 1
          predicates:
            - Path=/moduleone/**
        - id: moduleTwo
          uri: lb://moduleTwo  # 从注册中心拉取 moduleTwo 的地址
          order: 1
          predicates:
            - Path=/moduletwo/**

gateway中配置uri有三种方式:

  1. 服务地址: http://localhost:8031
  2. websocket:ws://localhost:8031
  3. 注册中心:lb://服务应用名称

注意: 用lb://服务应用名称这种方式,服务名不能有_,不然会识别不了报错
如:Invalid host: lb://module_two
gateway的正则指定服务名字符只能是a-zA-Z

  1. 测试接口
    都可以成功调通,说明可以成功从注册中心拉取服务地址完成路由功能。
    SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第4张图片

SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第5张图片

9.4 Predicate断言的实现

Predicate断言就是一个判断逻辑,返回值为布尔类型,如果为真才会往下进行路由。

9.4.1 内置断言工厂实现

内置断言工厂是SpringCloudGateway自带的断言工厂,可以直接通过配置文件配置。

  1. AfterRoutePredicateFactory-设定日期参数,允许在指定日期时间之后的请求通过。
    缩略配置举例:
- After=2022-04-15T14:48.421+08:00[Asia/Shanghai]
  1. BeforeRoutePredicateFactory-设定日期参数,允许在指定日期之前的请求通过
    缩略配置举例:
- Before=2022-04-15T14:48.421+08:00[Asia/Shanghai]
  1. BetweenRoutePredicateFactory-设定时间区间,只允许日期在区间内的请求通过
    缩略配置举例:
- Between=2022-04-15T14:48.421+08:00[Asia/Shanghai],2022-04-20T14:48.421+08:00[Asia/Shanghai]
  1. CookieRoutePredicateFactory-设定cookie名称和cookie值(或正则表达式),判断请求是否含有对应的cookie且其值是否匹配,匹配才会通过
    缩略配置举例:
- Cookie=mycookie,mycookievalue
  1. HeaderRoutePredicateFactory-设定请求头名称和请求头值(或正则表达式),判断请求是否含有对应的请求头且其值是否匹配,匹配才会通过
    缩略配置举例:
- Header=Phonenum,\d+
  1. HostRoutePredicateFactory-设定主机名host,允许符合的请求通过。
    缩略配置举例:
- Host=**.echoo.com
  1. MethodRoutePredicateFactory-设定请求方式,允许匹配的请求通过。
    缩略配置举例:
- Methode=GET
  1. PathRoutePredicateFactory-设定路径规则,判断请求地址是否满足设定的路径规则,满足通过
    缩略配置举例:
- Path=/user/*
  1. QueryRoutePredicateFactory-设定参数名和参数值(或正则表达式),允许符合的请求通过。
    缩略配置举例:
- Query=name,z.
  1. RemoteAddrRoutePredicateFactory-设定IP地址段,判断请求主机地址是否能匹配上。
    缩略配置举例:
- RemoteAddr=89.220.54.25/24
  1. WeightAddrRoutePredicateFactory-设定权重分组名称和权重值,同一个分组名的路由根据权重值转发
    缩略配置举例:
	routes:
        - id: moduleOne
          uri: lb://moduleOne  # 从注册中心拉取
          order: 1
          predicates:    
            - Path=/module/**       # 服务匹配路径是一样的
            - Weight=module_group,7 # 权重分组为 module_group,权重=7
        - id: moduleTwo
          uri: lb://moduleTwo  # 从注册中心拉取
          order: 1
          predicates:
            - Path=/module/**       # 服务匹配路径是一样的 
            - Weight=module_group,3 # 权重分组为 module_group,权重=3

像上面那样配置,断言的路径是一样的都是/module/**,通过同一个权重分组module_group,不同的权重值,使得70%的请求进入moduleOne服务,30%的请求进入moduleTwo服务

9.4.2 自定义断言工厂实现

除了内置的断言外,还可以通过代码实现自己的断言逻辑。

  1. 修改一下两个模块接口,添加一个id参数
    @RequestMapping("/test")
    public String test(@RequestParam Integer id) {
        return "return from moduleOne test,服务端口:" + port + ",id:" + id;
    }
  1. 创建自定义断言工厂
    需求:只允许id在指定范围内的请求通过
import org.apache.commons.lang.ArrayUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

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

/** 自定义断言工厂 */
@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {

    public static final String[] KEY_ARRAY = {"minId", "maxId"};

    /** 构造器 */
    public CustomRoutePredicateFactory() {
        super(CustomRoutePredicateFactory.Config.class);
    }

    /** 快捷配置字段 */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(KEY_ARRAY);
    }

    /** 断言逻辑 */
    @Override
    public Predicate<ServerWebExchange> apply(final CustomRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            public boolean test(ServerWebExchange serverWebExchange) {
                String id = serverWebExchange.getRequest().getQueryParams().getFirst("id");
                if (null != id) {
                    int idNum = Integer.parseInt(id);
                    return config.getMinId() < idNum && config.getMaxId() > idNum; // 允许通过
                }
                return false;
            }

            @Override
            public String toString() { // 重写 toString,不通过时输出自己指定的提示
                return String.format("minId:%d,maxId:%d", config.getMinId(), config.getMaxId());
            }
        };
    }

    /** 配置类 */
    @Validated
    public static class Config {
        private Integer minId;
        private Integer maxId;

        public Integer getMinId() {
            return minId;
        }

        public void setMinId(Integer minId) {
            this.minId = minId;
        }

        public Integer getMaxId() {
            return maxId;
        }

        public void setMaxId(Integer maxId) {
            this.maxId = maxId;
        }
    }
}
  1. 配置里使用自定义断言工厂
    gateway:
      discovery:
        locator:
          enabled: true # 让 SpringCloudGateway 可以发现 nacos 中的服务
      routes:
        - id: moduleOne
          uri: lb://moduleOne  # 从注册中心拉取
          order: 1
          predicates:
            - Path=/moduleone/**
        - id: moduleTwo
          uri: lb://moduleTwo  # 从注册中心拉取
          order: 1
          predicates:
            - Path=/moduletwo/**
            - Custom=90,100   # 使用自定义断言,minId=90 maxId=100
  1. 通过内置断言可以通过规律看到,断言工厂的名称都是功能名+RoutePredicateFactory,而在配置文件里配置断言是只要指定功能名就行,所以这里是- Customer
  1. 测试
    请求127.0.0.1:8051/moduletwo/test?id=95,参数在区间内,允许通过
    SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第6张图片
    请求127.0.0.1:8051/moduletwo/test?id=102,参数不在区间内,请求拒绝
    SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第7张图片

9.5 过滤器

SpringCloudGateway的过滤器可以在请求和响应之间加入一些自定义的逻辑。
其中过滤器又分为全局过滤器和局部过滤器,局部过滤器只能作用于某一个路由上。
过滤器和断言一样除却内置过滤器外,也支持用户自定义过滤器。

9.5.1 内置局部过滤器

SpringCloudGateway提供的内置局部过滤器

  1. AddRequestHeader
    设定Header及其值,为原始请求添加Header,缩略配置如下
- AddRequestHeader=Token,5461519844
  1. AddRequestParameter
    设定参数及其值,为原始请求添加参数,缩略配置如下
- AddRequestParameter=name,mary
  1. AddResponseHeader
    设定Header及其值,为响应添加Header,缩略配置如下
- AddResponseHeader=Token,5461519844
  1. DedupeResponseHeader
    设定去重的Header及其值,删除响应头中的重复值,缩略配置如下
- DedupeResponseHeader=SAMPLE-NAME SAMPLE-AGE

注意:如果指定多个Header,用空格隔开。

内置过滤器很多,这里只列出了以上几个。因为配置方法和使用都比较简单,所以需要直接去看文档使用即可。点击打开官方文档

9.5.2 自定义局部过滤器

与之前的自定义断言相似,过滤器是配置名+GatewayFilterFactory的固定配合。

  1. 自定义过滤器
    基于id参数范围做一个过滤
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {

    public static final String[] KEY_ARRAY = {"minId", "maxId"};

    /** 构造器 */
    public CustomGatewayFilterFactory() {
        super(CustomGatewayFilterFactory.Config.class);
    }

    /** 快捷配置字段 */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(KEY_ARRAY);
    }

    /** 过滤逻辑 */
    @Override
    public GatewayFilter apply(Config config) {

        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                String id = exchange.getRequest().getQueryParams().getFirst("id");
                if (null != id) {
                    int idNum = Integer.parseInt(id);
                    if (config.getMinId() < idNum && config.getMaxId() > idNum) {
                        return chain.filter(exchange);// 允许通过
                    }
                }
                // 条件不满足
                byte[] tips = ("拒绝访问id=" + id + "的数据").getBytes(StandardCharsets.UTF_8); // 准备提示信息,并转成字节数组
                DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(tips);           // 把提示字节数组放入响应的缓冲区
                exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); // 设置响应码
                return exchange.getResponse().writeWith(Flux.just(wrap));        // 返回提示
            }
        };
    }

    /** 配置类 */
    public static class Config {
        private Integer minId;
        private Integer maxId;

        public Integer getMinId() { return minId; }
        public void setMinId(Integer minId) { this.minId = minId; }
        public Integer getMaxId() { return maxId; }
        public void setMaxId(Integer maxId) { this.maxId = maxId; }
    }
}

  1. 修改配置文件
	...
    gateway:
      discovery:
        locator:
          enabled: true # 让 SpringCloudGateway 可以发现 nacos 中的服务
      routes:
        - id: moduleOne
          uri: lb://moduleOne  # 从注册中心拉取
          order: 1
          predicates:
            - Path=/moduleone/**
          filters:
            - Custom=10,20
  1. 测试
    SpringCloudAlibaba系列微服务搭建笔记六_Gateway_第8张图片

你可能感兴趣的:(微服务,微服务,gateway,java)