StarkWhite
V2EX  ›  Java

Java 是不是没有好的热部署方案?

  •  3
     
  •   StarkWhite · Jul 24, 2019 · 20549 views
    This topic created in 2671 days ago, the information mentioned may be changed or developed.

    像 Node,PHP 推代码到服务器就行了,Java 每次打 jar/war 包、停止旧服务、启动新服务,挺麻烦的,从启动到能访问还得等几分钟。

    Supplement 1  ·  Jul 24, 2019
    我目前是在做单机、单体服务,希望能够在不停服务的情况下更新服务。
    不管是 热部署、热更新、热加载,还是别的方式都请大家给出建议,谢谢
    Supplement 2  ·  Jul 25, 2019
    被人安利了 apijson,大家评估下
    Supplement 3  ·  Jul 25, 2019
    apijson 的热更新怎么样?
    https://v2ex.com/t/586011
    118 replies  •  2020-01-03 10:18:24 +08:00
    1  2  
    ❮ ❯
    NizumaEiji
    NizumaEiji
    Jul 24, 2019
    1
    OSGI?
    laravel
    laravel
    Jul 24, 2019
    2
    k8s
    gwybiaim
    gwybiaim
    Jul 24, 2019❤️2
    3
    有 hotswap 啊
    相关的工具也很多,比如阿里巴巴的 arthas
    https://alibaba.github.io/arthas/index.html
    http://hengyunabc.github.io/arthas-online-hotswap/
    airfling
    airfling
    Jul 24, 2019
    4
    这个是是 jvm 的性质导致的,没有好的解决方案,以前有 osgi,但是那个其实也是要很久
    zzf2019
    zzf2019
    Jul 24, 2019
    5
    不都是 灰度发布吗?从访问上来说非常平滑呀,这不是 java 的问题吧,node、php 都是只有一个服务吗
    nikandaoleshenme
    nikandaoleshenme
    Jul 24, 2019❤️2
    6
    不知道有种工具叫脚本么,
    再说了,要不是部署时间间隙这么长,哪有时间来 v 站逛 [搜狗表情]
    efaun
    efaun
    Jul 24, 2019
    7
    分布式是用来干嘛的
    chendy
    chendy
    Jul 24, 2019
    8
    反向代理 + 脚本
    k8s 之类自带滚动升级的东西
    lastpass
    lastpass
    Jul 24, 2019 via Android
    9
    完全可以很简单的热部署呀,我现在就在使用。
    在 windows 下编辑和调试代码,自动热部署到另一台 linux 服务器。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    10
    @zzf2019 就一台服务器,自己做个人小项目,怎么灰度发布 😂
    ala2008
    ala2008
    Jul 24, 2019
    11
    难道只有一个服务器?
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    12
    @cshijiel 感谢分享。但是在 arthas 官网的说明里没有热部署啊。hotswap 我再看看
    liangkang1436
    liangkang1436
    Jul 24, 2019
    13
    推荐 Jrebel
    https://jrebel.com/
    gwybiaim
    gwybiaim
    Jul 24, 2019
    14
    @StarkWhite 看第二个链接,三个命令就可以。最关键的是 redefine 命令,参考 https://alibaba.github.io/arthas/redefine.html
    guokeke
    guokeke
    Jul 24, 2019 via Android
    15
    单机可以用 nginx 反代多个 server 那样搞
    Xbluer
    Xbluer
    Jul 24, 2019 via iPhone
    16
    @StarkWhite 问题就变成了,个人小项目直接重启一下又不会太久,上啥热部署啊
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    17
    @laravel k8s 那一套太重了,还不如手动部署。。。
    Caballarii
    Caballarii
    Jul 24, 2019
    18
    java 从来不止起一个服务,停一个切路由起完了再切回来就好了
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    19
    @lastpass 你怎么实现的呢?
    jowan
    jowan
    Jul 24, 2019
    20
    PHP 也得看你是什么业务
    像我们的光推代码是不行的 还得重启队列,Socket 等服务
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    21
    @chendy 反向代理+脚本 看起来可行,只是写脚本太麻烦了,还有重启服务后状态会丢啊。

    k8s 那一套太重了,还不如手动部署。。。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    22
    @liangkang1436 感谢,但是 jrebel 官网打不开,蜜汁尴尬
    lastpass
    lastpass
    Jul 24, 2019 via Android
    23
    @StarkWhite #19 依靠 idea 和 jvm 的支持呀。首先 ssh 连接到远程服务器,配置 remote tomcat。tomcat 配置 jmx remote 开启远程调试。最后 idea 配置 automatic upload 以推送资源文件。
    lihongjie0209
    lihongjie0209
    Jul 24, 2019❤️2
    24
    类似这种问题, 你可以问一下 windows 团队为什么更新需要重启
    notreami
    notreami
    Jul 24, 2019
    25
    类比 Node,PHP 的话,springboot 的 devtools 就可以了,监听某个目录下的文件,有变化就重新加载运行。
    稳定点的话,使用模块化方式,比如 osgi、Jigsaw、Guice 等等,但是这些都太麻烦,对开发方式影响太大。
    crayhuang
    crayhuang
    Jul 24, 2019
    26
    jrebel+1
    szq8014
    szq8014
    Jul 24, 2019
    27
    想直接发布代码也没人有阻止呀~比如 maven 项目,就可以直接在服务器更新代码然后再 package 再 run。 [此处需要加狗头]
    redford42
    redford42
    Jul 24, 2019❤️5
    28
    不然 Java 图标为什么是一杯咖啡呢
    Raymon111111
    Raymon111111
    Jul 24, 2019
    29
    答案是 是

    没有简便的解决方案

    jvm 特性导致的
    rockyou12
    rockyou12
    Jul 24, 2019
    30
    单机 java 又是个人项目最好就别搞灰度了,费力不讨好。用 nginx 其实也不算简单,要你本地新旧两个服务用不同端口,然后 upstream。

    k8s 的 deployment 发布已经是最省事的灰度发布了,如果机器不够真的不如不做
    Infernalzero
    Infernalzero
    Jul 24, 2019❤️1
    31
    楼上也很多人说了,这个没有彻底的解决方案,jvm 特性导致
    只能是想办法减少启动时间,这里主要分两块,一块是 JIT 导致的,只能像阿里那样从 jvm 层面去改
    还有一块是 spring 导致的,因为 spring 的依赖注入,创建 bean 和扫描非常耗时,所以使得 java 应用启动更慢,这个目前业界的思路是把依赖注入放到编译构建阶段去做,对应的框架是 micronaut,但是这个要取代 spring 有点困难。
    qiyuey
    qiyuey
    Jul 24, 2019
    32
    热部署一定是需要的吗?
    passerbytiny
    passerbytiny
    Jul 24, 2019
    33
    “类似这种问题, 你可以问一下 windows 团队为什么更新需要重启” —— lihongjie0209

    纵观楼主的回复,还是这个最接近答案,其他人可以散了。
    fivesmallq
    fivesmallq
    Jul 24, 2019
    34
    你可以试试 play 1.x https://www.playframework.com/documentation/1.5.x/home

    支持热部署,springboot 的 devtools 太 low 了,实际上还是重启。

    不过这个特性不建议生产环境使用
    Takamine
    Takamine
    Jul 24, 2019
    35
    devtools。
    mineqiqi
    mineqiqi
    Jul 24, 2019
    36
    分布式的项目可以平滑过渡升级,但是并不是热部署
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    37
    @redford42 意思应该先自己冲一杯咖啡,然后在慢慢喝,喝完了可以瞄一眼进度?/滑稽
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    38
    @mineqiqi 没钱啊,个人小项目上分布式,得买几台服务器,而且运维麻烦
    luckyrayyy
    luckyrayyy
    Jul 24, 2019
    39
    nginx or docker
    lihongjie0209
    lihongjie0209
    Jul 24, 2019
    40
    @StarkWhite #38 没必要上分布式, 只用多节点就可以了. 一台服务器部署两个一样的应用, 监听不同的端口, nginx 做负载就好了
    est
    est
    Jul 24, 2019
    41
    node 那个不叫热部署吧。。。

    严格的说文件拷贝过程中出了什么差错还会出问题的。
    th00000
    th00000
    Jul 24, 2019
    42
    现在部署都是集群内的 rolling update, 楼主的热部署指的是在同一个服务不停的情况下, 直接推送更新吧, 在当前的集群环境下, 个人认为必要性越来越低.
    但是开发的时候爽啊
    不用改一行代码就重启了, 直接 reload 完事.
    ninjachen
    ninjachen
    Jul 24, 2019
    43
    对的,nginx 那边做个路由,然后反向代理到多个 port,也就是多个 java 实例
    Weixiao0725
    Weixiao0725
    Jul 24, 2019
    44
    从根本上来说,java 是做不到热更新的,因为 Java 中只要 jar 包中的文件被载入到内存后,就不支持删除。像 Tomcat 那种热部署,无非也就是把所有的数据结构重新载入一遍。
    akira
    akira
    Jul 24, 2019
    45
    热不热不重要,只要能"快速"
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    46
    @qiyuey 能省很多时间啊
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    47
    @cshijiel 感谢。
    “注意,redefine 后的原来的类不能恢复,redefine 有可能失败(比如增加了新的 field ),参考 jdk 本身的文档。”
    看来不稳定啊,还是有缺陷。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    48
    @akira 也是,就是想省事儿~ 如果有别的类似热更新、热加载之类方式的也行啊
    pursuer
    pursuer
    Jul 24, 2019 via Android
    49
    @Weixiao0725 classloader 是可以从内存卸载 jar 包的,只是如果不清除外部引用的话不会被 gc 清理
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    50
    @jowan 也是,这个又引申出来状态丢失问题。所有重启服务的方式,都会导致缓存丢失。。。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    51
    @th00000 对的,但我就一台服务器啊,搞不了集群
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    52
    @est 文件都拷贝出差错了,那用什么方式都会有问题吧 😂
    gwybiaim
    gwybiaim
    Jul 24, 2019
    53
    @StarkWhite 没错,redefine 有很多约束
    wr410
    wr410
    Jul 24, 2019
    54
    与其想办法怎么弄热部署,不如想办法从前端做多机均衡,如果你关闭指令发出之后,还有线程没跑完连接没断开,这时候你就知道前端均衡的好处了。
    yuankui
    yuankui
    Jul 24, 2019
    55
    需要一套新轮子,楼主等我消息 😆
    freexyxyz
    freexyxyz
    Jul 24, 2019
    56
    用 Jenkins 之类的工具,配个流水线就行了……
    est
    est
    Jul 24, 2019
    57
    @StarkWhite 比如你 index.html 先拷贝完毕,这个时候引用了 main.23333.js 然后这个文件还没拷完。

    是不是就出问题了。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    58
    @cshijiel hotswap 限制太大了,基本没法用啊,除了修复小 bug。。。
    “只允许修改方法体,不允许增加新的 class、不允许新增字段、不允许新增方法、不允许修改方法签名”
    cominghome
    cominghome
    Jul 24, 2019
    59
    事实上有成熟的流水线的话,热更新听起来就像一个伪需求
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    60
    @yuankui 期待,到时候记得通知我哈
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    61
    @fivesmallq
    例子挺全的,但一看核心库居然只有 10 多个类。
    原来我以为它基于 Spring,怎么看都不像,用它岂不是得还框架了。。。
    https://github.com/playframework/playframework/tree/master/core/play-java/src/main/java/play
    yuankui
    yuankui
    Jul 24, 2019❤️1
    62
    我们之前,有个 spring 应用,启动很慢,我专门统计了下每个 bean 的初始化时间长。后来发现主要是以下几类:

    - 那些需要远程连接服务进行初始化的,比如 RPC 连接初始化
    - 那些需要加载大量数据到内存中的(比如建立一个字典)


    所以,要想应用实现热加载,关键是让应用无状态,这就是 php 的主要是方式。

    另外一个就是状态向后兼容,比如之前内存保存在一个对象 class A(a,b,c),后面加了个字段 class A'(a,b,c,d),目前 jvm 里面还没有工具支持,将这种数据 A 对象升级成 A'对象,但是实际上,让人来做,是可以做的。js 是可以的,因为 js 运行时全是 object ( kv ),天然具有向后兼任性。所以即使 js 应用,有状态,数据还是能做到兼容,不需要重启。

    所以综上,主要是问题其实不是 Java 的问题,而是框架的问题,也就是 spring 的问题。(楼上不是有人说 play 支持热加载吗?我没具体看啥原理哈)

    可能目前这个还不是痛点,就是大家遇到了,也是尽量先不启动应用进行开发,而是进行“单元开发”=》“单元测试”

    等差不多了, 在起应用,然后这个时候,即使有小 bug,也可以利用 IDE 的动态加载,将 body 体里面的实现替换,这个是不需要重启的。
    arrow8899
    arrow8899
    Jul 24, 2019
    63
    拆分服务,减小部署包大小;
    减少不必要的依赖,加快启动速度;
    无状态;
    分布式负载均衡,滚动升级。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    64
    @yuankui 赞,感谢分享。网上看到说 Play 也是基于 hotswap 的原理,看起来也会有 #58 楼 hotswap 的各种不支持的问题。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    65
    @cominghome 个人小项目也整个流水线?真是袁隆平的锅 /狗头
    kaneg
    kaneg
    Jul 24, 2019 via iPhone
    66
    JSP 可以解决你的问题
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    67
    @kaneg JSP 这种古董技术都要淘汰了。。。现在都是前后端分离
    freexyxyz
    freexyxyz
    Jul 24, 2019
    68
    @StarkWhite 你想实现 “提交代码……然后服务器自动更新好” 用 CI/CD 工具就行……流水线就是 devops 里的一个叫法,可以玩的很高级,也可以往简单了玩……你配个持续部署出来就行了
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    69
    @freexyxyz 在公司也用 Jenkins,自己业务搞个小项目也用它不划算啊
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    70
    @StarkWhite 业余
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    71
    @rockyou12 是啊,费力不讨好。
    低配 ECS 云服务器,Nginx 重载配置到生效也得好几分钟 /囧
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    72
    @efaun 目前在做个人小项目,单机、单体服务,搞分布式运维麻烦,还要钱啊
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    73
    @Xbluer 不止是重启,还要打包,上传。。。
    efaun
    efaun
    Jul 24, 2019
    74
    @StarkWhite #70 低成本解决的话就弄定时任务,找个深夜无人的时候自动化发布
    yiyi11
    yiyi11
    Jul 24, 2019❤️1
    75
    如果你要的是只是效率,而不是真正的不停机更新,那么可以试下这个:
    https://help.aliyun.com/document_detail/108590.html?spm=a2c4g.11174283.3.5.1813190cmVAJlX

    Alibaba Cloud Toolkit
    idea 插件
    配置服务器的地址,一键打包,上传,部署 springboot 应用。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    76
    @efaun 唉,项目太小弄定时任务不划算
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    77
    @Takamine 楼上(#34 )说 devtools 太 low 了,实际上还是重启。这不是我想要的效果。。。
    wowo243
    wowo243
    Jul 24, 2019 via Android
    78
    jrebel 应该是最好的了,但是个人认为依然不够好
    momocraft
    momocraft
    Jul 24, 2019
    79
    我觉得至少要保证版本变更是原子的吧

    所谓 “ PHP 支持热加载” 听上去像面向巧合运营
    Xbluer
    Xbluer
    Jul 24, 2019 via iPhone
    80
    @StarkWhite maven 负责编译测试打包,scp 上传,,ssh 远程命令重启。以上用一个脚本封装一下完全没问题。
    StarkWhite
    StarkWhite
    OP
    Jul 24, 2019
    81
    @wowo243 它有啥问题呢?
    billlee
    billlee
    Jul 24, 2019
    82
    @StarkWhite #51 一台服务器也可以搞集群,你在一台机器上起两个服务就可以了
    okwork
    okwork
    Jul 24, 2019
    83
    楼主服务器配置了多少内存?

    用过一个 4G 内存的机器,项目一启 2G 就没了,后来加到 8G 搞 2 个端口,勉强实现灰度。
    itechify
    itechify
    Jul 24, 2019 via Android❤️1
    84
    我说下这个情况,让我想起之前几个月的坑。
    1.tomcat 有 autodeploy 选项,直接打 war 包到 webapps 下,会自动解压更新项目。但是都是会停掉服务对应的项目重新开启,也就是说虽然 tomcat pid 不变,但是项目还是停掉在开。况且这个热部署有个坑,就是热部署太多次会导致 Compressed class 满了从而 oom 异常了。(这个空间默认 1G,每次看你 class 总内存多少,除一下就知道多少次就回报异常了)
    所以这个选项对于楼主没用
    2.jrebel 对于开发环境很有用,但有时候还抽风会不生效。这个是 jvm 的一个插件,开发环境我信赖他可以帮助我节约时间,避免多次重启服务器浪费时间。但是对于生产环境,我并不需要每时每刻去实时修改,而是在开发时候,通过频繁修改本地代码实时更新校验修改是否正确,最后再更新到服务器上。况且这个插件还是收费的,你在生产环境里面部署,破解了,被商业检测出来会不会存在什么问题?况且有时候本地调试还会失效,万一生产上面抽风失效了呢?但是自己的小项目,可以尝试这种方式。
    3.问题关键是楼主的单体应用里面。假如是 nginx 配置服务集群均衡,配合上 CI 工具写个脚本一个 tomcat 重启更新就很难自己感受到重启这件事了。还节省自己打包,上传,移动到 webapps 这个步骤,除了实际上是要重启服务器这个操作,基本能满足楼主的要求了吧。
    愚见,并没有实际解决楼主问题,以上均自己工作经验一点分享。
    q397064399
    q397064399
    Jul 24, 2019
    85
    Java 重启太耗时,主要是 bean 的装配,这种工作本来应该放到编译时做的,但是为了更好的解耦,目前 Spring 依赖注入这套玩法灵活性很高,把很多编译时可以做的事情,都放到了运行时,有些 Bean 检查网络连接之类的,反正整个装配阶段是极其耗时的

    另外 Jvm 的特性我不是很清楚,

    另外热更新的这种事情最好不要做,序列化 /反序列化 会产生很多问题,有些小公司的项目本来就混乱,DB 的 model 类又不做版本管理,我至少吃过好几次序列化的亏,这还不是热更新,考虑到热更新 会出更多问题,没有良好的版本管理跟发布流程 建议不要做这种费力不讨好的事情,现在微服务大行其道,就是要利用小服务 这种加载 运行 速度快的特性,即使出问题 可以通过灰度 等方法来解决。
    realpg
    realpg
    Jul 25, 2019
    86
    @jowan #20
    你那些并不是 PHP 的内容啊……
    涉及的也不是 PHP 热更新的部分
    Weixiao0725
    Weixiao0725
    Jul 25, 2019
    87
    @pursuer 请问有相关的资料吗?如果 java 支持内存删除的话,我想那些打桩工具不需要只在启动的时候注入,任何程序运行的时候都可以重新卸载某个类然后再读入字节码,编辑和载入。
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    88
    @billlee 用不同的端口吗?
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    89
    @okwork 最低配的阿里云 ECS,1 核 1GHz,1G 内存,40G 云盘。。。
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    90
    @oneisall8955 感谢分享,打这么多字,辛苦了哈
    pursuer
    pursuer
    Jul 25, 2019 via Android
    91
    @Weixiao0725
    直接看 classloader 的 api 文档就可以
    加载卸载还是要以 classloader 为单位的。没有引用的 classloader,System.gc 就清理了,只是要保证没有引用很困难,要求工程的模块化做的很好。
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    92
    @pursuer 被人案例了 apijson 的热更新,但我没有看到它有用 classloader,这个靠谱吗?
    https://v2ex.com/t/586011
    wangxiaoaer
    wangxiaoaer
    Jul 25, 2019
    93
    我们一般开个新端口启动一个新的应用,nginx 指向新应用,重启 nginx,停止老应用。
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    94
    @wangxiaoaer 我那个垃圾服务器重启 nginx 得几分钟才能让配置生效。。。
    fivesmallq
    fivesmallq
    Jul 25, 2019
    95
    @StarkWhite https://github.com/playframework/play1 你看错了,你看的那个是 2.x 的,用 scala 重写的,1.x 的才是主要用 java 的。
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    96
    @fivesmallq 这样啊,感谢,我再瞅瞅
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    97
    @fivesmallq 这个代码量就正常了,像是一个 HTTP Server 框架该有的,哈哈
    fivesmallq
    fivesmallq
    Jul 25, 2019
    98
    @StarkWhite 对的,你跑下 demo,会喜欢的,适合中小型项目
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    99
    @fivesmallq 感谢,我试试。现在已经用 SpringBoot 开发了一些接口了。
    如果 Play 确实好用,而且迁移成本不高的话,可以迁过去看看。
    StarkWhite
    StarkWhite
    OP
    Jul 25, 2019
    100
    @StarkWhite 话说这个框架有么有什么大的坑呢?
    1  2  
    ❮ ❯
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2041 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 165ms · UTC 14:50 · PVG 22:50 · LAX 06:50 · JFK 09:50
    ♥ Do have faith in what you're doing.