[JAVA] Mesurez le goulot d'étranglement! Comment tracer uniquement les méthodes lentes dans AspectJ

Les problèmes de performances sont toujours un problème pour les ingénieurs. Il existe différents types de traitement lent. Si un processus est simplement plus lourd que le seuil, ou parce qu'il s'agit d'un lot, chaque processus n'est pas si lent, mais il sera lent à cause du chiritsumo. Surtout ce dernier est assez difficile à identifier. Même si vous utilisez Flight Recorder, il est assez difficile de savoir où il est lent pour Java simple, et l'introduction de l'APM à grande échelle a tendance à être un gros problème.

Donc, cette fois, j'ai essayé de voir si je pouvais utiliser AspectJ pour tracer des méthodes lentes. Vous pouvez obtenir l'exemple de code à partir de ce qui suit. https://github.com/koduki/example-aspectj

Qu'est-ce qu'AspectJ en premier lieu?

AspectJ est une bibliothèque Java pour réaliser l'AOP et l'orientation d'aspect. En gros, c'est "une méthode pour intégrer des préoccupations (aspects) transversales dans de multiples domaines tels que l'exploitation forestière et la vérification des autorisations dans le traitement existant". C'était assez populaire comme méthode pour compléter l'orientation des objets pendant un certain temps, mais il semble que je ne dis pas grand-chose ces jours-ci. C'est peut-être parce que le FW et autres sont bons pour les choses mentionnées dans l'exemple, donc ce n'est plus un domaine qui intéresse les ingénieurs individuels.

Cette fois, en créant un aspect de «journalisation des méthodes lentes», j'essaierai de sortir des logs sans modifier le traitement existant de la cible de mesure.

Création d'un aspect SlowMethodLogger

Fondamentalement, vous pouvez développer avec du code Java ordinaire, de sorte que vous pouvez facilement démarrer le développement avec NetBeans + Maven. pom.xml ressemble à ceci. Notez qu'il est nécessaire d'ajouter un plugin ainsi qu'une dépendance.

<?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>

Continuez à créer l'aspect. Cependant, vous pouvez l'écrire en tant que classe Java simplement en ajoutant @ Aspect.

@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);
            }
        }
    }   
}

C'est une méthode que @Around incorpore sous la forme d'un aspect appelé coupe par point. Cette fois, en utilisant @ Around, le temps d'exécution est mesuré en exécutant la méthode cible [procedingJoinPoint.proceed ()]. C'est un formulaire à enregistrer uniquement lorsque la valeur est supérieure à LIMIT_TIME après l'exécution. La cible à incorporer est spécifiée par exécution, et avec cette méthode d'écriture, tous les subordonnés sous cn.orz.pascal.example.aspectj sont enregistrés indépendamment de privé / public.

Si vous sortez simplement tous les journaux, cela ne se terminera pas comme un goulot d'étranglement dans le processus de bouclage 100 millions de fois, il est donc nécessaire de mettre en place une sorte de mécanisme d'échantillonnage pour l'enregistrement de la synchronisation et la synchronisation de l'enregistrement. Il y a.

Cette fois, c'est un exemple, j'utilise donc System.out.println, mais en réalité, il est préférable d'utiliser log4j2 ou quelque chose comme ça.

Création d'une application cible de mesure

Maintenant, créons une application à mesurer. Il n'est fondamentalement pas nécessaire de connaître l'aspect créé précédemment ainsi que la bibliothèque associée à AspectJ au moment du développement. Cependant, seul le package doit correspondre à celui spécifié dans ʻexecution`. * Normalement, l'exécution sera modifiée en fonction de la cible de mesure.

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;
    }
}

Je suppose que Sub est appelé dans la boucle depuis Main et que Sub est lent environ une fois toutes les trois fois.

Essayez de courir

Tout d'abord, exécutez l'application à mesurer normalement.

$ 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

C'est le temps attendu car 500ms font 4 tours. Ensuite, attachons l'aspect créé précédemment. Pour la pièce jointe, utilisez javaagent comme JFR et d'autres systèmes APM.

$ 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

Mesurer le degré de détérioration des performances

Je l'ai mesuré trois fois chacun, mais dans ce cas, il n'y avait pas beaucoup de différence de performance. Cependant, comme il s'agit d'un cas par cas, il est nécessaire de l'appliquer au code effectivement utilisé et de décider de l'introduction proprement dite.

Avec l'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

Aucun 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

Résumé

Cette fois, j'ai essayé d'utiliser AspectJ pour identifier les méthodes lentes. Il est plus facile à installer qu'un APM à grande échelle, il est donc pratique lorsque vous souhaitez le placer rapidement dans un environnement de développement ou de vérification. De plus, il semble plus facile de le faire avec AOP que de le faire soi-même, comme le mécanisme de traçage distribué. Je n'ai pas tellement essayé, mais maintenant je réalise la commodité d'AspectJ ...

Alors bon piratage!

Recommended Posts

Mesurez le goulot d'étranglement! Comment tracer uniquement les méthodes lentes dans AspectJ
Comment obtenir la date avec Java
Comment déboguer le traitement dans le modèle Ruby on Rails avec juste la console
Comment vérifier le journal dans le conteneur Docker
Comment créer la blockchain la plus simple de Ruby
Comment vérifier les commandes Rails dans le terminal
Comment implémenter UICollectionView avec du code uniquement dans Swift
Comment régler l'heure d'affichage sur l'heure japonaise dans les rails
[Java] Comment omettre le constructeur privé dans Lombok
Organisé comment interagir avec le JDK par étapes
Comment spécifier le chemin de la ressource dans l'importation HTML
Comment déboguer le fichier jar généré avec Eclipse
[Rails] Comment afficher les images dans la vue
Comment obtenir le nom d'une classe / méthode exécutée en Java
Comment utiliser la méthode getter / setter (en orientation objet)
Comment séparer les mots dans les noms dans les classes, les méthodes et les variables
Comment régler chrony lorsque le temps change dans CentOS7
Comment connecter les chaînes de la liste séparées par des virgules
Comment créer une partie d'espace réservé à utiliser dans la clause IN
Comment récupérer la valeur de hachage dans un tableau dans Ruby
Mettez en majuscule uniquement la plage spécifiée avec la sous-chaîne. (Comment utiliser la sous-chaîne)
Comment ajouter les mêmes index dans un tableau imbriqué
Comment se moquer de certaines méthodes de la classe testée
Comment dériver le dernier jour du mois en Java
[Rails] Comment afficher les informations stockées dans la base de données dans la vue
[Rails / Routing] Comment faire référence au contrôleur dans votre propre répertoire
[jOOQ] Comment utiliser CASE WHEN dans la clause WHERE / AND / OR
Comment obtenir l'identifiant de la clé PRIMAY incrémentée automatiquement dans MyBatis
Comment installer le langage utilisé dans Ubuntu et comment créer l'environnement
Comment stocker les informations saisies dans la zone de texte dans une variable de la méthode
Comment résoudre l'erreur inconnue apparue lors de l'utilisation de slf4j en Java
kotlin & Java: Comment masquer la barre d'outils uniquement pour des fragments spécifiques
Comment appeler plusieurs noms à la fois dans la même catégorie
Comment obtenir la longueur d'un fichier audio avec Java
Comment incrémenter la valeur de Map sur une ligne en Java
[Ruby on Rails] Comment se connecter avec seulement votre nom et mot de passe en utilisant le bijou
Comment utiliser la méthode link_to
Comment utiliser Lombok au printemps
Comment trouver May'n dans XPath
Comment utiliser la méthode include?
Comment masquer la barre de défilement dans WebView
Comment utiliser la méthode form_with
Comment exécuter JUnit dans Eclipse
Passer les paramètres régionaux i18n à JavaScript
Comment itérer indéfiniment en Ruby
Comment trouver l'angle moyen
Comment exécuter Ant dans Gradle
Comment maîtriser la programmation en 3 mois
Comment utiliser la classe wrapper
Comment apprendre JAVA en 7 jours
Comment obtenir des paramètres dans Spark
Comment appeler des classes et des méthodes
Comment installer Bootstrap dans Ruby
Comment utiliser InjectorHolder dans OpenAM