[JAVA] Messen Sie den Engpass! So verfolgen Sie nur langsame Methoden in AspectJ

Leistungsprobleme sind für Ingenieure immer ein Problem. Es gibt verschiedene Arten der langsamen Verarbeitung. Wenn ein Prozess einfach schwerer als der Schwellenwert ist oder weil es sich um eine Charge handelt, ist jeder Prozess nicht so langsam, aber aufgrund von Chiritsumo langsam. Insbesondere letzteres ist ziemlich schwer zu identifizieren. Selbst wenn Sie Flight Recorder verwenden, ist es ziemlich schwierig herauszufinden, wo es für einfaches Java langsam ist, und die Einführung von APM in vollem Umfang ist in der Regel eine große Sache.

Diesmal habe ich versucht zu prüfen, ob ich mit AspectJ langsame Methoden verfolgen kann. Den Beispielcode erhalten Sie aus dem Folgenden. https://github.com/koduki/example-aspectj

Was ist AspectJ überhaupt?

AspectJ ist eine Java-Bibliothek zum Erreichen von AOP und Aspektorientierung. Grob gesagt ist es "eine Methode, um Querschnittsthemen (Aspekte) in mehrere Bereiche wie Protokollierung und Berechtigungsprüfung in die vorhandene Verarbeitung einzubetten". Es war sehr beliebt als Methode, um die Objektorientierung für eine Weile zu ergänzen, aber es scheint, dass ich heutzutage nicht viel sage. Dies kann daran liegen, dass die FW und dergleichen für die im Beispiel genannten Dinge gut sind, sodass es sich nicht mehr um einen Bereich handelt, den einzelne Ingenieure interessieren.

Dieses Mal werde ich versuchen, Protokolle auszugeben, indem ich einen Aspekt der "Protokollierung langsamer Methoden" erstelle, ohne die vorhandene Verarbeitung des Messziels zu ändern.

Erstellen eines SlowMethodLogger-Aspekts

Grundsätzlich können Sie mit normalem Java-Code entwickeln, sodass Sie problemlos mit der Entwicklung mit NetBeans + Maven beginnen können. pom.xml sieht so aus. Beachten Sie, dass sowohl das Plugin als auch die Abhängigkeit hinzugefügt werden müssen.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cn.orz.pascal</groupId>
    <artifactId>example-aspectj</artifactId>
    <version>0.1</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.13</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.0.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-classpath</goal>
                        </goals>
                        <configuration>
                            <outputProperty>aspectj-weaver-path</outputProperty>
                            <includeGroupIds>org.aspectj</includeGroupIds>
                            <includeArtifactIds>aspectjweaver</includeArtifactIds>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.11</version>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>1.8.13</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <complianceLevel>1.8</complianceLevel>
                            <outxml>true</outxml>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Erstellen Sie den Aspekt weiter. Sie können es jedoch als Java-Klasse schreiben, indem Sie "@ Aspect" hinzufügen.

@Aspect
public class SlowMethodLogger {
    private static final long LIMIT_TIME;
    static {
        String limit = System.getenv("SLOW_METHOD_LIMIT");
        LIMIT_TIME = (limit == null) ? 0 : Long.parseLong(limit);
    }
    
    @Around("execution(* cn.orz.pascal.example.aspectj.*.*(..))")
    public Object logging(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long start = System.nanoTime();
        try {
            return proceedingJoinPoint.proceed();
        } finally {
            long end = System.nanoTime();
            long time = end - start;
            
            if (time >= LIMIT_TIME) {
                int line = proceedingJoinPoint.getSourceLocation().getLine();
                String method = proceedingJoinPoint.getSignature().toString();
                System.out.println("SLOW_METHOD:" + method + "\tLINE:" + line + "\tTIME(ns):" + time);
            }
        }
    }   
}

Es ist eine Methode, die @Around als einen Aspekt einbettet, der als Punktschnitt bezeichnet wird. Dieses Mal wird mit @ Around die Ausführungszeit durch Ausführen der Zielmethode [ProceedingJoinPoint.proceed ()] gemessen. Es ist ein Formular, das nur protokolliert werden soll, wenn der Wert nach der Ausführung größer als "LIMIT_TIME" ist. Das einzubettende Ziel wird durch Ausführung angegeben, und mit dieser Schreibmethode werden alle Untergebenen unter "cn.orz.pascal.example.aspectj" unabhängig von privat / öffentlich aufgezeichnet.

