当前位置:网站首页>Write a code hot deployment
Write a code hot deployment
2022-06-21 14:01:00 【Funny 2233】
The recent discovery javassist A very interesting class in ,HotSwapper, You can dynamically change a running class , So I want to write a code hot update gadget
Hot renewal effect ( Not only the hot change part, but also the original variable value is retained )
Test1 Logic 
During the writing process, I had a whim and the original MakeR Combined with automatic update of resource files
MakeR:https://blog.csdn.net/weixin_44598449/article/details/118309945
Automatically update resource file effects 
Adding or deleting files in the specified resource directory will automatically update R class
Updated R class 
HotSwapper It's very easy to use
// Virtual machine parameters must be specified :-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
HotSwapper swapper=new HotSwapper(8000);// The ports here should be the same as those configured above address Agreement
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// Compile code
int result = compiler.run(null, null, null,"E:/IDEA/MyTest3/src/main/java/part15/TestQ.java");
System.out.println("result=" + result);
// Get bytecode and hot load
byte[] bytes = Files.readAllBytes(Paths.get("E:\\IDEA\\MyTest3\\src\\main\\java\\part15\\TestQ.class"));
swapper.reload(TestQ.class.getName(),bytes);
The above program also relies on JDK\lib\tools.jar This jar package , You can introduce jar You can also rely on
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope><!-- Using environment variables -->
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
The code as a whole is very simple
Basically, by calling updateCode Update code , Determine whether the code file needs to be updated by the timestamp
@Slf4j
public class CodeUpdate {
private Path basePath;
private Map<String,Long> timeStampMap;
private JavaCompiler compiler;
private HotSwapper swapper;
private List<UpdateListenner> listenners;
private int updateCount;
public CodeUpdate(String basePackage, int port) {
initBasePath(basePackage);
initTimeStampMap();
initOther(port);
}
// -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
private void initOther(int port){
compiler = ToolProvider.getSystemJavaCompiler();
try {
swapper=new HotSwapper(port);
log.info(" Successfully connected to {} port ",port);
} catch (IOException|IllegalConnectorArgumentsException e) {
e.printStackTrace();
log.error(" Connection failed. Please configure virtual machine parameters :-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address={}",port);
}
listenners=new ArrayList<>();
}
public void addListenner(UpdateListenner listenner){
listenners.add(listenner);
}
public void removeListenner(UpdateListenner listenner){
listenners.remove(listenner);
}
private void initBasePath(String basePackage) {
StringBuilder builder=new StringBuilder();
builder.append(System.getProperty("user.dir")).append("/src/main/java");
String[] items = basePackage.split("\\.");
for (String item : items) {
builder.append('/').append(item);
}
basePath=Paths.get(builder.toString());
}
@SneakyThrows
private void initTimeStampMap(){
timeStampMap=new LinkedHashMap<>();
Files.walk(basePath).forEach(path -> {
File file = path.toFile();
if(file.getName().endsWith(".java")){
timeStampMap.put(file.toString(),file.lastModified());
}
});
}
@SneakyThrows
public void updateCode(){
updateCount=0;
Files.walk(basePath).forEach(path -> {
File file = path.toFile();
if(file.getName().endsWith(".java")){
if(timeStampMap.getOrDefault(file.toString(),0L)<file.lastModified()){
timeStampMap.put(file.toString(),file.lastModified());
updateCode(file);
updateCount++;
}
}
});
listenners.forEach(listenner -> listenner.update(updateCount));
}
private void updateCode(File file) {
int result = compiler.run(null, null, null,file.getAbsolutePath());
String className = getClassName(file);
if(result==0){
byte[] bytes=getAndDelete(file);
swapper.reload(className,bytes);
log.info(" Hot update {} success !",className);
}else {
log.error(" compile {} Failure !",className);
}
}
private String getClassName(File file) {
String path = file.toString();
String classPath = path.substring(path.indexOf("\\java\\") + 6, path.lastIndexOf('.'));
return classPath.replaceAll("\\\\",".");
}
@SneakyThrows
private byte[] getAndDelete(File file) {
String name = file.getName();
file=new File(file.getParent(),name.substring(0,name.lastIndexOf('.'))+".class");
byte[] bytes = Files.readAllBytes(file.toPath());
file.delete();
return bytes;
}
}
All that's left is to update the code and call updateCode Method , Actually, I prefer , hold updateCode Method is bound to a shortcut key , Apply updates manually
Use here WatchService Listen for file changes
Easy to use
WatchService watchService = FileSystems.getDefault().newWatchService();// Access to observation services
// Register folder ( Only folders can be registered ) And specify listening events
Paths.get(triggerDir).register(watchService,StandardWatchEventKinds.ENTRY_MODIFY);
/* Supported file Events StandardWatchEventKinds.OVERFLOW: Event lost or lost StandardWatchEventKinds.ENTRY_CREATE: Create entities in the directory or rename the directory StandardWatchEventKinds.ENTRY_MODIFY: Modification of entities in the directory StandardWatchEventKinds.ENTRY_DELETE: Delete or rename entities in the directory */
try {
while (!Thread.interrupted()){
WatchKey poll = watchService.take();// Block get events
//watchService.poll();// Non blocking access No event returned null
//watchService.poll(12, TimeUnit.SECONDS);// Specify the... With timeout
if(fileChange!=null)fileChange.change(poll.pollEvents());// must pollEvents Otherwise, the event will not disappear
poll.reset();// Reset for easy reuse
}
}finally {
watchService.close();// Close the service
}
The specific package is FileTrigger Listen for file changes
public class FileTrigger extends Thread{
private String triggerDir;
private WatchService watchService;
private FileChange fileChange;
@SneakyThrows
public FileTrigger(String triggerDir) {
this.triggerDir = triggerDir;
watchService = FileSystems.getDefault().newWatchService();// Access to observation services
}
public void setFileChange(FileChange fileChange) {
this.fileChange = fileChange;
}
@SneakyThrows
@Override
public void run() {
// Register folder ( Only folders can be registered ) And specify listening events
Paths.get(triggerDir).register(watchService,StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
try {
while (!Thread.interrupted()){
WatchKey poll = watchService.take();// Block get events
if(fileChange!=null)fileChange.change(poll.pollEvents());// must pollEvents Otherwise, the event will not disappear
poll.reset();// Reset for easy reuse
}
}finally {
watchService.close();
}
}
}
After that, I thought , Monitor file updates automatically R Just like Android
resources R class :https://blog.csdn.net/weixin_44598449/article/details/118309945
In fact, there is no code written here to directly modify the original MakeR Make it more suitable for multiple creation , And combine FileTrigger It's done.
After the bytecode is actually dynamically replaced, the original class file is not directly released
Here's how hot I am every second 100 Memory usage of secondary method area ![[ Failed to transfer the external chain picture , The origin station may have anti-theft chain mechanism , It is suggested to save the pictures and upload them directly (img-uhU4Uwp4-1631950563802)(E:\ Text learning files \markdown\image\image-20210917085144145.png)]](/img/d9/9dda5fdb9277ef9381c752e8f7ce0a.jpg)
Basically just 5s The method area starts from 2M Reached 30M, Then it started FullGC, But it is certainly not hotter per second during normal use 100 Secondary demand
边栏推荐
- Read distributed consistency protocols 2pc and 3pc
- 6. functions
- Babbitt yuancosmos daily must read: wechat may ban a official account for the first time on the grounds of "involving secondary transactions in digital collections", and the new regulations of the pla
- Detailed explanation of hashtable source code in C #
- Kotlin - sequence sequence
- 7hutool actual fileutil file tool class (common operation methods for more than 100 files)
- What are the log files in MySQL?
- Comprehensively analyze the key points of knowledge required for interface testing and interface testing
- Redis学习(3)—— 持久化机制
- Explanation of vim, makefile and GDB tools
猜你喜欢

Setting of Seaborn drawing style

Web3.js connection to metamask wallet transfer

What is Devops in an article?

Automatic operation and maintenance 4 - variables and encryption in ansible

MySQL - built in functions

MySQL - table operation

Record the processing process of slow response of primary system

Map collection traversal, adding, replacing and deleting elements

MySQL - view properties

Kube Prometheus grafana installation plug-in and grafana image renderer
随机推荐
Design interface automation test cases by hand: establish database instances and test case tables
JS 中的集合引用类型
What are the log files in MySQL?
Customize view to draw line chart (support zoom)
Highly available configuration of database (MySQL)
Detailed explanation of hashtable source code in C #
Record the troubleshooting process of excessive CPU usage
Add SSL security certificate to web site
Installation and application of MySQL 8.0 under Linux
7. pointer
C language elementary level (10) type rename typedef
[test process and theory - test process system]
流量加密之C2隐藏
Mr. Ali taught you how to use JMeter for pressure test (detailed drawing)
Tomorrow's interview, I can't sleep in the middle of the night to review the bottom implementation of STL
STM32F0-DAY1
Pingcap was selected as the "voice of customers" of Gartner cloud database in 2022, and won the highest score of "outstanding performer"
A blazor webassembly application that can automatically generate page components based on objects or types
8. structure
MySQL - index