大家都知道在微服务架构中,一个系统会被拆分为多个微服务,那么作为客户端如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。
这样的架构会存在诸多的问题:
1、每个业务都需要鉴权、限流、权限校验、跨域等逻辑 ,如果每个业务都各自为战,自己造轮子实现一遍 ,就会很蛋疼,完全可以抽出来,放到一个统一的地方去处理。
2、如果业务量比较简单的话,这样的方式前期不会存在什么问题,但是随着业务的发展越来越复杂,比如淘宝
、亚马逊打开一个页面会涉及到数百个微服务的协同工作 ,如果每一个微服务都分配一个域名的话,一方面客户端会很难维护,涉及数百个域名,另一方面是连接的瓶颈,想象一下你打开一个APP,通过抓包发现涉及到了数百个远程调用,这在移动端下会显得非常的低效。
3、后期如果需要对微服务进行重构,也会变得非常麻烦,需要客户端配合你一起改造,比如商品服务,随着业务变得复杂 ,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆成多个,同时需要客户端配合你进行改造,非常蛋疼。
上面的问题都可以借助API网关来解决。
所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。
网关作为流量的入口,常用的功能包括路由转发,权限校验、限流等
Spring Cloud GateWay 是Spring Cloud 官方退出的第二代网关框架,定位取代 Netflix Zuul1.0,相比Zuul来说,
Spring Cloud GateWay提供更优秀的性能,更强大的功能。
2.1、核心概念
路由(route)
路由是网关中最基础的部分,路由信息包括一个ID,一个目的URL,一组断言工厂,一组Filter组成,如果断言为真,则说明请求的URL和配置的理由匹配。
断言(predicates)
Java8中的断言函数, Spring Cloud GateWay 中的断言函数类似是Spring5.0框架中的ServerWebExchange,断言函数允许开发者去匹配Http request中的任何信息,比如请求头和参数等。
过滤器(Filter)
SpringCloud Gateway 中的Filter分为Gateway Filter和Global Filter,Filter可以对请求和响应进行处理。
2.2、工作原理
如上图所示,客户端向 Spring Cloud Gateway 发出请求,再由网关处理程序 GateWay Handler Mapping 映射确定与请求相匹配的路由,将其发送到网关Web处理程序 GateWay Web Handler,该处理程序通过指定的过滤器链将请求发送到我们实际的服务执行业务逻辑,然后返回,过滤器由虚线分隔的原因是,过滤器可以再发送代理请求之前和之后运行逻辑,所有 pre 过滤器逻辑均被执行,然后发出代理请求,发出代理请求后,将运行 post 过滤器逻辑。
Spring Cloud GateWay 创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给Route。
Spring Cloud GateWay 包含许多内置的RoutePredicateFactory.
所有这些断言都匹配HTTP请求的不同属性。
多个RoutePredicateFactory可以通过逻辑与(and)结合起来一起使用。
3.1、路由规则之path
3.2、路由规则之Query
3.3、路由规则之Method
3.4、路由规则之Datetime
3.5、路由规则之RemoteAddr
3.6、路由规则之Header
动态路由其实就是面向服务的路由,Spring Cloud GateWay 支持与Eureka整合开发,根据serviceId自动从注册中心获取服务地址并转发请求,这样做的好处是不仅可以通过单个端点来访问应用的所有服务,而且在添加或移动服务实例时不用再修改GateWay的路由配置。
在Gateway中,Filter的生命周期只有两个:“pre”和“post”。
PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
过滤器类型:Gateway 的Filter从作用范围可分为两种:GatewayFilter与GlobalFilter。
GatewayFilter:应用到单个路由或者一个分组的路由上。
GlobalFilter:应用到所有的路由上。
局部过滤器(GatewayFilter)是针对单个路由的过滤器,对访问的URL过滤,切面处理。在Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。
将 /api-gateway/product/ 请求重写成 /product/请求(属于前置过滤器网关)
将 Http ResponseStatus 值设置为456(属于后置过滤器网关)
全局过滤器作用于所有路由,无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。
SpringCloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:
示例通过自定义全局过滤器,完成统一的权限校验。
开发中的鉴权逻辑:
当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
以后每次请求,客户端都携带认证的token
服务端对token进行解密,判断是否有效。
如下图,对于验证用户是否已经登录及鉴权的过程,可以在网关统一校验。
校验的标准就是请求中是否携带token凭证,以及token的正确性。
下面我们通过自定义一个GlobalFIlter,去校验所有请求的请求参数中是否包含“token”,如果不包含请求参数“token”则不转发路由,否则执行正常逻辑。
package cn.jack.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义全局过滤器,需要实现GlobalFilter和Ordered接口
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
// 完成鉴权逻辑
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isEmpty(token)) {
System.out.println("鉴权失败。确少token参数。");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
if (!"jack".equals(token)) {
System.out.println("token无效...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 继续执行filter链
return chain.filter(exchange);
}
// 顺序,数值越小,优先级越高
@Override
public int getOrder() {
return 0;
}
}