V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
8629
V2EX  ›  Java

请教下 Java 热替换代码的技术

  •  1
     
  •   8629 · 11 天前 · 3966 次点击
    场景:把接收处理数据的类 源码放在 web 后台页面中在线编辑,编辑完这个类直接提交到 redis 中。然后线上服务就替换了这部分代码,不需要打包发布实现了更新服务。
    请问这是什么技术,什么关键字可以查询,有没有现成的 demo 可以学习下。感谢!
    59 条回复    2022-09-30 15:25:52 +08:00
    nothingistrue
        1
    nothingistrue  
       11 天前   ❤️ 1
    你这要能实现,得推翻 Java 的编译原理。
    sunjiayao
        2
    sunjiayao  
       11 天前   ❤️ 1
    groovy 你值得拥有
    XiLingHost
        3
    XiLingHost  
       11 天前
    把新的代码自动构建成一个镜像,然后调整服务路由做灰度发布就行了,调用方可以无感
    wxw752
        4
    wxw752  
       11 天前   ❤️ 3
    就因为这个问题,我们公司到现在还有一块业务用的 PHP
    LeegoYih
        5
    LeegoYih  
       11 天前
    GitHub 搜 Java HotSwap 还是有挺多 demo 的,不过能替换的程度有限
    twinsdestiny
        6
    twinsdestiny  
       11 天前
    groovy 可以
    justicelove
        7
    justicelove  
       11 天前
    groovy
    wangxiaoaer
        8
    wangxiaoaer  
       11 天前   ❤️ 2
    @nothingistrue ??? 怎么叫推翻呢。

    javax.tools 本身就提供了编译 java 文件的途径,再加上 classloader 替换,技术上应该是可行的。
    superchijinpeng
        9
    superchijinpeng  
       11 天前
    superchijinpeng
        10
    superchijinpeng  
       11 天前
    @nothingistrue 无非是 Class Load 和 Unload 一下
    chendy
        11
    chendy  
       11 天前
    编译 api+反射,不是替换是新增……
    每次生成一个新类名,编译出 class 文件,再把调用的地方的类名替换上去,完事
    justicelove
        12
    justicelove  
       11 天前
    可以搜一下 java 脚本 一般都会选择 Groovy, 也可以使用 spi, 在服务器上替换 jar 包
    aguesuka
        13
    aguesuka  
       11 天前
    鉴于有在线编辑的需求, 建议直接用 groovy 脚本. 完全兼容 java 对象, 无需编译, 也就不需要热替换.
    Dxxxxs
        14
    Dxxxxs  
       11 天前
    可以看一下 jvm 提供的 Instrumentation 类。JRebel 、springboot dev tools 、HotSwapAgent 都提供了类似的实现
    pannanxu
        15
    pannanxu  
       11 天前
    PF4J
    Jooooooooo
        16
    Jooooooooo  
       11 天前
    自定义 classloader 就行. 加载这个 jar. 然后跑方法的时候用反射拿到你热加载的那个.

    我们刚好有这么搞的, 引入算法包天天变, 就用的这个方法不用发版能用上最新的

    不过有几个注意点你得考虑下:

    1. 有 load 记得要 unload
    2. 如果是集群, 要保证所有集群都加载完了再去用, 所以需要维护机器当前加载某个 jar 的状态, 最好有个后台去查看和使用, 全部机器都加载完了才能真正使用
    VYSE
        17
    VYSE  
       11 天前
    关键字: JAVA 反序列化漏洞
    这么搞后台得管控好
    m2276699
        18
    m2276699  
       11 天前
    cn.hutool.core.compiler/sofa ark
    cpstar
        19
    cpstar  
       11 天前
    java 语言层面支持。然后就是你的应用底层需要支持。我目前正在用的一个平台微应用化就支持动态加载,关闭某个应用或者打开,更新了 jar 之后动态重加载。
    但是我目前用的平台支持 beanshell 、groovy 、javascript 的在线编辑和编译,java 代码的编辑和编译再加载不知道有没有库。javascript 方面使用的是 apache rhino 库,翻译 js 并运行。
    ic2y
        20
    ic2y  
       11 天前   ❤️ 1
    为什么不用规则引擎,专门处理这种事。例如 Aviator
    xiangxiangxiang
        21
    xiangxiangxiang  
       11 天前
    groovy 脚本+1 之前有场景就是 m 端维护 /发布代码块,然后在 c 端动态加载生效
    Vegetable
        22
    Vegetable  
       11 天前
    标准的 RPC API 、自动构建、容器化
    virusdefender
        23
    virusdefender  
       11 天前
    coala
        25
    coala  
       11 天前
    类似 JSP 呗.
    q1angch0u
        26
    q1angch0u  
       11 天前 via iPhone
    grpovy 啊…
    paullee
        27
    paullee  
       11 天前 via iPhone
    花这些功夫,用 k8s 部署,滚动更新,不是更舒服?
    humpy
        29
    humpy  
       11 天前
    可以做,jdk 提供了 JavaCompiler ,可以在运行时编译代码,将编译后的代码存在内存里,再实现一个 ClassLoader ,就能加载刚编译的类了。

    可以参考一下这篇文章,好像是微博的老师写的:
    https://zhenbianshu.github.io/2019/12/play_with_java_dynamic_compile.html
    misaka19000
        31
    misaka19000  
       11 天前 via Android
    可以用 ASM 动态替换字节码来做,或者用 ByteBuddy 使用更高级一些的 API
    molika
        32
    molika  
       11 天前
    jvm 上用 Clojure 天生支持
    codehz
        33
    codehz  
       11 天前
    还记得之前的 jndi 漏洞吗(
    听着就是在造 RCE(
    xuanbg
        34
    xuanbg  
       11 天前
    能搞!办法还不少,但没一个是安全的。想想也知道,这就相当于代码不经过审查和测试就直接上线,我写几个漏洞也是没什么问题的吧?谁还不写几个 bug 呢。。。
    nothingistrue
        35
    nothingistrue  
       11 天前
    @wangxiaoaer #8
    @superchijinpeng #10
    仔细看:“编辑完这个类直接提交到 redis 中”,“不需要打包”。这是想源码一步到底的,Classloader 可 load 不了。
    superchijinpeng
        36
    superchijinpeng  
       11 天前
    @nothingistrue URLClassLoader ,参考 Spark 或者 Flink 动态注册或移除 UDF
    zhang77555
        37
    zhang77555  
       11 天前
    JavaCompiler 把代码编译成 class 然后 URLClassLoader 加载
    建议定好接口和编码模板校验,免得这部分功能被滥用
    selca
        38
    selca  
       11 天前
    @codehz #33 私有的东西,目的就是造一个门去调,如果操作者能控制好鉴权,还是没问题的
    dddyyyttt
        39
    dddyyyttt  
       11 天前
    为什么没人提 arthas ?
    wangxiaoaer
        40
    wangxiaoaer  
       11 天前
    @nothingistrue 我理解他的意思是 java 代码存到 redis ,但是肯定不能和直接用,后台可以从 redis 读这些代码编译,替换。

    如果想直接从 redis 加载 java 文件就替换运行,那肯定是不行的。
    3032
        41
    3032  
       11 天前
    阿里的阿尔萨斯了解下
    vvtf
        42
    vvtf  
       11 天前
    1. 通过 Agent 拿到 Instrumentation
    2. 通过 Instrumentation#redefineClasses 替换类即可.
    leegradyllljjjj
        43
    leegradyllljjjj  
       11 天前
    v 我 50 ,我帮你守着服务器,你一提交代码我就帮你编译发布
    zgzhang
        44
    zgzhang  
       11 天前
    这样的东西很成熟呀,我做的类似的项目,核心原理就是 Java 的动态编译+spring bean 的替换,如果有需要可以联系我
    BiChengfei
        45
    BiChengfei  
       11 天前
    magic-api
    warcraft1236
        46
    warcraft1236  
       11 天前
    这个跟热更新不是一个原理吗
    thisisgpy
        47
    thisisgpy  
       11 天前
    先把 class restransform 回来,记录一下当前在用 classloader 的 hashcode ,新的代码编译后找到刚才的 classloader rebase 进去
    Znemo
        48
    Znemo  
       11 天前
    classloader 就能做到,但是这要围绕这种编程模型来架构,一般的业务代码可以这样热替换,核心代码例如 class 的加载、事件调度等就很难做到了,另外方法区的垃圾回收要关注,被替换掉的 class 要有有效的回收机制。除非精心设计,否则需要注意的问题还是蛮多的。
    hetal
        49
    hetal  
       10 天前   ❤️ 1
    换成 php 是不是更简单~
    viakiba
        50
    viakiba  
       10 天前
    写过这个介绍, 可以参考 https://blog.viakiba.cn/2020/03/30/java-hot-fix/
    byte10
        51
    byte10  
       10 天前
    OSGI 框架 应该也可以满足 OP 需求,另外还有一种 hotswap , hook 技术都可以做到,并不是特别复杂的事情,可以多了解下。
    iloveios
        52
    iloveios  
       10 天前 via iPhone
    赞同 49 楼
    muhuan
        53
    muhuan  
       10 天前
    songco
        54
    songco  
       10 天前 via iPhone
    Groovy 加 1

    以前做过一个比较大的平台,大量使用 groovy ,还是比较好用的,缺点是复杂逻辑用 groovy 容易埋坑

    Classloader 我在项目中也大量使用过,类似实现了一种插件机制,插件的升级就相当于替换了
    westoy
        55
    westoy  
       10 天前 via Android
    @hetal

    其实 erlang 那种才是

    php 其实并不是热更新,并发大一点很容易在更新时触发一半新版本夹了几个旧版本文件

    上古严谨点的 php 项目也是通过部署最新版本到一个新目录,然后启动新服务替代掉老服务,包括 zend 搞的 phpcloud ,推送文件变更后立刻访问也会提示正在重启应用
    ychost
        56
    ychost  
       7 天前
    建议 Groovy ,别搞 Java 热加载了一堆坑,
    LiangLin
        57
    LiangLin  
       5 天前
    jsp 了解以下
    b2byco
        58
    b2byco  
       5 天前
    Janino
    http://janino-compiler.github.io/janino/

    The ShippingCost class demonstrates how easy it is to use Janino as an expression evaluator.
    The ExpressionDemo class implements a command line-based test environment for the expression evaluator.
    The ScriptDemo class implements a command line-based test environment for the script evaluator.
    The ClassBodyDemo class implements a command line-based test environment for the class body evaluator.
    ggbond2233
        59
    ggbond2233  
       4 天前
    QLExpress
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1978 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 69ms · UTC 10:24 · PVG 18:24 · LAX 03:24 · JFK 06:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.