网关服务器性能,服务网关API路由导致的性能问题分析

2023-11-16

背景

酷家乐是从 16 年初开始进行服务化改造的,因为一些特殊原因,无法直接使用主流的dubbo 或 spring cloud,因此酷家乐研发团队在开源的基础上做了二次开发,迅速上线了一套定制型的微服务框架。

和其他微服务框架类似,酷家乐自己定制的微服务框架也有专门的服务网关,今天要讨论的就是服务网关中存在的一些问题。

何为服务网关?服务网关封装了系统内部架构,为每个客户端提供一个定制的 API。此外,服务网关还会提供如身份验证,限流,熔断,安全等功能。

所有的客户端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供 REST/HTTP 的访问 API。服务网关会将客户端的请求转发到能够正常处理该请求的微服务当中。

下图简单阐述了服务网关在常见系统中所处的位置和作用b4b9ac0a99b9cf4b95a5f3b2373caf59.png

下面会逐步介绍酷家乐在不断迭代微服务框架过程中,在服务网关处踩的各种坑。

故障回顾

高CPU占用

16 年某日,服务网关持续 CPU 报警,排查定位过程比较简单,随意挑选了一台服务器并获取其 thread dump,观察到大量的线程都在做以下操作8ce88d3dd78b51f42eab23064642d0c3.png

上图中,除了 IO 操作之外,主要就是字符串操作占用 CPU 时间。

最初期的服务网关基本就是根据 Spring MVC 的 Request Mapping 流程来模拟,对请求进行路由,查找匹配的服务并转发。

这里存在的问题也很明显,因为要遍历所有的 API Pattern,效率比较低,此外,由于有大量的 API 使用了 Restful 风格,包含 PathVariable 的 API 数量比较多,因此正则匹配次数也特别多,所以 CPU 占用率居高不下。

随着系统不断庞大,这个问题会越来越明显。

研究了前面所述的 Pattern 格式,我们发现在 REST 风格的 API URL 中,存在以下规律:

URL 基本都以“静态字符串”开头,形成了用于区别不同 service 的“名字空间”,动态部分一般都在URL Pattern的尾部。

根据这个规律,我们发现用字典树的结构来组织 URL Pattern 可以大大降低 URL 匹配所花费的 CPU 时间:

对于每个 URL Pattern,我们截取第一个动态表达式之前的“静态前缀”,形成一颗字典树。根节点代表的 token 为空,其余每个节点中包含两个集合:private static class Node {

Map children;// 子节点

List patterns;// 当前 URL 前缀所匹配的 Patterns

}

用该规则构建的一个字典树例子如下:1d789382ba637be6ac81ad3de4758b02.png

对于每个请求,我们进行以下操作:用“/”切分 URL,得到多个 token

从字典树的根开始,从上往下寻找匹配的最长路径

取出最后匹配的 Node 节点中的 patterns 集合,遍历该 patterns 集合,找出最终匹配的 URL Pattern

用字典树结构重写了整个 URL Route 逻辑之后,CPU 占用时间明显下降,达到了优化的目的。

FullGC

17年某个周四下午开始,服务网关开始间接性 FullGC,每次 FullGC 时间持续大概 2-3 秒,服务网关负载瞬时飙高后马上恢复。

当时出现这个报警有点不可理解,那一周因为峰会原因暂停了部署,所以所有服务的代码都是没有改变的,并且根据监控的数据来看,当时的访问量也没有增加太多,和之前几天的差不多。究竟为何产生 FullGC,还得看客观数据。

为其中一台服务器添加了 HeapDumpBeforeFullGC 和 HeapDumpAfterFullGC 后继续观察,获取到两份 dump 文件对比后发现,被 GC 掉的主要是 int[] 和 SimpleScalar 两类对象。

这里的 int 数组和 SimpleScalar 大量都是 unreachable,SimpleScalar 是 freemarker渲染产生的,因为历史原因,还有少部分页面渲染是在服务网关处理的,观察监控中 GC 时段的页面请求,没有发现异常。并且 SimpleScalar 不是最主要成员,因此排除相应可能性。

那么这些 int 数组到底是哪里来的?

这里还有一个疑点,HeapdumpBeforeFullGC 获取到的 dump 文件居然只有 1.4G 的大小,远远小于堆大小,这并不符合正常逻辑。

查看了 FullGC 前的部分 gclog,发现这段时间有比较多的 mix-gc,并且看到有不少 G1 Humongous Allocation 等信息。

看到这些,便能联想到为何那份 FullGC 前的 dump 文件小那么多了。

