前言

好久没写文章了, 今天之所以突然心血来潮, 是因为昨天出现了这样一个情况:

我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError, 但是因为经过我们精心优化的openjdk容器参数, 这次故障对用户完全无感知.

那么我们是如何做到的呢?

HeapDumpOnOutOfMemoryError VS ExitOnOutOfMemoryError

我们都知道, 在传统的虚拟机上部署的Java实例. 为了更好地分析问题, 一般都是要加上: -XX:+HeapDumpOnOutOfMemoryError这个参数的. 加这个参数后, 如果遇到内存溢出, 就会自动生成HeapDump, 后面我们可以拿到这个HeapDump来更精确地分析问题.

但是, "大人, 时代变了!"

容器技术的发展, 给传统运维模式带来了巨大的挑战, 这个挑战是革命性的:

  1. 传统的应用都是"永久存在的" vs 容器pod是"短暂临时的存在"
  2. 传统应用扩缩容相对困难 vs 容器扩缩容丝般顺滑
  3. 传统应用运维模式关注点是:"定位问题" vs 容器运维模式是: "快速恢复"
  4. 传统应用一个实例报HeapDumpError就会少一个 vs 容器HeapDump shutdown后可以自动启动, 已达到指定副本数
  5. ...

简单总结一下, 在使用容器平台后, 我们的工作倾向于:

  1. 遇到故障快速失败
  2. 遇到故障快速恢复
  3. 尽量做到用户对故障"无感知"

所以, 针对Java应用容器, 我们也要优化以满足这种需求, 以OutOfMemoryError故障为例:

  1. 遇到故障快速失败, 即尽可能"快速退出, 快速终结"
  2. 有问题java应用容器实例退出后, 新的实例迅速启动填补;
  3. "快速退出, 快速终结", 同时配合LB, 退出和冷启动的过程中用户请求不会分发进来.

-XX:+ExitOnOutOfMemoryError就正好满足这种需求:

传递此参数时,抛出OutOfMemoryError时JVM将立即退出。 如果您想终止应用程序,则可以传递此参数。

细节

让我们重新回顾故障: "我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError"

该customer应用概述如下:

  1. 无状态
  2. 通过Deployment部署, 有6个副本
  3. 通过SVC提供服务

完整的过程如下:

  1. 6个副本, 其中1个出现OutOfMomoryError
  2. 因为副本的jvm参数配置有: -XX:+ExitOnOutOfMemoryError, 该实例的JVM(PID为1)立即退出.
  3. 因为pid 1进程退出, 此时pod立刻出于Terminating状态, 并且变为:Terminated
  4. 同时, customer的SVC 负载均衡会将该副本从SVC 负载均衡中移除, 用户请求不会被分发到该节点.
  5. K8S检测到副本数和Deployment replicas不一致, 启动1个新的副本.
  6. 待新的部分Readiness Probe 探测通过, customer的SVC负载均衡将这个新的副本加入到负载均衡中, 接收用户请求.

在此过程中, 用户基本上是对后台故障"无感知"的.

当然, 要做到这些, 其实JVM参数以及启动脚本中, 还有很多细节和门道. 如: 启动脚本应该是: exec java ....$*

有机会再写文章分享.

新的疑问

上边一章, 我们解释了"为什么Java容器推荐使用ExitOnOutOfMemoryError而非HeapDumpOnOutOfMemoryError", 但是细心的小伙伴也会发现, 新的配置也会带来新的问题, 比如:

  1. JVM从fullgc -> OutOfMemoryError 这段时间内, 用户的体验还是会下降的, 怎么会是"故障无感知"呢?
  2. 用"ExitOnOutOfMemoryError"代替"HeapDumpOnOutOfMemoryError", 那我怎么定位该问题的根因并解决? 2个参数一起用不是更香么?

这些其实可以通过其他手段来解决:

  1. JVM从fullgc -> OutOfMemoryError 这段时间内, 用户的体验还是会下降的, 怎么会是"故障无感知"呢?

    1. 答: 配置合理的Readiness Probe, 只要Readiness Probe探测失败, K8S就会自动将这个节点从SVC中摘除. 那么合理的Readiness Probe在这里指的就是应用不可用时, Readiness Probe探测必然是失败的. 所以一般不能是探测某个端口是否在监听, 而是应该是探测对应的api是否正常. 如下方.
    2. 答: 通过Prometheus JVM Exporter + Prometheus + AlertManger, 配置合理的AlertRule. 如: "过去X时间, GC total time>5s"告警, 告警后人工介入提前处理.
  2. 用"ExitOnOutOfMemoryError"代替"HeapDumpOnOutOfMemoryError", 那我怎么定位该问题的根因并解决? 2个参数一起用不是更香么?
    1. 答: 目的是为了"快速退出, 快速终结". 毕竟做HeapDump也是需要时间的, 这段时间内可能就会造成体验的下降. 所以, 只有"ExitOnOutOfMemoryError", 退出地越快越好.
    2. 答: 至于分析问题, 可以通过其他手段分析, 如嵌入"Tracing agent"做Tracing的监控, 通过分析故障时的traces定位根因.
    3. Prometheus Alertrule gctime告警后, 人工通过jcmd等命令手动做heapdump.
