两次线上故障让我明白了jvm参数调优的重要性

一、案例        

       无论面试还是平常的交流中,我们都会谈到垃圾收集器,JVM参数调优等等话题。由于之前的项目访问量,以及服务器资源不紧缺所以对该参数并没有太深的理解,最近一段时间再做电商相关的项目,两次线上事故让我意识到JVM调优的重要性

       由于服务器相对紧缺,一台节点部署了多个服务,配置是4C16G150g的容量,上线一个月后发现服务器内存逐步增加,导致由此服务器告急,内存爆满,导致只能登陆阿里云控制台强制重启,第一次觉得是下项目过多,导致内存不足,所以向运维申请扩容服务器。但是因为其他项目的紧急处理,运维给忘记了,结果过了两个星期,线上又OOM了,同样的我们把原因归咎于项目多,运维延迟扩容背了锅。当内存增到24G的时候我们都觉得没问题了,但是我发现应该没那么快就内存不足,服务器重启后我时刻关注着服务器内存的消耗情况,并使用top命令追踪线程消耗内存的情况,突然发现,每隔一天内存消耗多一些。这个时候我的心里一阵发凉,突然意识到我们项目部署有问题。通过监控到商城的定时任务过多,内存消耗过大。

     em。。。。不对呀,为什么没有发生GC回收,突然想到我们的部署没有设置JVM参数,-Xms默认占用物理内存1/64,-Xmx默认占用物理内存的1/4,我的妈呀,这还了得,五六个项目跑着,还没等到gc,就OOM了,赶紧等到晚上对项目项目进行了配置,设置了JVM参数如下:

-Xms1024M -Xmx1024M -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCTimeStamps -XX:+PrintGCDetails  -Xloggc:/data/logs/gc.log

       设置后重启通过top命令,监控发现线程并没有占用过高的内存,并且gc日志也出现了回收的情况。又根据full gc的情况对大小对了调整。这才稳定了下来。

   二、常用top命令:

列名

含义

PID

进程id

PPID

父进程id

RUSER

Real user name

UID

进程所有者的用户id

USER

进程所有者的用户名

GROUP

进程所有者的组名

TTY

启动进程的终端名。不是从终端启动的进程则显示为 ?

PR

优先级

NI

nice值。负值表示高优先级,正值表示低优先级

P

最后使用的CPU,仅在多CPU环境下有意义

%CPU

上次更新到现在的CPU时间占用百分比

TIME

进程使用的CPU时间总计,单位秒

TIME+

进程使用的CPU时间总计,单位1/100秒

%MEM

进程使用的物理内存百分比

VIRT

进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES

SWAP

进程使用的虚拟内存中,被换出的大小,单位kb。

RES

进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA

CODE

可执行代码占用的物理内存大小,单位kb

DATA

可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb

SHR

共享内存大小,单位kb

nFLT

页面错误次数

nDRT

最后一次写入到现在,被修改过的页面数。

S

进程状态。

            D=不可中断的睡眠状态

            R=运行

            S=睡眠

            T=跟踪/停止

            Z=僵尸进程

COMMAND

命令名/命令行

WCHAN

若该进程在睡眠,则显示睡眠中的系统函数名

Flags

任务标志,参考 sched.h

top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况。

前五行是当前系统情况整体的统计信息区。下面我们看每一行信息的具体意义。

第一行,任务队列信息,同 uptime 命令的执行结果,具体参数说明情况如下:
14:06:23 — 当前系统时间
up 70 days, 16:44 — 系统已经运行了70天16小时44分钟(在这期间系统没有重启过的吆!)
2 users — 当前有2个用户登录系统
load average: 1.15, 1.42, 1.44 — load average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。
load average数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。如果这个数除以逻辑CPU的数量,结果高于5的时候就表明系统在超负荷运转了。

第二行,Tasks — 任务(进程),具体信息说明如下:
系统现在共有206个进程,其中处于运行中的有1个,205个在休眠(sleep),stoped状态的有0个,zombie状态(僵尸)的有0个。

第三行,cpu状态信息,具体属性说明如下:
5.9%us — 用户空间占用CPU的百分比。
3.4% sy — 内核空间占用CPU的百分比。
0.0% ni — 改变过优先级的进程占用CPU的百分比
90.4% id — 空闲CPU百分比
0.0% wa — IO等待占用CPU的百分比
0.0% hi — 硬中断(Hardware IRQ)占用CPU的百分比
0.2% si — 软中断(Software Interrupts)占用CPU的百分比
备注:在这里CPU的使用比率和windows概念不同,需要理解linux系统用户空间和内核空间的相关知识!

第四行,内存状态,具体信息如下:
32949016k total — 物理内存总量(32GB)
14411180k used — 使用中的内存总量(14GB)
18537836k free — 空闲内存总量(18GB)
169884k buffers — 缓存的内存量 (169M)

第五行,swap交换分区信息,具体信息说明如下:
32764556k total — 交换区总量(32GB)
0k used — 使用的交换区总量(0K)
32764556k free — 空闲交换区总量(32GB)
3612636k cached — 缓冲的交换区总量(3.6GB)
备注:
第四行中使用中的内存总量(used)指的是现在系统内核控制的内存数,空闲内存总量(free)是内核还未纳入其管控范围的数量。纳入内核管理的内存不见得都在使用中,还包括过去使用过的现在可以被重复利用的内存,内核并不把这些可被重新使用的内存交还到free中去,因此在linux上free内存会越来越少,但不用为此担心。
如果出于习惯去计算可用内存数,这里有个近似的计算公式:第四行的free + 第四行的buffers + 第五行的cached,按这个公式此台服务器的可用内存:18537836k +169884k +3612636k = 22GB左右。
对于内存监控,在top里我们要时刻监控第五行swap交换分区的used,如果这个数值在不断的变化,说明内核在不断进行内存和swap的数据交换,这是真正的内存不够用了。

第六行,空行。

第七行以下:各进程(任务)的状态监控,项目列信息说明如下:
PID — 进程id
USER — 进程所有者
PR — 进程优先级
NI — nice值。负值表示高优先级,正值表示低优先级
VIRT — 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
RES — 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
SHR — 共享内存大小,单位kb
S — 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程
%CPU — 上次更新到现在的CPU时间占用百分比
%MEM — 进程使用的物理内存百分比
TIME+ — 进程使用的CPU时间总计,单位1/100秒
COMMAND — 进程名称(命令名/命令行)

常用操作:
top   //每隔5秒显式所有进程的资源占用情况
top -d 2  //每隔2秒显式所有进程的资源占用情况
top -c  //每隔5秒显式进程的资源占用情况,并显示进程的命令行参数(默认只有进程名)
top -p 12345 -p 6789//每隔5秒显示pid是12345和pid是6789的两个进程的资源占用情况
top -d 2 -c -p 123456 //每隔2秒显示pid是12345的进程的资源使用情况,并显式该进程启动的命令行参数

该内容转自:http://www.cnblogs.com/peida/archive/2012/12/24/2831353.html
            http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316399.html

三、JVM参数

参考博文,博文写得很详细

https://blog.csdn.net/baidu_35140444/article/details/82980139?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

四、项目启动脚本生成代码

package com.platform.annotation;

import lombok.Data;

/**
 * @auth lipf
 * @date 2020/6/3 16:22
 */
@Data
public class BaseShell {
    //项目类型
    String PROJECT_TYPE="SHOP";
    //项目类型
    String PROJECT_CHINESE="商城";
    //jar包地址
    String PATH_JARS="/opt/deploy/xunxj_shop/platofrm-admin/";
    //项目jar名
    String NAME="platform-admin.jar";
    //jvm
    String JVM="256";
    //port
    String PORT="13080";
    //LOG
    String GC_LOG="logs/gc.log";
    //类型: 1ADMIN   2 API
    String TYPE="1";
}
package com.vpclub.test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

/**
 * @auth lipf
 * @date 2020/6/3 16:14
 */
public class GenerateShell {

    public static void main(String[] args) {
        BaseShell baseShell = new BaseShell();
        //农场小程序
        //farm_dev(baseShell);
        farm_prod(baseShell);
        //农场后台
        //farm_admin_dev(baseShell);
        //采购前端
        //farm_buyer_dev(baseShell);
        //商城后台
        //shop_admin_dev(baseShell);
        //shop_admin_prod(baseShell);
        shop_admin_prod_120(baseShell);
        //商城小程序
        //shop_api_dev(baseShell);
        //shop_api_prod(baseShell);
        //团购小程序
        //group_api_dev(baseShell);
        //清远门户后台
//        qingyuan_admin_dev(baseShell);
        //清远门户前端
        //qingyuan_api_dev(baseShell);
        String active="prod120\\";
//        String active="dev\\";
        StringBuffer result = new StringBuffer("#!/bin/bash").append("\n");
        //生成提示参数说明
        result.append(generatePrams(baseShell));
        result.append("\n").append("\n");
        //生成常量
        result.append(generateConstant(baseShell));
        result.append("\n").append("\n");
        //生成检查服务程序是否在运行
        result.append(generateIsExist(baseShell));
        result.append("\n").append("\n");
        //生成启动服务
        result.append(generateStart(baseShell));
        result.append("\n").append("\n");
        //生成停止服务
        result.append(generateStop(baseShell));
        result.append("\n").append("\n");
        //生成状态服务
        result.append(generateStatus(baseShell));
        result.append("\n").append("\n");
        //生成入口判断
        result.append(generateInDoor(baseShell));
        result.append("\n").append("\n");
        System.out.println(result);
        //生成文件
        // 生成的文件路径
        String path = "F:\\工作文档\\服务器部署\\shell脚本\\"+active;
        if("1".equals(baseShell.getTYPE())){
            path=path+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin_opration.sh";
        }else if ("2".equals(baseShell.getTYPE())){
            path=path+baseShell.getPROJECT_TYPE().toLowerCase()+"_api_opration.sh";
        }

        File file = new File(path);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        }
        try {
            file.createNewFile();
            // write 解决中文乱码问题
            OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(result.toString());
            bw.flush();
            bw.close();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /*
      清远门户后台测试环境请求
     */
    private static void qingyuan_admin_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("QINGYUAN");
        //项目类型
        baseShell.setPROJECT_CHINESE("清远门户");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/qingyuan_agricultural_portal/agricultural_manager/");
        //项目jar名
        baseShell.setNAME("platform-admin.jar");
        //jvm
        baseShell.setJVM("256");
        //port
        baseShell.setPORT("14081");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("1");
    }
    /*
      清远门户前台测试环境请求
     */
    private static void qingyuan_api_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("QINGYUAN");
        //项目类型
        baseShell.setPROJECT_CHINESE("清远门户");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/qingyuan_agricultural_portal/agricultural_portal/");
        //项目jar名
        baseShell.setNAME("platform-portal-api.jar");
        //jvm
        baseShell.setJVM("256");
        //port
        baseShell.setPORT("14082");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }

     /*
      团购小程序测试环境请求
     */
    private static void group_api_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("GROUP");
        //项目类型
        baseShell.setPROJECT_CHINESE("团购小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xunxj_shop/platofrm-groupbuy/");
        //项目jar名
        baseShell.setNAME("platform-groupbuy.jar");
        //jvm
        baseShell.setJVM("512");
        //port
        baseShell.setPORT("15070");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }

    /*
      商城小程序测试环境请求
     */
    private static void shop_api_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("SHOP");
        //项目类型
        baseShell.setPROJECT_CHINESE("商城小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xunxj_shop/platofrm-shop/");
        //项目jar名
        baseShell.setNAME("platform-shop.jar");
        //jvm
        baseShell.setJVM("512");
        //port
        baseShell.setPORT("19080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }
    /*
      商城小程序测试环境请求
     */
    private static void shop_api_prod(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("SHOP");
        //项目类型
        baseShell.setPROJECT_CHINESE("商城小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xxj_shop/");
        //项目jar名
        baseShell.setNAME("platform-shop.jar");
        //jvm
        baseShell.setJVM("512");
        //port
        baseShell.setPORT("19080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }
    /*
      商城后端测试环境请求
     */
    private static void shop_admin_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("SHOP");
        //项目类型
        baseShell.setPROJECT_CHINESE("商城后台");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xunxj_shop/platofrm-admin/");
        //项目jar名
        baseShell.setNAME("platform-admin.jar");
        //jvm
        baseShell.setJVM("512");
        //port
        baseShell.setPORT("13080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("1");
    }
    /*
      商城后端测试环境请求
     */
    private static void shop_admin_prod(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("SHOP");
        //项目类型
        baseShell.setPROJECT_CHINESE("商城后台");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xxj_shop_manage/");
        //项目jar名
        baseShell.setNAME("platform-admin.jar");
        //jvm
        baseShell.setJVM("1024");
        //port
        baseShell.setPORT("13080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("1");
    }

    /*
      120服务器商城后端测试环境请求
     */
    private static void shop_admin_prod_120(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("SHOP");
        //项目类型
        baseShell.setPROJECT_CHINESE("商城后台");
        //jar包地址
        baseShell.setPATH_JARS("/data/xxj_projects/xxj_shop/xxj_shop_admin/");
        //项目jar名
        baseShell.setNAME("platform-admin.jar");
        //jvm
        baseShell.setJVM("1024");
        //port
        baseShell.setPORT("13080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("1");
    }
    /*
      采购前端测试环境请求
     */
    private static void farm_buyer_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("BUYER");
        //项目类型
        baseShell.setPROJECT_CHINESE("采购前台");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xunxj_purchase_platform/platform-api/");
        //项目jar名
        baseShell.setNAME("platform-api.jar");
        //jvm
        baseShell.setJVM("256");
        //port
        baseShell.setPORT("16080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }
    /*
      农场后台测试环境请求
     */
    private static void farm_admin_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("FARM");
        //项目类型
        baseShell.setPROJECT_CHINESE("农场小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xunxj_purchase_platform/platform-admin/");
        //项目jar名
        baseShell.setNAME("platform-admin.jar");
        //jvm
        baseShell.setJVM("512");
        //port
        baseShell.setPORT("14080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("1");
    }
    /*
      农场后台正式环境请求
     */
    private static void farm_admin_prod(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("FARM");
        //项目类型
        baseShell.setPROJECT_CHINESE("农场小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xxj_buyer_manage/");
        //项目jar名
        baseShell.setNAME("platform-admin.jar");
        //jvm
        baseShell.setJVM("512");
        //port
        baseShell.setPORT("17080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("1");
    }
    /*
      农场小程序正式环境请求
     */
    private static void farm_prod(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("FARM");
        //项目类型
        baseShell.setPROJECT_CHINESE("农场小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xxj_farm/");
        //项目jar名
        baseShell.setNAME("platform-farm.jar");
        //jvm
        baseShell.setJVM("256");
        //port
        baseShell.setPORT("11080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }

    /*
      农场小程序测试环境请求
     */
    private static void farm_dev(BaseShell baseShell) {
        //项目类型
        baseShell.setPROJECT_TYPE("FARM");
        //项目类型
        baseShell.setPROJECT_CHINESE("农场小程序");
        //jar包地址
        baseShell.setPATH_JARS("/opt/deploy/xunxj_purchase_platform/platform-farm/");
        //项目jar名
        baseShell.setNAME("platform-farm.jar");
        //jvm
        baseShell.setJVM("256");
        //port
        baseShell.setPORT("11080");
        //LOG
        baseShell.setGC_LOG("logs/gc.log");
        //1 admin 2 api
        baseShell.setTYPE("2");
    }


    //生成参数判断
    private  static  String  generatePrams(BaseShell baseShell) {
        StringBuffer data = new StringBuffer();
        data.append("#使用说明,用来提示输入参数").append("\n");
        data.append("usage() {").append("\n");
        //1 admin 2 api
        if (baseShell.getTYPE().equals("1")){
            data.append("     ").append("echo \"Usage: ./opration.sh [start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin ")
                    .append(" | stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin")
                    .append(" | status")
                    .append("]\"").append("\n");
        }else{
            data.append("     ").append("echo \"Usage: ./opration.sh [start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api ")
                    .append(" | stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api")
                    .append(" | status")
                    .append("]\"").append("\n");
        }
        data.append("     ").append("exit 1").append("\n");
        data.append("}").append("\n");
        return data.toString();
    }

    //生成入口判断
    private  static  String  generateInDoor(BaseShell baseShell) {
        StringBuffer data = new StringBuffer();
        data.append("#根据输入参数,选择执行对应方法,不输入则执行使用说明").append("\n");
        data.append("case \"$1\" in").append("\n");
        data.append("     ").append("#"+baseShell.getPROJECT_CHINESE()+"入口").append("\n");
        //1 admin 2 api
        if (baseShell.getTYPE().equals("1")){
            data.append("     ").append("\"start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin\")").append("\n");
            data.append("       ").append("start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("\n");
            data.append("       ").append(";;").append("\n");
            data.append("     ").append("\"stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin\")").append("\n");
            data.append("       ").append("stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("\n");
            data.append("       ").append(";;").append("\n");
        }else{
            data.append("     ").append("\"start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api\")").append("\n");
            data.append("       ").append("start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("\n");
            data.append("       ").append(";;").append("\n");
            data.append("     ").append("\"stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api\")").append("\n");
            data.append("       ").append("stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("\n");
            data.append("       ").append(";;").append("\n");
        }
        data.append("     ").append("\"status\")").append("\n");
        data.append("       ").append("status").append("\n");
        data.append("       ").append(";;").append("\n");
        data.append("   *)").append("\n");
        data.append("    usage").append("\n");
        data.append("    ;;").append("\n");
        data.append("esac").append("\n");
        return data.toString();
    }
    //生成状态服务
    private  static  String  generateStatus(BaseShell baseShell) {
        StringBuffer data = new StringBuffer();
        data.append("#状态").append("\n");
        data.append("status(){").append("\n");
        //1 admin 2 api
        if (baseShell.getTYPE().equals("1")){
            data.append("     ").append("#"+baseShell.getPROJECT_CHINESE()+"后台状态").append("\n");
            data.append("     ").append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("\n");
            data.append("     ").append("if [ $? -eq \"0\" ]; then").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN}/${NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN} is running. Pid is ${pid}\"").append("\n");
            data.append("     ").append("else").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN}/${NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN} is not running\"").append("\n");
        }else{
            data.append("     ").append("#"+baseShell.getPROJECT_CHINESE().toLowerCase()+"前台api状态").append("\n");
            data.append("     ").append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("\n");
            data.append("     ").append("if [ $? -eq \"0\" ]; then").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API}/${NAME_"+baseShell.getPROJECT_TYPE()+"_API} is running. Pid is ${pid}\"").append("\n");
            data.append("     ").append("else").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API}/${NAME_"+baseShell.getPROJECT_TYPE()+"_API} is not running\"").append("\n");
        }
        data.append("     ").append("fi").append("\n");
        data.append("}").append("\n");
        return data.toString();
    }
    //生成停止服务
    private  static  String  generateStop(BaseShell baseShell) {
        /**
         #启动商城后台服务
         */
        StringBuffer data = new StringBuffer();
        //1 admin 2 api
        if (baseShell.getTYPE().equals("1")){
            data.append("#停止"+baseShell.getPROJECT_CHINESE()+"后台服务").append("\n");
            data.append("stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("(){").append("\n");
            data.append("     ").append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("\n");
            data.append("     ").append("if [ $? -eq \"0\" ]; then").append("\n");
            data.append("       ").append("kill -9 $pid").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN}/${NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN} is stoped...\"").append("\n");
            data.append("     ").append("else").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN}/${NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN} is not running\"").append("\n");
        }else{
            data.append("#停止"+baseShell.getPROJECT_CHINESE().toLowerCase()+"前台api服务").append("\n");
            data.append("stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("(){").append("\n");
            data.append("     ").append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("\n");
            data.append("     ").append("if [ $? -eq \"0\" ]; then").append("\n");
            data.append("       ").append("kill -9 $pid").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API}/${NAME_"+baseShell.getPROJECT_TYPE()+"_API} is stoped..\"").append("\n");
            data.append("     ").append("else").append("\n");
            data.append("       ").append("echo \"${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API}/${NAME_"+baseShell.getPROJECT_TYPE()+"_API} is not running\"").append("\n");

        }
        data.append("     ").append("fi").append("\n");
        data.append("}").append("\n");
        return data.toString();
    }
    //生成启动服务
    private  static  String  generateStart(BaseShell baseShell) {
        /**
         #启动商城后台服务
         */
        StringBuffer data = new StringBuffer();
        //1 admin 2 api
        if (baseShell.getTYPE().equals("1")){
            data.append("#启动"+baseShell.getPROJECT_CHINESE()+"后台服务").append("\n");
            data.append("start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("(){").append("\n");
            //data.append("     ").append("stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("\n");
            data.append("     ").append("nohup java -Xms").append("${JVM_"+baseShell.getPROJECT_TYPE()+"_ADMIN}").append("M ")
                    .append("-Xmx").append("${JVM_"+baseShell.getPROJECT_TYPE()+"_ADMIN}").append("M ")
                    .append(" -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:").append("${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN}/").append("${LOG_"+baseShell.getPROJECT_TYPE()+"_ADMIN}")
                    .append(" -jar  ").append("${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN}/$NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN")
                    .append(" --server.port=").append("${PORT_"+baseShell.getPROJECT_TYPE()+"_ADMIN} >"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin.out 2>&1 &")
                    .append("\n")
                    .append("     ").append("sleep 2").append("\n")
                    .append("     ").append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("\n");
        }else{
            data.append("#启动"+baseShell.getPROJECT_CHINESE()+"前台api服务").append("\n");
            data.append("start_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("(){").append("\n");
            //data.append("     ").append("stop_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("\n");
            data.append("     ").append("nohup java -Xms").append("${JVM_"+baseShell.getPROJECT_TYPE()+"_API}").append("M ")
                    .append("-Xmx").append("${JVM_"+baseShell.getPROJECT_TYPE()+"_API}").append("M ")
                    .append(" -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:").append("${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API}/").append("${LOG_"+baseShell.getPROJECT_TYPE()+"_API}")
                    .append(" -jar  ").append("${PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API}/$NAME_"+baseShell.getPROJECT_TYPE()+"_API")
                    .append(" --server.port=").append("${PORT_"+baseShell.getPROJECT_TYPE()+"_API} >"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api.out 2>&1 &")
                    .append("\n")
                    .append("     ").append("sleep 2").append("\n")
                    .append("     ").append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("\n");
        }
        data.append("}").append("\n");
        return data.toString();
    }
    //生成检查服务程序是否在运行
    private  static  String  generateIsExist(BaseShell baseShell) {
        /**
         #检查服务程序是否在运行
         is_exist_shop_admin(){
         pid=`ps -ef|grep $PATH_JARS_SHOP_ADMIN/$NAME_SHOP_ADMIN |grep -v grep|awk '{print $2}'`
         #如果不存在返回1,存在返回0
         if [ -z "${pid}" ]; then
         return 1
         else
         return 0
         fi
         }
         */
        StringBuffer data = new StringBuffer();
        data.append("#检查"+baseShell.getPROJECT_CHINESE()+"服务程序是否在运行").append("\n");
        //1 admin 2 api
        if ("1".equals(baseShell.getTYPE())){
            data.append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_admin").append("(){").append("\n");
            data.append("     ").append("pid=`ps -ef|grep $PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN/$NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN |grep -v grep|awk '{print $2}'`").append("\n");
        }else{
            data.append("is_exist_"+baseShell.getPROJECT_TYPE().toLowerCase()+"_api").append("(){").append("\n");
            data.append("     ").append("pid=`ps -ef|grep $PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API/$NAME_"+baseShell.getPROJECT_TYPE()+"_API |grep -v grep|awk '{print $2}'`").append("\n");
        }
        data.append("     ").append("echo $pid").append("\n");
        data.append("     ").append("#如果不存在返回1,存在返回0").append("\n");
        data.append("     ").append("if [ -z \"${pid}\" ]; then").append("\n");
        data.append("       ").append("return 1").append("\n");
        data.append("     ").append("else").append("\n");
        data.append("       ").append("return 0").append("\n");
        data.append("     ").append("fi").append("\n");
        data.append("}").append("\n");
        return data.toString();
    }

    //生成常量
    private  static  String  generateConstant(BaseShell baseShell) {
        /**
         * #商城(团购)后台
         * PATH_JARS_SHOP_ADMIN=/opt/deploy/xunxj_shop/platofrm-admin/
         * NAME_SHOP_ADMIN=platform-admin.jar
         * JVM_SHOP_ADMIN=256
         * PORT_SHOP_ADMIN=13080
         * LOG_SHOP_ADMIN=${PATH_JARS_SHOP_ADMIN}/logs/gc.log
         *
         * #商城前台api
         * PATH_JARS_SHOP_API=/opt/deploy/xunxj_shop/platofrm-shop/
         * NAME_SHOP_API=platform-shop.jar
         * JVM_SHOP_API=256
         * PORT_SHOP_API=19080
         * LOG_SHOP_API=${PATH_JARS_SHOP_API}/logs/gc.log
         */
        StringBuffer data = new StringBuffer();
        //1 admin 2 api
        if ("1".equals(baseShell.getTYPE())){
            data.append("#"+baseShell.getPROJECT_CHINESE()+"后台").append("\n");
            data.append("PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_ADMIN").append("=").append(baseShell.getPATH_JARS()).append("\n");
            data.append("NAME_"+baseShell.getPROJECT_TYPE()+"_ADMIN").append("=").append(baseShell.getNAME()).append("\n");
            data.append("JVM_"+baseShell.getPROJECT_TYPE()+"_ADMIN").append("=").append(baseShell.getJVM()).append("\n");
            data.append("PORT_"+baseShell.getPROJECT_TYPE()+"_ADMIN").append("=").append(baseShell.getPORT()).append("\n");
            data.append("LOG_"+baseShell.getPROJECT_TYPE()+"_ADMIN").append("=").append(baseShell.getGC_LOG()).append("\n");
        }else if ("2".equals(baseShell.getTYPE())){
            data.append("#"+baseShell.getPROJECT_CHINESE()+"前台api").append("\n");
            data.append("PATH_JARS_"+baseShell.getPROJECT_TYPE()+"_API").append("=").append(baseShell.getPATH_JARS()).append("\n");
            data.append("NAME_"+baseShell.getPROJECT_TYPE()+"_API").append("=").append(baseShell.getNAME()).append("\n");
            data.append("JVM_"+baseShell.getPROJECT_TYPE()+"_API").append("=").append(baseShell.getJVM()).append("\n");
            data.append("PORT_"+baseShell.getPROJECT_TYPE()+"_API").append("=").append(baseShell.getPORT()).append("\n");
            data.append("LOG_"+baseShell.getPROJECT_TYPE()+"_API").append("=").append(baseShell.getGC_LOG()).append("\n");
        }
        return data.toString();
    }

}

生成后的shell脚本如下:

#!/bin/bash
#使用说明,用来提示输入参数
usage() {
     echo "Usage: ./opration.sh [start_shop_api  | stop_shop_api | status]"
     exit 1
}


#商城小程序前台api
PATH_JARS_SHOP_API=/opt/deploy/xunxj_shop/platofrm-shop/
NAME_SHOP_API=platform-shop.jar
JVM_SHOP_API=512
PORT_SHOP_API=19080
LOG_SHOP_API=logs/gc.log


#检查商城小程序服务程序是否在运行
is_exist_shop_api(){
     pid=`ps -ef|grep $PATH_JARS_SHOP_API/$NAME_SHOP_API |grep -v grep|awk '{print $2}'`
     echo $pid
     #如果不存在返回1,存在返回0
     if [ -z "${pid}" ]; then
       return 1
     else
       return 0
     fi
}


#启动商城小程序前台api服务
start_shop_api(){
     nohup java -Xms${JVM_SHOP_API}M -Xmx${JVM_SHOP_API}M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGC -Xloggc:./${LOG_SHOP_API} -jar  ${PATH_JARS_SHOP_API}/$NAME_SHOP_API --server.port=${PORT_SHOP_API} >shop_api.out 2>&1 &
     sleep 2
     is_exist_shop_api
}


#停止商城小程序前台api服务
stop_shop_api(){
     is_exist_shop_api
     if [ $? -eq "0" ]; then
       kill -9 $pid
       echo "${PATH_JARS_SHOP_API}/${NAME_SHOP_API} is stoped.."
     else
       echo "${PATH_JARS_SHOP_API}/${NAME_SHOP_API} is not running"
     fi
}


#状态
status(){
     #商城小程序前台api状态
     is_exist_shop_api
     if [ $? -eq "0" ]; then
       echo "${PATH_JARS_SHOP_API}/${NAME_SHOP_API} is running. Pid is ${pid}"
     else
       echo "${PATH_JARS_SHOP_API}/${NAME_SHOP_API} is not running"
     fi
}


#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
     #商城小程序入口
     "start_shop_api")
       start_shop_api
       ;;
     "stop_shop_api")
       stop_shop_api
       ;;
     "status")
       status
       ;;
   *)
    usage
    ;;
esac


 

五、手动内存释放

有的会后内存还是会持续飙升,这个时候可以手动处理下:

echo 3 > /proc/sys/vm/drop_caches
这个文件可以设置的值分别为1、2、3。它们所表示的含义为:

echo 1 > /proc/sys/vm/drop_caches:表示清除 page cache。

echo 2 > /proc/sys/vm/drop_caches:表示清除回收 slab 分配器中的对象(包括目录项缓存和 inode 缓存)。slab 分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的 page cache

echo 3 > /proc/sys/vm/drop_caches:表示清除 page cache 和 slab 分配器中的缓存对象

处理前后:

执行前最好sync

     手动执行sync命令(描述:sync 命令运行 sync 子例程。如果必须停止系统,则运行sync 命令以确保文件系统的完整性。sync 命令将所有未写的系统缓冲区写到磁盘中,包含已修改的 i-node、已延迟的块 I/O 和读写映射文件)

两次线上故障让我明白了jvm参数调优的重要性_第1张图片

参考博文:

https://blog.csdn.net/lqglqglqg/article/details/82313966

https://www.jianshu.com/p/016f7cf0380d

https://blog.csdn.net/wyzxg/article/details/7279986/(AAA)

你可能感兴趣的:(线上部署)