From the school to A factory all the way sunshine vicissitudes of life

Please go to www.codercc.com

1. Application scenarios of piling

In real business development, there are common modules that need to be implemented at the system level, such as validation, permissions, and so on, which are implemented in a mature solution using AOP.

Usually link log on track, every company will have ELK solution, but the company’s business line of cases, usually require a business system in log print different markers to distinguish, more will be convenient for the subsequent different business department cost accounting as well as access control, etc., that is to say, in the format of the log output will have certain requirements.

In addition, in the actual screen problem often needs to be done in the context parameter can help the problem of high efficient screening, because at ordinary times active writing log in the system, in fact, a kind of defensive programming, so must be in writing code this or that kind of business when they consider the abnormal situation, basically the probability of problems appeared in the online will be small. For the most part, online problems must be something that is not considered in daily development and can only be analyzed by Arthas generally. When communicating with upstream and downstream services, upstream and downstream developers will often ask for service invocation parameters and link traceid, so as to perform efficient troubleshooting. Unfortunately, if the system is not buried ahead of time, you can only temporarily add code and then publish it to the pre-release environment, and if you are lucky enough to reproduce the problem, you can fix it. In this case, if the system can automatically print out the method’s context in and out parameters, and automatically plant traceId on each link, it can be more efficient in the troubleshooting scenario. The ability to standardize on this log can be abstracted into a common basic capability.

Therefore, in this appeal, if it comes to log standardization transformation needs a common solution to carry out, to complete the transformation of log format, of course, there are many ways to promote, such as heap concentration transformation: At the organizational level of the team, as a technology-driven matter, each student added the department’s special business mark KV pair in the original log.info (the same as logs of other log levels) according to the requirements of the company’s log format. Or to implement a set of Spring AOP solution, define some annotations for each business system to use, but for the existing code, need to invest manpower to change, add corresponding annotations on the class or method, this approach will also bring low human efficiency problem.

In view of the above problems, bytecode staking at the method level and log standardization can be realized through agent. AOP is the “guiding principle” of a class of solutions, and there will be many specific implementation methods, such as aspectJ, Cglib and other tools, by recording the execution time of the scheme as well as exceptions and method input and output parameters to complete the non-intrusive monitoring of business links. The idea is that the Agent mechanism provides “bytecode change” opportunities, and bytecode staking is a concrete way of landing AOP.

In JDK 1.5, Java introduced the java.lang.Instrument package, which provides tools to help developers dynamically modify Class types in a system while Java programs are running. One of the key components using the software package is Java Agent. From the name, it looks like a Java proxy of some sort, providing an opportunity to change the class bytecode. A number of development tools are implemented based on Java Agents, such as the popular hot deployment JRebel, various online diagnostic tools (BTrace, Greys), and Arthas, which ali recently opened source.

2. The agent is used

2.1 Agent Static Loading

Javaagent is a parameter to a Java command. The javaAgent argument can be used to specify a JAR package that has two requirements:

  1. The MANIFEst.mf file of this JAR package must specify a premain-class entry.
  2. The Class specified by premain-class must implement the Premain () method.

The premain method, literally, is the class that runs before main. When the Java virtual machine starts, the JVM runs the Premain method of the premain-class Class in the jar specified by -JavaAgent before executing the main function. The premain method is signed as follows:

public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)
Copy the code

Premain with Instrumentation is preferred by default. If the first method is loaded, the second method will not be loaded. If the first method does not, the second method is loaded.

Agent Static startup mode

Using JavaAgent requires several steps:

  1. Define a manifest.mf file that must include the premain-class option. Can-re-re-classes and can-retransform-classes are usually added as well.
  2. Create a premain-class Class containing a Premain method whose logic is determined by the user.
  3. Jar premain’s classes and manifest.mf files.
  4. Start the method to be proxied with the argument -JavaAgent: jar package path.

After the above steps, the JVM first executes the Premain method, through which most of the class loads pass, not all. Of course, the missing classes are mainly system classes, because many system classes execute before the Agent, and user classes are bound to be intercepted. In other words, this method intercepts most of the class loading activities before the main method starts. If you can intercept the class loading, you can rewrite the class using a third-party bytecode compiler such as ASM, Javassist, Cglib, and so on.

2.2 Example of static loading

  1. Start by creating an Agent class that contains premian methods and implements a custom bytecode rewriting class by implementing the ClassFileTransformer interface.

    public class PremainAgent {
        public static void premain(String agentArgs, Instrumentation inst) {
            System.out.println("agentArgs : " + agentArgs);
            inst.addTransformer(new CustomClassTransformer(), true);
        }
    
        static class CustomClassTransformer implements ClassFileTransformer {
    
            @Override
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                System.out.println("premain load class !!!");
                return classfileBuffer;
            }
        }
    
    }
    Copy the code
  2. Configure the mainfest.mf file

    Manifest-version: 1.0 can-re-re-classes: true can-retransform-classes: true premain-class: com.agent.example.PremainAgentCopy the code

    This file can also be automatically generated by configuring the Maven plug-in as follows:

    <plugin> <artifactId> Maven-jar-plugin </artifactId> <version>3.0.2</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> <manifestEntries> <Premain-Class>com.agent.example.PremainAgent</Premain-Class> <Agent-Class>com.agent.example.PremainAgent</Agent-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin>Copy the code
  3. Configure JVM parameters to specify the agent path and start the application

    - javaagent: the path - to/agent - core - 0.0.1 - the SNAPSHOT. The jarCopy the code

    After starting the application, the agent will intercept the class before loading it. You can see the output of the sample code:

    premain load class !!!
    premain load class !!!
    premain load class !!!
    premain load class !!!
    premain load class !!!
    premain load class !!!
    Copy the code