Wenn Sie einfach alle Protokolle ausgeben, wird dies nicht zu einem Engpass bei der 100-Millionen-Schleifenschleife. Daher muss ein Stichprobenmechanismus für die Aufzeichnung des Timings und das Protokollierungs-Timing eingerichtet werden. Es gibt.

Diesmal ist es ein Beispiel, also verwende ich System.out.println, aber in Wirklichkeit ist es besser, log4j2 oder so etwas zu verwenden.

Erstellen einer Messzielanwendung

Jetzt erstellen wir eine Anwendung, die gemessen werden soll. Grundsätzlich ist es nicht erforderlich, den zuvor erstellten Aspekt zu kennen, geschweige denn die Bibliothek, die sich zum Zeitpunkt der Entwicklung auf AspectJ bezieht. Es muss jedoch nur das Paket mit dem unter "Ausführung" angegebenen Paket übereinstimmen. * Normalerweise wird die Ausführung entsprechend dem Messziel geändert.

package cn.orz.pascal.example.aspectj;
public class Main {
    public static void main(String[] args) {
        System.out.println("run");
        Sub1 sub = new Sub1();
        for (int i = 0; i < Integer.parseInt(args[0]); i++) {
            sub.getProcess();
        }
    }
}
package cn.orz.pascal.example.aspectj;
public class Sub1 {
    private long count = 0;
    public void getProcess() {
        getPrivateProcess();
    }

    private void getPrivateProcess() {
        if (count % 3 == 0) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
        }
        count += 1;
    }
}

Ich habe es unter der Annahme gemacht, dass Sub in der Schleife von Main aufgerufen wird und Sub ungefähr alle drei Male langsam ist.

Versuche zu rennen

Führen Sie zunächst die zu messende Anwendung normal aus.

$ time SLOW_METHOD_LIMIT=500000000 java -cp "example-targetapp-0.1.jar" cn.orz.pascal.example.aspectj.Main 10
run

real    0m2.158s
user    0m0.015s
sys     0m0.000s

Es ist die erwartete Zeit, da 500 ms 4 Umdrehungen machen. Als nächstes fügen wir den zuvor erstellten Aspekt hinzu. Verwenden Sie zum Anhängen Javaagent wie JFR und andere APM-Systeme.

$ time SLOW_METHOD_LIMIT=500000000 java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" -javaagent:asp ectjweaver-1.8.13.jar cn.orz.pascal.example.aspectj.Main 10
run
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Sub1.getPrivateProcess() LINE:24 TIME(ns):500278916
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Sub1.getProcess()        LINE:20 TIME(ns):503194581
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Sub1.getPrivateProcess() LINE:24 TIME(ns):500142922
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Sub1.getProcess()        LINE:20 TIME(ns):501776905
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Sub1.getPrivateProcess() LINE:24 TIME(ns):500856763
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Sub1.getProcess()        LINE:20 TIME(ns):502748879
SLOW_METHOD:void cn.orz.pascal.example.aspectj.Main.main(String[])      LINE:15 TIME(ns):2021801158

real    0m2.459s
user    0m0.000s
sys     0m0.000s

Messen Sie den Grad der Leistungsverschlechterung

Ich habe es jeweils dreimal gemessen, aber in diesem Fall gab es keinen großen Leistungsunterschied. Da es sich jedoch um eine Einzelfallbasis handelt, ist es erforderlich, sie auf den tatsächlich verwendeten Code anzuwenden und über die tatsächliche Einführung zu entscheiden.

Mit Agent

$ time SLOW_METHOD_LIMIT=500000000 java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" -javaagent:asp ectjweaver-1.8.13.jar cn.orz.pascal.example.aspectj.Main 1000 > /dev/null
real    2m47.602s
$ time SLOW_METHOD_LIMIT=500000000 java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" -javaagent:asp ectjweaver-1.8.13.jar cn.orz.pascal.example.aspectj.Main 1000 > /dev/null
real    2m47.591s
$ time SLOW_METHOD_LIMIT=500000000 java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" -javaagent:asp ectjweaver-1.8.13.jar cn.orz.pascal.example.aspectj.Main 1000 > /dev/null
real    2m47.572s

Kein Agent

$ time java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" cn.orz.pascal. example.aspectj.Main 1000 > /dev/null
real    2m47.327s
$ time java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" cn.orz.pascal. example.aspectj.Main 1000 > /dev/null
real    2m47.319s
$ time java -cp "aspectjrt-1.8.13.jar;example-aspectj-0.1.jar;example-targetapp-0.1.jar" cn.orz.pascal. example.aspectj.Main 1000 > /dev/null
real    2m47.304s

