Archives par étiquette : redis

Gestion de session avec un cluster redis – suite et fin

suite à une remarque de Remy, j’ai modifié le test gatling pour bien vérifier qu’on récupère la même session lorsque le slave a pris la place du master.

Pour cela, dans le test, je fais une boucle sur la page index qui indique le nom de la personne si elle est logguée. J’utilise les fonctions de gatling de check pour vérifier que l’information est bien présente.

Ce qui donne bien:

gatling

 

error

Pendant le delai d’indisponibilité, on a des erreurs de type 500 et ensuite on repart bien sur la bonne session avec les bonnes données.

L’erreur 500 peut être traitée dans la valve pour gérer un peu ce type d’erreur et rediriger l’utilisateur vers une page adaptée mais ce n’est pas l’objet de cette article.

Gestion de session avec un cluster redis – suite

Maintenant que nous avons une application à tester, nous pouvons utiliser gatling pour simuler de la charge et voir comment se comporte l’application en cas de chute d’un noeud master.

Le script se contente de se logguer, et de rafraîchir 3 fois la page index. Il va injecter 20 users par seconde pendant 5 minutes:

 

J’ai arrêté un noeud master avec la commande supervisor :

Ce qui nous donne:

gatling-session

On peut voir qu’il y a des erreurs pendant près de 10s, le temps que le slave prenne le pas sur le master.

Il faudrait peut être modifier le session-manager pour tenir de ce problème. A suivre…

Gestion de session avec un cluster redis

Voyons comment mettre en pratique les deux articles précédents (ici et ici).

Les choses n’étant pas encore très évolués du cote de spring-data-redis pour le mode cluster, je suis revenu sur la version de session-webapp utilisant une valve tomcat.

Les seules modifications apportées sont la suppression de l’image redis (je vais utiliser la version cluster) et l’utilisation d’une nouvelle image docker de base pour les serveurs Tomcat.

En effet, j’ai modifié la valve pour utiliser la nouvelle classe JedisCluster à la place de Jedis.
Mais pour que cela fonctionne, j’ai du utilisé la version 3.0.0-SNAPSHOT de jedis. La version releasée actuelle n’implémente pas les commandes binary par la lecture/ecriture.

J’ai du également modifié l’image docker pour le cluster redis car par default, le cluster est configuré en 127.0.0.1. Mais quand le client java essaye de découvrir la topologie du cluster, il obtient ces ips, et ne peux pas se connecter aux autres noeuds. La modification permet d’établir la connexion sur l’ip du conteneur.

Il ne reste plus qu’à lancer tout ce petit monde:

redis:

tomcat & haproxy

 

Lorsque je crée une session, je la vois bien sur un noeud redis et sur un autre noeud, je vois bien le message de redirection:

 

 

 

Les sources sont ici:
tomcat-session-manager
webapp
docker-redis-cluster

 

Redis Cluster

Voici le résultat de quelques tests réalisé sur le cluster redis (3.0)

Je suis parti cette image docker https://github.com/Grokzen/docker-redis-cluster

Après un inévitable

l’image est prête à être utilisé. Par défaut, cette image va lancer 3 nœuds masters et 3 nœuds slaves (1 par nœud master).

Lançons la commande

On peut voir que les ports 7000 à 7005 sont remontés sur le système hôte. Ce sera plus facile pour interroger redis directement.

En interrogeant redis, il y a bien 6 nœuds qui constituent le cluster (cluster_known_nodes).

La répartition des clés sautent aux yeux quand on essaye d’insérer une valeur

En lisant ce blog, on voit que la clé de répartition, se calcule comme suit:

Dans notre cas, key=toto ce qui donne hash_slots=9548

Si on inspecte le cluster, on obtient

On peut donc voir que notre valeur est contenu entre 5461 et 10922 et donc est géré par le master 63e73580b843d499b665a7385967268528e836e9 soit le l’instance redis sur le port 7001. Ce qui est bien conforme avec l’information fourni par redis.

Que se passe t’il si on arrête un slave:

En relançant la commande

On voit bien l’erreur de connexion à l’instance sur le port 7005 mais cela n’a pas d’incidence sur la santé du cluster.

Que se passe t’il si on arrête un master.

Le cluster voit bien le problème de connexion à l’instance défaillante.
il voit aussi qu’il y a un slave qui n’a pas de master. A la dernière ligne, on voit qu’il y a un problème de cohérence sur le cluster car tous les slots de répartition ne sont pas couverts par le cluster

Mais cela est corrigé quand le slave devient master:

J’ai modifié le programme Java initié dans le précédent article et je mesure à 6s, le temps pour que le cluster soit de nouveaux opérationnels;

Il est peut être possible de jouer sur le paramétrage pour descendre ce temps, mais je n’ai pas encore trouvé comment.

On voit bien ici le problème lorsque que les données ne soit pas répliquées entre les différents masters. Lorsqu’un master est absent, il y a un « petit » laps de temps ou les données ne sont pas connues.

Redis Cluster en java

En commençant à regarder le mode cluster de redis à travers une image docker déjà construire https://github.com/Grokzen/docker-redis-cluster, je me suis aperçu que ce n’est pas si transparent. En effaçant, si on essaye d’ajouter une valeur mais que la clé ne correspond au bon noeud, redis ne renvoi pas OK mais plutôt l’indication de ou insérer la donnée:

Je me suis demandé comment gérer cela dans un programme Java. Est-ce le driver qui va faire le travail ou il faut le gérer à la main.

Si on utilise le driver standard de Jedis, voici ce qu’on obtiens:

Heureusement, Jedis propose une classe pour résoudre ce problème (Je n’ai rien trouvé coté JRedis).

 

Le driver est même capable de récupérer les autres noeuds. Dans l’exemple j’ai indiqué un seul noeud, mais avec le code suivant :

on voit bien que le driver a pu récupérer les autres noeuds:

les sources sont ici

Quelques liens utiles:
commandes redis cluster

– À la découverte de Redis Cluster 3.0

 

Partage de session avec spring-session

Un collègue m’a fait découvrir spring-session lors d’un échange sur la façon de stocker les sessions dans un tiers.

J’ai donc décidé de reprendre mon poc pour l’adapter à cette librairie.

La mise en place est assez simple. Il faut juste créer 2 classes.

1 pour la configuration (Config) et 1 pour que spring initialise mon application (Initializer). Dans une application existante la dernière classe n’est pas obligatoire. Il suffira de déclarer le bean config dans le fichier de context XML spring.

  • configuration:

Il suffit de placer l’annotation EnableRedisHttpSession et de déclarer un bean pour la connectionFactory. Par defaut JedisConnectionFactory se connecte sur la base redis en localhost. Dans mon cas, j’ai surchargé pour aller sur le host db (container redis)

Il faut supprimer aussi le fichier context.xml qui n’est plus nécessaire.
Il faut passer en servlet 3.0 pour que l’initialisation fonctionne.

et voilà, c’est tout.

On peut tester le tout avec les commandes:

 

  • Changements:

Par rapport au précédent article, le poc a subi quelques modifications pour fonctionner:

– passage à tomcat 8
– inclusion de spring 4
– inclusion de spring web
– inclusion de spring-data
– servlet 3.0

Les sources sont disponibles ici

Merci Raphaël pour l’info.

 

docker – liens entre container suite

Maintenant que nous avons la bonne version de docker, nous pouvons modifier le fichier context.xml de l’application pour pointer sur le hostname de redis :

On va aussi modifier le démarrage du serveur d’applications pour créer un lien vers redis:

tomcat et redis sont maintenant connectée sans avoir besoin de connaître l’ip de chacun.

Et voici le script de démarrage complet :