服务网关使用的是 jdk8+G1GC,G1 的各代存储地址是不连续的,每一代都使用了 n 个不连续的大小相同的 Region,每个 Region 占有一块连续的虚拟内存地址。

humongous object 即大小大于等于 Region 一半的对象,有如下几个特征:

humongous object 直接分配到了老年代空间,防止了反复拷贝移动。

humongous object 在 global concurrent marking 阶段的 cleanup 和 Full GC 阶段回收。

在分配 humongous object 之前先检查是否超过 initiating heap occupancy percent 和the marking threshold, 如果超过的话,就启动 global concurrent marking,为的是提早回收,防止 evacuation failures 和 Full GC。

因此猜测之所以 dump 文件大小差那么多的原因就在于产生了大量的 humongous object,而这些对象在 dump 之前就先被回收了。

通过监控和 GC 日志可以看出这些对象是短时间内迅速产生的。53c3b1859cd88367b3bf0091844e0161.png

为了证实猜想,并且找到产生这些对象的来源,通过监控和报警的数据,将其中一台即将执行 FullGC 的服务器移出线上服务,并且对其执行 dump,此时获取到的内容和之前 1 个多 G 的截然不同。

以下是新的 dump 解析结果:75b1c1626832e4137ad763c1d59a69d5.pnga012b1960eeb259b7389a374c43655de.png

这就能够很清楚地看到,大量的 int 数组是 Matcher 和 Pattern 对象产生的,而这两个对象就直接能想到是路由匹配时的正则产生的。

Pattern Compile 十分消耗性能,且会产生大量临时对象,之前这里采用的是 spring3 中 PathMatcher 的实现,这里会在每次请求时都生成对应的 Pattern。

知道了原因以后就可以做对应的优化了,优化目标是减少 Pattern 的 Compile 和 Matcher 对象的创建。

spring在4.0 版本以后也对 AntPathMatcher 做了优化,AntPathMatcher 内部对 Pattern 和 Matcher 都进行了缓存,并且对缓存大小进行了设置,当缓存大小大于 CACHE_TURNOFF_THRESHOLD 时,该缓存功能会被关闭

CACHE_TURNOFF_THRESHOLD 默认为 65536

具体实现可以参照 spring4 中 AntPathMatcher 的 getStringMatcher 方法和 tokenizePattern 方法。

优化部署后 FullGC 不再发生,该问题得到解决。

进一步优化

然而这里的优化还可以更进一步

spring的匹配实现还默认使用了 SuffixPaternMatch 和 TrailingSlashMatch,这些功能其实目前我们都没有用到,完全做了多余的判断,产生了多余的对象,这里可以都设置为 false。

上面提到的优化方式总结下来思路基本都是相同,减少正则匹配次数,但是这没有解决最根本的问题,无法很好地应对未来的扩容。

之前的网关效率最低之处在于,对于所有请求都需要进行遍历查找。那么如何解决这个问题呢?

其实对于这个问题,nginx 算是老司机了,nginx 的 location 基本都会基于前缀匹配来进行转发到对应的 upstream。

spring cloud 的 zuul 的实现也是类似,微服务场景下,后端微服务其实就对应了nginx 的 upstream。

因此,需要对微服务框架进行改造。

改进一:每个微服务可以设置自己的路由规则,服务网关优先根据路由规则进行匹配,直接将匹配到的请求转发到对应的服务。

例如  diyrenderservice 设置了路由规则namespace:drs

那么所有以/drs开头的请求就会直接转发到 diyrenderservice

但是如果这个路由规则没有命中,或者实际提供对应服务的微服务没有设置路由规则的话该怎么办呢?

这里只能退化为遍历查找,考虑到历史原因,还有很多 api 是以 /api 开头,并且后面几级的命名没有 namespace 可以区分,完全无规律,并且分布在多个服务中,这些都是服务拆分时的后遗症。

如果要把这些 API 都规范掉需要投入比较大的重构精力和时间,因此为了兼容,需要在路由配置没有匹配到的情况下,降级为遍历查找。

改进二:客户端请求指定服务名称,客户端请求每个 API 时都指定对应的服务名称,添加一个特殊的 header 存放该 API 对应的服务名称,服务网关先检查当前请求是否有这个特殊的 header,如果有,则直接转发到指定服务。

总结

虽然通过代码优化解决了 PathMatch 的问题,但是这类问题的产生和我们的 API 命名也有比较大的关系,部分 API 命名特别长,并且都是 PathVariable,对应的 PrefixTree 高度已经超过了8层,产生的 Pattern 数量也比较多,在路由的时候代价都比较高。

