Before I explain ： I haven't updated my blog for a long time , I have done a lot of things in the company this year , Including code analysis and hot deployment replacement , I haven't had time to write some articles , What a pity , Make up an introductory article while you go to bed at noon .
First of all, the hot deployment scenario is like this , The company has a lot of projects , Really BU There are about hundreds of projects in the division , Some projects can't be started properly , So some students are modifying the code , Or in the ordinary routine task development process is blind change , And then go to the company's code platform to release , The disgusting thing is here , Some projects run from build to release about 30 minute , So every time you modify the code to make it work, you need to 30 A period of 10 minutes , This greatly reduces the development efficiency of the company , Once inertia becomes a habit , It will be very difficult to change , So we really need one after modifying the code locally , An artifact that can take effect on the server in seconds , such , Our hot deployment plug-in was born .
Hot deployment is a hard nut to crack in the industry itself , It belongs to the category of reverse programming ,JVM Class loaded , Then hot deployment is necessary Remote desktop To do the unload and reload ,Spring There is context registration ,spring Bean Execute initialization lifecycle , Hot deployment is about destroying classes , Reinitialize , There are so many details in the design , Several hot deployment models in the industry have different processing methods , Because of the huge amount of underlying detail that needs to be dealt with , So at present, it is almost impossible to find a hot deployment plug-in that completely covers all functions , Generally, the hot deployment plug-ins that you hear are mainly foreign projects, such as the commercial version jrebel, Open source springloaded, And the rough ones spring dev tools. At present, these projects are ready-made complex open source projects or closed commercial projects , I want to modify the project to match my own company , It's very difficult . Gossip , To enter the body
Preface 1 ： What is hot deployment
So called hot deployment , Is to upgrade the software while the application is running , There's no need to restart the app . about Java For applications , Hot deployment is updating at run time Java Class file , Trigger at the same time spring Some column reloading procedures for . There is no need to restart in the process , And the modified code takes effect in real time
Preface ii ： Why we need hot deployment
The programmer restarts the service locally every day 5-12 Time , A single time is about 3-8 minute , Every day to Cargo Deploy 3-5 Time , Single time duration 20-45 minute , Deployment is frequent and frequent 、 Time consuming . The plug-in provides local and remote hot deployment capabilities to enable code changes to take effect in seconds ,RD Daily work is mainly divided into two scenarios: development self-test and joint debugging , The role of hot deployment in each scenario is described below ：
Preface 3 ： Where is the difficulty of hot deployment , Why is there no easy-to-use open source tool in the industry
Hot deployment is not the same as hot restart , image tomcat perhaps spring boot tool dev This kind of hot restart is equivalent to loading the project directly , Poor performance , Hot deployment of incremental files is very difficult , Need to be compatible with various middleware and user writing , High technical threshold , Need to be right JPDA（Java Platform Debugger Architecture）、java agent、 Bytecode enhancement 、classloader、spring frame 、Mybatis Framework and other integration solutions and other technical principles, in-depth understanding can fully support various frameworks , In addition, we need IDEA Plug in development capabilities , Form a whole product solution . Now there's hot deployment , Code is a little girl dressed up !
Preface 4 ： Why don't we spring boot devtools
Some friends asked me , Why not use it directly spring boot devtools, There are two reasons , First, it's only used in spring boot In the project , For ordinary java Project and spring xml Projects are not supported , The second most important point is that its thermal loading scheme is actually similar to tomcat The thermal loading is the same , It's just that it's hot loaded through nesting classloader To complete , This classloader Only load... At a time class file Changed class Binary , This will bring a question , In front of a very large project （ Start about 10min+） This situation , It looks very pale . In the final analysis, the reason lies in his reload The scope is simply too great , It's OK for some small projects , But the actual effect of some relatively large projects is very touching .
1、 Overall design
2.1、JVM Static before startup Instrument
Javaagent yes java A parameter of the command . Parameters javaagent Can be used to specify a jar package , And to the java Package has a 2 Requirements ：
This jar Bag MANIFEST.MF File must be specified Premain-Class term .
Premain-Class The specified class must implement premain() Method .
premain Method , Literally , Is running on main Class before function . When Java When the virtual machine starts , In execution main Function before ,JVM Will run first -javaagent specified jar In bag Premain-Class This class of premain Method .
2.3、instrument principle ：
instrument The underlying implementation of depends on JVMTI(JVM Tool Interface), It is JVM Some exposed interface collections for users to expand ,JVMTI It's based on event driven ,JVM Every time a certain logic is executed, the callback interface of some events will be called （ If any ）, These interfaces allow developers to extend their logic .JVMTIAgent Is a use of JVMTI The exposed interface provides loading when the agent starts (agent on load)、 Agent through attach Form loading (agent on attach) And agent uninstall (agent on unload) Dynamic library of functions . and instrument agent It can be understood as a kind of JVMTIAgent Dynamic library , Another name is JPLISAgent(Java Programming Language Instrumentation Services Agent), That is to say, it is specially for java Language written instrumentation services to provide support for agents .
2.3.1、 Load on startup instrument agent The process ：
Create and initialize JPLISAgent;
monitor VMInit event , stay JVM Do the following after initialization ：
establish InstrumentationImpl object ;
monitor ClassFileLoadHook event ;
call InstrumentationImpl Of loadClassAndCallPremain Method , In this method, I will call javaagent in MANIFEST.MF In the specified Premain-Class Class premain Method ;
analysis javaagent in MANIFEST.MF The parameters of the file , And according to these parameters to set JPLISAgent Some of the content in .
2.3.2、 Load at run time instrument agent The process ：
adopt JVM Of attach Mechanism to request the target JVM Load the corresponding agent, The process is roughly as follows ：
Create and initialize JPLISAgent;
analysis javaagent in MANIFEST.MF In the parameters of the ;
establish InstrumentationImpl object ;
monitor ClassFileLoadHook event ;
call InstrumentationImpl Of loadClassAndCallAgentmain Method , In this method, I will call javaagent in MANIFEST.MF In the specified Agent-Class Class agentmain Method .
2.3.3、Instrumentation The limitations of
Most of the time , We use Instrumentation They all use their bytecode instrumentation , Or in general, class redefinition (Class Redefine) The function of , But there are the following limitations ：
premain and agentmain The time to modify bytecode in both ways is after class file loading , That is to say, it must have Class Parameters of type , You can't redefine a class that doesn't exist by using bytecode files and custom class names .
Class bytecode modification is called class conversion (Class Transform), In fact, class transformation eventually returns to class redefinition Instrumentation#redefineClasses() Method , This method has the following limitations ：
The parent of the new class and the old class must be the same ;
The number of interfaces implemented by new and old classes should be the same , And it's the same interface ;
The new and old class accessors must be the same . The number of fields of the new class and the old class should be consistent with the field name ;
New and old classes must be added or deleted by private static/final Embellished ;
You can modify the method body .
In addition to the way above , If you want to redefine a class , We can consider the isolation based on classloader ： Create a new custom class loader to define a new class with new bytecode , However, there is a limitation that the new class can only be called through reflection .
2.4、 In those years JVM and Hotswap Love and kill each other
Around method body Of hotSwap JVM It's been improving
1.4 Start JPDA Introduced hotSwap Mechanism （JPDA Enhancements）, Realized debug At the time of the method body The dynamics of 1.5 Begin to pass JVMTI Realized java.lang.instrument (Java Platform SE 8 ) Of premain The way , Realized agent The dynamics of the way （JVM Specified at startup agent）1.6 added agentmain The way , Achieve runtime dynamism （ adopt The Attach API Bind to concrete VM）. Its basic realization is through JVMTI Of retransformClass/redefineClass Conduct method body Level bytecode update ,ASM、CGLib And so on are basically dynamic around these .
But for Class Of hotSwap There's been no movement （ such as Class add to method, add to field, Modifying inheritance relations and so on ）, Why? ？ Because it's complicated and it doesn't pay too much .
2.5、 How to solve Instrumentation The limitations of
because JVM Limit ,JDK7 and JDK8 It's not allowed to change the class structure , For example, new fields , Add methods and modify the parent class of the class , This is for spring It's fatal for the project , Suppose Xiao Gong wants to modify one spring bean, I added a new one @Autowired Field , There are a lot of such scenarios in practical application , So our support for this scenario is essential .
So how do we do it , Let's welcome the famous dcevm,dcevm(DynamicCode Evolution Virtual Machine) yes java hostspot Patch for ( Strictly speaking, it's a modification ), allow ( It's not unlimited ) Modify the loaded class file in the running environment . Currently, only method bodies are allowed to be modified in the virtual machine (method bodies),decvm, Can increase Delete class properties 、 Method
3、 Hot deployment technology analysis
3.1、 File monitoring
When hot deployment starts, two directories will be predefined locally and remotely first ,/var/tmp/xxx/extraClasspath and /var/tmp/xxx/classes,extraClasspath Custom extensions for us classpath url,classes Listen to the directory for us , When there is a document change , adopt idea Plug in to deploy to remote / Local , Trigger agent My listening Directory , To continue with the hot load logic below , Why don't we just replace the user's classPath The following resource file , Because the business side considers war Bag api project , and spring boot project , It's all about jar Package to start , In this way, we can't modify users directly class Of documents , Even user projects we can modify , Directly operate the user's class, It will also bring a series of security problems , So we're using expansion classPath url To modify and add files , And there's a scene , Multiple business side projects introduce the same jar package , stay jar It's equipped with mybatis Of xml Annotation , We have no way to modify this situation directly jar Source files in package , By expanding the path, you don't need to pay attention to jar Package to modify jar A file in the package and xml, Isn't it cool , In the same way, this method can carry out the whole jar Hot swapping of packages ( In the scheme design ). Here's a brief introduction to the core listener
3.2、jvm class reload
JVM Bytecode bulk overload logic , Through the new bytecode binary stream and the old class Object to generate ClassDefinition Definition ,instrumentation.redefineClasses(definitions), To trigger JVM heavy load , The initialization time will be triggered after overloading spring Plug in registered transfrom, In the next chapter, let's talk about spring How to overload .
newly added class How do we guarantee that we can load into classloader In the context of ？ Because the project is executed remotely , So the operating environment is complex , It could be jar Package mode start (spring boot), It could be an ordinary project , It could be war web project , In view of this situation, we have made a layer of classloader url expand User classLoader It's frame custom classLoader Generally referred to as the , for example Jetty The project is WebAppclassLoader, among Urlclasspath For the current project lib Under the file , for example spring boot The project is also from the current project BOOT-INF/lib/, wait , Different frames have slightly different custom positions . So in this case We have to get the user's customization classloader, If it's started in the normal way , Like ordinary spring xml With the help of plus Release , There is no custom for this classloader, Is the default AppClassLoader, So we start the user project with agent Bytecode enhancement to get real users classloader.
What we do ： Find the user's child classloader And then get it by reflection classloader The elements in Classpath, among classPath Medium URL Is the current project loading class All of the runtime required by class Environmental Science , And it includes the tripartite jar Package dependency, etc .
We get URL Array , Expand our custom classpath Add directory to URL The first bit of the array , So when there's a new one class when , We just need to put class The file is in the expansion classpath The corresponding package directory can be found below , When there are others bean Rely on the new class when , Will look for class files from the current directory .
Why not go straight to Appclassloader Strengthen ？ It's a customization of the framework classloader Consider such a scenario , The framework custom class loader has ClassA, Then at this time, the user added a Class B Thermal loading is required ,B class There are A Reference relation of , If we enhance AppClassLoader when , initialization B When an instance ClassLoader.loadclass First of all, from the UserClassLoader Start looking for classB, Rely on the principle of parental appointment ,B Be being Appclassloader Loaded , because B Class dependent A, So at the moment AppClassLoader load B You can't find it , Report at this time ClassNotFoundException. That is to say, we must expand the top class loader when we expand the class loader , Only in this way can we achieve the effect we want .
3.3、spring bean heavy load
spring bean reload In the process ,bean The destruction and restart process of , Among them, the fine node involves more . First, when you modify java class D when , adopt spring classpathScan Scan to verify the currently modified bean Whether it is spring bean（ Comment check ） And then trigger the destruction process （BeanDefinitionRegistry.removeBeanDefinition） This method will change the current spring In context bean D And dependence spring bean D Of Bean C Destroy them together , But the scope is only in the present spring Context , if C In the context of quilt Bean B rely on , Cannot update dependencies in a child context , here , When traffic comes in ,Bean B Relating to Bean C Or objects before hot deployment , So hot deployment failed , So we are spring During initialization , You need to maintain a parent-child context correspondence , When the sub context changes, if the scope of change involves Bean B when , The dependency in the child context needs to be updated again , So when there is multi context association, we need to maintain the multi context environment , And the current context entry needs reload. Entrance means ：spring mvc controller,Mthrift and pigeon, For different flow inlets , We use different methods reload Strategy .RPC The main operation of the framework entry is to unbind the registry , Re registration , Reloading the startup process, etc , Yes Spring mvc controller It's mainly about unbinding and registering url Mappping To realize the change switching of traffic entry class
3.4、spring xml heavy load
When users modify / newly added spring xml when , Need to be right xml All in bean Overload
again reload after , take spring Restart after destruction .
Be careful :xml The way of modification has changed a lot , It may involve the whole situation Aop The configuration of the CPU and the contents related to the pre processor and post processor , The scope of influence is the overall situation , So at present, only the ordinary xml bean New tags / modify , Other capabilities should be gradually released as appropriate .