热部署instrumentation工具类

简介: 利用jvm的instrumentation类实现实时修改class文件,用来更新线上代码。

import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
public class ReloadClassUtil {

/**
 * 手动上传class文件实时更新jvm内存中的字节码文件
 * @param classFile newClassFile
 * @param classPath com.xxx.xxx.xxx
 * @return success/fail
 */
@PostMapping("/reloadClass")
public Result<String> reloadClass(List<MultipartFile> classFiles, String classPath) {
    log.info("reloadClass start, classPath:{}", classPath);
    if (!permission) return new Result<>(ResultCodeEnum.UNAUTHORIZED);
    return reTransformClass(classFiles, classPath);
}

private Result<String> reTransformClass(List<MultipartFile> files, String path){
    try {
        Map<String, String> fileByteMap = new HashMap<>();
        for (MultipartFile file : files) {
            String className = path + "." + file.getOriginalFilename().replace(".class", "");
            reTransform(file.getBytes(), className);
            fileByteMap.put(className, Base64Utils.encodeBase64String(file.getBytes()));
        }
        syncOthersClient(fileByteMap);
        return new Result<>(ResultCodeEnum.SUCCESS);
    } catch (Exception e) {
        log.error("reloadClass exception", e);
        return new Result<>(ResultCodeEnum.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}

/**
 * 重载class
 * @param fileBytes 字节码文件
 * @param classPath 目标包+类名
 */
public static boolean reTransform(byte[] fileBytes, String classPath) throws Exception{
    inst = inst != null ? inst : ByteBuddyAgent.install();
    String targetClass = classPath.replace(".", "/");
    inst.addTransformer((loader, className, classBeingRedefined, protectionDomain, classfileBuffer) ->
            className.equals(targetClass) ? fileBytes : classfileBuffer, true);
    inst.retransformClasses(Class.forName(classPath));
    return true;
}

/**
 * 同步class文件给集群内其他服务器
 */
private void syncOthersClient(Map<String, String> fileByteMap){
    StringBuilder sb = new StringBuilder();
    fileByteMap.forEach((k,v) -> {
        sb.append(k).append(",");
        redisTemplate.opsForValue().set(k, v, cacheSecond, TimeUnit.SECONDS);
    });
    ReloadClassDto body = ReloadClassDto.builder().classPath(sb.substring(0, sb.length()-1)).build();
    mq.sendMsg(body);
}

@Value("${reload.cacheSecond:60}")
private long cacheSecond;

@Value("${reload.permission:false}")
private boolean permission;

private static Instrumentation inst;

@Resource
private RedisTemplate<String, String> redisTemplate;

}

相关文章
|
Linux 网络安全 数据库
MyCat下载与安装
MyCat下载与安装
3922 0
|
11月前
|
Java 关系型数据库 数据库连接
mybatis中的useGeneratedKeys和keyProperty
在 MyBatis 中,`useGeneratedKeys` 和 `keyProperty` 是用于处理数据库自动生成主键的关键配置。通过这些配置,可以方便地获取和使用数据库生成的主键值,提高开发效率和代码可读性。确保正确配置和使用这两个属性,可以在应用程序中高效地进行数据库操作。
434 25
|
测试技术 开发者 Python
深入理解Python装饰器:从基础到高级应用
本文旨在为读者提供一个全面的Python装饰器指南,从其基本概念讲起,逐步深入探讨其高级应用。我们将通过实例解析装饰器的工作原理,并展示如何利用它们来增强函数功能、控制程序流程以及实现代码的模块化。无论你是Python初学者还是经验丰富的开发者,本文都将为你提供宝贵的见解和实用的技巧,帮助你更好地掌握这一强大的语言特性。
194 4
|
存储 Cloud Native NoSQL
云原生时代的数据库选型与架构设计
云原生时代的数据库选型与架构设计
280 0
|
运维 Kubernetes Cloud Native
莉莉丝游戏云原生之路
本文将介绍莉莉丝游戏云原生化的背景、挑战,以及应对的解决方案,记录了莉莉丝游戏云原生化历程,为游戏架构云原生转型提供经验。
莉莉丝游戏云原生之路
|
存储 编译器 Linux
Cython 和 Python 的区别
Cython 和 Python 的区别
485 0
|
网络协议 Linux 网络安全
Linux配置SSH允许TCP转发
Linux配置SSH允许TCP转发
175 1
|
存储 关系型数据库 MySQL
Mysql行格式DYNAMIC和COMPACT区别
总之,选择哪种行格式取决于具体的应用场景,如数据类型分布、读写比例、存储与性能需求等。在处理大量文本或二进制数据且对存储空间敏感的应用中,DYNAMIC格式可能是更好的选择;而对于混合型数据且对读取性能有一定要求的场景,COMPACT格式可能更合适。在设计数据库时,评估这些因素并进行适当测试,可以帮助确定最适合的行格式。
790 0
|
关系型数据库 数据库 数据安全/隐私保护
在 Postgres 中使用模式
【8月更文挑战第11天】
673 0
在 Postgres 中使用模式
|
监控 算法 Java
JVM 调优之 glibc 引发的内存泄露
Pmap 提供了进程的内存映射,pmap 命令用于显示一个或多个进程的内存状态。其报告进程的地址空间和内存状态信息
JVM 调优之 glibc 引发的内存泄露