Nous avons donc réussi à déployer des applications qui sont connectées entre elles, sans connaître les ips de chacune à l’avance. En termes de scalabilité, il devient aisé de rajouter un serveur d’application. Il faut juste démarrer un nouveau conteneur et le linker au haproxy.

Dans un prochain article, nous essayerons de voir comment faire fonctionner un cluster de redis avec docker.

Les sources sont à jour ici: https://github.com/BenoitCharret/session-webapp/tree/article_3

Partage de session avec redis, tomcat et docker

Maintenant que nous avons 2 serveurs tomcat, ça serait bien de pouvoir les arrêter un par un (pour effectuer une livraison par exemples) sans que cela n’ait d’impact pour l’utilisateur. Je pense notamment à la gestion de la session utilisateur. Je vais donc essayer de stocker (sérialiser) la session dans une base nosql. Vu qu’il faut juste stocker un couple clé/valeur, c’est beaucoup moins lourd qu’une base de données.

Préparation du code

Pour commencer, je vais reprendre le travail de James Coleman sur ce sujet https://github.com/jcoleman/tomcat-redis-session-manager. Tout d’abord comme je ne suis pas très à l’aise avec gradle (pour le moment), j’ai commencé par créer un pom.xml pour gérer les dépendances et plus tard créer une image docker. Comme expliqué dans le préambule du repository, la détection de la modification de la session pose des soucis si on modifie directement l’objet après avoir fait un getAttribute()

J’ai donc décidé (pour l’exemple) de forcer l’enregistrement de la session à la fin de chaque requête. Il faudra vérifier avec un test de charge que ça ne pose pas de soucis pour une mise en production. Pour utiliser cette libraire, il faut la builder et l’ajouter au répertoire lib de tomcat (ça ne fonctionne pas si la librairie est embarqué dans le projet. Pour faciliter un peu les choses, maven va créer une librairie qui contiendra ses propres dépendances:

Ensuite, il suffit juste au niveau du projet de modifier le fichier META-INF/context.xml pour ajouter une valve et un manager:

On peut personnaliser la machine et le port de redis ainsi que la durée d’une session (ici j’ai réglé à 3 min). Maintenant que tout est configuré, voyons comment intégré cela dans docker.

Préparation des images

J’ai créé un fichier Dockerfile dans le projet tomcat-redis-session-manager pour créer une nouvelle image se basant sur jolokia/tomcat-6.0 et ajoutant notre librairie dans le répertoire lib.

D’ailleurs, il semble que docker est des soucis avec des répertoires symboliques target. Si je remplace la dernière ligne par

j’obtiens l’erreur suivante (/opt/tomcat étant un lien symbolique vers opt/apache-tomcat-${TOMCAT_VERSION})

J’ai ensuite modifier le fichier Dockerfile du projet session-webapp pour se baser sur l’image qu’on vient de créer:

Il ne reste plus qu’à lancer toutes les images pour voir le résultat

Lancement des images

Je commence par lancer redis:

avec la même commande que dans le dernier article, je récupère son ip (à mettre dans le context.xml) Je lance ensuite un redis-cli pour tester la connectivité

Mon serveur redis est donc opérationnel. Je lance maintenant 2 serveurs tomcat et haproxy. Je me connecte maintenant sur le site, je me loggue et fais des refresh sur la page index.jsp. Cela nous donne dans logs:

Je suis bien resté sur le même serveur à chaque fois et j’ai bien récupéré ma donnée product. Si ne n’avais pas fait la modification dans RedisSessionManager, la valeur de product serait toujours de 1. J’ai ensuite arrêté le serveur tomcat et fais des refresh sur index.jsp. Cela nous donne:

J’ai donc bien récupérer ma session et la valeur de product. Pour voir le contenu des clés de redis, on peut utiliser la commande (a ne pas faire en production):

Voila comment tester facilement la persistance de session avec redis et docker.

Les sources sont ici: