- 客户端多次请求不同的微服务,增加了客户端的复杂性
- 存在跨域请求,在某些场景下处理相对复杂
- 认证复杂及重复,每个微服务都需要独立认证
- 重构困难,随着项目的迭代和产品的发展,可能需要重新划分微服务,如将多个微服务合并为一个或将一个微服务分拆为多个,此时客户端调用微服务时成本又会增加
-
有一些微服务可能放置于防火墙后或其他访问限制,直接访问会不可达
所以出现了API网关,它是介于客户端和服务端之间的桥梁,所有外部请求都会先给过网关层再路由到相应的微服务进行处理,根据这一特性我们一般会在网关层用来处理安全,性能,监控等通用的功能等,而不会用来处理业务逻辑.
网关的优势及选型
- 易于监控,认证
- 减少客户端与各个微服务之间的交互,直接和网关交互
网关技术选型
- 自研
- Nginx衍生的Kong
- Netflex Zuul:zuul1,zuul2(闭源)
- Spring Cloud Gateway
下面我们主要以spring cloud gateway为例来讲网关.
Spring cloud gateway包含spring 5,spring boot 2,project reactor,它提供一种简单有效的路由转发请求,并提供横切关注点,如安全性,监控/指标和弹性等,它的特性如下:
- 基于Java 8 编码
- 支持Spring framework 5
- 支持spring boot 2
- 支持动态路由
- 支持基于HTTP请求的路由匹配(path,method,header,host etc)
- 过滤器作用于匹配的路由,修改下游HTTP请求和HTTP响应(增加/修改头部,增加/修改请求参数,改写请求路径)
- 支持spring cloud discovery client,与服务发现和注册配合使用
- 支持websocket,使用非阻塞API(主要与zuul1比较,zuul1是基于Servlet实现,基于阻塞I/O,不支持任何长连接,每次I/O操作都是从工作线程中选择一个执行,request-per-thread)
- 基于Filter链提供了身份验证,监控,负载均衡,限流,降级与应用检测等功能
spring cloud gateway重要概念
- 路由:路由信息由ID,目标URL,一组断言,一组filter.若断言为真,则请求URL和配置匹配.
- Filter:分两类gateway filter和globalfilter,过滤器将会对请求和响应进行修改处理.
- 断言:自定义匹配来自http request中的任务信息如请求头和参数
实践:
application.yaml
server:
port: ${port:8180}
ipAddr: ${ipAddr:127.0.0.1}
spring:
zipkin:
base-url: http://127.0.0.1:9411
discovery-client-enabled: false #
sleuth:
sampler:
probability: 1.0 #采样百分比 range[0.1~1.0]
application:
name: service-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lowerCaseServiceId: true
# lower-case-service-id: true
routes:
- id: service_product
uri: lb://service-product
order: 1 #数字越小,越先执行,即优先级越高
predicates:
- Path=/api/product/**
filters:
- StripPrefix=1
- id: service_order
uri: lb://service-order
order: 1 #数字越小,越先执行,即优先级越高
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
globalcors:
cors-configurations:
‘[/**]‘:
allow-credentials: true #允许携带认证信息
allowed-origins: #允许跨域的源(域名和IP),设置*为全部
- ‘*‘
allowed-headers: ‘*‘ # 允许跨域请求里的header字段,*为全部
allowed-methods: #允许跨域的方法
- GET
- POST
- OPTIONS
max-age: 3600 # 跨域允许的有效期
nacos:
discovery:
server-addr: ${server.ipAddr}:8848
namespace: 31885c53-49eb-4031-9450-89e78cfb48fa
logging:
level:
root: info
# pattern:
# console: ‘[%-5level] %clr(%d{HH:mm:ss}){cyan} %logger - %clr(%msg){green} [%clr(%thread{20}){cyan}]%n‘
上述配置文件包括应用名称,端口号以及注册中心的配置和配置中心的配置,跨域请求配置.日志打印级别及控制台打印格式,还可以指定日志文件存储等,最核心的是路由的配置.注意uri是使用lb:// 这里使用了负载均衡.
我们还会在代码中实现一个token过滤器,代码片段如下:
@Value("${auth.skip.urls:skip urls not config yet}")
private String[] skipAuthUrls; //忽略验证的urls
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath().trim();
Assert.notEmpty(skipAuthUrls, "skip auth urls is not null");
//如果当前路径不需要验证就放行
if (Stream.of(skipAuthUrls).anyMatch(url -> url.equalsIgnoreCase(path.trim()))) {
log.debug("{} 不需要鉴权,its order {}", path.trim(), getOrder());
return chain.filter(exchange);
}
//TODO 获取客户端 传来的token信息进行安全鉴定
//TODO 比如使用token去redis里查看是否过期,是否在黑名单 中等
//TODO 验证通过后走过滤器链即可
//TODO 验证未通过 直接返回客户端 相应的提示信息
return chain.filter(exchange);
}
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
*
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
@Override
public int getOrder() {
return -100;
}
以上代码片段处理了需要跳过鉴权的url,比如 注册,登录等
还通过Order接口指定了执行的顺序,这里的Order,数字越小,优先级越高.
以及鉴权过程,这里为了脱敏只写了TODO 提示.
完整代码 请参照github.com/gisonwin