SpringCloud---Gateway
(一).网关介绍1.前置问题当前我们的生产是介绍openfeign的环境。上图分别是product-service和order-service中的方法。这就有问题当前所有微服务的接口都是直接对外暴露的可以直接通过外部访问。为了保证对外服务的安全性服务端实现的微服务接口通常都带有一定的权限校验机制。由于使用了微服务原本一个应用的多个模块拆分成了多个应用我们不得不实现多次校验逻辑。当这套逻辑需要修改时我们需要修改多个应用所以会很麻烦。针对于上述问题一个常用的解决方案就是使用API网关。2.什么是API网关API网关(简称网关)也是一个服务通常是后端服务的唯一入口。它的定义类似设计模式中的门面模式。它就类似整个微服务架构的门面所有的外部客户端访问都需要经过它来进行调度和过滤。网关的核心功能(1).权限控制作为微服务的入口对用户进行权限校验如果校验失败则进行拦截(2).动态路由一切请求先经过网关但是网关不处理业务而是根据狗中规则把请求转发到某个微服务(3).负载均衡当路由的目标服务有多个时还需要做负载均衡(4).限流请求两两过高时按照网管中配置微服务能够接收的流量进行放行避免服务压力过大3.常见网关的实现(1).ZuulZuul是Netflix公司开源的一个API网关组件是Spring Cloud Netflix子项目的核心组件之一它可以和EurekaRibbonHystrix等组件配合使用(2).Spring Cloud GatewaySpring Cloud Gateway 是 Spring Cloud 的一个全新的API网关项目基于Spring SpringBoot等技术开发目的是为了替换掉Zuul。(二).Spring Cloud Gateway1.使用Gateway(1).创建网关项目API网关也是一个服务所以需要创建一个网关项目(2).引入网关相关依赖dependencies !--⽹关-- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency !--基于nacos实现服务发现依赖-- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency !--负载均衡-- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-loadbalancer/artifactId /dependency /dependencies(3).写启动类(4).配置文件在application.yml文件中添加配置server: port: 5050 spring: application: name: gateway cloud: nacos: discovery: server-addr: #服务器nacos地址 namespace: 68576334-aeee-446f-a704-c59fd9207ecf #设置命名空间 服务管理的命名空间,并不是配置管理的命名空间 gateway: routes: #网关路由相关配置 - id: product-service #路由规则id随便起不重复即可 uri: lb://product-service/** #目标服务地址lb表示“负载均衡” predicates: #路由条件当满足条件的时候网关应用会把请求跳转到目标服务地址 - Path/product/** #把符合Path规则的一切请求都代理到uri参数指定的地址 - id: order-service uri: lb://order-service/** predicates: - Path/order/**(5).运行测试可以发现访问的时候使用的是gateway的端口号是可以访问成功的2.Route Predicate Factories(1).PredicateⅠ.概念Pridicate是Java 8 提供的一个函数式编程接口它接收一个参数并返回一个布尔值用于条件过滤请求参数的校验下面通过一个例子来看①.定义一个Predicate②.使用这个PredicateⅡ.Predicate的其他写法A.内置函数(内部类)B.lambda写法Ⅲ.Predicate的其他方法方法说明boolean test (T t)判断条件可以理解为条件A根据逻辑返回布尔值PredicateT and (Predicate ? super T other)条件A 条件B当前Predicate的test other的test方法相当于进行两次判断default PredicateT negate()!条件A对当前判断进行 “!”操作即取非操作default Predicate? or(Predicate ? super T other)条件A || 条件B当前Predicate的test方法 || other 的test方法下面进行具体的演示(2).Route Predicate FactoriesRoute Predicate Factories路由断言工厂也称为路由谓词(函数)工厂在Spring Cloud Gateway中Predicate提供了路由规则的匹配机制我们在配置文件中写的断言规则只是字符串这些字符串会被Route Predicate Factory读取并操作转变为路由判断条件。例如前面写的Path就是通过Path属性来匹配URL前缀是 /product的请求这个规则是由PathRoutePredicateFactory这个类实现的Spring Cloud Gateway 默认提供了很多 Route Predicate Factory这些Predicate 会分别匹配HTTP请求的不同属性并且多个Predicate可以通过and逻辑进行组合名称说明示例After这个工厂需要一个日期时间(ZonedFateTime对象)匹配指定日期之后的请求predicates:- After2017-01-20T17:42:47.789-07:00[America/Denver]Before匹配指定日期之前的请求predicates:- Before2017-01-20T17:42:47.789-07:00[America/Denver]Between匹配两个指定时间之间的请求datetime2的参数必须在datetime1之后predicates:- Between2017-01-20T17:42:47.789-07:00[America/Denver] , 2017-02-20T17:42:47.789-07:00[America/Denver]Cookie请求中包含指定Cookie且该Cookie值符合指定的正则表达式predicates:- Cookiechocolate , ch.pHeader请求中包含指定Header且该Header值符合指定的正则表达式predicates:- HeaderX-Request-id , \dHost请求必须是访问某个host(根据请求中的Host字段进行匹配)predicates:- Host**.somehost.org,**.anotherhost.orgMethod匹配指定的请求方式predicates:- MethodGET,POSTPath匹配指定规则的路径predicates:- Path/order/**RemoteAddr请求者的IP必须为指定范围predicates:- RemoteAddr192.168.1.1*24下面通过一个示例来演示可以看到是可以访问成功的可以看到是访问不到的3.Gateway Filter FactoriesGateway Filter Factories网关过滤工厂。Predicate决定了请求由哪一个路由处理如果在请求前后需要加一些逻辑这个就是Filter(过滤器)的作用范围了。Filter 分为两种类型Pre类型和Post类型Pre类型过滤器路由处理之前执行(请求转发到后端服务之前执行)在Pre类型过滤器中可以做鉴权限流等等。Post类型过滤器请求执行完成后将结果返回给客户端之前执行Spring Cloud Gateway中内置了很多Filter用于拦截和链式处理web请求比如权限校验访问超时等设定。Spring Cloud Gateway从作用范围上把Filter分为GatewayFilter和GlobalFilterGatewayFilter应用到单个路由或者一个分组的路由上GlobalFilter应用到所有的路由上也就是对所有的请求生效(1).GatewayFilterA.概念GatewayFilter和Predicate类型都是在配置文件application.yml中配置每个过滤器的逻辑都是固定的。例如AddRequestParameterGatewayFilterFactory 只需要在配置文件中写AddRequestParameter就可以为所有的请求添加一个参数。B.使用下面通过一个例子来看Ⅰ.在application.yml中添加filter这个filter只添加在了order-service路由下因此支队order-service路由生效也就是对 /product/**的请求生效。Ⅱ.接收参数并打印在order-service服务中接收请求的参数并打印出来C.GatewayFilter说明Spring Cloud Gateway提供的Filter非常多下面列出一些常见过滤器的说明①.AddRequestHeader为当前请求添加Header- AddRequestHeaderX-Request-red, blue参数: Header的名称及值②.AddRequestParam为当前请求添加请求参数- AddRequestParameterred, blue参数: 参数的名称及值③.AddResponseHeader为响应结果添加Header- AddResponseHeaderX-Response-Red, Blue参数: Header的名称及值④.RemoveRequestHeader从当前请求删除某个Header- RemoveRequestHeaderX-Request-Foo参数: Header的名称⑤.RemoveResponseHeader从响应结果中删除某个Header- RemoveResponseHeaderX-Response-Foo参数: Header的名称⑥.RequestRateLimiter为当前网关的所有请求执行限流过滤如果被限流默认会相应HTTP -429-TooManyRequests。默认提供了RedisRateLimiter的限流实现采用令牌桶算法实现限流功能。filters:-name:RequestRateLimiterargs:redis-rate-limiter.replenishRate: 10redis-rate-limiter.burstCapacity: 20redis-rate-limiter.requestedTokens: 1redis-rate-limiter.replenishRate指的是令牌填充速度即每秒周允许多少个请求(不丢弃任何请求)redis-rate-limiter.burstCapacity指的是令牌桶容量即每秒钟用户最大能够执行的请求数量(不丢弃任何请求)如果将此值设置为0则将会阻止所有请求。redis-rate-limiter.requestedTokens指的是每次请求占用几个令牌默认为1关于限流使用的限流算法进行的实现具体有四种限流算法假设现在的流量限制为1000次/分钟a.固定窗口但是这种算法也有明显的缺点如果说在59秒~60秒之间突然来了1000个请求然后60~61秒之间突然又来了1000个请求此时相当于2秒钟来了2000个请求但是流量限制为1000次/分钟此时就可能会把服务器搞挂掉b.滑动窗口可以看到滑动窗口可以解决“固定窗口”存在的问题。但是滑动窗口并不能从根本上解决如果使用滑动窗口那么我们需要设置滑动窗口滑动的间隔c.漏桶算法但是这种算法也会出现“固定窗口”的问题如果一下子来了1000个请求此时全部会被放在桶里但是消费者还是按照固定的速率进行处理d.令牌桶算法现在如果在来请求之前桶里已经放了1000个令牌那么当这1000个请求突然来的时候每个请求都拿着一个令牌然后处理不会出现应激流量处理不好的问题⑦.Retry针对不同的响应进行重试当后端服务不可用时网关会根据配置参数来发起重试请求filters:-name:Retryargs:retries: 3statuses:BAD_REQUESTretries重试次数默认为3statusHTTP请求返回的状态码针对指定状态码进行重试对应org.springframework.http.HttpStatus⑧.RequestSize设置允许接收最大请求包的大小如果请求包大小超过设置的值则返回413 Payload TooLarge。请求包大小单位为字节默认为5Mfilters:-name:RequestSizeargs:maxSize: 5000000⑨.默认过滤器添加一个fliter并将其应用于所有路由这个属性需要一个filter的列表。可以看到在上面的图片中我就是将gateway中配置了默认过滤器然后当状态为BAD_REQUEST对应的状态码为400的时候就要重试3次。然后我在getDetail()方法中将状态码改成了400然后运行程序进行访问发现确实重试了3次。(2).GlobalFilterA.概念GlobalFilter是Spring Cloud Gateway中的全局过滤器它和GatewayFilter的作用域是相同的。GlobalFilter会应用到所有的路由请求上全局过滤器通常用于实现与安全性性能监控和日志操作等相关的全局功能。Spring Cloud Gateway内置的全局过滤器也有很多例如Gateway Metrics Filter网关指标提供监控指标Forward Routing Filter用于本地forward请求不转发到下游服务器LoadBalancer Client Filter针对下游服务实现负载均衡B.使用①.添加依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency②.添加配置③.运行测试可以看到所有的监控信息链接都显示出来了4.过滤器执行顺序如果一个项目中既有GatewayFilter又有GlobalFilter那么当请求路由后网关会把当前项目中的GatewayFilter和GlobalFilter合并到一个过滤器链(集合)中并进行排序依次执行过滤器对于每一个过滤器都必须要指定一个int类型的order值默认为0表示该过滤的优先级order值越小优先级越高执行顺序越靠前。Filter通过实现Order接口或者添加Order注解来指定order值Spring Cloud Gateway提供的Filter由Spring指定用户也可以自定义Filter由用户指定当过滤器的order值一样的时候会按照defaultFilter GatewayFilter GlobalFilter5.自定义过滤器(1).自定义GatewayFilter自定义GatewayFilter需要去实现对应的接口GatewayFilterFactorySpringBoot默认帮我们实现的抽象类是AbstractGatewayFilterFactory我们可以直接使用Ⅰ.定义GatewayFilterpackage org.com.spring; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; Slf4j Component public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactoryCustomConfig implements Ordered { public CustomGatewayFilterFactory() { super(CustomConfig.class); } Override public GatewayFilter apply(CustomConfig config) { return new GatewayFilter() { /** * ServerWebExchange HTTP请求-响应交互契约提供了对HTTP 请求 和 响应 的比例 * * GatewayFilterChain 过滤器链 * * Mono Reactor的核心类数据流发布者Mono最多只能触发一个事件可以把Mono用在异步完成任务时发出通知 * * chain.filter(exchange) 执行请求 * * Mono.fromRunnable() 创建一个包含Runnable元素的数据流 * * return */ Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { //执行步骤 : Pre类型 - 执行请求 - Post请求 log.info(Pre Filter ,config : {},config); return chain.filter(exchange).then(Mono.fromRunnable(()-{ log.info(Post Filter....); })); } }; } Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; //设置较低的优先级 } }针对这个Filter的配置使用CustomConfig定义package org.com.spring; import lombok.Data; Data public class CustomConfig { //和yml的配置对应 private String name; }注意①.类名统一以GatewayFilterFactory结尾因为在默认情况下过滤器的name会采用该定义类的前缀这里的nameCustom(yml配置中使用)②.apply方法中同时包含了Pre和Post过滤then方法中是请求执行结束之后处理的③.CustomConfig是一个配置类该类只有一个属性name和yml的配置对应④.该类需要交给Spring管理所以需要添加Component⑤.getOrder表示该过滤器的优先级值越大优先级越低Ⅱ.配置过滤器Ⅲ.运行测试可以看到自定义的GatewayFilter起作用了(2).自定义GlobalFilterGlobalFilter的实现比较简单不需要额外的配置只需要实现GlobalFilter接口自动会过滤所有的Filterpackage org.com.spring; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; Slf4j Component public class CustomGlobalFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info(Pre Global Filter); return chain.filter(exchange).then(Mono.fromRunnable(()-{ log.info(Post Global Filter); })); } Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }可以看到当GatewayFilter和GlobalFilter过滤器order一样时会先执行GatewayFilter