SpringCloud 网关【Gateway】

发布于:2022-11-09 ⋅ 阅读:(716) ⋅ 点赞:(0)

目录

1. Spring Cloud Gateway 

2. 使用 

2.1 导入pom依赖

2.2 application.yml文件

2.3 启动类配置

2.4 方式一

2.5 方式二

2.6 方式三(动态路由)


1. Spring Cloud Gateway 

Spring Cloud Gateway是Spring官方基于Spring5.0、SpringBoot2.0和Project Reactor等技术开发的网 关

旨在为微服务框架提供一种简单而有效的统一的API路由管理方式,统一访问接口。

Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标是替代Netflix的Zuul,其不仅提供统 一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点和限流等等。 它是基于Netty的响应式开发模式。 

 

1️⃣路由(route):路由是网关最基础的部分,路由信息由一个ID,一个目的URL、一组断言工厂和一 组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

2️⃣断言(Predicate):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配 来自http Request中的任何信息,比如请求头和参数等。

3️⃣过滤器(Filter):一个标准的Spring WebFilter,Spring Cloud Gateway中的Filter分为两种类型: Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

2. 使用 

     2.1 导入pom依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jmh</groupId>
    <artifactId>nacos_gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos_gateway</name>
    <description>Demo project for Spring Boot</description>

    <!--提供版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <spring-boot.version>2.4.1</spring-boot.version>
        <spring-cloud.version>2020.0.0</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
    </properties>

    <!--提供依赖-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--注册与发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--远程通信-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
        <!--对象赋值依赖-->
        <dependency>
            <groupId>ma.glasnost.orika</groupId>
            <artifactId>orika-core</artifactId>
            <version>1.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!--网关-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.35</version>
        </dependency>

    </dependencies>

    <!--提供依赖版本-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

    2.2 application.yml文件

server:
  port: 8084
spring:
  application:
    name: nacos-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    2.3 启动类配置

package com.jmh.nacos_gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
@SuppressWarnings("all")
public class NacosGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(NacosGatewayApplication.class, args);
    }

}

   2.4 方式一

  • application.yml文件 
server:
  port: 8084
spring:
  application:
    name: nacos-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false
          #为true代表开启基于服务发现的路由规则。
          enabled: true
          #配置之后访问时service-id无需大写
          lower-case-service-id: true




   2.5 方式二

  •  application.yml文件 
server:
  port: 8084
spring:
  application:
    name: nacos-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false
          #为true代表开启基于服务发现的路由规则。
          enabled: false
          #配置之后访问时service-id无需大写
          lower-case-service-id: true
      routes:
        # 路由标识(id:标识,具有唯一性)
        - id: user-consumer-api
          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: lb://nacos-consumer
          #优先级,越小越优先
          #order: 999
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
            - Path=/aa/**
          filters:
           #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
           #前缀过滤,请求地址:http://localhost:8084/usr/hello
           #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
           #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
            - StripPrefix=1
        # 路由标识(id:标识,具有唯一性)
        - id: user-provider-api
         #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: lb://nacos-provider
          #优先级,越小越优先
          #order: 999
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
            - Path=/bb/**
          filters:
            #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
            #前缀过滤,请求地址:http://localhost:8084/usr/hello
            #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
            #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
            - StripPrefix=1



    2.6 方式三(动态路由)

         2.6.1. application.yml文件

server:
  port: 8084
spring:
  application:
    name: nacos-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false
          #为true代表开启基于服务发现的路由规则。
          enabled: false
          #配置之后访问时service-id无需大写
          lower-case-service-id: true

#自定义配置
gateway:
  nacos:
    server-addr: ${spring.cloud.nacos.discovery.server-addr}
    # namespace: xxx-xx-xx-xx #可在这里设置获取是哪一个命名空间的文件 不设置默认公开的publie
    data-id: gateway.json #这个文件是nacos外部文件名称
    group: DEFAULT_GROUP


          2.6.2 nacos外部配置文件数据

  • gateway.json(注!文件后缀是.json是个json文件)
{
  "refreshGatewayRoute": true,
  "routeList": [
    {
      "id": "consumer-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/consumer/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://nacos-consumer",
      "order": 0
    },
    {
      "id": "provider-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/provider/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://nacos-provider",
      "order": 0
    }
  ]
}

          2.6.3 pojo模块

  •   FilterEntity
package com.jmh.nacos_gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 蒋明辉
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class FilterEntity {

    //过滤器对应的Name
    private String name;

    //路由规则
    private Map<String, String> args = new LinkedHashMap<>();

}
  •  GatewayNacosProperties
package com.jmh.nacos_gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ConfigurationProperties(prefix = "gateway.nacos")
@Component
public class GatewayNacosProperties {

    private String serverAddr;
    private String dataId;
    private String namespace;
    private String group;

}
  •  PredicateEntity
package com.jmh.nacos_gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 蒋明辉
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class PredicateEntity {

    //断言对应的Name
    private String name;

    //断言规则
    private Map<String, String> args = new LinkedHashMap<>();

}
  •  RouteEntity
package com.jmh.nacos_gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 蒋明辉
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class RouteEntity {

    //路由id
    private String id;

    //路由断言集合
    private List<PredicateEntity> predicates = new ArrayList<>();

    //路由过滤器集合
    private List<FilterEntity> filters = new ArrayList<>();

    //路由转发的目标uri
    private String uri;

    //路由执行的顺序
    private int order = 0;

}
  •  DynamicRoutingConfig
package com.jmh.nacos_gateway;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jmh.nacos_gateway.pojo.FilterEntity;
import com.jmh.nacos_gateway.pojo.GatewayNacosProperties;
import com.jmh.nacos_gateway.pojo.PredicateEntity;
import com.jmh.nacos_gateway.pojo.RouteEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 此类实现了Spring Cloud Gateway + nacos 的动态路由,
 * 它实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
 */