归根结底,路由部分最大的瓶颈在于正则匹配,而产生大量正则匹配的根源是 Restful 风格的 PathVariable。

反观一下互联网模式的数据库表结构设计,为了业务扩展和性能考虑,基本都是采用反范式的思路去设计,更多采用空间换时间的做法并保证最终一致性。这和学院派的范式设计截然相反。

回到这个问题上,API 命名似乎也走上了这条路,主流的 Restful 风格命名成了性能瓶颈的根源之一,人为将 PathVariable 都转换为 RequestParameter 可破。但是对于有 SEO 需求的页面来说,Restful 的写法还是需要的。当然我们也不能一棒子打死,就规定不能用 Restful 的写法了,只是从规范角度看,少写 Restful 风格的 API 可以提升性能。

关注我们

酷家乐质量效能团队热衷于技术的成长和分享,几乎每个月都会举办技术分享活动(海星日),每半年举办一次技术专题竞赛分享(火星日),并将优秀内容写成技术文章。

我们尽可能保障分享到社区的内容,是我们用心编写、精心挑选的优质文章。如果您想更全面地阅读我们的文章,请您关注我们的微信公众号"酷家乐技术质量"。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网关服务器性能,服务网关API路由导致的性能问题分析 的相关文章

  • STM32 ADC采样不准怎么办

    最近在使用STM32L011的ADC时 发现ADC采样值极其不准确 经过查找资料 和反复测试 总算摸出点门道 如将VDDA接到VDD 那么电源的波动会极大的影响ADC精度 不过使用内部参考电压可以计算实际的VDDA电压 说明 VREFINT
  • Ubuntu 16.04下基于Anaconda安装cuda、cudnn及Pytorch

    Anaconda的基本使用见之前的博客 Ubuntu 16 04下Anaconda的安装及使用 创建虚拟环境pytorch gpu 并激活该环境 conda create name pytorch gpu python 3 6 source
  • CSS浮动(1)

    一 结构伪类选择器 1 作用与优势 作用 根据元素在HTML中的结构关系查找元素 优势 减少对于HTML中类的依赖 有利于保持代码整洁 场景 常用于查找某父级选择器中的子元素 2 选择器 选择器 说明 E first child 匹配父元素
  • python 数据库的中文乱码问题

    在使用pymssql操作sqlserver数据库时 碰到了中文乱码的问题 之前从网上查了很多 又是python编码 又是数据库编码什么的 非常乱 试了很多 好多都解决不了问题 数据库是sqlserver 先说我碰到的问题 数据库的配置cha
  • 【安卓学习之常见问题】 AAPT: error: resource drawable/ (aka xxx) not found(含as快捷键说明)

    安卓学习之常见问题 AAPT error resource drawable aka xxx not found 含as快捷键说明 相关文章 android学习开源项目之BasePopup BasePopup PopupWindow 进行二
  • c语言编程题

    include stdafx h include
  • 概念解析

    注1 本文系 概念解析 系列之一 致力于简洁清晰地解释 辨析复杂而专业的概念 本次辨析的概念是 非极大值抑制 NMS 及其改进工作 非极大值抑制 NMS 原理 缺点和改进 1 背景介绍 在计算机视觉中 物体检测是一个核心且充满挑战的问题 众
  • MQTT 协议基本介绍

    目录 一 简介 二 基本特点 三 基本概念 四 简单示例 五 进一步了解MQTT 3 六 MQTT协议的工作方式 七 MQTT控制报文 CONNECT报文 CONNACK报文 八 清除会话 保留消息和QoS的组合 九 MQTT 5 0 协议
  • Hyperledger Fabric环境搭建流程记录详解

    Fabric环境搭建记录 什么是超级账本Fabric 1 Fabric的本质 与一般区块链技术的相同点 Fabric与其他区块链技术一样 都有一个账本 和以太坊相像 也允许使用智能合约 从本质上看 它是参与者共同管理交易的系统 是联盟链的典
  • python分析excel数据-对照Excel使用Python进行数据分析,更快掌握

    Excel和Python 作为数据分析的主流工具 在从效率提升到数据商业化的整个过程中 都起到了重要作用 不管是在Excel中通过鼠标点选实现 亦或是利用Python通过代码实现 数据分析中的很多基础功能都是相通的 在数据量级大跃进的今天
  • HTTP协议原理

    一 http协议是什么 HTTP协议 全称HyperText Transfer Protocol 中文名为超文本传输协议 是互联网中最常用的一种网络协议 广泛应用于Web浏览器和Web服务器之间的应用层通信协议 在Internet上的Web
  • 基于flask与tk的网络粘贴板

    基于flask与tk的网络粘贴板 由于本人一直用 linux 所以平时和我的队友们交流代码 十分的不方便 没有办法 我就自己写一个功能简单但是特别适合我自己用的一个网络粘贴板 只有两个按钮 下载 和 上传 我的这个应用分成两个部分 一个是服
  • MFC之树形控件25

    1 树形控件 先了解相关知识 1 创建基于对话框的项目 2 删除对话框原有的内容 添加树形控件TreeControl 3 右击树形控件属性 然后添加相应属性 4 在资源视图里面右击icon文件夹 添加资源 选择icon导入预先准备好的图片
  • idea plugins一直在转圈解决方法

    方案一 配置代理 https plugins jetbrains com 然后重启idea 方案二 打开ip查看网站查看plugins jetbrains com的ip ip查看 然后将ip配置到host文件中 打开host文件 C Win
  • 胖AP与瘦AP的区别以及胖瘦AP组网的优劣对比

    一 胖瘦AP如何区分 无线AP通常可以分为胖AP Fat AP 和瘦AP Fit AP 两类 不是以外观来分辨的 而是从其工作原理和功能上来区分 当然 部分胖 瘦AP在外观上确实能分辨 比如有WAN口的一定是胖AP 胖AP除了前面提到的无线
  • 算法编程题-字符串类型题目

    1 介绍 在笔试面试中 字符串类型题目相当广泛 原因有一下几点 1 字符串可以看做是字符类型的数组 与数组的排序 查找 调整有关 2 很多其他类型的题目最终可能会转化成字符串类型的题目 2 需要掌握的几个概念 1 回文 2 子串 连续 3
  • NLP实践——VQA/Caption生成模型BLIP-2的应用介绍

    NLP实践 VQA Caption生成模型BLIP 2的应用介绍 1 简介 2 模型下载 3 运行环境 4 模型应用 1 简介 今天介绍一个跨模态模型 也是最近比较火的一个工作 叫做BLIP 2 很久很久之前我写过一个简单的image ca
  • ubuntu14.04安装CUDA7.0、CUDNN7.0详细步骤

    一 安装CUDA7 0 CUDA 7 0在Linux下的安装步骤参见官网手册 CUDA Getting Started Linux 其中提及了 run deb 等安装 1 检查你的电脑是否支持CUDA 检查GPU是否支持 输入如下命令 如果
  • uniapp运行到小程序报错之[ app.json 文件内容错误] app.json: app.json 未找到

    解决方法 在project config json文件下面新增如下代码 unpackage dist dev mp weixin是你自己的代码 打包下dist文件夹下的项目名称 和pages同级的文件夹 miniprogramRoot un

