热部署 和 热加载

本文主要讲热部署和热加载的区别、原理,以及常用的热部署的方式实践心得,其中包括HotSwap、Spring-loaded、Spring-boot-devtools、HotCode2和JRebel,诸多方式任你选择,希望能为你的开发进一步提效

1 热部署和热加载

开篇先说下热部署和热加载的区别:
热部署:在服务器运行时重新部署应用,也就是说在不停止容器的情况下实现整个应用的重新加载部署,这种方式会释放内存,多用于生成环境
热加载:在应用运行时重新加载class,主要依赖于Java的类加载机制,如果监控的类文件有改变,则重新载入,多用于应用开发阶段,又叫开发者模式,在开发过程中会经常性的进行修改文件或debug,频繁启动应用会花费很多时间成本,热加载机制可以极大的提升开发效率,这也是写这篇文章的主要原因,提高大家的开发效率。
但我们通常所说的热部署其实包括热加载的,可以理解为热加载是热部署的一种VIP情况

2 热加载的原理

2.1 类加载过程

在了解热加载之前,首先说下类加载的过程,类加载的过程简单来说就是JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程;类加载的过程主要分为三大部分:加载、连接、初始化,其中连接又分为:验证、准备、解析;示例图如下:

image.png | left | 747x210

  • 编译:把Java文件编译成.class字节码的过程
  • 加载:类加载过程的开始,把class字节码文件从各个源通过类加载器载入内存中
  • 连接
    • 验证:确保类加载的正确性,保证加载进来的字节流符合JVM规范,不会造成安全问题
    • 准备:类变量(注意,不是实例变量,是static变量)分配内存,并且设置这些类变量的初始值
    • 解析:将常量池内的符号引用替换为直接引用的过程
  • 初始化:是类加载过程的最后一步,可以理解为是执行类构造器<clinit>()方法的过程,真正开始执行类中定义的Java程序代码或者说字节码。

2.2 剖析原理

        首先通过Java编译器把Java文件编译成class字节码,Java类加载器(classLoad)读取字节码到内存中,生成实例对象,一个类加载器中的Java全限定名是全局唯一的,也就是说一个类加载器只能加载一个同名类,classLoader内部会缓存已经加载过的class,重新加载的话,是直接读  取缓存的,如果是使用自定义的classLoader加载,不使用双亲委派模型,绕过判断,但是在JVM解析、验证class时也是会抛出异常的,所以实现__热部署的关键是可更改已加载的class文件,用新的class文件替换同名的old class文件(或者更改class字节码),或者是重新创建一个classLoader进行加载,然后把老的classLoader卸载掉__;第二种方案可以理解成热部署,所以这种方案为开发提效有限;而第一种方案是更新或替换old class文件所以这种热加载的方式对开发提效非常明显。

2.3 发展历程

     在JDK1.4时,Sun在JVM中引入了HotSwap的实验性技术,这一技术被合成到了Debugger API内部,其允许调试者使用同一个类标识来更新类的字节码,这就意味着在不重载容器的情况下,允许动态更新class文件,使应用程序在执行时可执行新的代码。
    从JDK1.5开始,提出了“Instrumentation”特性,可通过Instrumentation API直接提供给Java应用使用,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义,但在 JDK1.5 中,需要要求在运行前利用命令行参数或者系统参数来设置代理类。
    JDK1.6之后,对Instrumentation进行了加强,启动后的 instrument、本地代码(native code)instrument,以及动态改变 classpath 等等,“java.lang.instrument”包的具体实现,依赖于 JVMTI(Java Virtual Machine Tool Interface),JVMTI 提供了一套”代理”程序机制,可以支持第三方工具程序以代理的方式连接和访问 JVM,并利用 JVMTI 提供的丰富的编程接口,完成很多跟 JVM 相关的功能,JVMTI 还在虚拟机内存管理,线程控制,方法和变量操作等等方面提供了大量有价值的函数,Instrumentation 的最大作用,就是类定义动态改变和操作。--参见IBM Developer

3 Hot Swap实现热部署

这种方式有两个弊端:
  1 就是仅限于修改方法体,对比如新增方法、字段之类 就需要重新部署才可以了,这种方式有没有解决方案呐,答案是肯定的,收费的JRebel,或者免费的HotSwapAgent+DCEVM

