[JAVA] J'ai essayé de gérer les informations de connexion avec JMX

C'est un titre comme "J'ai essayé de danser", mais c'est un article sérieux.

Il y a quelque temps, j'ai fait des recherches sur JMX dans un projet auquel j'ai participé, et j'ai pensé que si j'utilisais cela, je n'aurais pas à créer un écran de configuration pour des paramètres simples. Je pense qu'il est difficile de le recommander aux clients, mais j'ai pensé qu'il serait possible de réduire le nombre d'étapes nécessaires pour créer un écran de configuration pour les produits internes, j'ai donc créé une application capable de gérer facilement les informations de connexion et j'ai examiné la convivialité.

environnement

Cette fois, c'est une fonction simple, donc je n'utilise pas DB. J'ai utilisé Spring pour créer une application Web simple qui se connecte simplement. Les bibliothèques utilisées sont les suivantes.

Spécifications de gestion des connexions

Je décrirai brièvement les spécifications de la gestion des connexions.

Cela suffit pour la fonction de gestion des connexions. C'est ennuyeux d'implémenter trop de fonctionnalités.

À propos de la mise en œuvre côté serveur

J'ai créé une application Web simple qui se connecte simplement. L'implémentation est une application Web très courante utilisant Spring MVC. Il est implémenté pour que les informations de connexion puissent être visualisées sur JMX.

Créer une interface MBean

J'ai créé un MBean pour gérer les informations de connexion. Commencez par créer une interface.

public interface LoginMonitorMBean {

    public static final String NAME = "examples.jmx:type=LoginMonitoring";

    public static final String LUI_ITEM_ID = "id";

    public static final String LUI_ITEM_NAME = "name";

    public int getLoginCount();

    public CompositeData[] getLoginInfos();

    void addLoginInfo(CompositeData loginUserInfo);

    public void removeLoginInfo(int id);

    public void resetLoginInfo();

    public int getMaxLoginCount();

    public void setMaxLoginCount(int count);

    public int[] getLoginLockIds();

    public void addLoginLockId(int id);

    public void removeLoginLockId(int id);

    public void resetLoginLockId();

    public static ObjectName createObjectName() {
        try {
            return new ObjectName(LoginMonitorMBean.NAME);
        } catch (MalformedObjectNameException e) {
            throw new IllegalArgumentException(e);
        }
    }

}

Il semble que MBean doit être ajouté à la fin du nom de l'interface. Ignorez les constantes et les méthodes statiques. C'est un groupe de méthodes que d'autres méthodes peuvent être utilisées à partir de jconsole. Une description de chaque méthode est donnée ci-dessous.

Nom de la méthode Explication
getLoginCount Vous pouvez obtenir le nombre de connexions.
getLoginInfos Vous pouvez obtenir l'ID et le nom de l'utilisateur connecté.
addLoginInfo Vous pouvez ajouter des informations de connexion.
removeLoginInfo Supprimez les informations de connexion de l'ID spécifié.
resetLoginInfo Réinitialisez vos informations de connexion.
getMaxLoginCount Vous pouvez obtenir le nombre maximum de connexions.
setMaxLoginCount Vous pouvez définir le nombre maximum de connexions.
getLoginLockIds Vous pouvez obtenir un tableau d'identifiants verrouillés.
addLoginLockId Vous pouvez ajouter un identifiant pour verrouiller.
removeLoginLockId Supprimez l'ID verrouillé.
resetLoginLockId Réinitialisez l'ID verrouillé.

Créer une classe de management

Créez une classe qui implémente l'interface créée précédemment.

public class LoginMonitor implements LoginMonitorMBean {
}

Une erreur se produit sauf si la classe de gestion est un nom de classe qui exclut les MBeans du nom de l'interface. Les détails d'implémentation sont omis, mais il s'agit essentiellement d'une implémentation simple qui contient et obtient les informations de connexion, l'ID de verrouillage, etc. dans le champ de classe.

Enregistrement MBean

Enregistrez le MBean contenant les informations de connexion. L'enregistrement est effectué au démarrage de l'application Web.

public class StartupBean {

    private static Logger log = LoggerFactory.getLogger(StartupBean.class);

    @PostConstruct
    public void initAfterStartup() {
        try {
            log.info("Processus d'enregistrement MBean");
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            mbs.registerMBean(new LoginMonitor(), LoginMonitorMBean.createObjectName());
        } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
            throw new IllegalStateException(e);
        }
    }

}

À propos de la classe qui contient les informations de connexion

Le contenu des données ne peut pas être visualisé à partir de jconsole dans les classes de données (classes de type DTO) qui implémentent le setter / getter général. Utilisez javax.management.openmbean.CompositeData pour le rendre visible depuis jconsole etc.

Regardons un exemple de conservation des informations de connexion.

public class LoginUserInfo {

    private static final String ID = LoginMonitorMBean.LUI_ITEM_ID;

    private static final String NAME = LoginMonitorMBean.LUI_ITEM_NAME;

    private final int id;

    private final String name;

    public LoginUserInfo(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void addMBean(LoginMonitorMBean mbean) throws OpenDataException {
        CompositeType compositeType = new CompositeType(
                "LoginUserInfo",
                "Type de données contenant les informations de connexion de l'utilisateur",
                new String[] { ID, NAME },
                new String[] { "ID utilisateur de connexion", "Nom d'utilisateur connexion" },
                new OpenType[] { SimpleType.INTEGER, SimpleType.STRING });
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(ID, id);
        dataMap.put(NAME, name);
        mbean.addLoginInfo(new CompositeDataSupport(compositeType, dataMap));
    }

    public static int getMBeanId(CompositeData data) {
        return (Integer) data.get(ID);
    }

}

Voici un exemple où la méthode addMBean enregistre les données de javax.management.openmbean.CompositeData. Ce LoginUserInfo est une classe de données simple juste pour contenir les identifiants de connexion communs et les noms d'utilisateur de connexion. L'ID utilisé lors de la connexion et le nom d'utilisateur obtenu à partir de la base de données etc. (valeur fixe car il s'agit d'une application simple) sont conservés. Les informations de cette classe sont converties en javax.management.openmbean.CompositeDataSupport, qui est une classe d'implémentation de l'interface javax.management.openmbean.CompositeData. En faisant cela, vous pouvez naviguer à partir de jconsole comme suit.

image.png

À propos de la fonction de connexion

La fonction de connexion est simple. Après la connexion, le MBean géré par JMX est retiré, la limite de connexion et l'ID de verrouillage sont vérifiés, et s'il n'y a pas d'erreur, les informations de connexion sont enregistrées dans le MBean. Le «LoginController» implémenté est le suivant.

@Controller
public class LoginController {

    @PostMapping("/login")
    public String login(@Validated @ModelAttribute LoginForm form, BindingResult result, Model model) {
        //Vérification des erreurs d'entrée
        if (result.hasErrors()) {
            model.addAttribute("validationError", "Erreur d'entrée");
            return "login";
        }

        //Vérification de connexion
        if (100 > form.getLoginId() && 300 < form.getLoginId()) {
            model.addAttribute("validationError", "Erreur d'identification");
            return "login";
        }
        if (!"testtest".equals(form.getLoginPasswd())) {
            model.addAttribute("validationError", "Erreur d'identification");
            return "login";
        }

        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        LoginMonitorMBean mbean = JMX.newMBeanProxy(mbs, LoginMonitorMBean.createObjectName(), LoginMonitorMBean.class);

        //Vérifiez le nombre de connexions
        if (mbean.getMaxLoginCount() > 0 && mbean.getLoginCount() >= mbean.getMaxLoginCount()) {
            model.addAttribute("validationError", "Limite du nombre de connexion");
            return "login";
        }

        //Vérification du verrouillage d'identité
        if (Arrays.stream(mbean.getLoginLockIds()).filter(lockId -> lockId == form.getLoginId()).findFirst()
                .isPresent()) {
            model.addAttribute("validationError", "ID verrouillé");
            return "login";
        }

        //Enregistrement des informations de connexion
        try {
            LoginUserInfo info = new LoginUserInfo(form.getLoginId(),
                    String.format("Utilisateur test (%d)", form.getLoginId()));
            info.addMBean(mbean);
        } catch (OpenDataException e) {
            e.printStackTrace();
            model.addAttribute("validationError", "Erreur système interne");
            return "login";
        }

        model.addAttribute("loginCount", mbean.getLoginCount());
        return "home";
    }

}

Paramètres de démarrage

Étant donné que cette application Web est Spring Boot, elle peut être démarrée comme suit.

java -jar jmx-examples-1.0.0.war

Afin de vérifier à distance le MBean enregistré, il est nécessaire de définir les paramètres de démarrage comme suit.

java -Dcom.sun.management.jmxremote.port=5000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar jmx-examples-1.0.0.war
Paramètres de démarrage Explication
com.sun.management.jmxremote.port Vous pouvez spécifier le numéro de port du serveur MBean.
com.sun.management.jmxremote.authenticate Cette fois, comme il s'agit d'une fonction simple, la fonction d'authentification lors de l'accès au serveur MBean est désactivée.
com.sun.management.jmxremote.ssl Cette fois, par souci de simplicité, SSL est désactivé lors de l'accès au serveur MBean.

Si vous le démarrez comme ci-dessus, vous pouvez y accéder par un processus distant depuis jconsole etc.

À propos de la mise en œuvre côté client

Il est accessible depuis les processus locaux et distants depuis jconsole, mais j'ai essayé de créer une application cliente qui accède à distance avec l'ID de processus de la même manière.

Reportez-vous à la page suivante pour savoir comment connecter JMX. 2 Monitoring and Management Using JMX Technology

Accès à distance

Pour accéder à distance au serveur MBean, vous devez spécifier les paramètres de démarrage de com.sun.management.jmxremote.port. L'adresse suivante est générée à l'aide du numéro de port spécifié dans le paramètre de démarrage ci-dessus.

service:jmx:rmi:///jndi/rmi://localhost:5000/jmxrmi

Utilisez ensuite l'adresse ci-dessus pour vous connecter au serveur MBean.

String connectAddress = "service:jmx:rmi:///jndi/rmi://localhost:5000/jmxrmi";
try (JMXConnector jmxc = JMXConnectorFactory.connect(new JMXServiceURL(connectAddress))) {
    MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
    LoginMonitorMBean lmbean = JMX.newMBeanProxy(mbsc, LoginMonitorMBean.createObjectName(),
            LoginMonitorMBean.class);
} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

Accès local

Un ID de processus est requis pour l'accès local. Si vous connaissez l'ID de processus, vous pouvez vous connecter, vous n'avez donc pas besoin de spécifier les paramètres de démarrage.

String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";
VirtualMachine vm = VirtualMachine.attach(pid);
String connectorAddress;
try {
    connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
    if (connectorAddress == null) {
        vm.startLocalManagementAgent();
        connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
    }
} finally {
    vm.detach();
}

Puisque la chaîne de caractères pour la connexion peut être obtenue avec la logique ci-dessus, il est possible de se connecter au processus local avec la description suivante décrite même pour une connexion à distance utilisant cette chaîne de caractères.

JMXConnector jmxc = JMXConnectorFactory.connect(new JMXServiceURL(connectAddress))

Informations JVM

En vous connectant au serveur MBean, vous pouvez obtenir les informations JVM en plus du MBean enregistré. Le MBean pour l'acquisition du nom de la machine virtuelle et de l'ID de processus et le MBean pour l'acquisition des informations de segment de mémoire peuvent être acquis avec la description suivante.

try (JMXConnector jmxc = JMXConnectorFactory.connect(new JMXServiceURL(connectAddress))) {
    MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

    //Afficher les informations d'exécution pour les machines virtuelles Java
    RuntimeMXBean rmxbean = ManagementFactory.getPlatformMXBean(mbsc, RuntimeMXBean.class);
    //Obtenir l'ID de processus, etc. du bean rmx

    //Afficher les informations de mémoire
    MemoryMXBean mmxbean = ManagementFactory.getPlatformMXBean(mbsc, MemoryMXBean.class);
    MemoryUsage memoryUsage = mmxbean.getHeapMemoryUsage();
    //Obtenir des informations de tas de mémoire

    //Afficher les informations du système d'exploitation
    OperatingSystemMXBean omxbean = ManagementFactory.getPlatformMXBean(mbsc, OperatingSystemMXBean.class);
    //Obtenir des informations sur le système d'exploitation d'omxbean
} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

Sommaire

Lorsque j'ai essayé d'utiliser JMX, c'était tellement simple et pratique que je n'avais pas besoin de créer un écran de réglage avec une seule zone de texte. Bien sûr, si vous rencontrez des problèmes de performances et des informations de configuration dans la base de données, vous pouvez vous demander si vous pouvez vous connecter à la base de données et combien la charge est, mais vous pouvez facilement modifier les paramètres avec jconsole, donc l'effort de créer l'écran J'ai senti qu'il pouvait être réduit. L'application créée cette fois-ci est stockée dans le référentiel suivant, alors jetez un œil.

jmx-examples

Recommended Posts

J'ai essayé de gérer les informations de connexion avec JMX
J'ai essayé de gérer la configuration des jambes de force avec Coggle
J'ai essayé d'interagir avec Java
J'ai essayé de démarrer avec Web Assembly
J'ai essayé de vérifier AdoptOpenJDK 11 (11.0.2) avec l'image Docker
J'ai essayé de faire une authentification de base avec Java
J'ai essayé de casser le bloc avec java (1)
J'ai essayé ce que je voulais essayer avec Stream doucement.
J'ai essayé d'implémenter le téléchargement de fichiers avec Spring MVC
J'ai essayé de lire et de sortir CSV avec Outsystems
J'ai essayé d'implémenter TCP / IP + BIO avec JAVA
[Java 11] J'ai essayé d'exécuter Java sans compiler avec javac
J'ai démarré MySQL 5.7 avec docker-compose et j'ai essayé de me connecter
J'ai essayé de démarrer avec Spring Data JPA
J'ai essayé de créer une fonction de connexion avec Java
J'ai essayé de dessiner une animation avec l'API Blazor + canvas
J'ai essayé d'implémenter Sterling Sort avec Java Collector
J'ai essayé UPSERT avec PostgreSQL.
J'ai essayé BIND avec Docker
J'ai essayé de vérifier yum-cron
J'ai essayé de créer un environnement de développement java8 avec Chocolatey
J'ai essayé de moderniser une application Java EE avec OpenShift.
J'ai essayé d'augmenter la vitesse de traitement avec l'ingénierie spirituelle
[Rails] J'ai essayé de créer une mini application avec FullCalendar
J'ai essayé de lier le chat avec le serveur de Minecraft avec l'API Discord
[Rails] J'ai essayé d'implémenter le traitement par lots avec la tâche Rake
J'ai essayé de créer un environnement de développement padrino avec Docker
J'ai essayé de démarrer avec Swagger en utilisant Spring Boot
J'ai essayé de pouvoir passer plusieurs objets avec Ractor
J'ai essayé de mâcher C # (indexeur)
J'ai essayé d'utiliser JOOQ avec Gradle
J'ai essayé l'analyse morphologique avec MeCab
J'ai essayé de résumer le support d'iOS 14
J'ai essayé la communication UDP avec Java
J'ai essayé d'expliquer la méthode
J'ai essayé GraphQL avec Spring Boot
J'ai essayé de résumer l'apprentissage Java (1)
J'ai essayé Flyway avec Spring Boot
J'ai essayé de résumer Java 8 maintenant
C # (polymorphisme: polymorphisme)
J'ai essayé de personnaliser Slim avec Scaffold
J'ai essayé d'expliquer Active Hash
J'ai essayé de résoudre le problème de la "sélection multi-étapes" avec Ruby
J'ai essayé de créer un environnement de serveur UML Plant avec Docker
J'ai essayé de me connecter à MySQL en utilisant le modèle JDBC avec Spring MVC
J'ai essayé d'implémenter la fonction de prévisualisation d'image avec Rails / jQuery
J'ai essayé de créer un environnement de développement http2 avec Eclipse + Tomcat
J'ai essayé d'implémenter un mappage OU flexible avec MyBatis Dynamic SQL
J'ai essayé de réimplémenter Ruby's Float (arg, exception: true) avec builtin
J'ai essayé de créer une application Android avec MVC maintenant (Java)
J'ai essayé de vérifier le fonctionnement du serveur gRPC avec grpcurl