当前位置:

SpringCloud04 网关springcloud gateway

访客 2024-01-05 468 0

网关springcloudgateway

上面的架构,会存在着诸多的问题:

  1. 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性

  2. 认证复杂,每个服务都需要独立认证。

  3. 存在跨域请求,在一定场景下处理相对复杂。

网关可以做什么?

  1. 路由转发。
  2. 身份认证。
  3. 统一跨域解决。
  4. 黑白名单ip
  5. 敏感词
  6. 限流

1.常用的网关

  1. nginx:它可以当网关

  2. zuul:早期的微服务就是使用的该组件作为网关,但是它的底层使用的servlet。它的效率非常慢。而且它是netflix的产品。netflix预计产品zuul2,但是zuul2夭折。

  3. springcloudgateway:它是spring公司出品的网关。它的效率是zuul的1.6倍。

2.springcloudgateway

SpringCloudGateway是Spring公司基于Spring5.0,SpringBoot2.0和ProjectReactor等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。它的目标是替代NetflixZuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控和限流。

3.如何使用

其实网关它也是一个微服务,那么我们也可以创建网关微服务。

引入spring-cloud-starter-gateway

<dependencies><!--这里引入了gateway的依赖后,不能引用spring-boot-starter-web依赖。因为:gateway它使用的是netty服务器。spring-boot-starter-web里面内置了tomcat服务器.--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency></dependencies>

(2)创建主启动类

@SpringBootApplicationpublicclassGatewayApp{publicstaticvoidmain(String[]args){SpringApplication.run(GatewayApp.class,args);}}

(3)修改配置文件

#配置路由spring:cloud:gateway:routes:-id:shop-product#路由的唯一标识。如果没有给定默认按照UUID生成uri:http://localhost:8001#真实转发的地址predicates:#断言如果断言满足要求,则转发到uri指定的真实地址.-Path=/product/**#如果客户的请求路径以product开头,则满足该断言要求,则转发的uri真实地址。-id:shop-orderuri:http://localhost:9001predicates:-Path=/order/**

(4)启动gateway

(5)演示

4.gateway负载均衡转发

上面配置文件有没有需要改进的?

  • 我们真实转发的地址,万一搭建是一个集群。我们观察到gateway本身也是一个微服务,是否可以从注册中心拉取相关的微服务,然后访问该服务呢。

(1)引入nacos注册中心的依赖

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

(2)修改配置文件

测试:

5.简洁版

#配置路由spring:cloud:gateway:routes:-id:shop-product#路由的唯一标识。如果没有给定默认按照UUID生成uri:lb://shop-product#真实转发的地址lb:---loadbalancedpredicates:#断言如果断言满足要求,则转发到uri指定的真实地址.-Path=/product/**#如果客户的请求路径以product开头,则满足该断言要求,则转发的uri真实地址。-id:shop-orderuri:lb://shop-orderpredicates:-Path=/order/**

思考:如果这时增加新的微服务,需要修改网关的路由配置。

改为自动路由发现。

(1)修改gateway的配置文件

(2)访问网关

6.gateway流程

6.1断言的种类

l基于Datetime类型的断言工厂

此类型的断言根据时间做判断,主要有三个:

AfterRoutePredicateFactory:接收一个日期参数,判断请求日期是否晚于指定日期

BeforeRoutePredicateFactory:接收一个日期参数,判断请求日期是否早于指定日期

BetweenRoutePredicateFactory:接收两个日期参数,判断请求日期是否在指定时间段内

-After=2019-12-31T23:59:59.78908:00[Asia/Shanghai]

l基于远程地址的断言工厂

RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中

-RemoteAddr=192.168.1.1/24

l基于Cookie的断言工厂

CookieRoutePredicateFactory:接收两个参数,cookie名字和一个正则表达式。判断请求

cookie是否具有给定名称且值与正则表达式匹配。

-Cookie=chocolate,ch.

l基于Header的断言工厂

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。判断请求Header是否

具有给定名称且值与正则表达式匹配。keyvalue

-Header=X-Request-Id,\d

l基于Host的断言工厂

HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。

-Host=**.testhost.org

l基于Method请求方法的断言工厂

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。

-Method=GET

l基于Path请求路径的断言工厂

PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。

-Path=/foo/{segment}基于Query请求参数的断言工厂

QueryRoutePredicateFactory:接收两个参数,请求param和正则表达式,判断请求参数是否具

有给定名称且值与正则表达式匹配。

-Query=baz,ba.

l基于路由权重的断言工厂

WeightRoutePredicateFactory:接收一个[组名,权重],然后对于同一个组内的路由按照权重转发

routes:

-id:weight_route1uri:host1predicates:

-Path=/product/**

-Weight=group3,1

-id:weight_route2uri:host2predicates:

-Path=/product/**

-Weight=group3,9

如果上面的内置断言无法满足需求可以自定义断言。【了解】

案例:年龄必须在18~65之间才能访问我指定的微服务。

自定义断言类

packagecom.aaa.predicate;importorg.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;importorg.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.server.ServerWebExchange;importjavax.validation.constraints.NotNull;importjava.time.ZonedDateTime;importjava.util.Arrays;importjava.util.List;importjava.util.function.Predicate;/***@program:qy156-shop-parent*@description:*@author:闫克起2*@create:2022-11-2116:27**/@ComponentpublicclassAgeRoutePredicateFactoryextendsAbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config>{publicAgeRoutePredicateFactory(){super(AgeRoutePredicateFactory.Config.class);}@OverridepublicList<String>shortcutFieldOrder(){returnArrays.asList("minAge","maxAge");}@OverridepublicPredicate<ServerWebExchange>apply(Configconfig){return(serverWebExchange)->{ServerHttpRequestrequest=serverWebExchange.getRequest();//获取传递的年龄Stringage=request.getHeaders().getFirst("age");if(StringUtils.hasText(age)){inta=Integer.parseInt(age);if(a>=config.getMinAge()&&a<=config.getMaxAge()){returntrue;}}returnfalse;};}@ValidatedpublicstaticclassConfig{@NotNullprivateintminAge;@NotNullprivateintmaxAge;publicintgetMinAge(){returnminAge;}publicvoidsetMinAge(intminAge){this.minAge=minAge;}publicintgetMaxAge(){returnmaxAge;}publicvoidsetMaxAge(intmaxAge){this.maxAge=maxAge;}}}


小结:

gateway:网关,路由转发

ribbon:实现负载均衡

openfeign:完成服务之间的调用。

nacos:注册中心

6.2gateway中的过滤器

为请求到达微服务前可以添加相应的请求设置,响应后为响应结果添加一些设置。

gateway内部含有很多种过滤。
https://www.cnblogs.com/zhaoxiangjun/p/13042189.html

过滤器工厂作用参数AddRequestHeader为原始请求添加HeaderHeader的名称及值AddRequestParameter为原始请求添加请求参数参数名称及值AddResponseHeader为原始响应添加HeaderHeader的名称及值DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略Hystrix为路由引入Hystrix的断路器保护HystrixCommand的名称FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称PrefixPath为原始请求路径添加前缀前缀路径PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host无RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatusRedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的urlRemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些HeaderRemoveRequestHeader为原始请求删除某个HeaderHeader名称RemoveResponseHeader为原始响应删除某个HeaderHeader名称RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正则表达式,重写后的值SaveSession在转发请求之前,强制执行WebSession::save操作无secureHeaders为原始响应添加一系列起安全作用的响应头无,支持修改这些安全响应头的值SetPath修改原始的请求路径修改后的路径SetResponseHeader修改原始响应中某个Header的值Header名称,修改后的值SetStatus修改原始响应的状态码HTTP状态码,可以是数字,也可以是字符串StripPrefix用于截断原始请求的路径使用数字表示要截断的路径的数量Retry针对不同的响应进行重试retries、statuses、methods、seriesRequestSize设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回413PayloadTooLarge请求包大小,单位为字节,默认值为5MModifyRequestBody在转发请求之前修改原始请求体内容修改后的请求体内容ModifyResponseBody修改原始响应体的内容修改后的响应体内容Default为所有路由添加过滤器过滤器工厂名称及值

Tips:每个过滤器工厂都对应一个实现类,并且这些类的名称必须以GatewayFilterFactory结尾,这是SpringCloudGateway的一个约定,例如AddRequestHeader对应的实现类为AddRequestHeaderGatewayFilterFactory。

举例:StripPrefix用于截断原始请求的路径。

测试:

例子:设置响应的状态码2500

6.3自定义全局过滤器

例子:认证过滤。

内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的认证校验。

开发中的鉴权逻辑:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)

  • 认证通过,将用户信息进行加密形成token[jwt],返回给客户端,作为登录凭证

  • 以后每次请求,客户端都携带认证的token[携带请求头]

  • 服务端对token进行解密,判断是否有效。

packagecom.aaa.filter;importcom.alibaba.fastjson.JSON;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.Ordered;importorg.springframework.core.io.buffer.DataBuffer;importorg.springframework.http.HttpStatus;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.http.server.reactive.ServerHttpResponse;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;importjava.nio.charset.StandardCharsets;importjava.util.HashMap;importjava.util.Map;/***@program:qy156-shop-parent*@description:*@author:闫克起2*@create:2022-11-2215:07**/@ComponentpublicclassLoginFilterimplementsGlobalFilter,Ordered{@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){ServerHttpRequestrequest=exchange.getRequest();ServerHttpResponseresponse=exchange.getResponse();//判断请求路径是否为放行。Stringpath=request.getPath().toString();if("/login".equals(path)){returnchain.filter(exchange);//放行}//获取请求头的token值。Stringtoken=request.getHeaders().getFirst("token");if(StringUtils.hasText(token)){//校验token是否有效if("admin".equals(token)){returnchain.filter(exchange);//放行}}//3.1设置状态码response.setStatusCode(HttpStatus.UNAUTHORIZED);//3.2封装返回数据Map<String,Object>map=newHashMap<>();map.put("msg","未登录");map.put("code","NOTLOGING");//3.3作JSON转换byte[]bytes=JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);//3.4调用bufferFactory方法,生成DataBuffer对象DataBufferbuffer=response.bufferFactory().wrap(bytes);//4.调用Mono中的just方法,返回要写给前端的JSON数据returnresponse.writeWith(Mono.just(buffer));}//优先级值越小优先级越高@OverridepublicintgetOrder(){return0;}}

7.统一跨域解决

第一种通过配置文件

spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowedOrigins:"*"allowedHeaders:"*"allowedMethods:"*"default-filters:-DedupeResponseHeader=VaryAccess-Control-Allow-OriginAccess-Control-Allow-Credentials,RETAIN_FIRST

第二种写一个配置类

importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.reactive.CorsWebFilter;importorg.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;importorg.springframework.web.util.pattern.PathPatternParser;@ConfigurationpublicclassCorsConfig{@BeanpublicCorsWebFiltercorsFilter(){CorsConfigurationconfig=newCorsConfiguration();config.addAllowedMethod("*");config.addAllowedOrigin("*");config.addAllowedHeader("*");UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource(newPathPatternParser());source.registerCorsConfiguration("/**",config);returnnewCorsWebFilter(source);}}

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~