zoukankan      html  css  js  c++  java
  • 记一次线上故常处理

    前言

    作为一个开发,肯定会遇到线上问题,遇到线上故障快速的定位解决,是开发者一项必备的能力。工作中可能会遇到各种故障,这边主要记录下之前遇到的一个线上问题

    问题描述

    早上到办公室打开钉钉,发现线上应用的一个实例重启了…..

    卧槽感觉情况不妙!!!

    迅速打开sls看下线上日志情况,一看果然有问题

    oom了….

    自己登录系统大致浏览了下,系统比较正常,应该是重启之后,使用过程中没发生异常情况,感觉应该是偶发情况,准备看下代码慢慢定位下问题,再解决。

    就在这时,系统的用户找过来了

    于此同时发现,公司内部运维系统消息推送群,出现一大波线上实例重启的日志

    感觉这个问题很严重呀…...

    问题分析

    再次到sls看下,竟然没有发现异常日志,这是怎么回事呢?

    找运维问下吧

    运维给出的回复是OOM了,但是我这边最近几次oom的日志,我这边不确定和上面的是不是同一个问题(上一次有明确的日志,这几次实例重启没有日志呀)

    随后运维给出了线上服务的cpu、内存使用情况

    OOMKilled 是因为实例内存超过了K8S给它分配的最高允许内存,所以K8S就把它oomkill掉

    但是我还是怀疑和上面是同一个问题,我结合上面日志描述的异常看了下代码,然后我找到上面报错日志指向的接口,一看真的是要骂娘(当然自己代码写的也很烂),这代码写的呀…...

    这种代码频繁访问接口,线程创建的多了,肯定会有很大的内存占用,K8s检测到了那就把实例杀掉了呀

    用过CompletableFuture的同学就知道,默认情况下 CompletableFuture 会使用公共的 ForkJoinPool 线程池,这个线程池默认创建的线程数是 CPU 的核数(也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置 ForkJoinPool 线程池的线程数)。如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上。这样等有大请求量过来,处理逻辑又很复杂,很多线程都在等待执行,慢慢拖垮了服务器。

    这个异常问题本质原因是我们创建了太多的线程,而能创建的线程数是有限制的,导致了异常的发生。能创建的线程数的具体计算公式如下

    (MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads

    • MaxProcessMemory 指的是一个进程的最大内存
    • JVMMemory JVM内存
    • ReservedOsMemory 保留的操作系统内存
    • ThreadStackSize 线程栈的大小

    在java语言里, 当你创建一个线程的时候,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生 java.lang.OutOfMemoryError: unable to create new native thread

    问题复现

    那么问题基本可以确定了,根据接口找到对应的页面,然后在预发环境频繁浏览那个页面,然后我让运维到宿主机和容器看下具体的情况(看看这线程数量涨的…..),果然又发现预发的实例也重启了!

    问题解决

    问题定位了清楚就好解决了呀

    刚才的代码对应的地方改成自定义线程池就好了

    //可以指定线程池  
    static CompletableFuture<Void> 
      runAsync(Runnable runnable, Executor executor)
    

    改完上线,线上立马稳了…...

    问题总结

    • 线程属于宝贵资源,使用的时候尽量使用线程池管理,在使用线程池的时候,要注意尽量使用自定义线程池,明确线程池中的各个参数
    • codereview很重要呀!!!

    码字不易,觉得还不错可以点个赞

    原文地址

    http://cbaj.gitee.io/blog/2020/06/19/%E8%AE%B0%E4%B8%80%E6%AC%A1%E7%BA%BF%E4%B8%8A%E6%95%85%E5%B8%B8%E5%A4%84%E7%90%86/#more

  • 相关阅读:
    Go基础系列:流程控制结构
    Go基础系列:数据类型转换(strconv包)
    Go基础系列:简单数据类型
    Go基础系列:常量和变量
    Go基础系列:map类型
    Go基础系列:Go slice详解
    go基础系列:数组
    Go基础系列:import导包和初始化阶段
    Go基础系列:构建go程序
    go基础系列:结构struct
  • 原文地址:https://www.cnblogs.com/bangaj/p/13527198.html
Copyright © 2011-2022 走看看