• 常见生产环境问题及排查方法


    陆续解决过很多生产问题,每次没有记录过一段时间就忘记了

    1. 服务启动的时候运行卡住了

      • case by case 解决,有时候需要把日志级别改为debug才能看到真正的原因
      • 有一次是因为mysql连接密钥错误,导致启动hold住,也没有任何提示
      • 有一次是因为封装了BeanDefinitionBuilder来自定义bean,实例类的一个属性变量executor增加了@Setter和@Getter注解,所以要求必须注入一个bean对象,程序定义了多个线程池对象不知道使用哪个,无限循环创建这个bean最后会内存溢出。
        • 解决方案一:给某个主线程池加上@Primary注解
        • 解决方案二:这个属性不需要直接设为null,beanDefinitionBuilder.addPropertyValue(“excecutor”, null);
    2. 服务内存溢出

    • 查看监控可以看到线程数在不断的增加
      • 查看线程总数
        • jstack -l {pid} |grep java.lang.Thread.State|wc -l
        • top -Hp {pid}
        • ps huH p {pid}| wc -l
        • cat /proc/{pid}/status
        • pstreee -p {pid} |wc -l #需要安装:yum -y install psmisc
        • ls -l /proc/{pid}/task|wc -l
    • 统计最多的线程名字,看到带有"EDS"名字的线程数很多,这个是订阅服务的线程,原因是每次执行方法都开启了一个服务订阅线程导致
    • dump内存使用MemoryAnalyzer分析,mac上安装mat软件分析
      case1:写了一个循环处理上亿的数据发现内存得不到泄露,java.lang.StackTraceElement对象有很多,最后用memoryAnalyzer分析出应该是cat记录了所有异常的堆栈信息导致OOM,处理过程中可以调用Cat.getManager().reset();清理异常堆栈
      在这里插入图片描述
    1. RabbitMQ消息堆积1
    • 查看监控看到有少数线程blocked状态,时间点和停止消费时间点吻合
    • 结论:connetcion断开导致consumer的消费线程blocked,应该是RabbitMQ的broker出现故障导致, 参考rabbitmq异常导致线程blocked
    • 重启服务问题才解决
    1. RabbitMQ消息堆积2
    • 查看监控tidb更新平均时间达到了600ms,导致消费速度低于生产速度
    • 先临时扩容消费者,再根据tidb集群监控看到CPU使用率达到100%, 去tidb管理界面看到另外一条业务线用了很多慢查询,联系他们修正sql
    1. RocketMQ消息没有发送成功
    • RocketMQ Producer没有找到namesrvaddress导致发送失败,失败没有剖出异常
    • producer没有设置instancename,rocketmq开源版本的clientConfig实例之间需要靠instance name来区分,否则会复用同一个底层的instance对象。实际是由于有一个consumer没有设置namesrvaddress导致producer的namesrvaddress时效了
    1. Mongodb查询超时
    • 给一个复杂查询的所有字段都设置了索引,因为有一个字段用了$in查询,所以导致索引失效
    • 解决方案:索引中删除$in查询的字段
    1. Thrift请求参数丢失【gson反序列化thrift对象时基础类型变量丢失】
    • 现象: thrift服务端接口,我调用都正常,但是第三方调用的时候缺失了一个int类型的参数,第三方把请求对象打印出来和断点调试都能看到有这个int参数变量,但是可以确定的是thrift客户端缺失了参数
    • 可以借助arthas工具,watch命令观察thrift调用的参数和返回watch com.xxx.NettyClientRequestInvoker invokeOnce "{params,returnObj}" 'params[0].serviceName equals("server1")' -x 2 -b, 在请求调用前确实没有参数
    • 仔细看第三方的代码,用了gson反序列化对象,断点调试的时候字段确实有值,但是thrift对象的isSetXXX()是false,导致thrift对象在传输的时候忽略了这个变量
    • 原因分析:Thrift请求丢失字段(记一次坑爹请求),thrift optional字段设置时,只能用__set_xx()赋值,否则无效,gson反序列的原理是使用反射给变量字段赋值的
    // com.google.gson.internal.bind.ReflectiveTypeAdapterFactory类内部代码
            return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
                void read(JsonReader reader, Object value) throws IOException, IllegalAccessException {
                    Object fieldValue = mapped.read(reader);
                    if (fieldValue != null || !isPrimitive) {
                        field.set(value, fieldValue); // gson反序列化时使用反射直接给变量赋值
                    }
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    thrift生成的类里面的StandardScheme会对基础类型变量判断是否设置过值isSetXXX

    private static class UserStandardScheme extends org.apache.thrift.scheme.StandardScheme<User> {
        public void write(org.apache.thrift.protocol.TProtocol oprot, User struct) throws org.apache.thrift.TException {
          struct.validate();
    
          oprot.writeStructBegin(STRUCT_DESC);
          if (struct.name != null) { // 对于字符串等基础变量判断是否为null
            oprot.writeFieldBegin(NAME_FIELD_DESC);
            oprot.writeString(struct.name);
            oprot.writeFieldEnd();
          }
          if (struct.isSetAge()) { // 对于基础类型会判断isSetXXX
            oprot.writeFieldBegin(AGE_FIELD_DESC);
            oprot.writeI32(struct.age);
            oprot.writeFieldEnd();
          }
          if (struct.address != null) { // 对于字符串等基础变量判断是否为null
            if(struct.isSetAddress()) { // 如果字段是optioal的,还要判断isSetXXX,public boolean isSetAddress() { return this.address != null; // 这个方法其实还是判断的字符串是否为null}
              oprot.writeFieldBegin(ADDRESS_FIELD_DESC);
              oprot.writeString(struct.name);
              oprot.writeFieldEnd();
            }
          }
          oprot.writeFieldStop();
          oprot.writeStructEnd();
        }
    
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. 我的服务监控正常但是上游却报错
      现象:查看自己的服务接口响应都是正常的,但是上游却报错调用超时
      现象分析:比较时间点,虽然自己的接口是在10ms内返回,上游请求时间是10:00:00.000,超时时间是10:00:05.000,但是看自己的服务响应其实是10:00:06.000开始-10:00:06.010结束
      可能原因:中间件打点监控不是完全合理,没有上游请求发出后到下游接口开始处理之间的耗时监控
    • 之前的请求缓冲在队列里面了,线程耗尽了,不能及时响应上游的请求
    • 容器所在物理机网络问题,有时候会出现网络抖动,导致下游的输出没有及时传输到上游服务
    1. app页面在ios上出现了闪烁
      现象:前端h5页面发布服务后,只在ios端出现了闪烁
      现象分析:前端生产环境回退了服务问题还在、客户端web容器已经2个月没有更新过、在safari浏览器打开不会出现闪烁、在beta环境正常
      原因:大佬定位到是反爬组上线了一个新的反爬插件

    优秀的排查文章

    Java系统线上生产问题排查一把梭

  • 相关阅读:
    postman的使用
    ThreeJS-3D教学八-OBJLoader模型加载+动画
    springboot上线打包+vuecli2部署在linux服务器上(打包上线)
    人工智能(AI)是一种快速发展的技术,其未来发展前景非常广阔。
    热门股权项目:富滇银行股份有限公司0.0232%股权转让
    大学生数学竞赛题,微积分方面的
    实时美颜的背后:视频直播美颜SDK的算法与原理
    Python升级之路( Lv15 ) 并发编程三剑客: 进程, 线程与协程
    ubuntu安装samba文件共享
    leetcode 39. 组合总和
  • 原文地址:https://blog.csdn.net/liufang1991/article/details/126903906