Hot substitution of Java class--concept, design and implementation

Source: Internet
Author: User
Tags switches

Build a Java-based online upgrade system

For many critical businesses or large Java systems, if system services must be paused for system upgrades, it can greatly affect the availability of the system, as well as increase the management and maintenance costs of the system. Therefore, if the system can be easily upgraded without stopping the system business, the above problem can be solved well. In this article, we will make an in-depth explanation of the basic techniques and design principles for building an online upgrade Java system based on an example. It is believed that readers can build their own online upgrade system according to the technology of the article.

Analysis of Java ClassLoader technology

In this article, we will not explain the details of Java ClassLoader in too much detail, but rather focus on the underlying concepts associated with building an online upgrade system. Detailed details on ClassLoader Many of the materials can be consulted, interested readers can read their own.

An important technique for building an online upgrade system is the ability to implement hot-swapping of Java classes-that is, to upgrade classes (objects) without stopping the running system. Java's ClassLoader is the basis for this technology.

In Java, the instantiation process of a class is divided into two parts: the loading of classes and the instantiation of classes. The loading of classes is also divided into explicit loading and implicit loading. When you create a class instance with the New keyword, you implicitly include the class's loading process. For the explicit loading of classes, the class.forname is commonly used. In fact, they all do the actual loading of the class by invoking the LoadClass method of the ClassLoader class. The LoadClass method of calling ClassLoader directly is another common technique for explicitly loading classes.

Figure 1. Java ClassLoader Hierarchy Diagram

ClassLoader has some hierarchical relationships and rules when loading classes. In Java, there are four types of ClassLoader: Bootstrapclassloader, Extclassloader, Appclassloader, and user-defined ClassLoader. These four kinds of loaders are responsible for loading classes of different paths, respectively, and forming a class-loaded hierarchy.

Bootstrapclassloader is at the top of the class loader hierarchy and is responsible for loading classes under the Sun.boot.class.path path, which defaults to the jar package specified by the core API or-xbootclasspath option in the Jre/lib directory. The Extclassloader load path is java.ext.dirs, which defaults to the Jre/lib/ext directory or the jar package loaded under the specified directory-djava.ext.dirs. The Appclassloader load path is Java.class.path, which defaults to the value set in the environment variable CLASSPATH. can also be specified by-classpath selection. User-defined ClassLoader can customize the class loading process according to the user's needs, and perform dynamic real-time loading of the specified classes at runtime.

The hierarchy diagram for these four kinds of loaders is shown in Figure 1. In general, these four kinds of loaders form a parent-child relationship, with the upper layer being the lower-level parents loader. When the class is loaded, the first step is to check to see if the specified class has been loaded and return the reference to the class directly if it is already loaded. If the specified class has not been loaded at the top level, it will attempt to load it up and down, until the user customizes the ClassLoader and throws an exception if it does not succeed. The loading process for Java classes is shown in Figure 2.

Figure 2. Loading process for Java classes

Each class loader has its own namespace, and for the same ClassLoader instance, only one of the classes with the same name can exist, and it is loaded only once. Regardless of whether the class has changed, the next time it needs to be loaded, it simply returns the loaded class reference from its own cache.

The application classes we write are loaded by default by Appclassloader. When we use the New keyword or class.forname to load a class, the class to be loaded is loaded by the ClassLoader (also Appclassloader) of the class that calls new or class.forname. In order to implement the hot substitution of Java class, we must first implement the coexistence of different version instances of the same name class in the system, and through the above we know that to realize the coexistence of different versions of the same class, we must load different versions of the class with different classloader. In addition, in order to be able to bypass the Java class's established loading process, we need to implement our own classloader, and in which the loading process of the class is fully controlled and managed.

Back to top of page

Write your own custom ClassLoader

To be able to fully control the loading process of a class, our custom ClassLoader needs to inherit directly from ClassLoader. First, let's introduce some important methods related to heat substitution in the ClassLoader class.

    • findloadedclass: Each classloader maintains its own copy of the loaded class namespace, where two classes with the same name cannot appear. Any class that is loaded by the ClassLoader, whether direct or indirect, is stored in its own namespace, which is the namespace in which the specified class already exists and returns a reference to the class if it exists, otherwise null is returned. The direct point here is that it exists on the loading path of the ClassLoader and is loaded by the loader, indirectly by delegating the class loader to the other ClassLoader to complete the actual loading of the class.
    • Getsystemclassloader : The new method in Java2. This method returns the ClassLoader used by the system. This method can be used to transfer part of the work to the System class loader in its own custom ClassLoader.
    • DefineClass: This method is a very important method in ClassLoader, it takes a byte array to represent the class bytecode, and converts it into a class instance, when the method transforms a classes, the first request to load the class of the parent class and the implementation of the interface class.
    • loadclass: Loads the entry method of the class, calling the method to complete the explicit loading of the class. By re-implementing this method, we can fully control and manage the loading process of the class.
    • resolveclass: link to a specified class. This is a necessary way to ensure that the class is available in some cases, as described in the "execution" chapter of the Java language specification.

With these methods in hand, let's implement a custom ClassLoader to complete the loading process: We specify a collection of classes that must be loaded directly by the ClassLoader for that ClassLoader, and when the class loader loads the class, if the class to be loaded belongs to a collection that must be loaded by that classloader. Then it is directly to complete the loading of the class, otherwise the class loading work is delegated to the system's class loader to complete.

Before presenting the sample code, there are two things to explain: 1, to achieve the same class of different versions of coexistence, then these different versions must be loaded by different classloader, so you can not delegate the loading of these classes to the system loader to complete, because they have only one copy. 2, in order to do this, we can not adopt the system default class loader delegate rules, that is, our custom class loader's parent loader must be set to NULL. The implementation code for this custom class loader is as follows:

Listing 1. Implementation code for a custom class loader
Class Customcl extends ClassLoader {private String basedir;//base directory of classes files that require the ClassLoader to be loaded directly, private HashSet dynaclazns;//required by the Class name loaded directly by class Loader public customcl (String basedir, string[] clazns) {super (NULL);//Specifies that the parent classloader is null this.based         IR = basedir;         Dynaclazns = new HashSet ();     Loadclassbyme (Clazns); } private void Loadclassbyme (string[] clazns) {for (int i = 0; i < clazns.length; i++) {Loadd             irectly (Clazns[i]);         Dynaclazns.add (Clazns[i]);         }} private Class loaddirectly (String name) {Class CLS = null;         StringBuffer sb = new StringBuffer (basedir);        String classname = Name.replace ('. ', File.separatorchar) + ". Class";         Sb.append (file.separator + classname);         File CLASSF = new file (sb.tostring ());         CLS = Instantiateclass (name,new fileinputstream (CLASSF), classf.length ());     return CLS; } private Class Instantiateclass (String Name,inputstreaM Fin,long len) {byte[] raw = new byte[(int) Len];         Fin.read (RAW);         Fin.close ();     return DefineClass (name,raw,0,raw.length); } protected class LoadClass (String name, Boolean resolve) throws ClassNotFoundException {class Cl         s = null;         CLS = Findloadedclass (name);         if (!this.dynaclazns.contains (name) && CLS = = null) CLS = Getsystemclassloader (). LoadClass (name);         if (CLS = = null) throw new ClassNotFoundException (name);         if (resolve) Resolveclass (CLS);     return CLS; } }

In the implementation of the ClassLoader, all classes that are specified to be loaded directly by it are loaded when the loader is instantiated, and when the class is loaded through LoadClass, if the class is not loaded and does not belong to a column that must be loaded by the ClassLoader, it is delegated to the system loader for loading. Understanding this implementation, the thermal substitution of the implementation class is only a step away, and we'll explain it in more detail in the next section.

Back to top of page

Implementing hot replacements for Java classes

In this section, we will combine the characteristics of the ClassLoader described earlier and implement the hot substitution of Java classes based on the custom ClassLoader implemented in the previous section. First we change the class name CUSTOMCL of the ClassLoader implemented in the previous section to HOTSWAPCL to explicitly express our intentions.

Now let's introduce our experimental method, for simplicity, our package is the default package, there is no hierarchy, and all error handling is omitted. To replace the class Foo, the implementation is simple and contains only one method, SayHello:

Listing 2. Sample class to replace
public class foo{public     void SayHello () {         System.out.println ("Hello world! (Version one) ");     }

Create a new directory swap under the current working directory, and place the compiled Foo.class file in the directory. The next step is to use the HOTSWAPCL we wrote earlier to implement the hot substitution of this class. Specifically, we write a timer task that executes every 2 seconds. Where we will create a new class loader instance to load the Foo class, generate the instance, and invoke the SayHello method. Next, we will modify the SayHello method in the Foo class to print, recompile, and replace the original Foo.class when the system is running, we will see that the system will print the changed content. The implementation of the scheduled task is as follows (other code omitted, please self-completion):

Listing 3. Partial code to implement timed tasks
public void Run () {     try {         //creates a new class loader each time        HOWSWAPCL cl = new HOWSWAPCL (". /swap ", New string[]{" Foo "});         Class cls = Cl.loadclass ("Foo");         Object foo = cls.newinstance ();         Method m = Foo.getclass (). GetMethod ("SayHello", New class[]{});         M.invoke (foo, new object[]{});         }  catch (Exception ex) {         ex.printstacktrace ();     }}

To compile and run our system, the following print will appear:

Figure 3. Results of operation before hot replacement

OK, now let's change the SayHello method of the Foo class to:

public void SayHello () {     System.out.println ("Hello world! (version) "); }

In the case of the system is still running, compile, and replace the swap directory of the original Foo.class file, we look at the screen printing, wonderful things happened, the newly changed class online immediately effective, we have implemented the Foo class hot replacement. The screen prints as follows:

Figure 4. Running results after hot replacement

A keen reader may ask, why not change Foo to Foo and call its SayHello method directly? Isn't that clearer? Let's explain the reasons and give a better approach.

If we adopt the transformation approach, the code will become this: Foo foo = (Foo)cls.newInstance(); if the reader follows this article to experiment, it will find that this sentence will throw classcastexception exception, why? Because in Java, even the same class file, if it is loaded by a different class loader instance, is not the same type. In the above example, the CLS is loaded by HOWSWAPCL, and the Foo Variable type fame and transformation of the Foo class is loaded by the loader of the class to which the Run method belongs (default is Appclassloader), so it is a completely different type, so a transformation exception is thrown.

Is it OK to call through the interface? We can define a IFoo interface, where the SayHello method, Foo implements the interface. This is the case: IFoo foo = (IFoo)cls.newInstance(); the method would have the same problem, because the external reputation and transformation part of the IFoo is loaded by the class loader that the run method belongs to, and the Foo class definition in implements IFoo IFoo is loaded by HOTSWAPCL, and therefore belongs to the non- The same type of transformation will still throw an exception, but as we instantiate the HOTSWAPCL:

HowswapCL cl = new HowswapCL("../swap", new String[]{"Foo"});

This only specifies that the Foo class is loaded by HOTSWAPCL, and its implementation of the IFoo interface file is delegated to the system ClassLoader to load, so the transformation succeeds, the code that takes the interface call is as follows:

Listing 4. Code that takes an interface call
public void Run () {     try {         HOWSWAPCL cl = new Howswapcl (".. /swap ", New string[]{" Foo "});         Class cls = Cl.loadclass ("Foo");         IFoo foo = (IFoo) cls.newinstance ();         Foo.sayhello ();     } catch (Exception ex) {         ex.printstacktrace ();     }}

Yes, it's a lot of clarity. In our experiment, whenever the timer is dispatched to the Run method, we create a new HOTSWAPCL instance, which is not required in the product code, but only when the replacement is needed to create a new ClassLoader instance.

Back to top of page

Design principles of online upgrade system

In the previous section, we give an example of a Java class hot swap, and with this technology, we have the foundation to implement an on-line upgrade system. However, for a real product system, upgrading the province is a very complex project, and if you want to upgrade online, it will be more complex. Among them, the implementation of the hot-swap class is only the last step, the requirements of online upgrade will have a profound impact on the overall design of the system. Let's talk about some of the principles of online upgrade system design:

    • At the beginning of the system design, it is necessary to consider which parts of the system need to be upgraded online later, and which parts are stable.

      Although we can design any part of the system can be upgraded online, but its cost is very high, there is no need. Therefore, it is advisable to clearly define the parts of the system that need to be upgraded online later. These parts are often the system business logic rules, algorithms and so on.

    • Design a uniform System state transition method.

      Replacing a class is just one step in the work of upgrading the system online, and in order for the system to function properly after the upgrade, you must maintain the consistency of the system state before and after the upgrade. Therefore, in the design of the need to consider the part of the online upgrade involved in the state of the system, which is designed to facilitate access to, set up and transform, and in a consistent manner.

    • Clear out the system upgrade control protocol.

      This principle is about the timing and process control of the online upgrade of the system, it is a very dangerous activity to upgrade without considering the current running state of the system. Therefore, in the system design, it is necessary to consider and set aside the control point of the system online upgrade, and define a clear, clear upgrade protocol to coordinate and control the upgrade order of multiple upgrade entities to ensure that the system at any time in the upgrade is in a certain state.

    • Consider the fallback mechanism when the upgrade fails.

      Even if we do a very meticulous design, it is difficult to fundamentally ensure that the system upgrade must be successful, especially for large distributed systems. Therefore, in the system design, consider the fallback mechanism after the failure of the upgrade.

Well, in this section we briefly introduce several important principles of the online upgrade system design, the next section we will give a simple example of how to implement an online upgrade system.

Back to top of page

Online Upgrade System Example

First, let's briefly describe the structure of this example and the work to be done. In our example, there are mainly three entities, one is the upgrade control entity, two are working entities, are based on activeobject implementation, communication via command message (for more information about Activeobject, see the author of another article "building a Java concurrency model framework" )。

The Upgrade Control entity provides an administrative command interface to receive external online upgrade commands in RMI mode. The working entity has two message queues, one to receive the task assigned to it (we send it a task command message with a timer), we call it the task queue, and the other is to interact with the upgrade control entity, which we call the control queue. The task in the work entity is simply to print out a string using the Foo class we described earlier, but this time the string is saved as a state in the working entity and dynamically set to the instance of the Foo class. The upgrade protocol process is as follows:

When the upgrade control entity receives an online upgrade command from RMI, it sends a ready-to-upgrade message to the task queue of two working entities and waits for a response. When a work entity receives a ready upgrade message in the task queue, it immediately sends a ready message to the upgrade control entity and then switches to the control queue to wait for further upgrade instructions. After the upgrade control entity has received a ready message from both work entities, it sends a start upgrade message to the control queue for both work entities, and then waits for the result. After the work entity receives the start of the upgrade message, it does the actual upgrade work, which is the hot substitution class we talked about earlier. Then, send an upgrade message to the upgrade control entity. After the upgrade control entity receives an upgrade message from two working entities, a resume work message is sent to the control queue for both work entities, and the work entity receives the resume work message and switches to the task queue to continue working. The upgrade process is complete.

The main code snippet is as follows (omitting the definition and execution details of the command message):

Listing 5. The main code snippet
Upgrade Control entity key Code class Upgradecontroller extends activeobject{int nready = 0;     int nfinished = 0;     Worker[] workers;             ...//Receive external Upgrade command message when the method is invoked public void Askforupgrade () {for (int i=0; i<workers.length; i++)    Workers[i].gettaskqueue (). Enqueue (New Prepareupgradecmd (workers[i));                }//Receive a work entity response when the Ready command message is triggered, the method is called public void Readyforupgrade (String worker_name) {nready++; if (Nready = = Workers.length) {for (int i=0; i<workers.length; i++) WORKERS[I].GETCONTROLQ         Ueue (). Enqueue (New Startupgradecmd (workers[i));         }}//receives a work entity response to the Upgrade complete command message, which fires when the method is called public void Finishupgrade (String worker_name) {nfinished++; if (nfinished = = Workers.length) {for (int i=0; i<workers.length; i++) workers[i].getc         Ontrolqueue (). Enqueue (New Contineworkcmd (workers[i)); }     }     ...... } // Work entity key code class Worker extends activeobject{upgradecontroller UGC;     HOTSWAPCL HSCL;     IFoo foo;     String state = "Hello world!";         ...//receive the Upgrade control entity when the prepare Upgrade command message is triggered, the method is called public void Prepareupgrade () {switchtocontrolqueue ();     Ugc.getmsgqueue (). Enqueue (New Readyforupdatecmd (Ugc,this));         }//receives the Upgrade Control entity's start Upgrade command message, which fires when the method is called public void Startupgrade (String worker_name) {doupgrade ();     Ugc.getmsgqueue (). Enqueue (New Finishupgradecmd (Ugc,this));     }//receives the Upgrade control entity's resume work command message when the method is called public void Continuework (String worker_name) {switchtotaskqueue ();        }//receives a timed command message that fires when the method is called public void DoWork () {Foo.sayhello (); }//Actual upgrade action private void Doupgrade () {HSCL = new Howswapcl ("...         /swap ", New string[]{" Foo "});         Class cls = Hscl.loadclass ("Foo");         Foo = (IFoo) cls.newinstance (); Foo.     SetState (state);    }}//ifoo Interface definition interface IFoo {void SetState (String); void SayHello (); }

In the implementation of the first version of the Foo class, only the strings that are set in are printed directly. In the second version, the set-in string is capitalized and then printed. The example is simple and is designed to express changes in rules or algorithmic aspects of the upgrade. In addition, we do not mention such as: Message timeout, upgrade failure, and other aspects of the anomaly, which must be considered in the actual product development.

Back to top of page

Summary

In this article, we explain in detail the basic technology of the Java online upgrade system: The thermal substitution of the class. In addition, some main guiding principles of online upgrade system design are also given. To enable readers to better understand these technologies and principles, we have finally given an example of an online upgrade system. It is important to note that building an online upgrade system is not only a technical issue, but also involves many management factors, such as how to manage, deploy the online upgrade part of the system and the non-online upgrade section to reduce the system management, maintenance costs and so on. I hope this article will provide some help for readers to build their own online upgrade system.

(RPM) Thermal substitution of Java classes--concept, design and implementation

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.