vendredi 24 novembre 2023

[Java 17] Améliorer la conception logicielle avec les sealed classes

 Introduction

Dans le monde de la programmation orientée objet, l’héritage est un concept fondamental qui permet à une classe d’hériter des attributs et des méthodes d’une autre classe. Cependant, le contrôle de l’héritage peut être un défi. C’est là qu’interviennent les classes scellées de Java 17.

Problématique
Dans la conception de logiciels, il est courant de vouloir limiter l’héritage à un ensemble spécifique de classes. Par exemple, vous pouvez avoir une classe OrderState et vouloir que seules certaines classes spécifiques, comme NewOrder, ProcessedOrder, ShippedOrder et DeliveredOrder, puissent en hériter. Sans les classes scellées, n’importe quelle classe pourrait hériter de votre classe, ce qui pourrait conduire à une utilisation non désirée ou imprévue de votre classe.

Solution
Les sealed  classes de Java 17 offrent une solution à ce problème. Une classe scellée est une classe ou une interface qui restreint les autres classes ou interfaces qui peuvent l’étendre. Cela vous permet de contrôler précisément quels états sont possibles pour une commande et d’associer des comportements spécifiques à chaque état. De plus, cela permet au compilateur de vérifier que vous gérez tous les états possibles dans votre code, ce qui peut aider à prévenir les erreurs.

Exemple d’implémentation
Pour illustrer l’utilisation des classes scellées dans la gestion de l’état d’une commande, considérons l’exemple suivant :

public sealed interface OrderState permits NewOrder, ProcessedOrder, ShippedOrder, DeliveredOrder {
    void handle();
}

public final class NewOrder implements OrderState {
    @Override
    public void handle() {
        System.out.println("Handling a new order.");
    }
}

public final class ProcessedOrder implements OrderState {
    @Override
    public void handle() {
        System.out.println("Handling a processed order.");
    }
}

public final class ShippedOrder implements OrderState {
    @Override
    public void handle() {
        System.out.println("Handling a shipped order.");
    }
}

public final class DeliveredOrder implements OrderState {
    @Override
    public void handle() {
        System.out.println("Handling a delivered order.");
    }
}

Dans cet exemple, OrderState est une interface scellée qui peut être implémentée uniquement par les classes NewOrder, ProcessedOrder, ShippedOrder et DeliveredOrder. Chaque classe représente un état différent d’une commande et peut avoir des comportements spécifiques. Par exemple, pour une NewOrder, la méthode handle() pourrait créer une nouvelle entrée dans une base de données, tandis que pour une DeliveredOrder, elle pourrait envoyer un e-mail de confirmation au client.

Discussion
L’utilisation des sealed  classes peut améliorer la sécurité, l’exhaustivité et les performances de votre code. Cependant, elles sont une fonctionnalité relativement récente de Java, introduite en aperçu dans Java 15 et stabilisée dans Java 17. Il est donc possible que de nombreux frameworks et bibliothèques n’aient pas encore adopté cette fonctionnalité. Néanmoins, les sealed  classes offrent un moyen puissant de contrôler l’héritage et peuvent être un outil précieux pour améliorer la conception de vos logiciels.

mercredi 22 novembre 2023

Optimisation du pool de threads dans une application Spring Boot 3.1.1 avec Tomcat

 

Introduction

Les applications Spring Boot sont souvent déployées avec un serveur intégré, comme Tomcat, qui suit le modèle thread-par-requête. Dans ce modèle, chaque requête est traitée par un thread unique provenant d’un pool de threads. La taille de ce pool de threads peut avoir un impact significatif sur les performances de l’application.

Problématique

La configuration par défaut du pool de threads de Tomcat dans une application Spring Boot peut ne pas être optimale pour toutes les situations. Par exemple, la taille par défaut du pool de threads peut atteindre 200 threads avec 10 threads de travail toujours en cours d’exécution. Bien que ces valeurs soient un bon point de départ pour un environnement de production, elles peuvent ne pas être idéales pour un environnement local où l’optimisation de la consommation des ressources est une priorité.

