有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准
https://blog.zysicyj.top
SpringCloud技术栈
开发分布式系统可能具有挑战性,复杂性已从应用程序层转移到网络层, 并要求服务之间进行更多的交互。将代码设为“cloud-native”就需要解决12- factor,例如外部配置,服务无状态,日志记录以及连接到备份服务之类的问 题,Spring Cloud项目套件包含使您的应用程序在云中运行所需的许多服务。
SpringCloud技术栈
SpringCloud技术栈非常丰富,这也是SpringCloud为什么在微服务领域中 如此受欢迎的原因之一,技术栈如上图,在服务注册与配置、服务调用、微服 务网关、消息组件、链路追踪、配置中心、安全控制、将极限流等诸多方面技 术栈都比较完善,而且阿里巴巴也出了一套SpringCloud Alibaba版本,主要集 成了Alibaba中主流的技术栈。
##SpringCloud经典技术介绍 微服务项目近几年非常火爆,推出来的相关技术解决方案热度也非常活 跃,但SpringCloud技术栈中也有一部分技术组件在逐步被淘汰或者闭源,但都 有更优秀的技术方案替代。在不久的将来,那些闭源或者将被淘汰的技术有很大概率将不在项目中使用,所以我们学习的时候可以直接学习更优秀的替代技术方案。
Eureka闭源
上面英文大概意思是:Eureka2.0 的开源工作已经停止,依赖于开源库 里面的Eureka2.x 分支构建的项目或者相关代码,风险自负。
Eureka在微服务项目中主要承担服务注册与发现工作,可以替代的技术方案比较多,而且很多方案都比Eureka优秀,比如Consul、Nacos等。
Hystrix停止更新
Hystrix在项目中主要做服务熔断、降级,但官方宣布将不在开发,目前处于维护状态,但官方表示 1.5.18 版本的 Hystrix 已经足够稳定,可以满足Netflix 现有应用的需求。
关于Hystrix可替代的产品也比较多,比如官方推荐的resilience4j,resilience4j是一个轻量级熔断框架,但resilience4j目前在国内使用频率还不高,功能也不够强,我们更推荐使用功能更加强悍的SpringCloudAlibabaSentinel。
Zuul过时
Zuul是一个微服务网关技术,但Zuul1.x使用的是阻塞式的API,不支持长连接,没有提供异步,高并发场景下性能低。SpringCloud官网推出了全新的微服务网关技术SpringCloudGateway,比Zuul性能更强悍、功能更丰富、且支持异步等多种特性。
SpringCloud Config实用性差
SpringCloudConfig主要用于管理项目的配置文件,每次要使用SpringCloudConfig的时候,总得经过一波操作和配置的折腾,才可以使用SpringCloudConfig实现配置管理,而且单独使用无法支持配置实时刷新,在项目中用起来,真比较头疼的。
当前有很多技术可以取代SpringCloudConfig,比如携程的Apollo、SpringCloudAlibabaNacos,功能都比SpringCloudConfig强,并且支持实时刷新配置。
SpringCloud Bus实用性差
SpringCloud Bus是服务消息总线,主要实现通知多个服务执行某个任务,一般和SpringCloud Config一起使用。这个功能其实不太使用,因为很多任务组件基本都具备消息通知功能,比如Nacos、Apollo都能实现所有服务订阅执行相关操作。
##SpringCloud项目场景
微服务技术目前已经在很多国内外大厂中都在广泛使用,那么在项目中该如何使用微服务技术呢?我们以滴滴快车业务未来,来讲解一下微服务技术结合业务应用讲解一下。
打车业务如上图:
1:打车的时候会选择车型,选择车型我们调用过程是:Gateway->Driver(加载司机列表)
2:选择车型后确认打车,相当于要下单了,调用过程是:Gateway->Order(下单)->Driver(司机状态更改)
3:打车结束后,用户进入支付,调用过程是:Gateway->Pay(支付)->Driver(更新司机状态)
->Order
(更新订单状态)SpringCloud Gateway
Spring Cloud Gateway 是Spring Cloud团队的一个全新项目,基于Spring5.0、SpringBoot2.0、Project Reactor 等技术开发的网关。旨在为微服务架构提供一种简单有效统一的API路由管理方式。
Spring Cloud Gateway 作为SpringCloud生态系统中的网关,目标是替代Netflix Zuul。Gateway不仅提供统一路由方式,并且基于Filter链的方式提供网关的基本功能。例如:安全,监控/指标,和限流。
总结:微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控,限流等相关功能。
现微服务网关的技术有很多,
-
nginx:Nginx(engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务
-
zuul :Zuul 是 Netflflix 出品的一个基于JVM 路由和服务端的负载均衡器。
-
spring-cloud-gateway:是spring 出品的基于spring的网关项目,集成断路器,路径重写,性能比Zuul好。 我们使用gateway这个网关技术,无缝衔接到基于springcloud的微服务开发中来。
gateway官网:https://spring.io/projects/spring-cloud-gateway
Gateway工作原理
我们在学习Gateway之前,先弄清楚Gateway的工作原理,后面使用它的各个功能时,就知道该如何使用了,工作流程图如下:
Gateway的执行流程如下:
1:Gateway的客户端回向SpringCloudGateway发起请求,请求首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文,然后网关的上下文会传递到DispatcherHandler。
2:DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器,比如将请求分发到对应
RoutePredicateHandlerMapping(路由断言处理器映射器)。
3:路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。
4:FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,处理完毕后,将Response返回到Gateway客户端。在Filter链中,通过虚线分割Filter的原因是,过滤器可以在转发请求之前处
理或者接收到被代理服务的返回结果之后处理。所有的Pre类型的Filter执行完毕之后,才会转发请求到被代理的服务处理。被代理的服务把所有请求完毕之后,才会执行Post类型的过滤器。##Gateway路由 Gateway路由配置分为基于配置的静态路由设置和基于代码动态路由配置,静态路由是指在application.yml中把路由信息配置好了,而动态路由则支持在代码中动态加载路由信息,更加灵活,我们接下来把这2种路由操作都实现一次。
###业务说明
1:用户所有请求以/order开始的请求,都路由到hailtaxi-order服务
2:用户所有请求以/driver开始的请求,都路由到hailtaxi-driver服务
3:用户所有请求以/pay开始的请求,都路由到hailtaxi-pay服务基于配置路由设置
如上图所示,正是Gateway静态路由配置:
1:用户所有请求以/order开始的请求,都路由到hailtaxi-order服务
2:用户所有请求以/driver开始的请求,都路由到hailtaxi-driver服务
3:用户所有请求以/pay开始的请求,都路由到hailtaxi-pay服务配置参数说明:
routes:路由配置
-id:唯一标识符
uri:路由地址,可以是lb://IP:端口 也可以是
lb://${spring.application.name}
predicates:断言,是指路由条件
-Path=/driver/**:路由条件。Predicate接受一个输入参数,返回一个布尔值结果。这里表示匹配所有以driver开始的请求。
filters:过滤器
-StripPrefix=1:真实路由的时候,去掉第1个路径,路径个数以/分割区分测试url:http://localhost:8001/driver/info/1###基于代码路由配置 我们同样实现上面的功能,但这里基于代码方式实现。所有路由规则我们可以从数据库中读取并加载到程序中。基于代码的路由配置我们只需要创建RouteLocator并添加路由配置即可,代码如下:
/***
*路由配置
*@parambuilder
*@return
*/
@Bean
publicRouteLocatorrouteLocator(RouteLocatorBuilderbuilder)
{
returnbuilder.routes()
.route("hailtaxi-driver",r->
r.path("/driver/**").uri("lb://hailtaxi-driver"))
.route("hailtaxi-order",r->
r.path("/order/**").uri("lb://hailtaxi-order"))
.route("hailtaxi-pay",r->
r.path("/pay/**").uri("lb://hailtaxi-pay"))
.build();
}在真实场景中,基于配置文件的方式更直观、简介,但代码的路由配置是更强大,可以实现很丰富的功能,可以把路由规则存在数据库中,每次直接从数据库中加载规则,这样的好处是可以动态刷新路由规则,通常应用于权限系统动态配置。
spring:
cloud:
gateway:
#路由配置
routes:
#唯一标识符
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
#路由断言
predicates:
-Path=/driver/**
#唯一标识符
-id:hailtaxi-order
uri:lb://hailtaxi-order
#路由断言
predicates:
-Path=/order/**
#唯一标识符
-id:hailtaxi-pay
uri:lb://hailtaxi-pay
#路由断言
predicates:
-Path=/pay/**Gateway-Predicate
上面路由匹配规则中我们都用了-Path方式,其实就是路径匹配方式,除了路径匹配方式,Gateway还支持很多丰富的匹配方式,我们对这些方式分别进行讲解。 关于Predicate学习地址,可以参考官网:
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-request-predicates-factories
或者:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1RELEASE/single/spring-cloud-gateway.html#gateway-request-predicates-factories
routes下面的属性含义如下:id:我们自定义的路由ID,保持唯一
uri:目标服务地址
predicates:路由条件,Predicate接受一个输入参数,返回一个布尔值结果。该属性包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)Predicate 来源于Java 8,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,通过 Header、请求参数等不同的条件来作为条件匹配到对应的路由。
下面的一张图(来自网络)总结了 Spring Cloud 内置的几种 Predicate 的实现:
我们在这里讲解几个断言匹配 方式。
Cookie
Gateway的Cookie匹配接收两个参数:一个是 Cookie name,一个是正则表达式。路由规则就是通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。如下配置:
gateway:
routes:
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
predicates:
-Path=/driver/**
-Cookie=username,itheima这里表示请求携带了cookie为username的数据,并且值为itheima,就允许通过。
Header 匹配
Header 匹配 和 Cookie 匹配 一样,也是接收两个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。配置如下
gateway:
routes:
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
predicates:
-Path=/driver/**
-Header=token,^(?!d+$)[da-zA-Z]+$上面的匹配规则,就是请求头要有token属性,并且值必须为数字和字母组合的正则表达式,例如携带token=19and30就可以通过访问。
请求方式匹配
通过请求的方式是 POST、GET、PUT、DELETE 等进行路由。配置如下:
gateway:
routes:
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
predicates:
-Path=/driver/**
-Method=GET,POST通过yml的完整代码如下(注释掉java的配置)
server:
port:8001
spring:
application:
name:hailtaxi-gateway
main:
allow-bean-definition-overriding:true
cloud:
#Consul配置
consul:
host:127.0.0.1
port:8500
discovery:
#注册到Consul中的服务名字
s e r v i c e - n a m e :
${spring.application.name}
#注册的服务的实例Id,最好不要重复,这里参考官网建议的方式带
随机数默认:应用名:port
#instance-id:
${spring.application.name}:${vcap.application.instance_id:${s
pring.application.instance_id:${random.value}}}
#自定义实例id为:应用名:ip:port
instance-id:
${spring.application.name}:${spring.cloud.client.ip-
address}:${server.port}
prefer-ip-address:true
#开启服务注册
register:true
#开启服务发现
enabled:true
#2分钟之后健康检查未通过取消注册
health-check-critical-timeout:2m
#consul健康检查的轮询周期
health-check-interval:10s
gateway:
#路由配置
routes:
#唯一标识符
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
#路由断言
predicates:
-Path=/driver/**
-Cookie=username,itheima
-Header=token,^(?!d+$)[da-zA-Z]+$
-Method=GET,POST
#唯一标识符
-id:hailtaxi-order
uri:lb://hailtaxi-order
#路由断言
predicates:
-Path=/order/**
#唯一标识符
-id:hailtaxi-pay
uri:lb://hailtaxi-pay
#路由断言
predicates:
-Path=/pay/**断言源码剖析
拿Cookie断言来说,首先看它的体系结构
publicclassCookieRoutePredicateFactory
extends
AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Con
fig>{
/**
*Namekey.
*/
publicstaticfinalStringNAME_KEY="name";
/**
*Regexpkey.
*/
publicstaticfinalStringREGEXP_KEY="regexp";
publicCookieRoutePredicateFactory(){
super(Config.class);
}
/*
通过shortcutFieldOrder方法设置Config配置类中的属性,需要根据
具体的规则来设置
通过shortcutType方法获取具体规则,具体参看:
org.springframework.cloud.gateway.support.ShortcutConfigurabl
e.ShortcutType
规则包括以下几种:
DEFAULT:按照shortcutFieldOrder顺序依次赋值
*/
@Override
publicList<String>shortcutFieldOrder(){
returnArrays.asList(NAME_KEY,REGEXP_KEY);
}
@Override
publicPredicate<ServerWebExchange>apply(Configconfig)
{
returnnewGatewayPredicate(){
@Override
publicbooleantest(ServerWebExchangeexchange){
List<HttpCookie>cookies=
exchange.getRequest().getCookies()
.get(config.name);
if(cookies==null){
returnfalse;
}
for(HttpCookiecookie:cookies){
if
(cookie.getValue().matches(config.regexp)){
returntrue;
}
}
returnfalse;
}
@Override
publicStringtoString(){
returnString.format("Cookie:name=%s
regexp=%s",config.name,
config.regexp);
}
};
}
/*
内部配置类是用来接收在配置文件中配置的参数的
routes:
#唯一标识符
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
#路由断言
predicates:
-Cookie=username,itheima
*/
@Validated
publicstaticclassConfig{
@NotEmpty
privateStringname;
@NotEmpty
privateStringregexp;
publicStringgetName(){
returnname;
}
publicConfigsetName(Stringname){
this.name=name;
returnthis;
}
publicStringgetRegexp(){
returnregexp;
}
publicConfigsetRegexp(Stringregexp){
this.regexp=regexp;
returnthis;
}
}
}尽管Spring Cloud Gateway已经包含了很多路由匹配规则,有时候我们需要开发自定义路由匹配规则来满足需求,下面简单的介绍一下如何自定义路由匹配规则。
案例
需求:转发带token的请求到hailtaxi-drvier服务中,这里定义请求带token是指包含某个请求头的请求,至于是什么请求头可以由配置指定
修改配置文件
gateway:
#路由配置
routes:
#唯一标识符
-id:hailtaxi-driver
uri:lb://hailtaxi-driver
#路由断言
predicates:
#自定义一个Token断言,如果请求包含Authorization的
token信息则通过
-Token=Authorization
创建 RoutePredicateFactory断言工厂默认命名规则必须按照”名称”+RoutePredicateFactory,如上TokenRoutePredicateFactory的断言名称为Token
@Slf4j
@Component//要交给spring容器管理
publicclassTokenRoutePredicateFactoryextends
AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Conf
ig>{
publicTokenRoutePredicateFactory(){
super(Config.class);
}
publicPredicate<ServerWebExchange>apply(Configconfig)
{
returnexchange->{
//打印配置文件参数值
StringheaderName=config.getHeaderName();
HttpHeadersheaders=
exchange.getRequest().getHeaders();
List<String>header=headers.get(headerName);
log.info("TokenPredicateheaders:{}",header);
//断言返回的是boolean值
returnheader!=null&&header.size()>0;
};
}
@Override
publicList<String>shortcutFieldOrder(){
returnArrays.asList("headerName");//指定配置文件中加载
到的配置信息应填充到Config的哪个属性上
}
@Override
publicShortcutTypeshortcutType(){
returnShortcutType.DEFAULT;
}
@Data
publicstaticclassConfig{//staticclass
privateStringheaderName;//存储从配置文件中加载的配置
}
}启动测试:http://localhost:8001/driver/info/1##Gateway过滤器 Spring Cloud Gateway根据作用范围划分为GatewayFilter和GlobalFilter,二者区别如下:
-
GatewayFilter: 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.gateway.default-filters配置在全局,作用在所有路由上;gateway内置了多种过滤器工厂,配套的过滤器可以直接使用,如下图所示:
-
GlobalFilter: 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
过滤器作为Gateway的重要功能。常用于请求鉴权、服务调用时长统计、修改请求或响应header、限流、去除路径等等。 关于Gateway过滤器的更多使用,大家可以参考官方地址:
https://docs.springio/spring-cloud-gateway/docs!RELEASE/reference/html/#gatewayfilter-factories
或者:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#gatewayfilterfactories
###过滤器分类
默认过滤器:出厂自带,实现好了拿来就用,不需要实现
全局默认过滤器
局部默认过滤器
自定义过滤器:根据需求自己实现,实现后需配置,然后才能用哦。
全局过滤器:作用在所有路由上。
局部过滤器:配置在具体路由下,只作用在当前路由上。默认过滤器十好几个,常见如下:
默认过滤器的使用
所谓默认过滤器就是系统自带的。有很多,这里简要说明几个:(通过java配置,注释掉yaml配置)
1.添加响应头
AddResponseHeaderGatewayFilterFactory属于GatewayFilter
对输出响应头设置属性,比如对输出的响应设置其头部属性名称为:X-Response-Default-MyName, 值为itheima
修改配置文件,配置如下:
spring:
cloud:
gateway:
# 配置全局默认过滤器 作用在所有路由上,也可单独为某个路由配置
default-filters:
# 往响应过滤器中加入信息
- AddResponseHeader=X-Response-Default-MyName,itheima请求http://localhost:8001/driver/info/1,响应数据添加了X-Response-Default-MyName: itheima,如下图:
前缀处理
在项目中做开发对接接口的时候,我们很多时候需要统一API路径,比如统一以/api开始的请求调用hailtaxi-driver服务,但真实服务接口地址又没有/api路径,我们可以使用Gateway的过滤器处理请求路径。
在gateway中可以通过配置路由的过滤器StripPrefix实现映射路径中的前缀处理,我们来使用一下该过滤器,再进一步做说明。
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/api/driver/**
filters:
- StripPrefix=1此处- StripPrefix=1表示真实请求地址是当前用户请求以/api开始的uri中去除第1个路径/api
上面配置最终执行如下表:
有时候为了简化用户请求地址,比如用户请求
http://localhost:8001/info/1
我们想统一路由到http://localhost:18081/driver/info/1
,可以使用PrefixPath过滤器增加前缀。gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/**
filters:
- PrefixPath=/driver上面配置最终执行如下表
自定义GatewayFilter
实现GatewayFilter接口
GatewayFilter 一般作用在某一个路由上,需要实例化创建才能使用,局部过滤器需要实现接口GatewayFilter、Ordered。 创建com.itheima.filter.PayFilter代码如下:
public class PayFilter implements GatewayFilter,Ordered {
/***
* 过滤器执行拦截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
System.out.println("GatewayFilter拦截器执行---pre-----
PayFilter");
return
chain.filter(exchange).then(Mono.fromRunnable(()->{
System.out.println("GatewayFilter拦截器执行---post-
----PayFilter");
}));
}
@Override
public int getOrder() {
return 0;
}
}使用局部过滤器:(使用下面RouteLocator的时候,配置文件中的路由记得注释或删除)
/***
* 路由配置
* @param builder
* @return
*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder
builder) {
return builder.routes()
.route("hailtaxi-driver", r ->
r.path("/api/driver/**")
.and().cookie("username","itheima")
.and().header("token","123456")
.filters(f->f.filter(new
PayFilter()).addResponseHeader("X-Response-Default-MyName",
"itheima")
.addRequestHeader("myheader", "1234567")
.stripPrefix(1)
)
// .and().method(HttpMethod.POST)
.uri("lb://hailtaxi-driver")
//.filter(new PayFilter())
)
.route("hailtaxi-order", r ->
r.path("/order/**").uri("lb://hailtaxi-order"))
.route("hailtaxi-pay", r ->
r.path("/pay/**").uri("lb://hailtaxi-pay"))
.build();
}为了更好看到效果,我们在RouterFilter添加System.out.println(“GlobalFilter拦截器执行”);再访问测试。
访问:
http://localhost:8001/api/driver/info/1
,注意使用postman发送请求时添加请求头,添加cookie。继承GatewayFilterFactory
如果定义局部过滤器,想在配置文件中进行配置来使用,可以继承AbstractGatewayFilterFactory
抽象类或者AbstractNameValueGatewayFilterFactory 这两个抽象类的区别就是前者接收一个参数(像StripPrefix和我们创建的这种),后者接收两个参数(像AddResponseHeader)
代码的编写可以参考:StripPrefixGatewayFilterFactory 和AddRequestHeaderGatewayFilterFactory
过滤器工厂默认命名规则必须按照”名称”+GatewayFilterFactory`,如上StripPrefixGatewayFilterFactory的过滤器名称为StripPrefix
继承AbstractGatewayFilterFactory
在网关中统一支付方式,编写一个过滤器:PayMethodGatewayFilterFactory,
编写过滤器
@Slf4j
@Component //一定要将其交给spring容器管理
public class PayMethodGatewayFilterFactory extends
AbstractGatewayFilterFactory<PayMethodGatewayFilterFactory.Co
nfig> {
public PayMethodGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String paymethod = config.getPayMethod();
String msg = config.getMsg();
log.info("PayMethodGatewayFilterFactory 加载到的配
置信息为:{}---{}",paymethod,msg);
//将paymethod添加到请求头中
exchange.getRequest().mutate().header("paymethod",paymethod)
;
return chain.filter(exchange);
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("payMethod","msg");//指定从yml中
提前出来的配置信息填充到配置类中哪个属性,按规则配置
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.DEFAULT;//默认规则
}
/**
* 加载从yml中提取出来的配置信息
*/
@Data
public static class Config {
private String payMethod;
private String msg;
}
}
配置文件中使用如下:
gateway:
#路由配置
routes:
#唯一标识符
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由断言
predicates:
- Path=/driver/**
- Cookie=username,itheima
- Header=token,^(?!d+$)[da-zA-Z]+$
- Method=GET,POST
- Token=Authorization
filters:
- PayMethod=alipay,业务整合再次测试,查看hailtaxi-driver 服务接收到请求后是否多了paymethod请求头信息
继承AbstractNameValueGatewayFilterFactory
直接查看AddRequestHeaderGatewayFilterFactory源码,分析即可!
自定义GlobalFilter
定义全局过滤器需要实现GlobalFilter,Ordered接口:
GlobalFilter:过滤器拦截处理方法
Ordered:过滤器也有多个,这里主要定义过滤器执行顺序,里面有个方法
getOrder()会返回过滤器执行顺序,返回值越小,越靠前执行需求:
我们创建全局过滤器并完成常见业务用户权限校验,如果请求中有带有一个名字为token的请求参数,则认为请求有效放行,如果没有则拦截提示授权无效。
创建全局过滤器:com.itheima.filter.RouterFilter,代码如下:
@Component
public class RouterFilter implements GlobalFilter,Ordered {
/***
* 路由拦截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
log.info("RouterFilter----------------");
//获取请求参数
String token =
exchange.getRequest().getQueryParams().getFirst("token");
//如果token为空,则表示没有登录
if(StringUtils.isEmpty(token)){
//没登录,状态设置403
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_
LARGE);
//结束请求
return exchange.getResponse().setComplete();
}
//放行
return chain.filter(exchange);
}
/***
* 拦截器顺序
* @return
*/
@Override
public int getOrder() {
return 0;
}
}此时请求,我们不携带token参数,效果如下:
我们携带token参数则可以正常访问,效果如下:
跨域配置
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
在Spring Cloud Gateway中配置跨域是非常简单的,如下面application.yml所示:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT另外一种写法就需要创建CorsWebFilter过滤器,代码如下:
/**
* 配置跨域
* @return
*/
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// cookie跨域
config.setAllowCredentials(Boolean.TRUE);
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
// 配置前端js允许访问的自定义响应头
config.addExposedHeader("Authorization");
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}限流(自学)
网关可以做很多的事情,比如,限流,当我们的系统 被频繁的请求的时候,就有可能 将系统压垮,所以 为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。
令牌桶算法讲解
令牌桶算法是常见的限流算法之一,我们讲解一下漏桶算法:
1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
2)根据限流大小,设置按照一定的速率往桶里添加令牌;
3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻
辑,处理完业务逻辑之后,将令牌直接删除;
5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不
会删除令牌,以此保证足够的限流令牌桶算法的实现,有很多技术,Guaua是其中之一,redis客户端也有其实现。
限流案例
spring cloud gateway 默认使用redis的RateLimter限流算法来实现,外面来简要实现一下:
首先需要引入redis的依赖:
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-
reactive</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>同时不要忘记Redis配置:
redis:
host: 127.0.0.1
port: 6379定义KeyResolver 在Application引导类中添加如下代码,KeyResolver用于计算某一个类型的限流的KEY也就是说,可以通过KeyResolver来指定限流的Key。
我们可以根据IP来限流,比如每个IP每秒钟只能请求一次,在GatewayApplication定义key的获取,获取客户端IP,将IP作为key,如下代码:
/***
* IP限流
* @return
*/
@Bean(name="ipKeyResolver")
public KeyResolver userKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange
exchange) {
//获取远程客户端IP
String hostName =
exchange.getRequest().getRemoteAddress().getAddress().getHost
Address();
System.out.println("hostName:"+hostName);
return Mono.just(hostName);
}
};
}在路由中配置如下:
gateway:
#路由配置
routes:
#唯一标识符
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由断言
predicates:
- Path=/driver/**
- Cookie=username,itheima
- Header=token,^(?!d+$)[da-zA-Z]+$
- Method=GET,POST
- Token=Authorization
filters:
- PayMethod=alipay,业务整合
- name: RequestRateLimiter #请求数限流 名字不能随便写
,使用默认的facatory
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1参数说明:
redis-rate-limiter.replenishRate是您希望允许用户每秒执行多少请求,
而不会丢弃任何请求。这是令牌桶填充的速率
redis-rate-limiter.burstCapacity是指令牌桶的容量,允许在一秒钟内完
成的最大请求数,将此值设置为零将阻止所有请求。
key-resolver: “#{@ipKeyResolver}” 用于通过SPEL表达式来指定使用哪
一个KeyResolver.如上配置: 表示 一秒内,允许 一个请求通过,令牌桶的填充速率也是一秒钟添加一个令牌。
最大突发状况 也只允许 一秒内有一次请求,可以根据业务来调整 。
我们请求
http://localhost:8001/driver/info/1?token=aa
执行测试,效果如下:
原文始发于微信公众号(程序员朱永胜):SpringCloud架构基础(上)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/203555.html