Zusammenfassung

Dieses Mal habe ich versucht, mit AspectJ langsame Methoden zu identifizieren. Die Installation ist einfacher als bei einem vollwertigen APM. Daher ist es praktisch, wenn Sie es schnell in eine Entwicklungs- oder Verifizierungsumgebung integrieren möchten. Außerdem scheint es einfacher zu sein, dies mit AOP zu tun, als es selbst zu erstellen, beispielsweise mit dem Mechanismus der verteilten Ablaufverfolgung. Ich habe es nicht so oft ausprobiert, aber jetzt erkenne ich die Bequemlichkeit von AspectJ ...

Dann viel Spaß beim Hacken!

Recommended Posts

Messen Sie den Engpass! So verfolgen Sie nur langsame Methoden in AspectJ
So erhalten Sie das Datum mit Java
So debuggen Sie die Verarbeitung im Ruby on Rails-Modell nur mit der Konsole
So überprüfen Sie das Protokoll im Docker-Container
So erstellen Sie die einfachste Blockchain in Ruby
So überprüfen Sie Rails-Befehle im Terminal
So implementieren Sie UICollectionView mit Code nur in Swift
So stellen Sie die Anzeigezeit in Rails auf japanische Zeit ein
[Java] So lassen Sie den privaten Konstruktor in Lombok weg
Organisierte schrittweise Interaktion mit dem JDK
So geben Sie den Ressourcenpfad beim HTML-Import an
So debuggen Sie die generierte JAR-Datei mit Eclipse
[Schienen] So zeigen Sie Bilder in der Ansicht an
So erhalten Sie den Namen einer Klasse / Methode, die in Java ausgeführt wird
Verwendung der Getter / Setter-Methode (in Objektorientierung)
So trennen Sie Wörter in Namen in Klassen, Methoden und Variablen
So stellen Sie die Chronik ein, wenn sich die Zeit in CentOS7 verschiebt
So verbinden Sie die Zeichenfolgen in der Liste durch Kommas getrennt
So erstellen Sie ein Platzhalterteil zur Verwendung in der IN-Klausel
So rufen Sie den Hashwert in einem Array in Ruby ab
Großschreibung nur des angegebenen Bereichs mit Teilzeichenfolge. (Verwendung von Teilzeichenfolgen)
So fügen Sie dieselben Indizes in ein verschachteltes Array ein
Wie man einige Methoden der zu testenden Klasse verspottet
So leiten Sie den letzten Tag des Monats in Java ab
[Schienen] Anzeigen von Informationen, die in der Datenbank gespeichert sind
[Rails / Routing] So verweisen Sie auf den Controller in Ihrem eigenen Verzeichnis
[jOOQ] Wie in der WHERE / AND / OR-Klausel WANN FALLEN
So erhalten Sie die ID des automatisch inkrementierten PRIMAY-Schlüssels in MyBatis
Wie installiere ich die in Ubuntu verwendete Sprache und wie erstelle ich die Umgebung?
Speichern der im Textbereich eingegebenen Informationen in einer Variablen in der Methode
So beheben Sie den unbekannten Fehler, der bei der Verwendung von slf4j in Java aufgetreten ist
kotlin & Java: So blenden Sie die Symbolleiste nur für bestimmte Fragmente aus
So rufen Sie mehrere Namen gleichzeitig in derselben Kategorie auf
So ermitteln Sie die Länge einer Audiodatei mit Java
So erhöhen Sie den Wert von Map in einer Zeile in Java
[Ruby on Rails] So melden Sie sich nur mit Ihrem Namen und Passwort mit dem Gem-Gerät an
Verwendung der link_to-Methode
Wie man Lombok im Frühling benutzt
So finden Sie May'n in XPath
Verwendung der include? -Methode
So blenden Sie die Bildlaufleiste in WebView aus
Verwendung der Methode form_with
So führen Sie JUnit in Eclipse aus
Übergeben Sie das Gebietsschema i18n an JavaScript
Wie man in Ruby auf unbestimmte Zeit iteriert
So finden Sie den durchschnittlichen Winkel
Wie man Ant in Gradle ausführt
Wie man die Programmierung in 3 Monaten beherrscht
Verwendung der Wrapper-Klasse
Wie man JAVA in 7 Tagen lernt
So erhalten Sie Parameter in Spark
Aufrufen von Klassen und Methoden
So installieren Sie Bootstrap in Ruby
Verwendung von InjectorHolder in OpenAM