Introduction à Jedis - la bibliothèque cliente Redis de Java

Introduction à Jedis - la bibliothèque client Java Redis

1. Vue d'ensemble

Cet article estan introduction to Jedis, une bibliothèque client en Java pourRedis - le magasin de structures de données en mémoire populaire qui peut également persister sur le disque. Il est géré par une structure de données basée sur le magasin de clés pour conserver les données et peut être utilisé comme base de données, cache, courtier de messages, etc.

Premièrement, nous allons expliquer dans quel type de situations Jedis est utile et en quoi cela consiste.

Dans les sections suivantes, nous développons les différentes structures de données et expliquons les transactions, le traitement en pipeline et la fonctionnalité de publication / abonnement. Nous concluons avec le pooling de connexion et Redis Cluster.

2. Pourquoi Jedis?

Redis répertorie les bibliothèques clientes les plus connues sur leursofficial site. Il existe plusieurs alternatives à Jedis, mais seulement deux autres méritent actuellement leur étoile de recommandation,lettuce etRedisson.

Ces deux clients possèdent des fonctionnalités uniques telles que la sécurité des threads, la gestion transparente de la reconnexion et une API asynchrone, des fonctionnalités qui manquent à Jedis.

Cependant, il est petit et considérablement plus rapide que les deux autres. En outre, il s'agit de la bibliothèque client de choix des développeurs Spring Framework et de la plus grande communauté des trois.

3. Dépendances Maven

Commençons par déclarer la seule dépendance dont nous aurons besoin dans lespom.xml:


    redis.clients
    jedis
    2.8.1

Si vous recherchez la dernière version de la bibliothèque, consultezthis page.

4. Installation de Redis

Vous devrez installer et lancer une des dernières versions de Redis. Nous utilisons actuellement la dernière version stable (3.2.1), mais toute version ultérieure à la version 3.x devrait convenir.

Trouvezhere plus d'informations sur Redis pour Linux et Macintosh, ils ont des étapes d'installation de base très similaires. Windows n'est pas officiellement pris en charge, mais ceport est bien entretenu.

Après cela, nous pouvons directement plonger et nous connecter à partir de notre code Java:

Jedis jedis = new Jedis();

Le constructeur par défaut fonctionnera correctement sauf si vous avez démarré le service sur un port autre que celui par défaut ou sur une machine distante. Dans ce cas, vous pouvez le configurer correctement en transmettant les valeurs correctes en tant que paramètres au constructeur.

5. Structures de données Redis

La plupart des commandes d'opération natives sont prises en charge et, de manière pratique, elles partagent normalement le même nom de méthode.

5.1. Les cordes

Les chaînes sont le type le plus fondamental de valeur Redis, utile lorsque vous devez conserver des types de données clé-valeur simples:

jedis.set("events/city/rome", "32,15,223,828");
String cachedResponse = jedis.get("events/city/rome");

La variablecachedResponse contiendra la valeur32,15,223,828. Couplé à la prise en charge de l'expiration, dont nous parlerons plus loin, il peut fonctionner comme une couche de cache rapide et simple à utiliser pour les demandes HTTP reçues sur votre application Web et pour d'autres exigences de mise en cache.

5.2. Listes

Les listes Redis sont simplement des listes de chaînes, triées par ordre d'insertion et en font un outil idéal pour implémenter, par exemple, les files d'attente de messages:

jedis.lpush("queue#tasks", "firstTask");
jedis.lpush("queue#tasks", "secondTask");

String task = jedis.rpop("queue#tasks");

La variabletask contiendra la valeurfirstTask. N'oubliez pas que vous pouvez sérialiser n'importe quel objet et le conserver sous forme de chaîne, afin que les messages de la file d'attente puissent transporter des données plus complexes en cas de besoin.

5.3. Sets

Les ensembles Redis sont une collection non ordonnée de chaînes qui s'avèrent utiles lorsque vous souhaitez exclure des membres répétés:

jedis.sadd("nicknames", "nickname#1");
jedis.sadd("nicknames", "nickname#2");
jedis.sadd("nicknames", "nickname#1");

Set nicknames = jedis.smembers("nicknames");
boolean exists = jedis.sismember("nicknames", "nickname#1");

L'ensemble Javanicknames aura une taille de 2, le deuxième ajout denickname#1 a été ignoré. De plus, la variableexists aura une valeur detrue, la méthodesismember vous permet de vérifier rapidement l'existence d'un membre particulier.