@SuppressWarnings("all")
@Slf4j
@Component
public class DynamicRoutingConfig implements ApplicationEventPublisherAware {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    @Autowired
    private GatewayNacosProperties gatewayProperties;

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService,
     * 再使用ConfigService开启一个监听,
     * 并且在监听的方法中刷新路由信息。
     */
    @Bean
    public void refreshRouting() throws NacosException {
        //创建Properties配置类
        Properties properties = new Properties();
        System.out.println(gatewayProperties);
        //设置nacos的服务器地址,从配置类GatewayProperties中获取
        properties.put(PropertyKeyConst.SERVER_ADDR, gatewayProperties.getServerAddr());
        //设置nacos的命名空间,表示从具体的命名空间中获取配置信息,不填代表默认从public获得
        if (gatewayProperties.getNamespace() != null) {
            properties.put(PropertyKeyConst.NAMESPACE, gatewayProperties.getNamespace());
        }
        //根据Properties配置创建ConfigService类
        ConfigService configService = NacosFactory.createConfigService(properties);
        //获得nacos中已有的路由配置
        String json = configService.getConfig(gatewayProperties.getDataId(), gatewayProperties.getGroup(), 5000);
        if(null==json){
            log.warn("路由配置为空");
            return;
        }
        this.parseJson(json);

        //添加监听器,监听nacos中的数据修改事件
        configService.addListener(gatewayProperties.getDataId(), gatewayProperties.getGroup(), new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            /**
             * 用于接收远端nacos中数据修改后的回调方法
             */
            @Override
            public void receiveConfigInfo(String configInfo) {
                log.warn(configInfo);
                //获取nacos中修改的数据并进行转换
                parseJson(configInfo);
            }
        });

    }

    /**
     * 解析从nacos读取的路由配置信息(json格式)
     */
    public void parseJson(String json) {
        log.warn("从Nacos返回的路由配置(JSON格式):" + json);
        boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute");
        if (refreshGatewayRoute) {
            List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class);
            for (RouteEntity route : list) {
                update(assembleRouteDefinition(route));
            }
        } else {
            log.warn("路由未发生变更");
        }
    }


    /**
     * 路由更新
     */
    public void update(RouteDefinition routeDefinition) {

        try {
            this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
            log.warn("路由删除成功:" + routeDefinition.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        try {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            log.warn("路由更新成功:" + routeDefinition.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

    }

    /**
     * 路由定义
     */
    public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {

        RouteDefinition definition = new RouteDefinition();

        // ID
        definition.setId(routeEntity.getId());

        // Predicates
        List<PredicateDefinition> pdList = new ArrayList<>();
        for (PredicateEntity predicateEntity : routeEntity.getPredicates()) {
            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(predicateEntity.getArgs());
            predicateDefinition.setName(predicateEntity.getName());
            pdList.add(predicateDefinition);
        }
        definition.setPredicates(pdList);

        // Filters
        List<FilterDefinition> fdList = new ArrayList<>();
        for (FilterEntity filterEntity : routeEntity.getFilters()) {
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setArgs(filterEntity.getArgs());
            filterDefinition.setName(filterEntity.getName());
            fdList.add(filterDefinition);
        }
        definition.setFilters(fdList);

        // URI
        URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri();
        definition.setUri(uri);

        return definition;
    }

}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到