Spring Cloud Gateway网关
(一)SpringCloudeGateway基本使用Spring Cloud Gateway是Spring官方基于Spring5.0、SpringBoot2.0和Project Reactor等技术开发的网关旨在为微服务框架提供一种简单而有效的统一的API路由管理方式,统一访问接口。Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标是替代Netflix的Zu
·
(一)SpringCloudeGateway基本使用
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.新建一个Gateway的模块,在pom.xml中导入依赖
<?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.gateway</groupId> <artifactId>code</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gateway</name> <description>gateway</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-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</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>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-loadbalancer</artifactI> </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.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.1</version> <configuration> <mainClass>com.gateway.code.GatewayApplication</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
2.编写application.yml配置
server: #此处的8085端口号,就好像以前外置的tomcat的8080,让我们通过浏览器进行访问 #但此服务只是做了一个路由,它会将请求路由到其它微服务(一般是消费者)进行处理 port: 8085 spring: application: #微服务名 name: gateway cloud: nacos: discovery: #指定nacos注册中心的地址 server-addr: 127.0.0.1:8848 gateway: discovery: locator: #localhost:8085/provider/user/aa #localhost:8085/consumer/user/aa #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false #为true代表开启基于服务发现的路由规则。 enabled: true #配置之后访问时service-id无需大写 lower-case-service-id: true # routes: # # 路由标识(id:标识,具有唯一性) # - id: user-consumer-api # #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死 # uri: lb://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://provider # #优先级,越小越优先 # #order: 999 # #路由条件(predicates:断言) # predicates: # # 路径匹配, # - Path=/bb/cc/** # 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: dynamic-routing.json group: DEFAULT_GROUP
3.使用服务名访问效果如下,访问之前先启动生成者和消费者的启动类
注:根据服务名访问
4.使用路由访问
server: #此处的8085端口号,就好像以前外置的tomcat的8080,让我们通过浏览器进行访问 #但此服务只是做了一个路由,它会将请求路由到其它微服务(一般是消费者)进行处理 port: 8085 spring: application: #微服务名 name: gateway cloud: nacos: discovery: #指定nacos注册中心的地址 server-addr: 127.0.0.1:8848 gateway: discovery: locator: #localhost:8085/provider/user/aa #localhost:8085/consumer/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:8085/user/aa 不能进 #localhost:8085/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: # 路径匹配, #localhost:8085/api/prv/user/aa - 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: dynamic-routing.json group: DEFAULT_GROUP
运行效果如下:
(二)动态路由设置
gateway :nacos :server-addr : $ { spring.cloud.nacos.discovery.server-addr }# namespace: xxx-xx-xx-xxdata-id : dynamic-routing.jsongroup : DEFAULT_GROUP配合读取类使用
1.在Nacos里面新建一隔dynamic-routing.json配置
注:点新建+号哦,我这边演示的是我已经新建好的配置
注:配置内容如下(直接复制即可)
{
"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
}
]
}
2.在gateway模块中的pom.xml配置中导入fastjson的依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.35</version> </dependency>
3.导入pojo实体类
作用:用来做yml注入
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;
}
注:下面三个实体类是用来将json格式转换成java类
(实体类中的属性对应配置内容中的json数据)
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<PredicateEntity> predicates = new ArrayList<>();
//路由过滤器集合
private List<FilterEntity> filters = new ArrayList<>();
//路由转发的目标uri
private String uri;
//路由执行的顺序
private int order = 0;
}
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<String, String> args = new LinkedHashMap<>();
}
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<String, String> args = new LinkedHashMap<>();
}
2.此实现了Spring Cloud Gateway + nacos 的动态路由
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;
}
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);
}
}
/**
* 根据自己的路由实体类生成gateway的路由定义对象
*/
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;
}
}
运行效果如下:
更多推荐
已为社区贡献1条内容
所有评论(0)