目录
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可以对请求和响应进行处理。
- "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.0modelVersion>
- <groupId>com.jmhgroupId>
- <artifactId>nacos_gatewayartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <name>nacos_gatewayname>
- <description>Demo project for Spring Bootdescription>
-
-
- <properties>
- <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
- <maven.compiler.source>1.7maven.compiler.source>
- <maven.compiler.target>1.7maven.compiler.target>
- <spring-boot.version>2.4.1spring-boot.version>
- <spring-cloud.version>2020.0.0spring-cloud.version>
- <spring-cloud-alibaba.version>2021.1spring-cloud-alibaba.version>
- properties>
-
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-testartifactId>
- dependency>
-
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
- dependency>
-
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-openfeignartifactId>
- dependency>
-
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-loadbalancerartifactId>
- dependency>
-
- <dependency>
- <groupId>ma.glasnost.orikagroupId>
- <artifactId>orika-coreartifactId>
- <version>1.4.6version>
- dependency>
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- dependency>
-
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-bootstrapartifactId>
- dependency>
-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webfluxartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-gatewayartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-actuatorartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
- <version>2.2.9.RELEASEversion>
- dependency>
- <dependency>
- <groupId>io.projectreactorgroupId>
- <artifactId>reactor-testartifactId>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>fastjsonartifactId>
- <version>1.2.35version>
- dependency>
-
- dependencies>
-
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-dependenciesartifactId>
- <version>${spring-boot.version}version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-dependenciesartifactId>
- <version>${spring-cloud.version}version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-alibaba-dependenciesartifactId>
- <version>${spring-cloud-alibaba.version}version>
- <type>pomtype>
- <scope>importscope>
- dependency>
- dependencies>
- dependencyManagement>
-
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- plugin>
- plugins>
- build>
-
- project>
- server:
- port: 8084
- spring:
- application:
- name: nacos-gateway
- cloud:
- nacos:
- discovery:
- server-addr: 127.0.0.1:8848
- 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);
- }
-
- }
- 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
-
-
-
-
- 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.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外部配置文件数据
- {
- "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模块
- 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
args = new LinkedHashMap<>(); -
- }
- 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;
-
- }
- 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
args = new LinkedHashMap<>(); -
- }
- 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
predicates = new ArrayList<>(); -
- //路由过滤器集合
- private List
filters = new ArrayList<>(); -
- //路由转发的目标uri
- private String uri;
-
- //路由执行的顺序
- private int order = 0;
-
- }
- 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
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;
- }
-
- }