一次Kubernetes Pod内存异常导致的测试环境耗时异常问题排查过程

概述

在使用公司内部后台系统测试环境时发现一个请求加载慢的问题,简简单单的列表,查询MongoDB数据库,测试环境不过几百上千条数据而已,请求耗时居然高达5~6秒:
在这里插入图片描述
作为对比,生产环境的请求响应截图如下:
在这里插入图片描述
经过持续跟进,该后台系统所有列表页面测试环境普遍比生产环境慢,不管是MongoDB还是MySQL数据库。

既然不是一个页面,也就是说查询的数据库类型不止一种,查询的DB和表不止一个,可排除因为测试环境和生产环境数据库表的索引不一致导致的。

是的,来到这家公司,发现之前根本就没有一个完善、规范、可审计、可追踪的数据库表变更上线审批工单系统;不管是开源的还是自研的,都没有。入职3个月来,收拾各种烂摊子,搭建并维护一个简陋版的开源SQL审计上线平台Archery。但是不能保证同一张DB数据表,测试和生产环境的表定义Schema相同。

另外,不管是测试还是生产环境,应用发布都是基于Git Tag。使用GitLab的compare功能,不难得知代码是同一套。于是把问题的症结抛给运维。但是没有得到很好的答复。

事实上,同后端架构技术交接一样,运维交接也是零,没有任何Wiki记录文档,没有任何交接文档,自己摸索去吧。基础设施,包括Kubernetes、网络、ELK、Nginx配置、网络转发,也是各种乱七八糟。

排查

测试环境请求慢

上面两个请求耗时异常慢的接口,都是在backend服务,都是从gateway-b网关服务转发到具体的业务承载服务。

gateway有如下两个Pod:
在这里插入图片描述
请求转发时,随机选择一个Pod节点,默认情况下ELK查看的是所有Pod里搜集到的应用日志。如果只想查看某个Pod的日志,要么在ELK日志查询平台指定IP:
在这里插入图片描述

要么使用Rancher的日志查看功能:
在这里插入图片描述

另一个Pod:
在这里插入图片描述
上面的日志截图不完全,一个比较完全的网关转发层日志记录截图如下:在这里插入图片描述
gateway只是一个网关转发层,接口耗时还是得去看一下具体的接收请求的服务,如backend服务,找到如下日志:
在这里插入图片描述
截图里的日期时间以及TraceId不是重点。可看到backend服务使用ControllerLogAop记录requestBody和responseBody日志,某一次真实请求耗时仅12ms。算上请求跨微服务转发,也不可能长达几秒。所以问题应该在网关层应用上。

另外,关于日志记录多扯一句,由于所有应用都是经过gateway网关服务转发,完全可以在gateway服务里记录接口请求的requestBody和responseBody。除了在gateway里记录请求日志。在真正承载业务请求的若干个服务里也冗余Ctrl + C/V若干个ControllerLogAop类。也就是说,两层日志记录。

PS:这个测试环境请求慢的问题,优先级很低,重启可以解决,有空就去排查,前前后后1个多月搜集到若干个截图,还没定位到问题根源,也没有彻底解决。

可以看到日志打印类是PermissionFilter,看下源码(有删减):

