SpringCloud之网关Gateway

这期内容用的是Spring Cloud官方的服务路由Spring Cloud Gateway 

一、gateway基本使用

1、含义

Spring Cloud Gateway是Spring官方基于Spring5.0、SpringBoot2.0和Project Reactor等技术开发的网关
旨在为微服务框架提供一种简单而有效的统一的API路由管理方式,统一访问接口。
Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标是替代Netflix的Zuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点和限流等等。
它是基于Netty的响应式开发模式。

SpringCloud之网关Gateway_第1张图片

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可以对请求和响应进行处理

SpringCloud之网关Gateway_第2张图片

2、基本搭建

①、新建模块

SpringCloud之网关Gateway_第3张图片

SpringCloud之网关Gateway_第4张图片 ②、导入依赖




    4.0.0
    com.gateway
    code
    0.0.1-SNAPSHOT
    gateway
    Demo project for Spring Boot



    
        8
        8
        2.4.1
        2020.0.0
        2021.1
    

    
        
            org.springframework.boot
            spring-boot-starter-webflux
        

        
            org.springframework.boot
            spring-boot-starter-test
        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        

        
            org.projectlombok
            lombok
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
            org.springframework.cloud
            spring-cloud-loadbalancer
        
        
        
            com.alibaba
            fastjson
            1.2.35
        

    

    
        
            
                org.springframework.boot
                spring-boot-dependencies
                ${spring-boot.version}
                pom
                import
            
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring-cloud-alibaba.version}
                pom
                import
            
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.1
                
                    1.8
                    1.8
                    UTF-8
                
            
            
                org.springframework.boot
                spring-boot-maven-plugin
                2.4.1
                
                    com.gateway.code.GatewayApplication
                
                
                    
                        repackage
                        
                            repackage
                        
                    
                
            
        
    


③、nacos中注册服务

yml文件:

server:
  #此处的8084端口号,就好像以前外置的tomcat的8080,让我们通过浏览器进行访问
  #但此服务只是做了一个路由,它会将请求路由到其它微服务(一般是消费者)进行处理
  port: 8084

spring:
  application:
    #微服务名
    name: gateway
  cloud:
    nacos:
      discovery:
        #指定nacos注册中心的地址
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
#          以前:localhost:8082/user/aa
#          路由:localhost:8084/consumer/user/aa
#          路由:localhost:8084/provider/user/aa
          #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false
          #为true代表开启基于服务发现的路由规则。
          enabled: false
          #配置之后访问时service-id无需大写
          lower-case-service-id: true
#      routes:  #路由
#        # 路由标识(id:标识,具有唯一性)
#        - id: user-consumer-api
#          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
#          uri: lb://consumer
#          #优先级,越小越优先
#          #order: 999
#          #路由条件(predicates:断言)
#          predicates:
#            # 路径匹配,
##             localhost:8082/user/aa  不能进入
##            localhost:8082/cum/user/aa 能
#            - Path=/cum/**
#          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://provider
#          #优先级,越小越优先
#          #order: 999
#          #路由条件(predicates:断言)
#          predicates:
#            # 路径匹配,
#            - Path=/api/prv/**
#          filters:
#            #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
#            #前缀过滤,请求地址:http://localhost:8084/usr/hello
#            #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
#            #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
#            - StripPrefix=2
logging:
  level:
    #开启spring-Cloud-gateway的日志级别为debug,方便debug调试
    org.springframework.cloud.gateway: trace
    org.springframework.http.server.reactive: debug
    org.springframework.web.reactive: debug
    reactor.ipc.netty: debug
#springboot监控Actuator,暴露所有端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
#自定义配置
gateway:
  nacos:
#    #配置中心地址
    server-addr: ${spring.cloud.nacos.discovery.server-addr}
#    #指定环境
    # namespace: xxx-xx-xx-xx
#    #读取配置管理中的Data Id
    data-id: dynamic-routing.json
    group: DEFAULT_GROUP

④、启动类GatewayApplication

增加注解

@EnableDiscoveryClient

运行:

SpringCloud之网关Gateway_第5张图片

3、路由方式1

yml文件:

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

 得到三种服务方式:

SpringCloud之网关Gateway_第6张图片

SpringCloud之网关Gateway_第7张图片

 4、路由定义

路径分离:

yml文件:

      routes:  #路由
        # 路由标识(id:标识,具有唯一性)
        - id: user-consumer-api
          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: lb://consumer
          #优先级,越小越优先
          #order: 999
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
#             localhost:8082/user/aa  不能进入
#            localhost:8082/cum/user/aa 能
            - Path=/cum/**
          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://provider
          #优先级,越小越优先
          #order: 999
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
            - Path=/api/prv/**
          filters:
            #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
            #前缀过滤,请求地址:http://localhost:8084/usr/hello
            #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
            #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
            - StripPrefix=2

SpringCloud之网关Gateway_第8张图片

 5、动态路由设置

①、自定义配置

yml文件:

#自定义配置
gateway:
  nacos:
#    #配置中心地址
    server-addr: ${spring.cloud.nacos.discovery.server-addr}
#    #指定环境
    # namespace: xxx-xx-xx-xx
#    #读取配置管理中的Data Id
    data-id: dynamic-routing.json
    group: DEFAULT_GROUP

在配置列表中新增配置dynamic-routing.json:

SpringCloud之网关Gateway_第9张图片

配置内容:

{
  "refreshGatewayRoute": true,
  "routeList": [
    {
      "id": "consumer-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/cum/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://consumer",
      "order": 0
    },
    {
      "id": "provider-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/api/pvr/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "2"
          }
        }
      ],
      "uri": "lb://provider",
      "order": 0
    }
  ]
}

②、解析

Ⅰ、yml注入到实体类

        FilterEntity:

package com.gateway.code.pojo;

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

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

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

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

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

}

GatewayNacosProperties:

package com.gateway.code.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.gateway.code.pojo;

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

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

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

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

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

}

RouteEntity:

package com.gateway.code.pojo;

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

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

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

    //路由id
    private String id;

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

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

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

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

}

Ⅱ、读取

DynamicRoutingConfig:

package com.gateway.code;

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.gateway.code.pojo.FilterEntity;
import com.gateway.code.pojo.GatewayNacosProperties;
import com.gateway.code.pojo.PredicateEntity;
import com.gateway.code.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(json==null){
            return;
        }
//        json是什么
        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);
            }
        });

    }

    /**
     * 1、解析从nacos读取的路由配置信息(json格式)
     * 2、根据配置文件完成写入或者更新
     */
    public void parseJson(String json) {
        log.warn("从Nacos返回的路由配置(JSON格式):" + json);
        boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute");
        if (refreshGatewayRoute) {
            List 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);
        }

    }

    /**
     * 根据自己的路由实体类生成gateway的路由定义对象
     */
    public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {

        RouteDefinition definition = new RouteDefinition();

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

        // Predicates
        List 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 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;
    }

}

SpringCloud之网关Gateway_第10张图片

 

本期内容结束~~~~~

你可能感兴趣的:(spring,cloud,gateway,spring)