从0开始学架构: 3. 微服务网关源码剖析

一、微服务网关层的整体架构思考

先回顾下网关层的功能:

1.请求鉴权

发布商品,登录鉴权。

2. 数据完整性检查

数据定长Header,变长body

3. 协议转换

JSON-> HashMap(string,obj)
如果value不支持json嵌套的话,就string就可以。hashmap(string,string)

4. 路由转发

根据CMD转发到不同业务逻辑层

5. 服务治理

限流,降级,熔断等。

其中最核心的是1和4.
1涉及到session存到哪,
4涉及到如何把众多的HTTP请求路由到逻辑层的RPC接口。新上一个业务(逻辑层),网关不需要的重启的情况下如何发现它。

二、自研网关的各个关键点

2.1 自研网关需求剖析

打造一个高性能的分布式网关(SaaS),实现HTTP请求转发到RPC服务、接口请求鉴权、反作弊(风控、antispam)等相关功能。

要实现如下功能:

  1. 高性能分布式模块(这里模块指的Process)
  2. 鉴权功能
  3. 路由能力
  4. 简单实用的反作弊

落实到技术层面,解决方案如下:

  1. 无状态设计
  2. 过滤器责任链设计(拦截器)
  3. 反作弊设计(拦截器)
    可以理解为责任链的一部分
  4. 路由方案设计

架构参考

整体架构基于Spring boot框架。


image.png
  • 网关本质上是一个websever。

  • 注意上面的Filter责任链模式。 跨域问题
    比如,访问www.baidu.com,调用的js中有www.iqiyi.com。那么到了iqiyi的网关层,它发现主域名是来自于其他网站,就会考虑让不让它访问iqiyi,这就是一个跨域问题。

  • 网关属于高并发模块
    所以要逻辑简单,业务逻辑剥离到logic层

  • 缓存设计、异步线程设计
    反作弊模块,希望有一个黑名单缓存,比如map或set。(因为和外部交互影响性能)。黑名单应该是由数据分析得到的,比如放到redis里。那么我如何在进程内缓存呢?
    这时就要一个异步加载机制,比如每隔5s从redis里读出来覆盖set。(最终一致性),即进程内和进程外通讯。

  • 进程外缓存,配置平台
    进程外缓存就指的redis集群
    配置平台是路由逻辑用的,后面详解。

image.png

上面的图不对,需要换一下

2.2 跨域问题

从一个源(www.baidu.com)加载的文档或脚本(js),不能访问另一个源(www.iqiyi.com)的资源。

image.png

  • Access-Control-Allow-Origin
    栗子:
    比如baidu的网页想嵌入iqiyi的一个url资源,那么它会先去iqiyi的网关层探测我能不能访问你的资源,如果iqiyi允许的话,会在网关层建立一个豁免清单(允许哪些第三方的域名访问)。

  • 跨cookie Access-control-allow-credentials
    cokie是种在url里的,比如种在api1.baidu.com里的,那么api2.baidu.com就不行了。如果设置为允许跨cokie,那么cokie在任何一个二级域名里都可以(*.baidu.com).

  • 允许哪些方法进行跨域访问

2.3 Session设计

image.png

2.3.1 session绑定

本质就是将uid hash到固定的节点。
问题是高可用怎么保证呢? session一般是没必要持久化的。
思路是网关冗余,模仿数据库主从复制。


image.png

但是,网关间的数据复制如何设计呢?(要考虑vip、主从复制等,简单问题复制化了)。所以session绑定在工业界很少用。
所以一般用session绑定

2.3.2 session 复制

image.png

而且是最终一致性的,存在session重写。这种方案也是不合理的。

2.3.3 session共享

image.png

这里的redis是redis cluster。
使用缓存服务Redis,统一存储Session。

优点

  • 网关层无状态
  • 缓存服务本身高可用

缺点

  • 多一次服务调用IO
    思考如果减少IO调用,且网关是无状态化的。很自然的想到session存在客户端。

2.3.4 Session 客户端缓存

image.png

可以存在app的sqlite里
优点:

  • 简单高性能
  • 网关层无状态
    缺点:
  • 依赖客户端Cookie(session)存储
    每次带cookie,消耗一些app端的流量。

