请教下 Java 热替换代码的技术 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
8629
V2EX    Java

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

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

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

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

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

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

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

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

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

    Classloader 我在项目中也大量使用过,类似实现了一种插件机制,插件的升级就相当于替换了
    westoy
        55
    westoy  
       2022-09-24 03:56:07 +08:00 via Android
    @hetal

    其实 erlang 那种才是

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

    上古严谨点的 php 项目也是通过部署最新版本到一个新目录,然后启动新服务替代掉老服务,包括 zend 搞的 phpcloud ,推送文件变更后立刻访问也会提示正在重启应用
    ychost
        56
    ychost  
       2022-09-26 22:29:30 +08:00
    建议 Groovy ,别搞 Java 热加载了一堆坑,
    LiangLin
        57
    LiangLin  
       2022-09-28 19:54:39 +08:00
    jsp 了解以下
    b2byco
        58
    b2byco  
       2022-09-29 10:44:52 +08:00
    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  
       2022-09-30 15:25:52 +08:00
    QLExpress
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5145 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 09:29 PVG 17:29 LAX 01:29 JFK 04:29
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86