响应缓存

一、无感响应缓存背景 #

1.1 背景描述 #

服务稳定性对于微服务至关重要,尤其对于高负载、响应体过大、响应过慢类型的微服务。响应缓存可作为系统稳定性提升的一个重要手段,可以极大减轻微服务后端的负载。针对频繁重复请求的后端接口配置响应缓存后一方面减少后端服务自身负载提高稳定性,另一方面可以减少整个调用链路的延迟,提高访问速度。

image-20210520175112283

1.2 请求流程图 #

  • ① 服务A调用服务B /info接口
  • ② 请求首先到达服务B sidecar,根据请求查找缓存,没有则发起转发请求到服务后端
  • ③ 服务后端响应数据到sidecar
  • ④ sidecar把响应结果缓存到高速内存中
  • ⑤ sidecar返回/info请求结果
  • ⑥ 服务A再次发起/info调用
  • ⑦ 服务B sidecar 直接返回缓存数据

image-20210520174855356

1.3 功能配置流程 #

由控制面提供统一api配置响应缓存功能的开启、关闭、清除缓存等;控制面把配置统一下发到redis,sidecar实时感应配置

image-20210602133604571

二、无感缓存平台功能 #

2.1 规则配置 #

image-20210602134304392

2.2 指标遥测界面 #

image-20210602134401248

三、使用场景&测试展示 #

3.1 地区服务使用场景 #

地区服务作为中台基础服务提供查询全球地区的功能,每次请求时根据请求参数查询数据库中地区列表返回相应的数据;地区服务提供的功能是一种读多写极少的功能,可以配置响应缓存功能提高服务吞吐、降低数据库查询压力;

3.2 规则配置示例 #

  • 缓存占用sidecar最大内存 50M
  • 缓存没有过期时间
  • 地区服务所有的接口都开启响应缓存功能
  • 缓存key计算规则为,请求uri + method + 所有get参数 + 所有post参数
  • 缓存未命中时请求后端服务,后端服务响应状态码为200301404时记录缓存
{
    "unique_id": "地区服务uniqueID",
    "last_update_time": 1600000123123,
    "max_memory_size": 50,
    "ttl": -1,
    "caches": [
        {
            "match": {
                "prefix": "*"
            },
            "cache": {
                "key": {
                    "params": {
                      	"GET": ["*"],
                        "POST": ["*"]
                    }
                },
                "response_code": [
                    200,
                  	301,
                  	404
                ]
            }
        }
    ]
}

3.3 平台配置示例 #

image-20210602151031126

3.3.1 未开启响应缓存功能压测示例 #

qps ≈ 220

50响应 30 ms,

90响应 80 ms

image-20210602150654495

3.3.2 开启缓存后压测示例 #

qps ≈ 9600

50响应 370 微秒

90响应 50 ms

image-20210602150654495

3.4 压测总结 #

  • 开启响应缓存后吞吐性能大概提升了50倍左右,同时降低了地区服务查询数据库的压力。

image-20210602155508987

  • 平均响应延迟下降了4倍左右

image-20210602155625352

四、内部相关实现 #

数据面根据控制面下发规则开启响应缓存功能,协议规则设计如下:

{
    "unique_id": "aaa",
    "last_update_time": 1600000123124,
    "max_memory_size": 50,
		"flush": false,
    "ttl": 300,
    "caches": [
        {
            "match": {
                "prefix": "*",
                "path": "",
                "regex": "",
              	"methods": ["POST", "GET"],
                "headers": [
                    {
                        "name": "",
                        "value": "",
                        "regex": false
                    }
                ]
            },
            "cache": {
              	"key": {
                  "headers": [
                    "from_appkey"
                    "from_channel"
                  ],
                  "params": {
                    "GET":["*"],
                    "POST":["user_id"]
                  },
                },
                "response_code": [
                  200,
                  301,
                  404
                ],
                "ttl": 300
            }
        }
    ]
}

4.1 Unique_id:string #

需要开启响应缓存的服务识别号

4.2 Max_memory_size:int #

缓存所占用的最大sidecar内存空间,单位M

4.3 Flush: bool #

是否清除所有缓存

4.4 Ttl:int #

默认超时间单位秒,-1代表无超时时间,0代表不开启响应缓存功能

4.5 Caches:array #

{
  "match":{},
  "cache":{},
}
  • match, 匹配请求的url配置
  • cache ,缓存策略配置

4.6 Match #

用了匹配规则流量,属于规则流量则开启响应缓存功能

{
  "prefix":"",
  "path":"",
  "regex":"",
  "methods": ["GET","POST"],
  "headers": []
}
  • 路径(path)匹配
    • prefix,表示路由会匹配 path 的前缀,该配置的优先级高于 path 和 regex。 如果 prefix 被配置,那么请求首先要满足 path 的前缀与 prefix 配置相符合。
    • path,表示路由会匹配精确的 path,该配置的优先级高于 regex。如果 path被配置,那么请求首先要满足 path 与 path 配置相符合。
    • regex,表示路由会按照正则匹配的方式匹配 path。如果 regex 被配置,那么请求首先要满足 path 与 regex 配置相符合。
    • 路径匹配配置同时存在时,只有高优先级的配置会生效。
  • Methods 匹配
    • 表示请求需要匹配的请求方法,可以有多个,为空则全部匹配,与path是同时满足条件时匹配成功
  • Header 匹配
    • headers,表示一组请求需要匹配的 header。请求需要满足配置中所有的 Header 配置条件才算匹配成功。

4.7 Header #

匹配规则流量的header,符合规则路由则开启缓存,为空则不匹配

{
  "name":  "",
  "value": "",
  "regex": false
}
  • name,表示 header 的 key。
  • value,表示 header 对应 key 的 value。
  • regex,bool 类型,如果为 true,表示 value 支持按照正则表达式的方式进行匹配。

4.8 Cache #

缓存不存在时,sidecar请求后端服务获取响应数据,根据cache配置做缓存策略

{
  "key": {
    "headers": [],
    "params": {
      "GET":["*"],
      "POST":[]
    },
  },
  "response_code": [
    200,
    301,
    404
  ],
  "ttl": 300
}
  • response_code,可为空,后端响应状态码匹配时写入缓存,默认200、301、404,为空时所有响应都缓存
  • ttl,可为空,针对match设置的缓存过期时间,不存在则使用全局设置的过期时间
  • key,缓存唯一索引计算规则,默认根据uri + method,可配置请求参数加入计算, * 代表所有参数

4.9 key #

缓存唯一索引计算规则

{
  "headers": [],
  "params": {
    "GET":["*"],
    "POST":[]
  }
}
  • headers,根据headers参数计算例如:from_appidfrom_appkeyfrom_channel等等,为空则忽略
  • params,请求参数计算,GET参数、POST参数等不填则忽略

4.10 缓存存储结构示例 #

缓存的数据结构为key,value结构,通过请求的uri、method、header、param计算出唯一key,当key相同时命中缓存,返回key对应的响应数据

GET/info => {"data":[...], "state":1}
GET/info?user_id=10&type=normal => {"data":[...], "state":1}
POST/userList?page=0&limit=10&from_appkey=aaaa&from_channel=2 => {"data":[...], "state":1}
POST/userList?page=1&limit=20&from_appkey=bbbbb&from_channel=99 => {"data":[...], "state":1}