readinessProbe:
httpGet:
path: /actuator/info
port: 8088
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3

总结

新的技术带来新的变革, 我们需要以发展的眼光看待"最佳实践, 最佳配置".

2016年, 针对虚机部署的Java的最优参数, 在今天来看, 并不一定仍是最优解.

三人行, 必有我师; 知识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.

为什么 java 容器推荐使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError ?的更多相关文章

  1. Java 容器相关知识全面总结

    Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题.因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAda ...

  2. 工作随笔—Java容器基础知识分享(持有对象)

    1. 概述 通常,程序总是运行时才知道的根据某些条件去创建新对象.在此之前,不会知道所需对象的数量,甚至不知道确切的类型,为解决这个普遍的编程问题:需要在任意时刻和任意位置创建任意数量的对象,所以,就 ...

  3. Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本

    Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本 前言 序言 再高大上的框架,也需要扎实的基础才能玩转,高频面试问题更是基础中的高频实战要点. 适合阅读人群 J ...

  4. 【Java心得总结六】Java容器中——Collection

    在[Java心得总结五]Java容器上——容器初探这篇博文中,我对Java容器类库从一个整体的偏向于宏观的角度初步认识了Java容器类库.而在这篇博文中,我想着重对容器类库中的Collection容器 ...

  5. Java 容器(list, set, map)

    java容器类库的简化图: (虚线框表示接口, 实线框表示普通的类, 空心箭头表示特定的类实现了接口, 实心箭头表示某个类可以生成箭头所指的类对象) 继承Collection的主要有Set 和 Lis ...

  6. Java - 容器详解

    一.ArrayList 长度可变数组,类似于c++ STL中的vector. 元素以线性方式连续存储,内部允许存放重复元素. 允许对元素进行随机的快速访问,但是向ArrayList中插入和删除元素的速 ...

  7. java容器---集合总结

    思考为什么要引入容器这个概念? Java有多种方式保存对象(应该是对象的引用),例如使用数组时保存一组对象中的最有效的方式,如果你想保存一组基本类型的数据,也推荐使用这种方式,但大家知道数组是具有固定 ...

  8. java 书籍推荐 JavaEE程序员必读图书大推荐

    java 书籍推荐 JavaEE程序员必读图书大推荐 转自:http://www.cnblogs.com/xlwmin/articles/2192775.html 下面是我根据多年的阅读和实践经验,给 ...

  9. java容器的理解(collection)

    容器类(Conllection)对于一个开发者来说是最强大的工具之一,可以大幅提高编程能力.容器是一个将多个元素组合到一个单元的对象,是代表一组对象的对象,容器中的对象成为它的元素. 容器适用于处理各 ...

  10. Java面试题-Java容器

    一.Java容器分类 Java容器划分为两个概念Collection.Map Collection: 一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素,不关心是否重 ...

随机推荐

  1. aws上传文件、删除文件、图像识别

    目录 aws的上传.删除s3文件以及图像识别文字功能 准备工作 安装aws cli 初始化配置AWS CLI s3存储桶开通 图像识别文字功能开通 aws的sdk 上传文件 方法一 方法二 删除文件 ...

  2. Spring中过滤器(Filter)和拦截器(Interceptor)的区别和联系

    在我们日常的开发中,我们经常会用到Filter和Interceptor.有时同一个功能.Filter可以做,Interceptor也可以做.有时就需要考虑使用哪一个比较好.这篇文章主要介绍一下,二者的 ...

  3. 题解 CF1011B Planning The Expedition

    Solution 考虑 二分 . 首先要确定二分的对象,显然二分天数较为简单. 每次找到的 \(mid\) 需要判断是否能让整队人吃饱,那就调用一个 check() . 对于 check() ,求出每 ...

  4. 五、Python操作redis

    五.Python操作redis 一.python对redis基本操作 (1)连接redis # 方式1 import redis r = redis.Redis(host='127.0.0.1', p ...

  5. Kubeadm部署k8s单点master

    Kubeadm部署k8s单点master 1.环境准备: 主机名 IP 说明 宿主机系统 master 10.0.0.17 Kubernetes集群的master节点 CentOS 7.9 node1 ...

  6. PageRank原理分析

    pagerank是将众多网页看成一个有向图,每个页面就是有向图中的节点.计算每个节点的出度和入度.如果一个网站被大量其他的网页引用,那么他就会有更高的pr分数. 原理 对于所有与节点i相连的节点,用他 ...

  7. python tcp select 多路复用

    1 #!/usr/bin/python 2 # -*- coding: UTF-8 -*- 3 # 文件名:tcpserver.py 4 5 import socket 6 import time 7 ...

  8. pinpoint部署

    pinpoint是一个分析大型分布式系统的平台,提供解决方案来处理海量跟踪数据,主要面向基于tomcat的Java 应用. pinpoint使用HBASE储存数据. 下面介绍pinpoint部署及应用 ...

  9. Perl语言中一些内置变量等,$x、qw、cmp、eq、ne等

    转载 Perl语言中一些内置变量等,$x.qw.cmp.eq.ne等 字母 符号 释义 eq  = = equal(等于) ne != not equal(不等于) cmp 比较 qq  " ...

  10. Excel表格复制填写

    =if(A1<>"",A1,"") #A1可以为任意表格单元