2 HotSwap需要依托于IDE集成,比如主流的IDE:IntlliJ IDEA、Eclipse、MyEclipse、NetBeans等

下面HotSwap以IDEA为例实现方法体内的热部署功能

3.1 IDEA基于HotSwap的热部署功能

使用IDEA自动编译部署功能,实现应用的热部署(Hot Swap)

3.1.1 检查是否开启HotSwap,默认是开启的

image.png | left | 527x207

3.1.2 开启IDEA自动编译功能

弹出registry浮层面板,快捷键control+shift+a,然后找到compiler.automake.allow.when.app.runing勾选

image.png | left | 554x439

快捷键不对的可以在keymap面板中找下:

image.png | left | 551x145

3.1.3 设置IDEA自动编译

image.png | left | 655x2

3.1.4 手工编译(注意类加载顺序)

修改某个类中的方法中的内容后,在菜单栏中Build菜单中Recompile或右键点击Recompile,编译完成之后,再次访问,可验证加的内容是否生效

image.png | left | 341x170

或:

image.png | left | 320x121

点击Recompile之后,第一次会弹出框让选择:是否重新加载classes,选择是

image.png | left | 296x109

3.1.5 验证结果

热部署2.png | center | 687x184

4 Spring-loaded实现热部署

  spring-loaded是针对Springboot类的项目,springloaded热部署只可以修改已有方法与页面,如果是新加的方法或者类则无法实现热部署,并且需要配置启动参数-javaagent(-javaagent:.\lib\springloaded-1.2.8.RELEASE.jar -noverify)。这种方式需要指定springloaded jar包的位置,我不怎么用,就不多说,有兴趣的同学可以自己玩下。

<dependency>  
           <groupId>org.springframework</groupId>  
           <artifactId>springloaded</artifactId>
       </dependency>  

5 Spring-boot-devtools实现热部署

注意:PandoraBoot不支持devtools实现热部署
      Devtools是为springboot应用提供的一个开发者服务模块,其原理就是上面第二章节所说的先重新创建类加载器然后销毁old类加载器;devtools在监控到代码有更改之后,会快速重启应用,这里重启并不是整个应用重启,而是重新加载部分classLoader,因为其有两个classLoader,一个classLoader加载不会改变的类(如各种第三方jar包、二方包等),另一个classLoader加载会更改的类可以理解成自己开发的类,被称为RestartClassLoader,这样有代码更新时,会重新创建一个RestartClassLoader,并销毁之前的RestartClassLoader,由于只加载部分类,所以相对整个应用重启 用时相对较少,但相较于JRebel和hotSwap速度还是大打折扣的,比hotSwap优势就是可以支持静态资源文件(页面、配置文件等,监控范围是classpath下的文件)、新增方法、字段之类的更新。

源码:

image.png | left | 693x183

image.png | left | 636x279

5.1 常用配置项

  • 默认情况下,以下文件夹下的文件修改不会使应用重启,但是会重新加载(devtools内嵌了一个LiveReload server,当资源发生改变时,浏览器刷新)。

    META-INF/maven/<strong>,META-INF/resources/</strong>,resources/<strong>,static/</strong>,public/<strong>,templates/</strong>,**/*Test.class,**/*Tests.class,git.properties,META-INF/build-info.properties
    
  • 如果想改变默认的设置,可以自己设置不重启的目录:spring.devtools.restart.exclude=static/**,public/**,这样的话,就只有这两个目录下的文件修改不会导致restart操作了。

  • 如果要在保留默认设置的基础上还要添加其他的排除目录:spring.devtools.restart.additional-exclude

源码:

image.png | left | 747x109

  • 如果想要使得当非classpath下的文件发生变化时应用得以重启,使用:spring.devtools.restart.additional-paths,这样devtools就会将该目录列入了监听范围

源码:

image.png | left | 747x126

  • 如果你想关闭自动restart功能,通过spring.devtools.restart.enabled属性配置即可
  • 如果想开启或关闭远程debug功能,通过spring.devtools.remote.debug.enabled属性配置,默认是true

源码:

image.png | left | 736x134

  • 实现页面热部署,通过spring.thymeleaf.cache属性配置为false即可,默认为true

还有远程相关的其他一些配置、代理配置、重启轮询时间等待时间等的配置可以查看源码中的spring-configuration-metadata.json文件

5.2 使用示例

1 添加依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
    </dependency>

<plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <fork>true</fork>
        </configuration>
      </plugin>

Eclipse: 下面是在eclipse中实现热部署的演示,项目启动后,新加类TestController,并新增getName()方法,保存后可以观察到后台已进行热部署了,在浏览器中访问如下RPC,可正常返回结果

image.png | left | 674x405

6 JRebel 实现热部署

强烈推荐,有商业版和个人免费版(个人版申请通道好像已关闭,但有试用版或其他解决方案(仅供学习使用,请支持正版))
它可以在无需动态类加载器的情况下更新类,且只做极少的限制(改变static静态字段值不支持),它的工作原理就是监控磁盘上的已编译的class文件,并通过rebel.xml配置文件把归档的应用(zip、jar、war)和应用模块间建立一个对应关系,通过该文件JRebel可以直接找到归档应用对应的源文件,当某个类被更新时,该文件告诉JRebel在哪里可以找到源文件,使其被从工作区中而不是从归档文件中读取。也就是因为它的这一特性,使其可以对诸如Html、xml、.properties、.yml等资源类的文件也可以及时更新。

6.1 使用示例

6.1.1 安装JRebel

image.png | left | 672x104

6.1.2 使用JRebel启动项目

安装完成之后,会在工作栏中显示出JRebel图标

image.png | left | 393x72


image.png | left | 452x110

6.1.3 验证JRebel是否启动成功

项目启动时,控制台出现如下信息时表示JRebel配置正确可以正常使用

image.png | left | 571x283

6.1.4 设置热部署模块

在IDEA视图窗口中打开JRebel,选中jrcon复选框,JRebel会自动在对应模块下生成rebel.xml文件,该文件将被放置到项目的源代码树中,对于Maven项目,它将被放置到src/main/resources中,以便在构建期间自动获取。

image.png | left | 400x229

注:每个模块下resources资源文件下会产生一个rebel.xml配置文件,文件的功能基本同HotCode2的workspace.xml,用于指定target/classes的dir路径,当文件有更新时JRebel会自动将其映射到工作区。

6.1.5 验证

image.png | left | 545x270

点击保存之后,可以在控制台看到JRebel打印的Reloading日志,说明新加的方法已经生效

image.png | left | 553x144

7 总结

有了热部署不仅能大幅度节省重启项目的时间,而且能够在编程时避免中断思路,将更多的时间用于Coding和思考问题。
本文主要是基于本地开发的维度进行演示说明,具体使用哪一种各位可根据自己开发中实际情况进行自由选择,当然个人还是比较优先推荐JRebel,稳定好用省心,其次是HotCode2,如果原生的SpringBoot类(PandoraBoot不支持)的项目可以使用Spring-boot-devtools或者Spring-loaded尝尝鲜,最后支持工程热部署方式最少的但也是最方便的就是HotSwap,各个IDE工具基本都已集成开启即可使用。

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
加载热部署是两种不同的技术,它们都旨在提高开发效率和减少重启服务的次数,但在实现方式和适用场景上有所不同。 加载是指在应用程序运行过程中,对已加载的类进行更新,使得新的代码逻辑能够立即生效,而无需重启整个应用程序。加载通常只针对变更的文件代码进行局部更新,对于新文件或目录无效。加载在Java语言中比较成熟,可以通过类加载器的机制实现。它适用于开发阶段,可以快速验证代码的修改,提高开发效率。 热部署则是指在应用程序运行过程中,对整个项目进行更新,包括新增、修改或删除文件。热部署可以将新的文件或目录直接部署到应用程序中,无需重启整个应用程序。热部署通常用于开发环境或某些特定的生产环境,可以快速应用变更,提高开发和部署的效率。 总结来说,加载主要针对代码的变更,实现局部更新,适用于开发阶段;而热部署则是对整个项目进行更新,适用于开发和部署阶段。 #### 引用[.reference_title] - *1* [热部署加载的区别](https://blog.csdn.net/qq_37432174/article/details/106226613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [热部署加载有什么区别?](https://blog.csdn.net/qq_16498553/article/details/125967074)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值