记一次生产事故排查

背景:刚接手一个新工程,是一个给国内top级医院开发的老项目,因为历史原因,代码质量略低,测试难度略高。

上线很久的功能,最近一直频繁的爆发各种问题,经排查发现都是因为在业务过程中im聊天账号绑定异常所致。

修复方案一:

因为im工程比较老,日志打印不完善,因此从日志看初步分析业务dubbo接口未调用到im服务因此导致了绑定失败,此时我开始在工程里设置dubbo接口该方法的重试机制,retries设置为2,timeout设置为3000,由于本身既有的一次rpc调用,因此实际在调用失败的情况下业务会对im接口进行3次rpc的调用。

鉴于项目的各种难度和稳定性要求,另在业务代码侧再次进行了三次递归重试(客户端超时30s,正常情况不会因为重试而超时,慢是肯定的)。

同时因为服务此时并没有打印traceId进行链路跟踪机制的拓展,因此另开发traceId写入MDC并通过log4j2打印到cosole并通过钉钉表达式进行采集汇总输出,针对dubbo接口也通过扩展spi进行了traceId的透传。

服务消费者端代码如下:

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;import javax.servlet.FilterChain;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** MDC属性设置  日志用* 方法屏蔽actuator接口*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter(urlPatterns = {"/*"})
public class MDCFilter extends OncePerRequestFilter {private  Logger LOGGER = LoggerFactory.getLogger(MDCFilter.class);@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {if(request.getRequestURI().contains("/actuator/health")){return;}try {String traceId = request.getHeader("traceId");if(StringUtils.isNotBlank(traceId)) {TraceIdUtil.create(traceId);} else {TraceIdUtil.create();}TraceContext.addAttachment("traceId",MDC.get("traceId"));chain.doFilter(new ContentCachingRequestWrapper(request), response);} catch (Exception exception) {LOGGER.error("Web端traceId处理异常", exception);} finally {TraceContext.clear();TraceIdUtil.clear();}}}
import java.util.UUID;
import org.slf4j.MDC;public class TraceIdUtil {public static final String TRACE_ID = "traceId";public TraceIdUtil() {}public static void create() {create(UUID.randomUUID().toString().replace("-", ""));}public static void create(String traceId) {MDC.put("traceId", traceId);}public static String get() {return MDC.get("traceId");}public static void clear() {MDC.clear();}
}
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;@Activate(group = {"consumer"})
public class DubboTraceFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {RpcContext rpcContext = RpcContext.getContext();if (rpcContext.isConsumerSide()) {rpcContext.setAttachment("traceId", TraceContext.getTraceId());}return invoker.invoke(invocation);}
}
public class TraceContext {private static ThreadLocal<Map<String,Object>> threadLocal = new ThreadLocal<Map<String, Object>>(){@Overrideprotected Map<String, Object> initialValue() {return new LinkedHashMap<String, Object>();}};public static void addAttachment(String key, Object value){threadLocal.get().put(key,value);}public static void addAttachments(Map<String, Object> map){threadLocal.get().putAll(map);}public static Map<String, Object> getAttachments(){return threadLocal.get();}public static  void clear(){threadLocal.remove();}public static String getTraceId(){return String.valueOf(threadLocal.get().get("traceId"));}}

启动类添加注解:@ServletComponentScan

resource目录新建文件com.alibaba.dubbo.rpc.Filter

文件内容

dubboTraceFilter=com.xxx.filter.DubboTraceFilter(文件路径)

文件结构如下

服务提供者端代码如下:

import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;@Activate(group = {"provider","consumer"})
public class TraceIdFilter implements Filter {private static final Logger LOGGER = LoggerFactory.getLogger(TraceIdFilter.class);@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {try{String traceId = RpcContext.getContext().getAttachment("traceId");if ( !StringUtils.isEmpty(traceId) ) {// *) 从RpcContext里获取traceId并保存MDC.put("traceId", traceId);} else {// *) 交互前重新设置traceId, 避免信息丢失TraceIdUtil.create();RpcContext.getContext().setAttachment("traceId", MDC.get("traceId"));}// *) 实际的rpc调用return invoker.invoke(invocation);}catch (Exception e){LOGGER.error("rpcContext get traceId exception:",e);return null;}finally {TraceIdUtil.clear();}}
}

提供者端SPI扩展相同

此外:1、Log4j2需要解析自定义的traceId标签,可以用%X{traceId}打印

           2、resources目录下文件路径注意com.alibaba还是apache dubbo,请与依赖保持一致

 至此,问题修复完成。

代码上线后,发现问题依然存在。(上述方案仅可在一定程度上避免,并不能完全修复)

修复方案二:

再次排查发现,原来是im接口有另一个实现也在提供服务,此时找到了问题的元凶,摘除该无用实现问题解决。(也可考虑group隔离,因为另一实现无用,因此摘除)

经验总结:im的另一实现是前几天上线一新功能随之发布的,因为dubbo接口默认路由的随机性及测试账号的历史原因,导致该问题在测试过程未被发现。实际上当我们上线任何新功能,都应该全面进行评估上线代码是否会对老功能造成影响,而不应该只局限在眼前的需求,从而造成更大的损失。

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

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

相关文章

权威评测:K9、sc、希喂三款主食冻干大比拼,哪款更适合布偶猫?

关注布偶猫的饮食&#xff1a;作为肉食动物&#xff0c;它们肠胃脆弱需谨慎对待。主食冻干是理想之选&#xff0c;它既符合猫咪天然的饮食结构&#xff0c;又采用新鲜生肉为原料。搭配其他营养元素&#xff0c;既美味又营养&#xff0c;还能增强抵抗力。我们将为您测评市场上热…

数模学习day08-拟合算法

这里拟合算法可以和差值算法对比 引入 插值和拟合的区别 与插值问题不同&#xff0c;在拟合问题中不需要曲线一定经过给定的点。拟 合问题的目标是寻求一个函数&#xff08;曲线&#xff09;&#xff0c;使得该曲线在某种准则下与所 有的数据点最为接近&#xff0c;即曲线拟…

揭开JavaScript数据类型的神秘面纱

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 JavaScript作为一门动态类型语言,其数据类型一直是开发者们关注的话题。本文将深入探讨Jav…

深度解析分布式算法:构建高效稳定的分布式系统

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

uni-app中轮播图实现大图预览

参考效果 当轮播图滑动切换的时候更新自定义下标&#xff0c;当图片被点击的时候大图预览。 参考代码 商品详情页轮播图交互 <script setup lang"ts"> // 轮播图变化时 const currentIndex ref(0) const onChange: UniHelper.SwiperOnChange (ev) > …

MPEG4Extractor

1、readMetaData 必须要找到 Moov box&#xff0c;找到 Mdat box或者 Moof box&#xff0c;并且创建了 ItemTable 大端 box 分为 box header 和 box content&#xff1a; box header由8个字节组成&#xff0c;前面四个字节表示这个box 的大小&#xff08;包含这个头的8字节&a…

PCL 格网法计算点云的占地面积

目录 一、算法原理二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT生成的文章。 一、算法原理 该方法主要用于粗略统计机载点云的占地面积。方法原理是将点云沿 X O Y XOY

【深度学习】SDXL tensorRT 推理,Stable Diffusion 转onnx,转TensorRT

文章目录 sdxl 转 diffusers转onnx转TensorRT sdxl 转 diffusers def convert_sdxl_to_diffusers(pretrained_ckpt_path, output_diffusers_path):import osos.environ["HF_ENDPOINT"] "https://hf-mirror.com" # 设置 HF 镜像源&#xff08;国内用户使…

长期使用外接键盘,外物压着自带键盘,容易导致华硕飞行堡垒FX53VD键盘全部失灵【除电源键】

华硕飞行堡垒FX53VD键盘全部失灵【除电源键】 前言一、故障排查二、发现问题三、使用方法总结 前言 版本型号&#xff1a; 型号 ASUS FX53VD&#xff08;华硕-飞行堡垒&#xff09; 板号&#xff1a;GL553VD 故障情况描述&#xff1a; 键盘无法使用&#xff0c;键盘除开机键外…

大学生如何当一个程序员——第三篇:热门专业学习之路6

文章出自https://www.bjsxt.com/xiulian.html#1F 各位小伙伴想要博客相关资料的话关注公众号&#xff1a;chuanyeTry即可领取相关资料&#xff01; 大数据和云计算学习 1.大数据学习之前“必看”2.Hadoop框架3.数据仓库技术4.Spark内存计算框架5.机器学习和数据挖掘6.Storm流式…

深入浅出:原生态App封装的艺术

一、原生态App封装的优势 性能的极致&#xff1a;原生App直接调用设备的硬件资源&#xff0c;减少了中间层的干扰&#xff0c;从而实现更快的运行速度和更流畅的动画效果。 2. 用户体验的完美&#xff1a;原生App可以访问并遵循特定平台的设计指南&#xff0c;提供与操作系统无…

开发分销商城小程序app,轻松助你业绩倍增

开发分销商城小程序app&#xff0c;轻松助你业绩倍增&#xff01; 1. 一键分享&#xff0c;业务拓展&#xff1a;分销商城小程序可生成独特的分销链接与二维码&#xff0c;让你的分销员分享给亲朋好友、社交媒体粉丝。迅速扩大销售网络&#xff0c;提升产品知名度。 2. 佣金管…

口碑公认好的洗地机有哪些?行业公认口碑洗地机

随着扫地机、洗地机等智能清洁家电的不断坚固&#xff0c;近年来洗地机成为备受欢迎的智能家电产品。这些洗地机不仅在家居清洁方面发挥了有效作用&#xff0c;而且大大节省了开支节省了时间成本。只需手持操控&#xff0c;就能在下班后轻松进行清洁&#xff0c;使清洁体验变得…

Maven之属性管理

1.属性管理 1.1 属性配置与使用 ①&#xff1a;定义属性 <!--定义自定义属性--> <properties><spring.version>5.2.10.RELEASE</spring.version> </properties>②&#xff1a;引用属性 <dependency><groupId>org.springframewor…

Mysql 重要知识点1(含面试题1)

Mysql 知识点较多&#xff0c;这里涵盖了基本知识点&#xff0c;包括SQL语句 、重要面试题等。后面还有几章Mysql的知识点&#xff0c;分别是刷题总结与进阶优化SQL 面试题等。 目录 Mysql 安装Mysql 重要知识点SQL 重要语句面试题精选 Mysql 安装 1.官网下载mysql5.7版本压缩…

C++突破栈区内存限制(堆区)

栈区的内存空间一般是在windows上1M-2M 堆区64位&#xff0c;windows10一般是1G-2G

日志服务管理和inode号

一、系统日志管理 1.1系统日志的介绍 在现实生活中&#xff0c;记录日志也非常重要&#xff0c;比如银行的转账记录&#xff0c;飞机上的黑盒子&#xff0c;那么将系统和应用发生的事件记录至日志中&#xff0c;以助于排错和分析使用 日志记录的内容包括&#xff1a; 历史事…

springboot学生综合测评系统源码和论文

随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;学生综合测评系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;学校规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的变化&#xff0c;而…

scanf函数和printf函数

1.scanf函数 int scanf ( const char * format, ... );函数功能&#xff1a; 从键盘读取数据如果读取成功&#xff0c;返回读取到的数据个数如果读取失败&#xff0c;返回EOF 不常见的读取格式&#xff1a; %md -->读取m个宽度的数据 int main() {int n 0;scanf("%4d&…

Java里的实用类

1.枚举 语法&#xff1a; public enum 变量名{ 值一&#xff0c;值二} 某个变量的取值范围只能是有限个数的值时&#xff0c;就可以把这个变量定义成枚举类型。 2…装箱&#xff08;boxing&#xff09; 和拆箱&#xff08;unboxing&#xff09; 装箱&#xff08;boxing&…