这个用的比较多,因为响应延迟低,且高可用。

2.3.5 session 生成算法

image.png

session=AES(uid + TTL + checksum)
用户第一次登录时,网关生成session,然后返回给客户端,之后每次登录时带着这个session。
本地算法,AES解密看TTL是否过期,checksum是否正确。

远程校验,是因为session在很多公司是在业务逻辑层生成的(因为更多的是业务逻辑的判断),而不在网关层生成。TTL过期后要到业务逻辑层去校验,业务逻辑层返回给GW一个新的session,GW返回给APP。这个可能很少概率。

2.3.6 session 拦截器

image.png

拦截器中会生成logid,logid是app请求的唯一标识。

以上主要两部分功能,1 是session校验,2是生成logid。

2.4 网关层反作弊需求

  • 针对恶意流量,从网关层面进行拦截,防止对后端服务造成高并发压力
  • 为了防止误伤,对于黑名单数据定期释放能力


    image.png

分析:

  1. 初期阶段
    数据量小的时候,可以内存中local cache。

  2. 高可用设计
    进程内缓存解决不了高可用

  3. 黑名单
    持久化怎么做

image.png

上面是数据量比较小的时候,那么数据量大的时候怎么做,几十G的话进程内缓存不了。
风控挖掘的时候可以存在redis里,但是为了这种小概率的事读redis会导致耗时增加。
所以可以用布隆过滤器。

思考

  • 数据量大怎么做(布隆过滤器)
    如果每次读redis,为了这小概率事件开销太大
    但是布隆过滤器具体怎么做?后面详解
  • 实时性怎么破?
    如果5s拉一次可能不够实时,如果实时性要求高,可以改成redis有更新然后push的方式。

2.5 自研网关各个击破:之路由

image.png

RPC客户端比如Dubbo,用来和网关后面的业务逻辑层的RPC services通信。

架构设计原则

  1. 初期阶段约定大于配置:
    url什么规范,接口的约定。

  2. 服务端简洁化设计

  • 功能够简单
  • 使用用够方便

负载均衡和服务发现

  • RPC框架实现

熔断设计

Hystrix

协议约定

  • 网关和APP间的数据协议是JSON
  • 网关层调用业务逻辑层入参统一为Map
  • 业务逻辑层返回统一的Result对象{code,data,msg}
    code:0, data:xxx, msg: success
    网关层拿到后再转化成json,然后返回给APP。

2.5.1 路由设计

image.png

上图其中Sevrice比如分别对应用户、商品、交易的logic。
其中RPC框架比如是Dubbo的RPC客户端,调用Dubbo的RPC服务端(即后端的logic)。

uri->Service(比如User logic),Method对应User logic中的get user方法。
这块主要做两件事:

  1. 建立url和服务间的映射关系(服务启动初始化,内存里建立映射)
  2. RPC通过反射实现远程调用。(反射生成对应的对象供调用)

25.2 具体实现:

image.png
image.png
image.png

上面讲的其实网关1.0的路由实现,有如下几个问题:

  1. 启动耗时大
    比如有几百个业务逻辑,都去网关建立路由映射关系,那么服务启动初始化的时候就会非常耗时。

  2. 业务升级需要重启,耦合严重,降低开发效率。
    比如新加个服务,需要重新到网关层注册(建立路由映射),那么网关就需要重启进程。

那么2.0就需要解决这两个问题。

2.6 网关2.0

image.png
image.png
  • 路由到通用接口。接口再去路由到具体的服务。
    这样就不需要引入service,不需要重启。基于接口编程。
    proxy每增加一个服务的时候,需要上报CLASS和Method给存储层(Mysql),网关层每隔5s扫描存储层加载到内存里。这样网关层根据CMD对应的Class和Method拿到Service IP,然后调用固定的接口。
image.png
image.png

思考 网关3.0的演进

  • 进一步解耦如何做?
    一定需要mysql作为存储层吗? 可否用注册中心或配置中心来做?
  • 泛化调用的逻辑?

三、开源框架推荐

image.png
image.png

你可能感兴趣的:(从0开始学架构: 3. 微服务网关源码剖析)