Solution

Spring Boot permet de personnaliser la configuration du pool de threads de Tomcat à l’aide de propriétés spécifiques. Par exemple, vous pouvez définir le nombre maximum de threads de traitement de requêtes via la propriété server.tomcat.threads.max. De plus, vous pouvez définir le nombre minimum de threads qui doivent toujours être en cours d’exécution via la propriété server.tomcat.threads.min-spare.

server:
  port: 9001
  tomcat:
    connection-timeout: 2s
    keep-alive-timeout: 15s
    threads:
      max: 50
      min-spare: 5

Mise à jour pour Spring Boot 3.1.1

Avec la version 3.1.1 de Spring Boot, la configuration du pool de threads reste la même. Cependant, il est important de noter que la configuration optimale du pool de threads peut varier en fonction des spécificités de votre application et de votre environnement. Il est donc recommandé de surveiller régulièrement les performances de votre application et d’ajuster la configuration du pool de threads en conséquence.

Discussion

Déterminer la meilleure configuration pour un pool de threads est complexe et il n’existe pas de formule magique pour le calculer. Une analyse des ressources, une surveillance et de nombreux essais sont généralement nécessaires pour trouver une configuration appropriée. En ajustant ces valeurs, vous pouvez optimiser la consommation des ressources de votre application Spring Boot tout en maintenant de bonnes performances.

Les limites des threads de plateforme

Par défaut, Spring Boot fonctionne avec un pool de threads de 200 threads. Cette valeur peut être modifiée dans les propriétés de l’application pour dire, 500 ou 1000 ou 2000, si vous ajoutez suffisamment de mémoire à votre machine ou VM ou conteneur.
Cependant, si le nombre d’utilisateurs simultanés dépasse cette limite, c’est-à-dire le nombre de threads, certains utilisateurs subiront un problème de performance car ils devront attendre leur tour.

Utilisation de threads virtuels

Nous pouvons surmonter cette limitation en utilisant des threads virtuels au lieu de threads de plateforme pour servir chaque requête utilisateur dans Spring Boot.
En faisant cela, le nombre de threads virtuels qui peuvent fonctionner en parallèle est théoriquement illimité.

jeudi 16 novembre 2023

Redis pour la mise en cache : Comparaison des techniques Write-Behind, Write-Through et Write-Around

 Introduction
Redis est une base de données en mémoire qui est couramment utilisée pour le cache en raison de sa haute performance et de sa flexibilité. Il existe plusieurs techniques de mise en cache avec Redis, notamment le write-behind, le write-through et le write-around.

Problématique
Lors de l’utilisation de Redis pour la mise en cache, un défi majeur est de maintenir la cohérence des données entre le cache et la base de données principale. De plus, il peut y avoir des problèmes de performance lors de l’écriture de données, en particulier lorsqu’il y a un grand nombre d’opérations d’écriture à haute vitesse.

Solutions

  • Write-behind: Dans cette approche, l’application écrit les données uniquement dans Redis, puis Redis met à jour de manière asynchrone la base de données principale. Cela permet d’améliorer les performances d’écriture car les opérations d’écriture sont mises en file d’attente, permettant à l’application de continuer rapidement pendant que le cache rattrape son retard.
  • Write-through: Dans cette méthode, les données sont synchronisées immédiatement entre le cache et la base de données principale. Cela garantit que les données entre le cache et la base de données principale sont toujours cohérentes.
  • Write-around: Cette technique n’est pas mentionnée dans les résultats de la recherche, donc je ne peux pas fournir d’informations à ce sujet.

Discussion
Chaque technique a ses avantages et ses inconvénients. Le write-behind peut améliorer les performances d’écriture, mais il peut y avoir une courte période d’incohérence des données entre le cache et la base de données principale. D’autre part, le write-through garantit la cohérence des données, mais il peut ne pas être aussi rapide que le write-behind car les données sont synchronisées immédiatement

Le choix de la technique dépend des exigences spécifiques de l’application.

https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/