开发指南须知

本次实践主要在版本:2.2.0.BUILD-SNAPSHOT上进行构建,这个项目提供了构建在Spring生态系统之上API网关。

Spring Cloud Gateway的介绍

Spring Cloud Gateway目标是用一个简单、有效的方式路由到API,并且提供横切的一些关注点,例如:安全、监控、系统性能和弹性等。

API网关介绍

API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:

  1. 客户端会多次请求不同的微服务,增加了客户端的复杂性。
  2. 存在跨域请求,在一定场景下处理相对复杂。
  3. 认证复杂,每个服务都需要独立认证。
  4. 难以重构,随着项目的迭代,可能需要重新划分微服务。
    • 例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
  5. 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。

以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性。

SpringCloud Gateway技术基础

它是基于spring官方Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。

如何引用Spring Cloud Gateway

maven坐标为:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

有关使用当前Spring Cloud构建系统的详细信息,如果你引入了starter,但不想开启gateway,可以设置:

spring.cloud.gateway.enabled=false。

注意

  1. Spring Cloud Gateway 构建在 Spring Boot 2.0, Spring WebFlux, and Project Reactor之上,因此,许多熟悉的同步库(例如:Spring Data 、Spring Security)或模式不适用于Spring Cloud Gateway。

  2. Spring Cloud Gateway需要SpringBoot和SpringWebFlux提供的netty运行时,它不再运行于传统的Servlet容器或一个WAR包。

Route

网关基本构件块,也是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言predicates和一组filters组成。如果聚合断言为真,则匹配路由,说明请求的URL和配置。

Predicate

Java8中的断言函数。Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于http request中的任何信息,比如请求头和参数等。

Java 8 Function Predicate. 输入类型是SpringFramework ServerWebExchange. 这允许开发人员匹配来自HTTP请求的任何内容,例如头或参数。

Filter

使用特定工厂构造的 Spring Framework GatewayFilter 实例。在这里,可以在发送downstream 请求之前或之后修改requests和responses。

一个标准的Spring webFilter。Spring cloud gateway中的filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。

理解:
  1. 断言(Predicate):请求匹配;
  2. 过滤器(Filter):对请求或者返回进行过滤增强。

网关提供API全托管服务,丰富的API管理功能,辅助企业管理大规模的API,以降低管理成本和安全风险,包括协议适配、协议转发、安全策略、防刷、流量、监控日志等功能。一般来说网关对外暴露的URL或者接口信息,我们统称为路由信息。

Spring Cloud Gateway的工作原理

GatewayClient请求 Spring Cloud Gateway,如果Gateway Handler Mapping 确定请求与路由匹配,该请求被发送到Gateway Web Handler。此Handler运行时发送请求到具体的请求,其中通过过滤器链。

过滤器链被虚线分隔的原因是过滤器可以在发送代理请求之前或之后执行逻辑。执行所有“预”过滤逻辑,然后发出代理请求。在发出代理请求后,将执行“post”过滤器逻辑。URIs 在路由中没有设置端口,则按照HTTP和HTTPS默认端口设置为80和443。

Spring cloud Gateway发出请求。然后再由Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway web handler。Handler再通过指定的过滤器链将请求发送到我们实际的服务执行业务逻辑,然后返回。

Spring Cloud Gateway-路由断言工厂

Spring Cloud Gateway匹配路由作为SpringWebFlux HandlerMapping基础设施的一部分。Spring Cloud Gateway包含许多内置的路由断言工厂,这些断言匹配不同属性的HTTP请求,可以组合多个路由断言工厂,并通过逻辑组合。

Before Route Predicate Factory

Before Route Predicate Factory 有一个时间参数,此断言匹配发生在该时间参数之前的请求。

这个路由匹配发生在 Jan 20, 2017 17:42 Mountain Time (Denver)之前的请求。

After Route Predicate Factory

After Route Predicate Factory有一个时间参数,此断言匹配发生在该时间参数之后的请求。

这个路由匹配发生在 Jan 20, 2017 17:42 Mountain Time (Denver)之后的请求。

Between Route Predicate Factory

Between Route Predicate Factory 有两个时间参数。此断言匹配发生在这两个时间之间的请求。

这个路由匹配发生在 Jan 20, 2017 17:42 Mountain Time (Denver)与Jan 21, 2017 17:42 Mountain Time (Denver)之间的请求。可应用于维护窗口。

Cookie Route Predicate Factory

Cookie Route Predicate Factory 有两个参数,包括cookie名称和正则表达式。此断言匹配cookies包括给定的名称和符合正则表达式的值。

此路由匹配cookie名称为chocolate ,cookie值为ch.p正则表达式,匹配chap,chbp等。

