SpringCloud系列篇:核心组件之网关组件

Welcome Huihui's Code World ! !

接下来看看由辉辉所写的关于SpringCloud的相关操作吧

目录

Welcome Huihui's Code World ! !

一.网关组件是什么 

二. 网关组件的详解

生活例子

例子剖析

三.代码演示【网关的三种使用方式】 

0.配置

1.根据服务名访问

2.根据路径访问

3.动态路由

路由

断言

过滤器

配置类

路由处理类

注意点:


一.网关组件是什么 

      网关(Gateway)组件是指用于构建具有统一入口的微服务架构中的一个组件,它可以实现动态路由、负载均衡、熔断、安全控制等功能。在微服务架构中,每个服务都有自己的入口,客户端需要知道每个服务的地址和端口号才能访问它们。而网关组件可以为所有服务提供一个统一的入口,客户端只需要知道网关的地址和端口号,就可以通过网关访问所有的服务。

        常见的网关组件包括Zuul、Spring Cloud Gateway、Kong、Nginx等。这些网关组件都具有一定的功能和特点,可以根据具体需求选择合适的网关组件。

       Spring Cloud Gateway 

SpringCloud系列篇:核心组件之网关组件_第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系列篇:核心组件之网关组件_第2张图片

现在,你想要给小区外的朋友打电话

SpringCloud系列篇:核心组件之网关组件_第3张图片

但是你不知道他们具体的电话号码,只知道他们的姓名。这时候,你需要通过小区的门禁系统(网关)来连接到外部电话网络,然后通过输入朋友的姓名(数据)来查找他们的电话号码(其他网络)并打通电话。


例子剖析

在这个例子中,小区的门禁系统就是一个网关,它连接了小区内部的电话系统和外部的电话网络,帮助你实现了与外部电话网络的通信。类似地,在计算机网络中,网关起到了连接不同网络或协议的桥梁作用,使得数据能够在它们之间进行传输和转换。

三.代码演示【网关的三种使用方式】 

0.配置

在进行下面三种方式的讲解之前,咱先得将文件都给配置好


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


org.springframework.cloud
spring-cloud-starter-gateway

然后需要新建一个项目,我这里建的是一个springboot的项目

SpringCloud系列篇:核心组件之网关组件_第4张图片

1.根据服务名访问

这里只需要配置好就行了

      discovery:
        #根据服务名
        locator:
          #true代表开启基于服务发现的路由规则
          enabled: true
          #配置之后访问时service-id无需大写
          lower-case-service-id: true

然后就可以按照nacos中的服务器名中进行访问了

SpringCloud系列篇:核心组件之网关组件_第5张图片

2.根据路径访问

其中我是没有使用负载均衡,如果使用了负载均衡,那么在那个uri那里写的就是负载均衡的格式

单体

routes:
        - id: user-consumer-api
          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: http://localhost:8081
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
            - Path=/aa/**
          filters:
            - StripPrefix=1

负载均衡

routes:
        - id: user-consumer-api
          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: lb//consumer
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
            - Path=/aa/**
          filters:
            - StripPrefix=1

3.动态路由

这里就是自己定义了一个规则,然后需要编写路由,断言,过滤器,以及读取配置的文件的配置类,还有关于路由操作的处理类

#自定义配置
gateway:
  nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    #     namespace: xxx-xx-xx-xx
    data-id: dynamic-routing.json
    group: DEFAULT_GROUP

路由

package gateway.demo.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;

}

断言

package gateway.demo.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<>();

}

过滤器

package gateway.demo.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<>();

}

配置类

package gateway.demo.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;

}

路由处理类

package gateway.demo;

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 gateway.demo.pojo.FilterEntity;
import gateway.demo.pojo.GatewayNacosProperties;
import gateway.demo.pojo.PredicateEntity;
import gateway.demo.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;

    @Autowired
    private ObjectMapper mapper;

    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);
        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 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 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系列篇:核心组件之网关组件_第6张图片还有就是这个文件中如果你使用的是负载均衡,那么uri也是需要写负载均衡的,如果不是的话,那就写的需要访问服务的路径【我的这个是单体的,不是负载均衡的】

{
  "refreshGatewayRoute": true,
  "routeList": [
    {
      "id": "consumer-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/con/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "http://localhost:8081",
      "order": 0
    },
    {
      "id": "provider-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/pro/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "http://localhost:8082",
      "order": 0
    }
  ]
}

当我们去更改nacos中的配置时,这个也是会实时加载的,这个都是写在那个路由处理类中

SpringCloud系列篇:核心组件之网关组件_第7张图片

好啦,今天的分享就到这了,希望能够帮到你呢!   

你可能感兴趣的:(Spring,Cloud,spring,cloud,spring,boot,后端)