5.4. Des hachis

Les hachages Redis mappent entre les champsString et les valeursString:

jedis.hset("user#1", "name", "Peter");
jedis.hset("user#1", "job", "politician");

String name = jedis.hget("user#1", "name");

Map fields = jedis.hgetAll("user#1");
String job = fields.get("job");

Comme vous pouvez le constater, les hachages sont un type de données très pratique lorsque vous souhaitez accéder individuellement aux propriétés d’un objet, car vous n’avez pas besoin de récupérer l’ensemble de l’objet.

5.5. Ensembles triés

Les ensembles triés ressemblent à un ensemble où chaque membre est associé à un classement utilisé pour le tri:

Map scores = new HashMap<>();

scores.put("PlayerOne", 3000.0);
scores.put("PlayerTwo", 1500.0);
scores.put("PlayerThree", 8200.0);

scores.entrySet().forEach(playerScore -> {
    jedis.zadd(key, playerScore.getValue(), playerScore.getKey());
});

String player = jedis.zrevrange("ranking", 0, 1).iterator().next();
long rank = jedis.zrevrank("ranking", "PlayerOne");

La variableplayer contiendra la valeurPlayerThree car nous récupérons le premier joueur et il est celui avec le score le plus élevé. La variablerank aura une valeur de 1 carPlayerOne est la deuxième du classement et le classement est basé sur zéro.

6. Transactions

Les transactions garantissent des opérations d'atomicité et de sécurité des threads, ce qui signifie que les demandes d'autres clients ne seront jamais traitées simultanément pendant les transactions Redis:

String friendsPrefix = "friends#";
String userOneId = "4352523";
String userTwoId = "5552321";

Transaction t = jedis.multi();
t.sadd(friendsPrefix + userOneId, userTwoId);
t.sadd(friendsPrefix + userTwoId, userOneId);
t.exec();

Vous pouvez même faire dépendre le succès d'une transaction d'une clé spécifique en la «regardant» juste avant d'instancier vosTransaction:

jedis.watch("friends#deleted#" + userOneId);

Si la valeur de cette clé change avant que la transaction ne soit exécutée, la transaction ne sera pas terminée avec succès.

7. Pipelining

Lorsque nous devons envoyer plusieurs commandes, nous pouvons les regrouper en une seule demande et économiser le temps système nécessaire à la connexion en utilisant des pipelines. Il s’agit essentiellement d’une optimisation du réseau. Tant que les opérations sont mutuellement indépendantes, nous pouvons tirer parti de cette technique:

String userOneId = "4352523";
String userTwoId = "4849888";

Pipeline p = jedis.pipelined();
p.sadd("searched#" + userOneId, "paris");
p.zadd("ranking", 126, userOneId);
p.zadd("ranking", 325, userTwoId);
Response pipeExists = p.sismember("searched#" + userOneId, "paris");
Response> pipeRanking = p.zrange("ranking", 0, -1);
p.sync();

String exists = pipeExists.get();
Set ranking = pipeRanking.get();

Notez que nous n'obtenons pas un accès direct aux réponses de commande, mais plutôt une instanceResponse à partir de laquelle nous pouvons demander la réponse sous-jacente une fois le pipeline synchronisé.

8. Publish/Subscribe

Nous pouvons utiliser la fonctionnalité de courtier de messagerie Redis pour envoyer des messages entre les différents composants de notre système. Assurez-vous que les threads abonné et éditeur ne partagent pas la même connexion Jedis.

8.1. Abonné

Abonnez-vous et écoutez les messages envoyés à une chaîne:

Jedis jSubscriber = new Jedis();
jSubscriber.subscribe(new JedisPubSub() {
    @Override
    public void onMessage(String channel, String message) {
        // handle message
    }
}, "channel");

S'abonner est une méthode de blocage, vous devrez vous désabonner explicitement desJedisPubSub. Nous avons remplacé la méthodeonMessage mais il y a beaucoup plus deuseful methods disponibles à remplacer.

8.2. Éditeur

Ensuite, envoyez simplement des messages à cette même chaîne à partir du fil de discussion de l'éditeur:

Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");

9. Mise en commun des connexions

Il est important de savoir que la façon dont nous avons traité notre cas Jedis est naïve. Dans un scénario réel, vous ne souhaitez pas utiliser une seule instance dans un environnement multi-thread, car une instance unique n'est pas thread-safe.

