基于SpringCloudGateway实现微服务网关
Posted Java鱼仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于SpringCloudGateway实现微服务网关相关的知识,希望对你有一定的参考价值。
(一)什么是微服务网关
后端写完所有的微服务之后,最终是要交给前端去调用。我们都知道每个微服务都有各自的端口号,如果前端直接通过IP加端口的方式去调用微服务会很麻烦。如果想对请求增加限制也会变得十分困难。这个时候微服务网关就出现了。
微服务网关又成为API网关,它是系统对外的唯一入口。APl网关封装了系统内部架构,为每个客户端提供一个定制的APl。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
听起来有些抽象,下面就做一张图给大家展示一下:前端的请求统一由微服务网关进行管理,微服务网关可以调用各个微服务,同时自身还有多种功能。
API网关的职责有身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理,最重要的功能是和外界联系。
常见的API网关实现方式有Zuul,traefix,Spring Cloud Gateway等等。目前主流的微服务网关是Spring Cloud Gateway。
(二)Spring Cloud Gateway网关
Spring Cloud Gateway是Spring 官方基于Spring 5.0,Spring Boot 2.0和Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的API路由管理方式,统一访问接口。Spring Cloud Gateway作为Spring Cloud 生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
2.1 核心概念:
路由(route):路由是网关最基础的部分,路由信息由一个ID,一个目的URL,一组断言工厂和一组Filter组成。
断言(predicates):java8中的断言函数,Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自Http Request中的任何信息。当断言为真时,则匹配路由。
过滤器(filter):对请求和响应进行过滤
下面通过实践来操作微服务网关,本项目有依赖于Nacos与Sentinel,如果不需要这部分的内容可以在配置文件中去除。
2.2 搭建环境:
1、引入工程导入依赖
创建api_gateway_server工程,在工程中的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.javayz</groupId>
<artifactId>nacos-discovery-spring-cloud-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacos-discovery-spring-cloud-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<projec.build.sourceEncoding>UTF-8</projec.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<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-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--springcloudgateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.7.2</version>
</dependency>
</dependencies>
<!--当前项目继承的依赖-->
<dependencyManagement>
<dependencies>
<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>
<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>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<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>
</plugin>
</plugins>
</build>
</project>
2、配置启动类
创建GatewayApplication类
@EnableDiscoveryClient
@SpringBootApplication
public class NacosDiscoverySpringCloudGatewayApplication
public static void main(String[] args)
SpringApplication.run(NacosDiscoverySpringCloudGatewayApplication.class, args);
3、编写配置文件
这里对配置文件的编写使用.yml格式的配置文件,在resource文件夹下创建applicaiion.yml。这里需要注意的是gateway下routes的配置,首先用id自定义网关;接着定义uri,意思是当访问的是9090端口(即访问的是网关时),就自动把ip和端口号变成该uri;最后写断言predicates,这里的Path=/index/**
指得是自动匹配/index/**
的路径。当请求地址为http://localhost:9090/index/1
时,微服务网关会去访问http://nacos-discovery-consumer/index/1
,这就是微服务网关调用微服务的功能。
server:
port: 9090
spring:
application:
name: nacos-discovery-spring-cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #启用discoveryClient网关集成,实现服务发现
routes:
- id: route1
uri: lb://nacos-discovery-consumer #lb://表示负载均衡
predicates: #判断条件
- Path=/test,/index/**
(三) 路由配置详解
路由断言中有许多匹配规则,之前只介绍了Path,其实还有许多匹配规则
predicates:
#匹配一个时间,在这个时间之后发生的请求才能访问(格式是ZonedDateTime)
- After=2021-03-14T11:31:08.377+08:00[Asia/Shanghai]
#匹配一个时间,在这个时间之前发生的请求才能访问(ZonedDateTime)
- Before=2021-03-14T11:31:08.377+08:00[Asia/Shanghai]
#匹配两个时间,在这个时间之间才能访问
- Between=2021-03-14T11:31:08.377+08:00[Asia/Shanghai],2021-03-14T12:31:08.377+08:00[Asia/Shanghai] #路由断言之间
#路由断言Cookie匹配,匹配给定名称(如master)或者正则表达式,如下面配置,请求的cookie中必须有token=master才能访问
- Cookie=token,master
#根据请求头信息匹配,使用方式和cookie类似
- Header=XXX,XXX
#根据host地址匹配
- Host=**.somehost.org
#根据请求方法匹配
- Method=GET
#根据请求路径进行匹配
- Path=***
#根据参数匹配,如果配置为token,则所有的请求必须要带上token=XXX
- Query=token
#根据远程IP匹配
- RemoteAddr=192.168.1.1/24
#权重,第一个参数是组名,第二个参数是权重,两个route下配置不同权重,会根据权重负载访问
- Weight=group1,8
定义请求路径时,我们还可以据微服务名称拉取请求路径,下面是两种不同方法定义uri,实现同样的功能。
uri: http://127.0.0.1:9001 #当访问9090端口时,自动把ip和端口替换为此uri
uri: lb://userservice #根据微服务名称拉取请求路径
总体的配置:
server:
port: 9090
spring:
application:
name: nacos-discovery-spring-cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #启用discoveryClient网关集成,实现服务发现
routes:
- id: route1
uri: lb://nacos-discovery-consumer #lb://表示负载均衡
predicates: #判断条件
- Path=/test,/index/**,/**
nacos:
discovery:
server-addr: 192.168.78.128:8848
username: nacos
password: nacos
sentinel:
eager: true
transport:
dashboard: localhost:8080
3.1 自定义断言配置
自定义断言的实现分为两步,假设我们现在要实现一个自定义的过滤器,规则是必须携带一个token,并且token要等于指定的值。
1、定义配置类:这里包含在配置文件要传入的对象值
@Data
public class TokenConfig
private String token;
2、定义路由断言工厂:首先是命名必须是XXXRoutePredicateFactory,继承 AbstractRoutePredicateFactory
@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig>
public TokenRoutePredicateFactory()
super(TokenConfig.class);
//用来把配置中的值变成一个集合
@Override
public List<String> shortcutFieldOrder()
return Collections.singletonList("token");
//通过jdk1.8的断言接口来返回true或者false,如果是true表示过滤器不拦截
@Override
public Predicate<ServerWebExchange> apply(TokenConfig config)
return exchange ->
//获取request请求参数
MultiValueMap<String,String> valueMap=exchange.getRequest().getQueryParams();
boolean flag=false;
List<String> list=new ArrayList<>();
//将request请求的value保存到集合里
valueMap.forEach((k,v)->
list.addAll(v);
);
//判断有没有和配置文件中相同的值
for (String s:list)
log.info("Token"+s);
if (StringUtils.equalsIgnoreCase(s,config.getToken()))
flag=true;
break;
return flag;
;
最后在断言处写上我们自定义的内容:
predicates: #判断条件
- Path=/test,/index/**,/**
- Token=javayz
只有链接是http://localhost:9090/sentinel/test1?token=javayz
带上token=javayz
的请求才能访问。
3.2 断言不匹配404页面自定义
SpringCloudGateway默认给出的404页面是
这样的页面实在不友好,我们可以通过自定义的方式让他返回一个JSON字符串
新建一个类叫做MyErrorWebExceptionHandler:
public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler
public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext)
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
/**
* 指定响应处理方法为JSON处理
* @param errorAttributes
* @return
*/
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes)
return RouterFunctions.route(RequestPredicates.all(),this::renderErrorResponse);
/**
* 根据code获取对应的HttpStatus
* @param errorAttributes
* @return
*/
@Override
protected int getHttpStatus(Map<String, Object> errorAttributes)
return (int) errorAttributes.get("status");
/**
* 构建异常信息
* @param request
* @param ex
* @return
*/
private String buildMessage(ServerRequest request,Throwable ex)
StringBuilder builder = new StringBuilder("Failed to handle request:");
builder.append(request.methodName());
builder.append(" ");
builder.append(request.uri());
if (ex!=null)
builder.append(ex.getMessage());
return builder.toString();
/**
* 返回的json数据
* @param status
* @param errorMsg
* @param data
* @return
*/
public static Map<String,Object> response(int status,String errorMsg,Object data)
Map<String,Object> map=new HashMap<>();
map.put("code",status);
map.put("message",errorMsg);
map.put("data",data);
return map;
再编写一个配置类,将自定义的异常页面代码注入到Bean容器中:
@Configuration
public class GatewayConfiguration
private final ServerProperties serverProperties;
private final ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ServerProperties serverProperties, ApplicationContext applicationContext, ResourceProperties resourceProperties, ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer)
this.serverProperties = serverProperties;
this.applicationContext = applicationContext;
this.resourceProperties = resourceProperties;
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
@Bean("myErrorWebExceptionHandler")
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes)
MyErrorWebExceptionHandler exceptionHandler=new MyErrorWebExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext
);
exceptionHandler.基于SpringCloudGateway实现微服务网关
物联网架构成长之路(56)-SpringCloudGateway+JWT实现网关鉴权
基于Spring cloud gateway定制的微服务网关