随机推荐

  • Angular学习笔记48:响应式表单-FormArray 和 FormGroup的多层嵌套

    继Angular学习笔记46 响应式表单 使用FormBuild快速构建表单 可以使用FormBuilder快速便捷的构建出需要的表单 有时候 在FormArray中 不仅仅是一个控件 有可能是多个 这个时候 这个FormArray中的元素
  • Dynamics CRM on premise 和 Skype for Business 集成显示用户状态

    Applies To Dynamics 365 online Dynamics 365 on premises Dynamics CRM 2016 Dynamics CRM Online Using Skype for Business w
  • EMC一些常见问题(面试也会稍微提及)

    什么是EMC测试 如何进行EMC测试 什么是EMC测试 如何进行EMC测试 EMC EMI设计 电子发烧友网 elecfans com 1 为什么要对产品做电磁兼容设计 答 满足产品功能要求 减少调试时间 使产品满足电磁兼容标准的要求 使产
  • STMF103学习笔记(三)——按键输入

    实验三 按键输入 注意输入模式的设置 按下时为高电平设置为下拉模式 按下时为低电平设置为上拉模式 这样输入时 上拉模式的得到高点平 下拉模式得到低电平 具体原理还不是很懂 按键IO输入初始化参考代码 void KEY Init 这里初始化k
  • 图像分割与目标检测与区别

    检测与分割的区别 https www leiphone com category yanxishe Fah5xOL3Qb96k1NL html 1 目标检测 预测包围盒 YOLO Fast RCNN 似乎还有个SSD 输入 一个矩阵 输入图
  • 6-python异常、错误、模块、包

    文章目录 1 异常 2 错误 3 模块 4 包 综合练习 1 异常 打开一个不存在的文件会引发异常 FileNotFoundError Errno 2 No such file or directory D 不存在的文件 txt f ope
  • Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException

    spring boot mybaits 出现异常 The error may exist in file F javatext basi code frame springboot query2 target classes mapper
  • 刷脸作为数据入口应用在新零售领域上

    日常生活中 我们所常见的人脸识别大多应用在安防领域 例如刷脸门禁 刷脸签到和刷脸考勤等 将人脸识别技术应用在零售消费领域 可能要从支付宝推出蜻蜓刷脸支付收银机开始 人脸识别技术在零售消费领域上作为支付凭证 除了刷脸支付 人脸识别技术应用在零
  • VUE 之 Jspreadsheet CE电子表格数据处理

    需求 将表格内容作为list集合 向后端传递 注意 需求默认展示表格为30行空表格 后端接口会拦截空值 所以需将未输入的行去掉 默认的Jspreadsheet 实现步骤 获取Jspreadsheet 数据并处理成后端所需参数格式 var t
  • 如何查看在rabbitMQ里面堆积的消息

    回顾上一节的内容 通过配置了stream来对MQ的简单的封装 主要定义接口 里面定义input以及output来绑定交换机 获取消息通道MessageChannel以及订阅通道SubscribableChannel对象 而消费端配置 str
  • Python编程基础之三对象

    一 简介 Python使用对象模型来存储数据 构造任何类型的值都是一个对象 再加上内建类型 标准类型运算符和内建函数 有助于更好的理解Python是如何工作的 二 详解 1 Python的对象 所有的 Python 对像都拥有三个特性 身份
  • Windows与网络基础22-数据封装与解封装

    数据的封装和解封装 目标 理解数据的封装与解封装过程 针对于一个简单的网络环境 能够独立讲解出网络传输过程 目录 一 数据封装过程 二 数据解封装过程 三 每一层对应的网络设备 四 简单网络数据封装解封装实例 一 数据封装过程 应用层 将原
  • 12、文件链接、磁盘阵列、文件系统、网络协议、数据封装过程

    一 文件链接 ln s 软链接 ln 硬链接 区别 1 软链接产生新的inode号 硬链接不产生新的inode号 ls i 看inode 2 源文件删除后 软链接文件不可以用 硬链接文件可用 3 软链接可以跨分区 硬链接不可以跨分区 4 不
  • 关于扫描二维码拒绝获取摄像头权限导致的错误解决方法

    这个问题烦了我2天 在网上查阅资料 也许是自己的理解错误 怎么改都不行 今天换了一种思维 解决了这个问题 废话不多说 先上代码 try mCameraManager openDriver catch IOException e e prin
  • ROS集成开发环境搭建【安装VScode】

    1 下载 注意 下载操作是在虚拟机中的Ubuntu中进行的 可以下载到 home 下载 文件夹中 vscode 下载链接 最新版本 Documentation for Visual Studio CodeFind out how to se
  • (c/c++)——STL

    文章目录 一 迭代器 iterator 二 容器 1 string 2 Vector 最常用 3 list 4 map 5 unordered map 6 queue 三 算法 1 for each 2 find if 3 sort 一 迭
  • 大数据毕业设计 人脸识别与疲劳检测系统 - python opencv 图像识别

    文章目录 0 前言 1 课题背景 2 Dlib人脸识别 2 1 简介 2 2 Dlib优点 2 3 相关代码 2 4 人脸数据库 2 5 人脸录入加识别效果 3 疲劳检测算法 3 1 眼睛检测算法 3 2 打哈欠检测算法 3 3 点头检测算
  • taro框架开发小程序经验总结

    最近这一年来 做了三个小程序 第一个小程序用的原生框架 所有的样式和js都要自己写 不好看还写得头疼死 写第二个小程序的时候正觉得VUE3流行 想熟悉一下VUE3 因此找到了taro框架 这个框架好不好我也无从分辨 暂时能用就行 一 搭建项
  • 手把手看监控--当不设置JVM-Xms时

    背景 运维埋的一个坑 在该应用上只配置留 Xmx 没有配置 Xms 表象 堆内存从0 2G开始 最大到0 8G 就开始执行GC 导致频繁GC 大致间隔1分钟 次 从下图左侧即可看到 解决 增加 Xms重新发版本 堆内存 GC间隔明显看着好多
  • 网关服务器性能,服务网关API路由导致的性能问题分析

    背景 酷家乐是从 16 年初开始进行服务化改造的 因为一些特殊原因 无法直接使用主流的dubbo 或 spring cloud 因此酷家乐研发团队在开源的基础上做了二次开发 迅速上线了一套定制型的微服务框架 和其他微服务框架类似 酷家乐自己