Heureusement, nous pouvons facilement créer un pool de connexions vers Redis que nous pourrons réutiliser à la demande, un pool sécurisé sur le plan thread et fiable tant que vous renvoyez la ressource au pool lorsque vous avez terminé.

Créons lesJedisPool:

final JedisPoolConfig poolConfig = buildPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "localhost");

private JedisPoolConfig buildPoolConfig() {
    final JedisPoolConfig poolConfig = new JedisPoolConfig();
    poolConfig.setMaxTotal(128);
    poolConfig.setMaxIdle(128);
    poolConfig.setMinIdle(16);
    poolConfig.setTestOnBorrow(true);
    poolConfig.setTestOnReturn(true);
    poolConfig.setTestWhileIdle(true);
    poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(60).toMillis());
    poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(30).toMillis());
    poolConfig.setNumTestsPerEvictionRun(3);
    poolConfig.setBlockWhenExhausted(true);
    return poolConfig;
}

Étant donné que l'instance de pool est thread-safe, vous pouvez la stocker de manière statique, mais vous devez veiller à la détruire pour éviter les fuites lors de l'arrêt de l'application.

Maintenant, nous pouvons utiliser notre piscine de n'importe où dans l'application en cas de besoin:

try (Jedis jedis = jedisPool.getResource()) {
    // do operations with jedis resource
}

Nous avons utilisé l'instruction Java try-with-resources pour éviter d'avoir à fermer manuellement la ressource Jedis, mais si vous ne pouvez pas utiliser cette instruction, vous pouvez également fermer la ressource manuellement dans la clausefinally.

Assurez-vous que vous utilisez un pool tel que décrit dans votre application si vous ne voulez pas faire face à de vilains problèmes multi-threading. Vous pouvez évidemment jouer avec les paramètres de configuration du pool pour l’adapter à la meilleure configuration de votre système.

10. Cluster Redis

Cette implémentation Redis offre une évolutivité facile et une haute disponibilité, nous vous encourageons à lire leursofficial specification si vous ne les connaissez pas. Nous ne couvrirons pas la configuration du cluster Redis car cela est un peu en dehors de la portée de cet article, mais vous ne devriez pas rencontrer de problèmes pour le faire lorsque vous avez terminé avec sa documentation.

Une fois que cela est prêt, nous pouvons commencer à l'utiliser à partir de notre application:

try (JedisCluster jedisCluster = new JedisCluster(new HostAndPort("localhost", 6379))) {
    // use the jedisCluster resource as if it was a normal Jedis resource
} catch (IOException e) {}

Nous n'avons besoin que de fournir les détails de l'hôte et du port à partir de l'une de nos instances principales, elle détectera automatiquement le reste des instances du cluster.

C'est certainement une fonctionnalité très puissante, mais ce n'est pas une solution miracle. Lorsque vous utilisez Redis Cluster, vous ne pouvez pas effectuer de transactions ni utiliser de pipelines, deux fonctionnalités importantes sur lesquelles de nombreuses applications s'appuient pour garantir l'intégrité des données.

Les transactions sont désactivées car, dans un environnement en cluster, les clés seront conservées dans plusieurs instances. Atomicity et sécurité des threads ne peuvent pas être garanties pour les opérations impliquant l'exécution de commandes dans différentes instances.

Certaines stratégies de création de clés avancées garantissent que les données qu'il est intéressant de conserver dans la même instance seront conservées de cette manière. En théorie, cela devrait vous permettre d’effectuer des transactions en utilisant l’une des instances Jedis sous-jacentes du cluster Redis.

Malheureusement, vous ne pouvez actuellement pas savoir dans quelle instance Redis une clé particulière est enregistrée à l'aide de Jedis (qui est actuellement prise en charge de manière native par Redis). Par conséquent, vous ne savez pas laquelle des instances vous devez effectuer l'opération de transaction. Si cela vous intéresse, vous pouvez trouver plus d'informationshere.

11. Conclusion

La grande majorité des fonctionnalités de Redis sont déjà disponibles dans Jedis et son développement avance à un bon rythme.

Il vous donne la possibilité d'intégrer un puissant moteur de stockage en mémoire dans votre application en toute simplicité, sans oublier de configurer le pooling de connexions pour éviter les problèmes de sécurité des threads.

Vous pouvez trouver des exemples de code dans lesGitHub project.