Header Route Predicate Factory

Header Route Predicate Factory 包括两个参数包括头名称和值的正则表达式。此断言匹配一个头信息包括该名称和符合该正则表达式值得请求。

此路由匹配头名称为X-Request-Id且值匹配\d+ 表达式(包含一个或多个数字)。

Host Route Predicate Factory

Host Route Predicate Factory包括一个参数host 名称模式列表。此模式是一种 Ant 风格模式,以 "." 作为分隔符。此断言匹配Host头。另外Host头来源有两种:第一种是请求地址;第二种是自己在http的header头中放入Host变量值。

URI 模板变量也支持这种格式 {sub}.myhost.org。

此路由匹配头文件中的Host值www.somehost.org,beta.somehost.org 或 www.anotherhost.org。此示例均为默认端口80,如果为其他端口,需要在表达式中定义。

此断言提取URI模板变量(如上面示例中定义的子变量)作为名称和值的映射,并将其放置在ServerWebExchange.getAttributes()中,其键在ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE属性中定义。

在过滤其中加入如下代码示例:
Map uri_template_variables_attribute=exchange.getAttribute(ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
   // Map variables=ServerWebExchangeUtils.getUriTemplateVariables(exchange);
     uri_template_variables_attribute.forEach((K,V)->{
            System.out.println("K:"+K+"--V:"+V);
      });
  • 访问Host为:www.myhost.org
  • 输出结果:  K:sub--V:www

Filter规律器

Global Filter接口与GatewayFilter具有相同的签名。这些是有条件地应用于所有路由的特殊过滤器。

Combined Global Filter and GatewayFilter Ordering
  • 当请求进入(并匹配路由)时,Filtering Web Handler会将GlobalFilter的所有实例和GatewayFilter的所有路由特定实例添加到过滤器链。

  • 此组合过滤器链通过org.springframework.core.Ordered接口进行排序。可以通过实现getOrder()方法或者使用@Order注解。

Spring Cloud Gateway区分了过滤器逻辑执行的“请求”和“响应”阶段,具有最高优先级的过滤器将是“请求”阶段的第一个和“响应”阶段的最后一个 。

ANT通配符有三种:

而上面多数的匹配规则运算符号都是有AntPathMatcher对象进行实现的

private AntPathMatcher antPathMatcher = new AntPathMatcher();
  • ?:匹配任意一个单个字符
    • :匹配0或者任意数量的字符
  • ** :匹配0和或者更多目录的字符
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered { private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
//商城api接口,校验用户必须登录
if(antPathMatcher.match("/api/**/auth/**", path)) {
List<String> tokenList = request.getHeaders().get("token");
if(null == tokenList) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
} else {
// Boolean isCheck = JwtUtils.checkToken(tokenList.get(0));
// if(!isCheck) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
// }
}
}
//内部服务接口,不允许外部访问
if(antPathMatcher.match("/**/inner/**", path)) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
}
return chain.filter(exchange);
} @Override
public int getOrder() {
return 0;
} private Mono<Void> out(ServerHttpResponse response) {
JsonObject message = new JsonObject();
message.addProperty("success", false);
message.addProperty("code", 28004);
message.addProperty("data", "鉴权失败");
byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
//response.setStatusCode(HttpStatus.UNAUTHORIZED);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
}

自定义异常处理

服务网关调用服务时可能会有一些异常或服务不可用,它返回错误信息不友好,需要我们覆盖处理。

ErrorHandlerConfig
@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfig { private final ServerProperties serverProperties; private final ApplicationContext applicationContext; private final ResourceProperties resourceProperties; private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public ErrorHandlerConfig(ServerProperties serverProperties,
ResourceProperties resourceProperties,
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
this.serverProperties = serverProperties;
this.applicationContext = applicationContext;
this.resourceProperties = resourceProperties;
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
} @Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext);
exceptionHandler.setViewResolvers(this.viewResolvers);
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}

参考资料

【SpringCloud技术专题】「Gateway网关系列」(3)微服务网关服务的Gateway全流程开发实践指南(2.2.X)的更多相关文章

  1. 【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南

    Minio的元数据 数据存储 MinIO对象存储系统没有元数据数据库,所有的操作都是对象级别的粒度的,这种做法的优势是: 个别对象的失效,不会溢出为更大级别的系统失效. 便于实现"强一致性& ...

  2. 深入Java微服务之网关系列1:什么是网关

    ​ 前言 近来,在想着重构一个新的产品.准备采用微服务的技术解决方案,来搭建基础设施框架.网关,是一个必不可少的组件.那么,网关到底是什么? 其又有什么特点或者特性,成为微服务必不可少的组件呢?今天, ...

  3. 【SpringCloud技术专题】「原生态Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)

    前提介绍 Feign是SpringCloud中服务消费端的调用框架,通常与ribbon,hystrix等组合使用. 由于遗留原因,某些项目中,整个系统并不是SpringCloud项目,甚至不是Spri ...

  4. 【SpringCloud技术专题】「Eureka源码分析」从源码层面让你认识Eureka工作流程和运作机制(上)

    前言介绍 了解到了SpringCloud,大家都应该知道注册中心,而对于我们从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进 ...

  5. 【Java技术专题】「性能优化系列」针对Java对象压缩及序列化技术的探索之路

    序列化和反序列化 序列化就是指把对象转换为字节码: 对象传递和保存时,保证对象的完整性和可传递性.把对象转换为有字节码,以便在网络上传输或保存在本地文件中: 反序列化就是指把字节码恢复为对象: 根据字 ...

  6. 【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

    零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does n ...

  7. 🏆(不要错过!)【CI/CD技术专题】「Jenkins实战系列」(3)Jenkinsfile+DockerFile实现自动部署

    每日一句 没有人会因学问而成为智者.学问或许能由勤奋得来,而机智与智慧却有懒于天赋. 前提概要 Jenkins下用DockerFile自动部署Java项目,项目的部署放心推向容器化时代机制. 本节需要 ...

  8. 🏆【CI/CD技术专题】「Docker实战系列」(1)本地进行生成镜像以及标签Tag推送到DockerHub

    背景介绍 Docker镜像构建成功后,只要有docker环境就可以使用,但必须将镜像推送到Docker Hub上去.创建的镜像最好要符合Docker Hub的tag要求,因为在Docker Hub注册 ...

  9. ☕【难点攻克技术系列】「海量数据计算系列」如何使用BitMap在海量数据中对相应的进行去重、查找和排序

    BitMap(位图)的介绍 BitMap从字面的意思,很多人认为是位图,其实准确的来说,翻译成基于位的映射,其中数据库中有一种索引就叫做位图索引. 在具有性能优化的数据结构中,大家使用最多的就是has ...

随机推荐

  1. UVA12412 师兄帮帮忙 A Typical Homework (a.k.a Shi Xiong Bang Bang Mang) 题解

    Content 自己去看题面去. Solution 算不上很繁琐的一道大模拟. 首先,既然是输出 \(0\) 才退出,那么在此之前程序应当会执行菜单 \(\Rightarrow\) 子操作 \(\Ri ...

  2. CF169A Chores 题解

    Content 两兄弟要分担 \(n\) 件家务,第 \(i\) 件家务有一个复杂度 \(h_i\).兄弟俩以一个数 \(x\) 为界.所有满足复杂度 \(>x\) 的家务都给哥哥做,其余的给弟 ...

  3. LuoguB2029 大象喝水 题解

    Update \(\texttt{2021.12.4}\) 修改了原先的错误代码,给各位造成影响,在此表示很抱歉. Content 大象要喝 \(20\) 升水,但现在只有一个深 \(h\) 厘米,半 ...

  4. textarea标签换行符以br存入数据库 ,br转 textArea换行符

    textArea换行符转 <br/> textarea标签回车符是/n,在html里识别回车是<br/>,在存入数据库之前要进行转换成<br/>,在取出展示在htm ...

  5. Log4j2 消停了,Logback 开始塌房了?

    今天一早,还没起床,拿起手机赫然看到一个头条信息,标题着实让我心理咯噔了一下! 马上起床,直奔官网,看看到底什么问题?塌的有多厉害? 既然是1.2.9版本以下问题,那就直接找到1.2.9版本修复了些啥 ...

  6. Spring Boot程序插入时间和MySQL数据库显示时间不一样(设置数据库时区)

    首先查看数据库时区 show variables like "%time_zone%"; # 设置全局时区 mysql> set global time_zone = '+8 ...

  7. 年底了是时候学新技术了「GitHub 热点速览 v.21.52」

    作者:HelloGitHub-小鱼干 年底了,又有新技术冒出来需要你来 Pick 了,第一个先要被 Pick 的是即将到来的元旦英文版:Happy New Year,再来的话就是这周非常火的新一代爬虫 ...

  8. 【LeetCode】237. Delete Node in a Linked List 解题报告 (Java&Python&C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 设置当前节点的值为下一个 日期 [LeetCode] ...

  9. 【LeetCode】800. Similar RGB Color 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 遍历 日期 题目地址:https://leetcode ...

  10. 【LeetCode】862. Shortest Subarray with Sum at Least K 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 队列 日期 题目地址:https://leetcod ...