2.3 Dynamic Loading of Agent

The way premain works is that it provides a chance to make changes to the class before the application starts executing main. The only time to change the bytecode class after main or after the business application is running properly is through the AgentMain method, as follows:

Instrumentation#retransformClasses(Class<?) Instrumentation#retransformClasses(Class<? >... Classes) allows the corresponding classes to be re-converted, Public static void AgentMain (String agentArgs, Instrumentation inst) public static void agentmain (String agentArgs)Copy the code

The specific steps are basically the same as those of static loading:

  1. Create an Agent class that contains the AgentMain method and complete the corresponding Agent logic in the subclass. And, if bytecode changes need to be made, you can also implement the ClassFileTransformer interface and place the implementation classes into Instrumentation;

  2. Complete mainfest.mf file and configure agent-class and other options as follows:

    Agent-Class: com.agent.example.AgentMainAgent
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    Copy the code

    The mainfest.mf file can also be configured via maven plugin, which is automatically generated during packaging. The configuration is as follows:

    <plugin> <artifactId> Maven-jar-plugin </artifactId> <version>3.0.2</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> <manifestEntries> <Agent-Class>com.agent.example.AgentMainAgent</Agent-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin>Copy the code

2.4 the agent

In fact, the dynamic Agent mode means that the service application can inject an Agent during the operation and complete the corresponding agent logic with the agent. How to inject the JVM while it is running naturally involves communication between two JVM processes, which can be done using VirtualMachine.

VirtualMachine represents a Java VirtualMachine (JVM) that the program needs to monitor. VirtualMachine provides system information (such as memory dump, thread dump, class information statistics (such as the number of loaded classes and instances, etc.), loadAgent, Methods like Attach and Detach (the opposite of the Attach action, removing an agent from the JVM) can be very powerful. This class allows us to connect remotely to the JVM by passing in a JVM PID (process ID) to the ATTACH method.

The agent class injection operation is just one of its many functions, through the loadAgent method to JVM register a agent agent, agent agent in the agent will get an Instrumentation instance, the instance can change the class bytecode before the class load, You can also reload after the class is loaded. When methods of Instrumentation instances are called, they are handled using methods provided in the ClassFileTransformer interface.

VirtualMachine attach(PID) method to attach to a running Java process, then loadAgent(agentJarPath) can be used to inject agent JAR packages into the corresponding process. The corresponding process then calls the AgentMain method.

2.5 Dynamic Loading Example

  1. Start by creating an Agent class that contains the AgentMain method and load a new class that implements the ClassFileTransformer interface into instrument.

    public class AgentMainAgent {
        public static void agentmain(String agentArgs, Instrumentation inst) {
            System.out.println("start agentmain");
            inst.addTransformer(new CusDefinedClass(), true);
        }
    
        static class CusDefinedClass implements ClassFileTransformer {
            @Override
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                System.out.println("agentMain load class !!!");
                return classfileBuffer;
            }
        }
    }
    Copy the code
  2. Package the whole agent and complete the configuration of mainfest.MF file.

  3. The agent is dynamically mounted to a running JVM process using the VirtualMainche class in the test class

    public class AgentTest { public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { List<VirtualMachineDescriptor> vms = VirtualMachine.list(); for (VirtualMachineDescriptor vm : vms) { if ("com.agent.example.AgentTest".equals(vm.displayName())) { VirtualMachine machine = VirtualMachine.attach(vm.id()); Machine. LoadAgent ("/path - to/agent - core - 0.0.1 - the SNAPSHOT. Jar "); } System.out.println(vm.displayName()); }}}Copy the code

    VirtualMachine. List () can list the JVM processes that are currently running. In this example, the JVM that is currently executing is identified by the process name. This section describes how to mount the Agent to the target VM in loadAgent mode. Example code is as follows:

    start agentmain
    com.agent.example.AgentTest
      
    agentMain load class !!!
    agentMain load class !!!
    Copy the code

Agent mechanism provided in the application before or after the application execution, to be able to get the timing of the class bytecode, and by changing the class bytecode way to complete the corresponding business logic, such as the method level of standardization of monitoring, logging, and so on AOP common business scenarios, this way of business application of invasive is the lowest, And the performance is considerable. The use of bytecode, business monitoring based on bytecode staking and problems encountered in actual development will be summarized in the following articles.

The resources

www.cnblogs.com/rickiyang/p…

www.cnblogs.com/huanshilang…