vlambda博客
学习文章列表

从源码分析微服务网关的设计

微服务网关的功能

一个高性能的分布式网关,负责将HTTP请求转发到RPC服务、进行接口权限校验、反作弊拦截等相关功能,如下图所示。


定长Header包含的内容如上图所示。


网关很重要的一个作用就是将外部的HTTP请求转化为内部的RPC请求。

RPC的架构可以看下图:

https://www.huaweicloud.com/articles/8502a2cd50c9ade2327ca09ae0897fe1.html

RPC有如下框架:

从源码分析微服务网关的设计

目前RPC的实现有两大类。

跟语言平台绑定的开源 RPC 框架主要有下面几种。

  • Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于 2011 年末对外开源,仅支持 Java 语言。

  • Motan:微博内部使用的 RPC 框架,于 2016 年对外开源,仅支持 Java 语言。

  • Tars:腾讯内部使用的 RPC 框架,于 2017 年对外开源,仅支持 C++ 语言。

  • Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,仅支持 Java 语言


而跨语言平台的开源 RPC 框架主要有以下几种。

  • gRPC:Google 于 2015 年对外开源的跨语言 RPC 框架,支持多种语言。

  • Thrift:最初是由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年贡献给了 Apache 基金,成为 Apache 开源项目之一,支持多种语言。

  • hprose:一个MIT开源许可的新型轻量级跨语言跨平台的面向对象的高性能远程动态通讯中间件。它支持众多语言:nodeJs, C++, .NET, Java, Delphi, Objective-C, ActionScript, JavaScript, ASP, PHP, Python, Ruby, Perl, Golang。


微服务网关的高性能设计

在介绍了API网关基本功能过后,接下来我们介绍设计出高性能的分布式网关的考虑点。

从增加吞吐量、降低延时的角度,实现网关的无状态化是很关键的。


从源码分析微服务网关的设计

接下来,我们先看SpringBoot的WebServer实现网关。

从源码分析微服务网关的设计

在上图中,Serverlet好理解,不再赘述。

Filter中包含跨域、反作弊、Session鉴权三大功能。

  • 什么是跨域?

跨域指的是跨网络domain访问。例如客户端是1.david.com。与,目标端是10.wei.com。从david.comwei.com的访问就叫跨域访问。

  • 什么是反作弊/风控?

客户端有没有权限,哪些用户(uid)是黑名单,哪些ip是黑名单,哪些token是黑名单?

这些没名单最好加载到网关的本地缓存。因此,在网关层内部,通常设置一个本地缓存,用于存储hashmap。这样客户端请求到来以后,去haspmap去匹配。黑名单中存在则拒绝,不存在则放行。如果缓存不放在本地,会增加相响应延迟。但缓存中的内容需要定期更新。因此需要异步加载的机制。异步加载机制是异步计算出黑名单(通过访问记录的spark计算)

从源码分析微服务网关的设计

接下来,我们查看网关时序图:

从源码分析微服务网关的设计

跨域的实现

接下来,我们先看一下跨域的代码实现:

  • cross-origin resources sharing CROS:建立豁免清单

  • access-control-allow-credentials   是否允许cookike跨域

  • access-control-allow-origin   对http请求头设置豁免

  • access-control-allow-method允许提交请求的方法


建立豁免清单:在代码中,开启跨域访问,允许源端为1.david.com的域访问请求。也就是说从这个源加载脚本或者API,可以访问目标域。

 
public class OptionCheckFilter extends BaseFilter {private static final String ALLOWED_HOST = "http://1.david.com";

容许Cookie跨域,做Session检查

从源码分析微服务网关的设计

Session的无状态设计

接下来,我们看session设计。

读写请求的上下文对象,我们称之为session(username+password)。

session和登录用户数有关,有一个用户登录就有一个session。如果允许一个用户多端登录,那session会更多。因此session不能在网关上单机存储。需要设置session的分布式。

从源码分析微服务网关的设计


Session的绑定。这种方法是有状态的,不靠谱。

从源码分析微服务网关的设计

Session全复制,session多了后,这个方案不靠谱!

从源码分析微服务网关的设计

通过Redis存储session:

从源码分析微服务网关的设计


Session存储在客户端:

从源码分析微服务网关的设计

接下来,我们介绍一下Session的生成算法。

Session是遗传具有一定时效性的加密字符串字符串(通常有效期7天),通常由服务端生成和解析。


Session通常包含的字段有:deviceid、clientType、uid、ts、checksum等。总之要和业务相关。加密方案通常采用AES,加密和解析都在服务端。



网关的反作弊/风控设计

针对恶意流量,从网关层面进行拦截,反正对后端服务造成高并发压力。Eileen防止误伤,对黑名单数据定期释放的能力。通过黑名单,规避爬虫、网络攻击的问题。


黑名单可以缓存在网关内部的缓存,使用Key-List存储。

从源码分析微服务网关的设计

从源码分析微服务网关的设计

在上面的方案中,如果业务量特别大时,使用进程内缓存就不靠谱。就需要将黑名单存在外部缓存,但这时候可以增加一个布隆过滤器。(https://juejin.cn/post/6844904007790673933)


网关的路由逻辑设计

访问请求通过网关的Filter层以后,就会进入Controller层。这时会先进行请求参数处理。对请求做一个解析,拿出请求的URI和parm参数。接下来通过RPC的框架调用ServiceName,然后调用Service的Method。


也就是,首先建立URI和Service的联系,然后建立URI和Method的联系。而URI就是一个请求包里的CMD。

从源码分析微服务网关的设计

上图的核心实现:

  1. 扫描出URI对ServiceName的映射

  2. RPC通过反射实现远程调用(通过Invoker)

下面代码是业务逻辑层的代码。

从源码分析微服务网关的设计

客户端调用方法如下:

api.wei.com/david/getSettleBillDetaild


网关通过扫描注解获取映射关系。

下面代码是网关上的。

从源码分析微服务网关的设计

从源码分析微服务网关的设计


路由的本质是通过URI找到method,通过反射进行Service的调用

从源码分析微服务网关的设计

但是,以上方法很不优雅。主要问题在于,当我们业务逻辑层增加新的模块时,需要重启网关。


我们在业务逻辑层增加Proxy,他负责URI->Class.method的映射。将URI到ServerName的映射放到存储层,如MySQL中。

从源码分析微服务网关的设计

通过上图展示的逻辑,当业务逻辑层有新的模块,网关也不需要重启生效。因为Proxy会自动上报给存储层,而网关会定期自动扫描存储层。

我们以客户端请求如下链接为例:10.wei.com/user/get?uid=1


最后,我们看一下proxy的启动和代理阶段: