1. 前言
本文主要介绍Java一键秒级远程热部署插件HotSeconds的使用,HotSeconds分为HotSecondsClient和HotSecondsServer,HotSecondsClient是IDEA插件,可以在插件市场下载,HotSecondsServer是服务端javaagent插件,主要实现对服务端的热部署。
远程热部署的难点是什么?
热加载仅仅是热更新class文件,但是远程热部署需要整合类热加载,热更新资源文件,热更新类之后刷新缓存,秒级生效,一键操作,远程热部署支持等一些列的功能
- 热加载类: 包括修改代码,新增字段,新增方法,新增类,修改动态代理类等等
- 热更新资源文件: 比较常见的比如mybatis的xml,自定义加载的xml或者properties,当然还有各种框架的html/css/js等,只要你愿意你可以扩展任意资源文件热更新
- 热更新类之后刷新缓存: 比如Spring bean新增一个Autowired的字段,或者SpringMVC新增一个@RequestMapping函数,这些已经不是热加载一个类的范畴了,需要刷新缓存/上下文。
- 秒级生效: 做到这一点很难,很多热部署的工具只是简单的重启应用或者用classloader重新加载所有的类,这样做效率和性能都比较低。
- 一键化操作: 不需要敲一大堆命令,不需要太深的技术底蕴,只需要新手一键化的操作就能实现基本的热部署
- 远程热部署: 很多插件都是只能本地热部署,不能让本地代码一键热更新到远程,千万不要以为简单的上传文件+本地热部署就是远程热部署了,远程的环境远远要比本地复杂的多
本插件的内核在1,4,5,6已经全部实现,2,3在扩展项目包也在慢慢完善,目前内核已经支持Spring生态(Spring+SpringMVC+SpringBoot),Mybatis热更新。
2. 功能介绍
2.1 插件安装
2.1.1 HotSecondsServer安装
2.1.2 HotSecondsClient安装
在IDEA的Plugins->Marketplace中搜索HotSecondsClient,下载安装即可
安装完后,底部可以看到HotSecondsClient控制台,另外在Run菜单和Debug旁边也有快捷按钮,但是控制台是最全的,HotSecondsClient1.8.6兼容了最新版UI在Debug旁边的头像按钮的显示。
2.2 连接远程服务器
点击HotSecondsClient控制台->HotSeconds Settings->Settings,弹出配置框,可以配置远程ip,端口和路径映射
路径映射(Path mappings)
- class: 可以不用配置,默认上传到服务器的remote_src目录
- 资源文件: 需要配置本地哪些文件上传到服务器的哪个目录
- jar包:需要配置,服务器检测到jar包上传时会热加载每个class
配置完之后,点击Start HotSeconds连接远程服务器,连接的远程ip为HotSecondsClient控制台的tab页签的ip,如果没有tab页签,读取的是hot-seconds.xml中的ip
2.3 单文件热更新
2.3.1 java文件热更新
在打开的java文件上右键,选中 Hot swap this file to remote,会先编译,编译完后会将class文件(包括内部类)热更新到远程服务器
如果没有语法错误,但是遇到编译失败,可以先Build一下工程,再试一下热更新。
2.3.2 资源文件热更新
在资源文件上右键也可以选择 Hot swap this file to remote,那么资源文件是怎么实现热部署的呢?
这里是根据Path mappings中的路径映射,将本地的资源文件热部署到远程对应目录
但是资源文件非常复杂,比如xml ,html, css, js,properties等等文件,框架也非常的多,又是怎么实现热部署的呢?
路径映射只是将文件上传到服务器端,真正生效的是上传完之后刷新资源文件的缓存的逻辑,这就涉及到插件扩展,本插件内核集成了MyBatis和MyBatis Plus的xml的热更新,更多的资源文件热更新,详见插件扩展。
2.3.3 jar包热更新
在打开的jar上面右键,可以看到两个选项,Hot deploy the Jar to remote 和 Hot swap this file to remote。
前者是将整个jar的所有class热部署的服务器,后者只热更新选中的class。
2.3.4 远程编译并热部署
在java文件上右键选中 Hot swap this file to remote,会在本地编译然后热更新到远程,但是有的时候本地编译并不方便,比如本地和远程服务器的JDK版本可能存在巨大的差异,这个时候就需要远程编译了。
在java文件上右键,选择Remote compilation and hot swap 可以在远端编译并热更新。
2.4 批量热部署
最低版本要求: HotSecondsClient1.8.0+HotSecondsServer-future
点击HotSecondsClient控制台上的批量热部署按钮,弹出批量热部署修改的文件的面板,可以将修改的文件批量热部署到远程,HotSecondsClient1.8.3增加了Git/SVN差异文件热部署。
其中resource同2.3.2,也是需要配置Path mappings。
2.5 自动热部署
在配置框中选中Auto deployment,只要修改了资源文件并保存,就可以将资源文件自动热部署到远程服务器,不过建议还是用2.3.2单文件热部署(设置快捷键)+2.4批量热部署修改文件,因为自动热部署的时候可能你代码还没写完。
为什么不对class自动热部署呢?
最初的版本是对所有文件包括class都自动热部署的,只要监听到目录文件变化就自动热部署,但是后面发现事与愿违,比如build工程或者切换分支的时候也会自动热部署不需要的东西,所以后面的版本就禁用class自动热部署了,因为2.3.1热更新+快捷键完全可以取代自动热部署。
2.6 获取远程字段&远程执行函数
2.6.1 获取远程字段
需要升级到HotSecondsServer-future3+HotSecondsClient1.10.0版本
在类的字段上右键选择Get value from server,可以获取远程的字段值到本地
如果是静态字段,直接返回结果到HotSecondsClient控制台,如果是非静态字段,会弹出当前类的所有对象实例,选中后会获取该对象的字段值到本地。
2.6.2 远程执行函数
在函数上,右键,选择Run method on remote server,可以在远程服务器上触发这个函数并返回到本地控制台,如果是对象会转换成json字符串。
这里分四种情况,静态,非静态,有参数,无参数的,这里的参数仅限于int,byte,short,long,double,float,boolean,char和String类型,除了静态无参方式,其他方式都需要升级到HotSecondsServer-future3+HotSecondsClient1.10.0版本
- 静态无参:最万能的方法,可以调用一切函数
- 静态有参:会弹出输入参数框,每个参数用英文,隔开,String类型不需要引号
- 非静态无参:会弹出当前类的所有对象实例,选择后触发方法返回结果到本地
- 非静态有参:会弹出输入参数框,每个参数用英文,隔开,String类型不需要引号,并选择类的所有对象实例中的一个,触发方法返回结果到本地
那么如果是参数复杂的函数该如何调用呢?
参数是对象形式的复杂函数,手动输入参数也挺费劲的,因为一切是为了让使用更简单,所以我也就没有设计成输入的方式,
可以写一个静态无参函数,然后调用需要的函数就行了,中间需要获取一些对象(比如获取Spring bean实例, 单例,List等)可以自己在代码里面维护,如果不想额外维护,可以直接引用我另一个开源项目FindInstancesOfClass的pom依赖,可根据类获取所有对象实例。改完代码先远程热部署,再远程执行函数,针对查看内存变量和在沙箱环境下修复线上数据,是非常方便的。
这样就不用写接口,不用http请求,也不用调rpc请求,直接就能触发需要的代码,热部署+远程执行函数,是驾驭整个服务器最基本的力量。
2.7 插件扩展
每个公司都有自己的框架,市面上的新框架也有很多,本插件能兼容万物,可以扩展热更新文件的前置逻辑和后置逻辑,从这一点上来看,本插件能热部署整个世界一切的一切。
复制HotSecondsServer.zip中的IHotExtHandler.java到你的项目,实现这个接口,然后在hot-seconds-remote.xml里面配置上你的类名就可以了,这样热部署的时候一些需要刷新缓存和上下文的逻辑就可以触发。
IHotExtHandler接口有两个函数,preHandle和afterHandle,前者是同步触发执行,后者是异步触发执行的。
void preHandle(ClassLoader classLoader, String path, byte[] content);
void afterHandle(ClassLoader classLoader, Class<?> classz, String path, byte[] content);
有的时候需要获取Spring上下文或者框架某个对象实例该怎么做呢?要么是自己维护一套,要么是通过构造函数插桩字节码的方式,不过这里还是推荐用我另一个项目FindInstancesOfClass,直接根据Class就能获取所有内存中的对象实例,无需辅助和维护。
更多的框架支持详见扩展项目:
https://github.com/Liubsyy/HotSecondsExtension
项目在不断的更新和完善中
2.8 多语言支持
设置里面可以选择多语言支持
2.9 多ip+port连接支持
远程服务端机器比较多,可以添加和管理不同的远程连接源
添加完之后就可以在设置里面看到所有的连接源了,可以选择不同的远程连接源进行连接和热部署
2.10 快捷键
在Keymap->Plugins->HotSecondsClient里面可以自定义快捷键,建议设置HotSeconds Start/Stop和Hot swap this file的快捷键
所有快捷键关键字如下
功能 | 关键字(可设置快捷键) |
---|---|
热部署开关 | HotSeconds Start/Stop |
热部署当前文件 | Hot swap this file |
远程编译并热部署文件 | Remote compilation and Hot swap |
热部署整个jar | Hot deploy the JAR to remote |
远程执行方法 | Run method on remote server |
批量热部署修改文件弹框 | Batch Hot Reloading Modified Files |
增加连接的IP | Add IP |
弹出所有可连接的ip | ip list |
弹出设置框 | settings |
以下是我个人的一些快捷键设置(我把IDEA默认的快捷键冲突的都删掉了,IDEA默认的快捷键我觉得并无大用,还占用坑位),仅供参考:
Command+~ : HotSeconds Start/Stop
Command+1 : Hot swap this file
Command+2 : Remote compilation and Hot swap
Command+3 : Batch Hot Reloading Modified Files
Command+Right-Click : Run method on remote server
Command+0 : settings
2.11 代理服务器
针对k8s/docker/虚拟机,如果本地不能连接容器内的机器,该怎么实现远程热部署呢?这个时候就需要代理服务器了。
代理服务器原理是,将本地的消息发到代理服务器,然后由代理服务器转发到各个容器服务器,这样只需要一台代理服务器和一个暴露端口即可实现内网集群热部署。
本地 ---------> 代理服务器 ---------> 各个容器目标服务器
需要满足三个基本条件
- 本地能连接代理服务器
- 代理服务器能连接容器服务器
- 如果代理服务器也是容器,且通过宿主机映射端口到代理服务器,那么要保证宿主机暴露端口=映射的代理服务器端口
有了这三个条件就可以设置代理服务器了,首先在代理服务器上解压缩HotSecondsProxy.zip,修改proxy-config.xml文件,host填当前代理服务器的ip(填宿主机ip还是容器ip根据具体情况定),这个ip是暴露给HotSecondsClient进行TCP/IP连接的ip,然后执行start.sh启动,可关闭终端后台运行。
HotSecondsServer要保证在future4版本及以上,HotSecondsClient要保证在1.10.3版本及以上,然后在HotSecondsClient -> Settings -> Proxy Server中可以设置代理服务器和端口(填proxy-config.xml中的host和port),开启后,其他使用方式不变,连接的目标服务器(目标服务器不是代理服务器)都填内网ip和port就行,相当于是基于代理服务器的ip去连接各个目标服务器和发数据。
3.支持范围
3.1 插件内核支持范围
开源组件 | 范围 |
---|---|
Java | 修改代码,新增字段,新增方法,新增类,修改动态代理类 |
Spring | 包括Spring,SpringMVC,SpringBoot生态 |
MyBatis | 支持mapper修改xml文件(扩展包HotSecondsExtension已支持新增mapper和xml) |
MyBatis Plus | 同MyBatis |
Freemarker Thymeleaf Velocity-Spring | 刷新缓存 |
更多的组件/框架热更新请关注扩展开源项目HotSecondsExtension
3.2 支持环境
本插件支持本地热部署,以及远程热部署(测试和沙箱环境),线上不推荐用本插件进行热部署。
为什么不推荐线上用热部署呢?
Java和普通脚本语言不一样,运行的字节码已经加载到内存里了,如果线上一边运行高并发程序,一边还要热更新会对性能造成很大的影响,无异于轰炸机一边轰炸,一边还要加油。另外一点就是,没有哪个IDE是直接连接线上热部署的,这样很不安全,生产环境一定是越简单越安全。
那么线上该如何热更新呢?
线上使用热更新一般是热修复问题,可以直接用Java底层的Instrument机制实现类的替换,不过这样只能修改代码块,不能修改/新增字段和方法。
成熟一点的开源工具可以用arthas,底层也是基于Instrument API,不能修改/新增字段和方法。
4. 使用例子演示
这里用几个gif动图演示一下热部署的一些功能:
增加Spring注入字段和SpringMVC函数
热更新MyBatis
多文件批量热更新
5. 未来
- 不断更新和完善新框架和市面流行框架的热部署,把扩展包做成一个新的项目
- 支持更高版本JDK的热部署
- 插件增加一些实用的功能,类似于远程执行函数这种功能(已经实现),欢迎大家给出建议
- 扩展插件到其他的编程语言和IDE