Spring Cloud Aibaba 学习目录
1. Spring Cloud Alibaba 微服务介绍(一)
2. Spring Cloud Alibaba 之Nacos 安装(二)
3. Spring Cloud Alibaba 微服务组件 Nacos 注册中心(三)
4. Spring Cloud Alibaba 微服务负载均衡 Ribbon(四)
5. Spring Cloud Alibaba 微服务整合 OpenFeign(五)
6. Spring Cloud Alibaba 微服务组件 Nacos 配置中心(六)
7. Spring Cloud Alibaba 微服务组件 Sentinel 服务保护(七)
8. Spring Cloud Alibaba 分布式事务概念(八)
9. Spring Cloud Alibaba 微服务组件 Seata 分布式事务(九)
10. Spring Cloud Alibaba 服务网关 Gateway(十)
11. Spring Cloud Alibaba 微服务组件 Skywalking 分布式任务链(十一)
语雀文档:Spring Cloud Aibaba 学习 · 语雀
网关简介
Spring Cloud Gateway是Spring Cloud推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。相对Zuul有哪些优势?Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。
为什么要使用网关
大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。
这样的架构,会存在着诸多的问题:
- 每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
- 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一下你打开一个APP,通过抓包发现涉及到了数百个远程调用,这在移动端下会显得非常低效。
- 后期如果需要对微服务进行重构的话,也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务变的越来越复杂,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆分成多个,同时需要客户端配合你进行改造,非常蛋疼。
上面的这些问题可以借助API网关来解决。
所谓的API网关:就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。
添加上API网关之后,系统的架构图变成了如下所示:
什么是Spring Cloud Gateway
网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等。
Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul1.0。相比 Zuul 来说,Spring CloudGateway 提供更优秀的性能,更强大的有功能。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。
Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。
其他的网关组件
在 SpringCloud 微服务体系中,有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关替代Zuul,那就是SpringCloud Gateway网上很多地方都说Zuul是阻塞的,Gateway是非阻塞的,这么说是不严谨的,准确的讲Zuul1.x是阻塞的,而在2.x的版本中,Zuul也是基于Netty,也是非阻塞的,如果一定要说性能,其实这个真没多大差距。
而官方出过一个测试项目,创建了一个benchmark的测试项目:spring-cloud-gateway-bench,其中对比了:
Spring Cloud Gateway 特性
- 基于 Java 8 编码;
- 基于Spring Framework 5,Project Reactor和Spring Boot 2.0构建
- 支持动态路由,能够匹配任何请求属性上的路由。
- 支持内置 到 Spring Handler 映射 中的 路 由 匹配;
- 支持基于 HTTP 请求的路由匹配( Path、 Method、 Header、 Host 等);
- 集成了Hystrix断路器
- 过滤器作用于匹配的路 由;
- 过滤器可以修改 HTTP 请求和HTTP 响应( 增加/ 修改 头部、 增加/ 修改 请求 参数、 改写 请求 路径 等);
- 支持 Spring Cloud DiscoveryClient 配置路由,与服务发现与注册配合使用。
- 支持限流
- 支持地址重写
- 路由(route)
核心概念
路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。
- 断言(predicates)
Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。
- 过滤器(Filter)
SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。
工作原理
执行流程大体如下:
- Gateway Client向Gateway Server发送请求
- 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
- 然后网关的上下文会传递到DispatcherHandler,它负责将请求分发给RoutePredicateHandlerMapping
- RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
- 如果过断言成功,由FilteringWebHandler创建过滤器链并调用
- 请求会一次经过PreFilter--微服务--PostFilter的方法,最终返回响应
Spring Cloud Gateway 快速开始
环境搭建
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
yml 配置文件
server:
port: 9999
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
uri: http://localhost:9100 # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
predicates:
# 断言,路径相匹配的进行路由 (谓词)
- Path=/order/** # 路径匹配
filters:
- StripPrefix=1 # 转发去掉第一层路径
geteway-order 客户端服务配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
<scope>provided</scope>
</dependency>
</dependencies>
OrderController
@RestController
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@GetMapping("getOrder")
public OrderDTO getOrder(){
return orderService.getOrder();
}
}
测试访问
通过网关访问 gateway-service 服务
http://127.0.0.1:9999/order/getOrder
集成Nacos
引入依赖
<!--alibaba-nacos-discovery(阿里注册中心discovery)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml 配置
server:
port: 9999
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
# uri: http://localhost:9100 # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
uri: lb://gateway‐order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
- Path=/order/** # 路径匹配
filters:
- StripPrefix=1 # 转发去掉第一层路径
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
测试访问
http://127.0.0.1:9999/order/getOrder
gateway-order 服务同上也要配置 nacos 注册中心
动态路由配置
yml 配置
server:
port: 9999
spring:
application:
name: gateway-service
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
cloud:
## gateway配置
gateway:
discovery:
locator:
# 开启自动代理 (自动装载从配置中心serviceId)
enabled: true
# 服务id为true --> 这样小写服务就可访问了
lower-case-service-id: true
测试访问
http://127.0.0.1:9999/gateway-order/getOrder
说明:gateway-order 服务名
路由断言工厂
Spring Cloud Gateway Predicate Factory
Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。
- Spring Cloud Gateway 包含许多内置的 Route Predicate Factories。
- 所有这些断言都匹配 HTTP 请求的不同属性。
- 多个 Route Predicate Factories 可以通过逻辑与(and)结合起来一起使用。
路由断言工厂 RoutePredicateFactory 包含的主要实现类如图所示,包括 Datetime、 请求的远端地址、 路由权重、 请求头、 Host 地址、 请求方法、 请求路径和请求参数等类型的路由断言。
Path 路径匹配
在getaway配置文件中添加 Path
规则:
以 order 为条件匹配,如果请求路径符合规则,则放行
spring:
application:
name: gateway-service
cloud:
## nacos注册中心
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: user #路由的ID 保证唯一
uri: lb://gateway‐order # 目标服务地址
predicates:
# 断言,路径相匹配的进行路由 (谓词)
- Path=/order/** # 路径匹配
测试结果
http://127.0.0.1:9999/order/getOrderDetail
输出结果
时间断言
spring:
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
uri: lb://gateway-order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
# - Path=/order/** # 路径匹配
- After=2021-12-27T16:20:00+08:00[Asia/Shanghai] # 在指定时间之后的请求会匹配该路由
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
如果当前时间小于配置时间 - After=2021-12-27T16:20:00+08:00[Asia/Shanghai] 请求会报404,如图
- [Asia/Shanghai] 代表地区
请求URL http://127.0.0.1:9999/order/getOrderDetail
Query 断言
spring:
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
uri: lb://gateway-order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
# - Path=/order/** # 路径匹配
# - After=2021-12-27T17:20:00+08:00[Asia/Shanghai] # 在指定时间之后的请求会匹配该路由
- Query=token,^[0-9]*$ # 可以指定参数和值 ?name=数字 才允许访问
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
Query=token
:比如,http://127.0.0.1:9999/order/getOrderDetail?token=123
Method断言
当我们请求方法是Post 则报错
spring:
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
uri: lb://gateway-order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
# - Path=/order/** # 路径匹配
- Method=GET # 必须是Get才可以访问
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
- 必须是 Get 请求方式
RemoteAddr
spring:
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
uri: lb://gateway-order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
# - Path=/order/** # 路径匹配
- RemoteAddr=192.168.10.1/0 # 匹配远程地址请求是 RemoteAddr 的请求,0表示子网掩码
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
RemoteAddr=192.168.10.1/0
:比如:http://127.0.0.1:9000/member/getUserInfo?userId=1
Header
- Header=X-Request-Id, \d+
在请求头必须要添加X-Request-Id 字段,value可以正则表达式
自定义路由断言
配置自定义断言工厂
自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
- 必须spring组件 bean
- 类必须加上 RoutePredicateFactory 作为结尾
- 必须继承 AbstractRoutePredicateFactory
- 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
- 需要结合 shortcutFieldOrder 进行绑定
- 通过apply进行逻辑判断 true就是匹配成功 false匹配失败
package com.zlp.gateway.service.config;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
/**
* 自定义路由断言工厂 <br/>
* <p>命名需要以RoutePredicateFactory结尾 比aRoutePredicateFactory 那么yml在使用时a就是断言工厂的名字</p>
* @date: 2021/4/16 14:07
*/
@Slf4j
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.User> {
public CheckAuthRoutePredicateFactory() {
super(User.class);
}
/**
* 自定义配置类
* @param user
* @date: 2021/4/16 14:04
* @return: java.util.function.Predicate<org.springframework.web.server.ServerWebExchange>
*/
@Override
public Predicate<ServerWebExchange> apply(User user) {
return exchange -> {
log.info("进入apply:name={}" ,user.getName());
if (user.getName().equals("kitty")) {
return true;
}
return false;
};
}
public static class User{
@Setter
@Getter
private String name;
}
}
yml 配置
spring:
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
uri: lb://gateway-order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
- Path=/order/** # 路径匹配
- name: CheckAuth # 自定义断言工厂
args:
name: kitty2
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
测试访问
当我们在配置信息中的name和apply方法中的name条件为true就是匹配成功 false匹配失败
return exchange -> {
log.info("进入apply:name={}" ,user.getName());
if (user.getName().equals("kitty")) {
return true;
}
return false;
};
网关过滤器 GatewayFilter
网关过滤器用于拦截并链式处理 Web 请求,可以实现横切与应用无关的需求,比如:安全、访问超时的设置等。修改传入的 HTTP 请求或传出 HTTP 响应。Spring Cloud Gateway 包含许多内置的网关过滤器工厂一共有 22 个,包括头部过滤器、 路径类过滤器、Hystrix 过滤器和重写请求 URL 的过滤器, 还有参数和状态码等其他类型的过滤器。根据过滤器工厂的用途来划分,可以分为以下几种:Header、Parameter、Path、Body、Status、Session、Redirect、Retry、RateLimiter 和 Hystrix。
具体详细内容参考我之前写的文章 Spring Cloud : Gateway 网关过滤器 GatewayFilter(四)
Gateway 整合 Sentinel 流控降级
网关作为内部系统外的一层屏障, 对内起到一定的保护作用, 限流便是其中之一. 网关层的限流可以简单地针对不同路由进行限流,也可针对业务的接口进行限流,或者根据接口的特征分组限流。
https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
添加配置
spring:
cloud:
gateway:
routes:
- id: order #路由的ID 保证唯一
# uri: http://localhost:9100 # 目标服务地址:uri以lb://开头(lb代表从注册中心获取服务),后面就是需要转发到的服务名称
uri: lb://gateway-order
predicates:
# 断言,路径相匹配的进行路由 (谓词)
- Path=/order/** # 路径匹配
filters:
- StripPrefix=1 # 转发去掉第一层路径
# nacos config
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
## sentinel 配置
sentinel:
transport:
dashboard: 127.0.0.1:8058
/**
* sentinel 配置信息
* @date: 2021/12/27 17:07
*/
@Configuration
public class GatewayConfig {
@PostConstruct
public void init() {
//初始化网关限流规则
initGatewayRules();
//自定义限流异常处理器
initBlockRequestHandler();
}
/**
* 初始化限流规则
* @date: 2021/12/27 18:02
*/
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
/**
* resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
* count:限流阈值
* intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
*/
rules.add(new GatewayFlowRule("order_route")
.setCount(1)
.setIntervalSec(1));
// 加载网关规则
GatewayRuleManager.loadRules(rules);
}
/**
* 自定义限流异常处理器
*
* @date: 2021/12/27 18:08
*/
private void initBlockRequestHandler() {
HashMap<String, String> result = new HashMap<>(2);
result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
result.put("msg", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(result));
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
测试访问
sentinel 控制台地址:http://localhost:8858/#/dashboard
自动加载限流配置规则
http://127.0.0.1:9999/order/getOrder
配置限流规则是:每秒只能访问一下,超过则限流
项目地址https://gitee.com/gaibianzlp/springcould-alibaba-example.githttps://gitee.com/gaibianzlp/springcould-alibaba-example.githttps://gitee.com/gaibianzlp/springcould-alibaba-example.git点关注不迷路,觉得对你有帮助请给一个赞或者长按一键三连,谢谢!