首页 > 其他分享 >ShutdownHook妙用

ShutdownHook妙用

时间:2023-09-12 15:36:11浏览次数:30  
标签:妙用 ShutdownHook 线程 JVM 服务器 连接 客户端

上期文章分享了ShutdownHook的API和基本使用,但是少了一些实际工作中的案例,总感觉没啥大用一样。

最近总结工作中可以用到ShutdownHook来解决一些实际问题的例子,分享给大家。

任务统计

FunTester测试框架定义了好几个自定义的异步关键字,例如funfunnyfunner等。一旦使用到异步,肯定会用到线程池。但是Java线程池销毁需要手动操作,之前的视线中是放在daemon线程中实现。如果我想在每次JVM关闭之前都统计一下线程池执行任务总数,就不能写在daemon线程中了,原因有二:

  1. daemon线程可能出现异常,导致退出。
  2. 用户可能会主动停止程序,例如C

虽然可以通过自定义daemon线程解决这个问题。如果你想使用一种优雅的方法完成这个任务的话,那么ShutdownHook绝对是不二选择。

static {  
    addShutdownHook {  
        if (asyncPool != null) {  
            print "finished: " + getFunPool().getCompletedTaskCount() + " task"  
        }  
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean()  
        print(" uptime:" + runtimeMXBean.getUptime() + " s")  
    }  
}

通过静态代码块把两个ShutdownHook注册到JVM中,这一点也是从Web3j异步线程池源码中学到的。当然这只是个初级版本,这里抛砖引玉。

这里本地的动态QPS模型经常会用到java.util.Scanner来接收控制台输入信息,也需要进行资源回收。

static {  
    Runtime.getRuntime().addShutdownHook(new Thread(() -> closeScanner()));  
}

释放连接

ShutdownHook最重要的一个功能就是释放资源,通常如果是非服务形式启动一个JVM进程的话,只有确保JVM进程能够正常退出的话,通常不用担心资源泄露或者无法回收的问题。如果对于Java服务的话,通常使用池化框架来管理连接资源。对于测试工作来讲,我觉得足够了,只有很少细微的差别。

所以我对com.funtester.httpclient.FunHttp做了一点改造,这次用的JavaAPI

    /**
     * 结束测试,关闭连接池
     */
    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                ClientManage.httpsClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                ClientManage.httpAsyncClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }));

    }

这里查了一些客户端主动断开连接和客户端JVM直接关闭对服务端的影响,可供参考:

  1. 客户端主动发送断开连接: 当客户端在与服务器建立的 HTTPS 连接上主动发送断开连接请求(通过关闭连接),服务器会接收到这个请求,并根据 HTTP 协议的规范进行处理。服务器会知道连接被关闭,这可以触发服务器端的一些清理操作,例如释放连接资源,清理会话状态等。这是一个正常的连接关闭过程,服务器端会收到关闭连接的通知。

  2. 客户端直接关闭: 当客户端断开与服务端连接(例如断网),服务器无法立即感知到这个变化。这是因为客户端和服务器之间的连接是通过网络进行的,服务器没有直接的方式知道客户端是否断开了网络连接。在这种情况下,服务器会等待一段时间,直到发现客户端不再发送请求或响应。服务器可能会将这个连接保持一段时间,然后最终超时并关闭连接。

总之,客户端主动发送断开连接是一个明确的行为,服务器能够立即响应。而客户端断网可能会导致连接在服务器端保持一段时间,直到服务器超时或检测到连接不再活动。服务器通常会实现一些超时机制来管理连接,以防止持久的无效连接占用资源。

所以如果考虑这些细微的差别,还是选用主动断开回收这些资源。

PS:到这里,当我们需要添加超过1个ShutdownHook的时候,就可以非常明显地感受到daemon线程实现方案的差异,因为ShutdownHook可以到处写,多点开花。

应对JVM异常退出

如果你在使用docker和k8s的话,压测很容易导致JVM进程被迫退出。这个时候容器可能还在,只是JVM进程退出了,如果我们在基础依赖中,添加上下面这段代码,就可以发送消息。

    static {
        addShutdownHook {
            send("我挂了,顺便采集了快照!")
            snapshot()
        }
    }

状态记录

性能测试中,造数据的工作同行是繁琐且量大,一般都会使用脚本或者平台实现。但在实际使用当中总会遇到各种各样的异常,导致部分已经创建好的数据丢失,或者需要重新查库才能恢复。

下面是一个例子,场景:需要创建100w个新用户(假设用户属性均在已经封装好的方法中完成设置)。

import com.funtester.frame.Save
import com.funtester.frame.SourceCode
import com.funtester.utils.RWUtil

class HookTest extends SourceCode {

    static Vector<Integer> ids = RWUtil.readByNumLine(getLongFile("ids"))

    static void main(String[] args) {
        100_0000.times {
            if (ids.size() > 100_0000) fail()
            fun {
                def id = create()
                if (id > 0) {
                    ids << id
                }
            }
        }
    }

    static {
        addShutdownHook {
            Save.saveIntegerList(ids, getLongFile("ids"))
        }
    }

    static int create() {
        getRandomInt(Integer.MAX_VALUE)
    }
}

这里循环100w次,使用默认的线程池并发创建用户,如果用户的ID是大于0,认为正常用户,添加到线程安全的队列中。

一旦遇到异常,ShutdownHook中的方法会把已经创建好的用户ID存在文件中。每次执行,已创建好的用户ID集合会从文件中进行初始化。

多次执行,直到fail()抛异常,最后一次ids文件就是我们需要的创建用户的ID集合。

标签:妙用,ShutdownHook,线程,JVM,服务器,连接,客户端
From: https://blog.51cto.com/FunTester/7445720

相关文章

  • 深入探索Linux文件链接技术:ln命令的妙用
    当谈及Linux系统中的文件管理和链接技术,ln命令是一个不可或缺的工具。ln命令用于创建硬链接和软链接,它在Linux文件系统中发挥着重要作用,为用户提供了更大的灵活性和组织能力。在本文中,我们将深入探讨ln命令是什么,以及如何使用它来创建链接,以提高文件管理的效率。什么是ln......
  • 地级市绿色专利申请和授权的计算(妙用readlines)
    需求:工作中需要计算地级市绿色专利申请和授权数据,需要利用readlines进行分行文本值提取,然后进行转换、匹配和记录写入,最后需要分析汇总,用于后续的深度数据挖掘。解决:importrefile_path='/home/pxing/codes/realsense_ws/src/realsense-ros/scripts/Assembling/joint.txt'wit......
  • 解锁Python集合的妙用:常用函数与实例深度解析
    Python的集合(Set)是一种无序且不重复的数据结构,拥有强大的去重和集合运算功能。在这篇博客中,我们将深入探讨集合的常用函数,并通过实际案例为你展示其灵活应用。创建集合集合可以通过花括号来创建,也可以使用内置函数set()来转换其他可迭代对象为集合。#创建集合my_set={1,2,3}......
  • 创建元组的三种方式、字典中的setdefault和get妙用、类中的重载方法__add__()
    创建元组的三种方式#print(tuple([input(),input()]))#print((input(),input()))t=input(),input()print(t)#可以将列表转换成tuple,也可以直接()创建tuple,或者将多个变量赋值给一个值会自动转换成tuple字典中的setdefault和get妙用setdefault类似get方法w=input()......
  • v-model限制输入的妙用
    示例v-model.number即限制输入数字(原理是每次输入都会转化为数字,非数字则会丢弃)还有更多,如:v-model.trim:去除首尾空格。可以避免误输入一些不能看见的首尾空格。v-model.lazy:延迟更新。默认情况下,v-model在每次输入时都会立即触发input事件,而使用v-model.lazy可以将更新触发......
  • C++11可变模版参数妙用
    //参考:https://blog.csdn.net/wmy19890322/article/details/121427697点击查看代码//创建对象template<typenameT,typename...Args>T*CreateInstance(Args...args){ returnnewT(std::forward<Args>(args)...);}//可变参数模版实现泛化的delegatetemplate<typena......
  • Android 妙用TextView实现左边文字,右边图片
    原文:Android妙用TextView实现左边文字,右边图片-Stars-One的杂货小窝有时候,需要文字在左边,右边有个箭头,我个人之前会有两种做法:使用线性布局来实现或者使用约束布局,一个左对齐,一个右对齐这几天突然想到是否可以使用TextView的设置图标的方式实现,研究发现确实可......
  • Java8新特性之Optional类的妙用
    该新特性也是翻java八股翻到的,感觉有点小用,所以记录一下通常我们在调用方法获取参数时,常常会出现要避免空指针,从而进行一系列判断非空的代码,这部分十分冗长且啰嗦还重复,故可以使用一下今天的主角:Java.util.Optional类创建方式共有三种方式供您选择直接创建空的Optional对......
  • 小设计,大作用——防腐层的妙用
    前言最近在学习了解领域驱动模型DDD相关的内容,但是由于没有实际的项目支撑,所以大都是停留在一些理论层面。我发现这里面的一些设计思想还是非常有实用价值的,可以直接应用于你目前的项目中,今天我就来谈谈防腐层的妙用。一个简单的例子大家在做项目中是否有过这样的经历,你的项目......
  • Typora 妙用+阿里云+Picgo上传--实现他人可以查看自己的博客
    Typora妙用+阿里云+Picgo上传关水水来源链接:大学生写博客,做笔记请你不要忘了Typora工具|带你从零开始认识和使用Typora_哔哩哔哩_bilibili如果还是不行可以根据此链接来排查自己的错误链接:Typora+PicGo+阿里云OSS实现图片上传_早上好我是DJ同学的博客-CSDN博客当时是无法在T......