@Slf4j
@Component
public class PermissionFilter implements GlobalFilter, Ordered {private static final String BLACK_TOKEN = "BLACK_TOKEN:";@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate JwtTokenUtil jwtTokenUtil;@Value("${jwt.header}")private String tokenHeader;@Value("${gwb.referer}")private String imsHost;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {final int NO_OPERATION_PERMISSION_CODE = 9641;final int AUTH_FAILED = 9642;ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();String requestPath = request.getURI().getPath();log.info(requestPath);long s1 = System.currentTimeMillis();long s3 = 0;HttpHeaders headers = request.getHeaders();String username = headers.getFirst("username");if (!requestPath.contains("/auth/login/ldap")) {Assert.notNull(username, "header中的username不能为空");final String requestHeader = headers.getFirst(this.tokenHeader);Boolean invalid;String blackToken = null;if (StringUtils.isEmpty(requestHeader)) {log.error("token为空!");invalid = true;} else {try {long s2 = System.currentTimeMillis();log.info("header time consuming:{}ms", s2 - s1);String authToken = requestHeader.substring(7);blackToken = (String) redisTemplate.opsForValue().get(BLACK_TOKEN + authToken);invalid = jwtTokenUtil.isTokenExpired(authToken);String tokenName = jwtTokenUtil.getUsernameFromToken(authToken);s3 = System.currentTimeMillis();log.info("redis and token time consuming:{}ms", s3 - s2);if (!username.equals(tokenName)) {Response<Void> response = Response.error(AUTH_FAILED, "token非法!");log.info("token中用户与username不一致!");DataBuffer bodyDataBuffer = response.bufferFactory().wrap(JsonUtil.beanToJson(response).getBytes(StandardCharsets.UTF_8));return response.writeWith(Mono.just(bodyDataBuffer));}} catch (Exception e) {log.error("jwt校验发生异常!", e);invalid = true;}}if (invalid || !ObjectUtils.isEmpty(blackToken)) {Response<Void> response = Response.error(AUTH_FAILED, "token已失效!");log.info("token失效!");DataBuffer bodyDataBuffer = response.bufferFactory().wrap(JsonUtil.beanToJson(response).getBytes(StandardCharsets.UTF_8));return response.writeWith(Mono.just(bodyDataBuffer));}String postData = (String) redisTemplate.opsForValue().get(username);HashSet<String> roles;if (StringUtils.isBlank(postData)) {roles = Sets.newHashSet();} else {roles = (HashSet<String>) JSON.parseObject(postData).get("roles");}long s4 = System.currentTimeMillis();log.info("redis time consuming:{}ms", s4 - s3);// 初始值,默认为false,表示无权限AtomicBoolean isPermission = new AtomicBoolean(false);if (roles.contains(requestPath)) {log.info("path={}", requestPath);isPermission.set(true);} else {roles.forEach(role -> {if (requestPath.contains(role)) {log.info("role={}", role);log.info("path={}", requestPath);isPermission.set(true);}});}// 停止转发没有用户登录的请求if (!isPermission.get()) {Response<Void> response = Response.error(NO_OPERATION_PERMISSION_CODE, "权限不足,请检查配置!");log.info("用户没有操作权限");DataBuffer bodyDataBuffer = response.bufferFactory().wrap(JsonUtil.beanToJson(response).getBytes(StandardCharsets.UTF_8));return response.writeWith(Mono.just(bodyDataBuffer));}long s5 = System.currentTimeMillis();log.info("other time consuming:{}ms", s5 - s4);}return chain.filter(exchange);}@Overridepublic int getOrder() {return Integer.MIN_VALUE;}
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试环境XXL-Job任务调度异常

上面的问题并没有定位到根源。于此同时,微服务若干个定时任务采用XXL-Job调度平台,基于Spring Cloud Gateway来实现请求转发,参考Spring@Scheduled定时任务接入XXL-JOB的一种方案-基于SC Gateway。

测试环境定时调度任务收到如下执行异常告警邮件:
在这里插入图片描述
进入测试环境的XXL-Job管理平台,查看调度日志:
在这里插入图片描述
可知问题是偶发,具体的错误日志:

[com.aaaaa.gateway.config.SampleXxlJob#httpJobHandler]-[99]-[Thread-72] java.net.ConnectException: Connection refused (Connection refused)at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:403)at java.base/java.net.Socket.connect(Socket.java:591)

熟悉的连接被拒绝:java.net.ConnectException: Connection refused

进一步分析应用层日志,8点5分和5点5分两次的定时任务执行成功:
在这里插入图片描述
打印xxlJob调度执行返回数据=这一行日志,也就是有回调动作的,才算是任务执行成功。

实际上,任务调度已经随机下发成功,即选择一个Kubernetes Pod成功,只是没有收到执行成功的回调。

穷途末路

上面两个问题都定位不到根源,穷途末路。

本地Debug模式启动gateway网关应用,借助于IDEA插件Profiler,也没分析出个啥。

本地Debug模式启动包括gateway网关应用在内的多个服务,通过gateway转发请求到别的服务,如backend,速度也很快,Postman显示不到1s。

考虑到本地可以连接到测试环境Redis节点,编写单元测试:

@Test
public void testRedis() {long s1 = System.currentTimeMillis();String postData = (String) redisTemplate.opsForValue().get("my.domain.name");HashSet<String> roles = (HashSet<String>) JSON.parseObject(postData).get("roles");long s2 = System.currentTimeMillis();log.info("time consuming:{}ms", s2 - s1);
}

多次执行结果:

time consuming:130ms
time consuming:114ms

本地连接Redis速度挺快,不到150ms。为啥测试环境kubernetes集群连接Redis取数据耗时,短的要1s左右,长的要10s左右???

分析过SkyWalking Dashboard,没看出个啥。

Kubernetes Pod内存不一致

分析kubernetes Pod。借助于Prometheus + Grafana提供的分析面板Dashboard:
在这里插入图片描述
发现两处不太正常的地方:

  • 两个Pod内存指标数据不一致,差距有点大。

具体来说,一个Pod Current内存是1.419GiB
在这里插入图片描述
另一个是2.013GiB。
在这里插入图片描述

  • 都是保持着持续上涨的趋势

从1月24日应用发布以来到2月4日,两个Pod的Limit和Requested不变,是一条直线。其中Requested都是512MiB,Limit都是4GiB。

Current和Cache一直保持增长,Current总是大于Cache。截图没有体现出来,截止到2月4日,Current为1.580GiB,Cache为1.502GiB:
另一个Pod差不多也是这样的增长趋势:
在这里插入图片描述
但在1月29日凌晨左右,Cache超过Current保持一路高升趋势,到2月4日Cache高达3.193GiB,Current高达2.405GiB:
在这里插入图片描述
其余指标,如CPU和Network IO一直都很平稳。

参考

  • kubernetes-pod-high-cache-memory-usage

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/668467.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java程序设计】【C00194】基于SSM的高校财务处理管理系统(论文+PPT)

基于SSM的高校财务处理管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的高校从财务处理系统 本系统分为财务和管理员2个功能模块。 财务&#xff1a;财务登录高校财务处理系统后&#xff0c;能对首页、个人中心…

链表经典算法(+OJ刷题)

文章目录 前言一、移除链表元素二、链表的中间节点三.反转链表四.合并两个有序链表五.分割链表六.环形链表的约瑟夫问题总结 创作不易&#xff0c;点赞收藏一下呗&#xff01;&#xff01;&#xff01; 前言 在上一节&#xff0c;我们介绍了单链表的增&#xff0c;删&#xff…

docker手动迁移镜像

1&#xff0c;将镜像保存在本地 docker save 镜像名称:版本号 > 镜像名称.tar 2&#xff0c;下载镜像 通过 ftp 工具或者命令&#xff0c;下载到本地 3&#xff0c;上传镜像到目标 docker 所在服务器 4&#xff0c;导入镜像 docker load < 镜像名称.tar

Electron实战(一):环境搭建/Hello World/打包exe

文章目录 Electron安装Node.jsNodeJs推荐配置开始Electron项目创建index.js文件创建src目录运行打包生成exe生成安装包踩坑 下一篇Electron实战(二)&#xff1a;将Node.js和UI能力&#xff08;app/BrowserWindow/dialog)等注入html Electron Electron是一个使用JavaScript, HT…

AI-数学-高中-22-tanx的图像与性质

原作者视频&#xff1a;三角函数】9tanx的图像与性质&#xff08;易中档&#xff09;_哔哩哔哩_bilibili 做题时注意先画图&#xff0c;再计算。

RabbitMQ(保姆级教程)

RabbitMQ学习 基础 1. 同步通信和异步通信 同步调用 下一步动作必须依赖上一步 异步调用 通知到位就行&#xff0c;不对消费者做强制要求&#xff0c;只要求最终一致性就行 2. MQ技术选项 消息先进先出&#xff0c;RabbitMQ默认有序 Erlang 是面向并发&#xff0c…

Allegro如何把Symbols,shapes,vias,Clines,Cline segs等多种元素一起移动

Allegro如何把Symbols,shapes,vias,Clines,Cline segs等多种元素一起移动 在用Allegro进行PCB设计时,有时候需要同时移动某个区域的所有元素,如:Symbols,shapes,vias,Clines,Cline segs等元素。那么如何操作呢? 首先就是把Symbols,shapes,vias,Clines,Cline …

【实训】网络规划与部署实训

一 实训目的及意义 本周实训主要是了解网络规划与部署&#xff0c;熟悉三大厂商华为、思科、锐捷交换机路由器以及相关协议的原理和配置&#xff0c;提高学生的动手能力和分析规划部署能力。 实训主要针对计算机网络系统集成的设计与实现的实际训练&#xff0c;着重锻炼学生熟练…

可视化特效汇总-2023年

基于threejs可视化效果展示、基于mapbox白膜展示、基于cesium模型加载、磁场专题图绘制

【Redis】字符串原理--简单动态字符串SDS

一.SDS定义 free 属性值为0&#xff0c;标识SDS没有分配任何未使用空间。len 属性值为5&#xff0c;标识SDS保存了一个5字节长度的字符串。buf 属性是一个char类型数组&#xff0c;数组的前5个字节保存了&#xff0c;R e d i s 五个字符&#xff0c;最后一个保存空字符串 \0…

架构篇33:传统的可扩展架构模式-分层架构和SOA

文章目录 分层架构SOA小结相比于高性能、高可用架构模式在最近几十年的迅猛发展来说,可扩展架构模式的发展可以说是步履蹒跚,最近几年火热的微服务模式算是可扩展模式发展历史中为数不多的亮点,但这也导致了现在谈可扩展的时候必谈微服务,甚至微服务架构都成了架构设计的银…

JAVA建造者模式详解

建造者模式 1 建造者模式介绍 建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式. 定义: 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 **建造者模式要解决的问题 ** 建造者模式可以将部件和其组装过程分开…

Linux 查看系统信息 + 服务信息命令(简记)

概述 作用&#xff1a;Linux 运维工作中常用的命令速查 小步教程 (xiaobuteach.com) Linux 命令大全 | 菜鸟教程 (runoob.com) 文本编辑器vim 本章大纲 | 小步教程 vim 多文件编辑 | 小步教程 常用 ps 查看服务启动命令 Linux ps 命令 | 菜鸟教程 (runoob.com) # 查找…

第七讲:考试介绍与考纲解读

第七讲:考试介绍与考纲解读 考试介绍考试人群考试报名考试环境考纲解读选择题(40分)操作题(60分)小结本讲简介:终于,熬过了略微枯燥的基础课程,我们进入了二级考试训练阶段。在这讲中,我们将对全国计算机等级考试(66)Python 语言程序设计(二级)进行 360全方位无死…

微服务的幂等性

微服务架构设计的中心思想是将服务进行拆分&#xff0c;但是在这个过程中&#xff0c;如果被依赖的服务发生奔溃&#xff0c;就会引起一系列问题。为了解决这个问题&#xff0c;就会引入重试的机制&#xff0c;重试又会引入幂等性的问题&#xff0c;下面我们就分析这个过程&…

Java GC-常见垃圾回收器

目录 前言一、垃圾回收器分类二、垃圾回收器介绍1、Serial 收集器2、ParNew 收集器3、Parallel Scavenge 收集器4、Serial Old 收集器5、Parallel Old 收集器6、CMS 收集器&#xff08;多线程标记清除算法&#xff09;7、G1 收集器 三、项目中垃圾收集器选型 前言 Java的垃圾回…

乐意购项目前端开发 #7

一、购物车 本地购物车 创建cartStore.js文件 创建cartStore.js文件, 将购物车列表数据存在pinia中 import { ref, computed } from "vue"; import { defineStore } from "pinia"; import { useUserStore } from "./user"; import {insertCart…

PyTorch使用

前言 系统环境&#xff1a;win10 使用Anaconda&#xff0c;Anaconda的安装自行百度。 conda 23.7.4 目录 前言 创建虚拟环境 1、查看当前有哪些虚拟环境 2、创建虚拟环境pytorch 3、激活及关闭pytorch虚拟环境 4、删除pytorch虚拟环境 使用yolov5测试 1、切换至yolo…

idea 快捷键ctrl+shift+f失效的解决方案

文章目录 搜狗输入法快捷键冲突微软输入法快捷键冲突 idea的快捷键ctrlshiftf按了没反应&#xff0c;理论上是快捷键冲突了&#xff0c;检查搜狗输入法和微软输入法快捷键。 搜狗输入法快捷键冲突 不需要简繁切换的快捷键&#xff0c;可以关闭它&#xff0c;或修改快捷键。 微…

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇Error: only one service expected goctl一键转换生成rpc服务错误解决方案的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 今天寸铁在…