Einführung in Jedis - die Java Redis Client Library

Einführung in Jedis - die Java Redis Client Library

1. Überblick

Dieser Artikel istan introduction to Jedis, eine Clientbibliothek in Java fürRedis - der beliebte speicherinterne Datenstrukturspeicher, der auch auf der Festplatte bestehen bleiben kann. Es wird von einer Keystore-basierten Datenstruktur gesteuert, um Daten zu speichern, und kann als Datenbank, Cache, Nachrichtenbroker usw. verwendet werden.

Zunächst werden wir erklären, in welchen Situationen Jedis nützlich sind und worum es geht.

In den folgenden Abschnitten werden die verschiedenen Datenstrukturen erläutert und Transaktionen, Pipelining und die Publish / Subscribe-Funktion erläutert. Wir schließen mit Connection Pooling und Redis Cluster.

2. Warum Jedis?

Redis listet die bekanntesten Client-Bibliotheken inofficial siteauf. Es gibt mehrere Alternativen zu Jedis, aber derzeit verdienen nur zwei weitere ihren Empfehlungsstern,lettuce undRedisson.

Diese beiden Clients verfügen über einige einzigartige Funktionen wie Threadsicherheit, transparente Wiederverbindungsbehandlung und eine asynchrone API, die allen Jedis fehlt.

Es ist jedoch klein und erheblich schneller als die beiden anderen. Außerdem ist es die Clientbibliothek der Wahl der Spring Framework-Entwickler, und es hat die größte Community von allen dreien.

3. Maven-Abhängigkeiten

Beginnen wir damit, die einzige Abhängigkeit zu deklarieren, die wir inpom.xmlbenötigen:


    redis.clients
    jedis
    2.8.1

Wenn Sie nach der neuesten Version der Bibliothek suchen, lesen Siethis page.

4. Redis Installation

Sie müssen eine der neuesten Versionen von Redis installieren und starten. Wir haben zur Zeit die neueste stabile Version (3.2.1), aber jede Version nach 3.x sollte in Ordnung sein.

Finden Siehere weitere Informationen zu Redis für Linux und Macintosh, sie haben sehr ähnliche grundlegende Installationsschritte. Windows wird nicht offiziell unterstützt, aber diesesport ist gut gepflegt.

Danach können wir direkt in unseren Java-Code eintauchen und uns mit ihm verbinden:

Jedis jedis = new Jedis();

Der Standardkonstruktor funktioniert einwandfrei, es sei denn, Sie haben den Dienst auf einem nicht standardmäßigen Port oder einem Remotecomputer gestartet. In diesem Fall können Sie ihn korrekt konfigurieren, indem Sie die richtigen Werte als Parameter an den Konstruktor übergeben.

5. Redis Datenstrukturen

Die meisten nativen Operationsbefehle werden unterstützt und haben normalerweise den gleichen Methodennamen.

5.1. Streicher

Zeichenfolgen sind die grundlegendste Art von Redis-Werten. Sie sind hilfreich, wenn Sie einfache Schlüsselwert-Datentypen beibehalten möchten:

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

Die VariablecachedResponse enthält den Wert32,15,223,828. In Verbindung mit der Ablaufunterstützung, die später erläutert wird, kann die Cache-Ebene für HTTP-Anforderungen, die in Ihrer Webanwendung eingehen, und für andere Caching-Anforderungen blitzschnell und einfach verwendet werden.

5.2. Listen

Redis-Listen sind einfache Listen von Zeichenfolgen, die nach Einfügereihenfolge sortiert sind, und eignen sich ideal zum Implementieren von beispielsweise Nachrichtenwarteschlangen:

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

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

Die Variabletask enthält den WertfirstTask. Denken Sie daran, dass Sie jedes Objekt serialisieren und als Zeichenfolge beibehalten können, damit Nachrichten in der Warteschlange bei Bedarf komplexere Daten enthalten können.

5.3. Sets

Redis Sets sind eine ungeordnete Sammlung von Strings, die nützlich sind, wenn Sie wiederholte Mitglieder ausschließen möchten:

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");

Die Java-Mengenicknames hat eine Größe von 2, die zweite Addition vonnickname#1 wurde ignoriert. Außerdem hat die Variableexists den Werttrue. Mit der Methodesismember können Sie schnell überprüfen, ob ein bestimmtes Mitglied vorhanden ist.

5.4. Hashes

Redis Hashes ordnen die Felder vonStringund den Werten vonStringzu:

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");

Wie Sie sehen, sind Hashes ein sehr praktischer Datentyp, wenn Sie einzeln auf die Objekteigenschaften zugreifen möchten, da Sie nicht das gesamte Objekt abrufen müssen.

5.5. Sortierte Sets

Sortierte Mengen sind wie eine Menge, bei der jedem Mitglied eine Rangfolge zugeordnet ist, die zum Sortieren verwendet wird:

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");

Die Variableplayer enthält den WertPlayerThree, da wir den Top-1-Spieler abrufen und er derjenige mit der höchsten Punktzahl ist. Die Variablerank hat den Wert 1, daPlayerOne die zweite in der Rangfolge ist und die Rangfolge auf Null basiert.

6. Transaktionen

Transaktionen garantieren Atomaritäts- und Threadsicherheitsoperationen, was bedeutet, dass Anfragen von anderen Clients bei Redis-Transaktionen niemals gleichzeitig bearbeitet werden:

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();

Sie können einen Transaktionserfolg sogar von einem bestimmten Schlüssel abhängig machen, indem Sie ihn unmittelbar vor dem Instanziieren IhrerTransaction "beobachten":

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

Wenn sich der Wert dieses Schlüssels ändert, bevor die Transaktion ausgeführt wird, wird die Transaktion nicht erfolgreich abgeschlossen.

7. Pipelining

Wenn wir mehrere Befehle senden müssen, können wir diese in einer Anforderung zusammenfassen und durch die Verwendung von Pipelines Verbindungsaufwand sparen. Dies ist im Wesentlichen eine Netzwerkoptimierung. Solange die Operationen voneinander unabhängig sind, können wir diese Technik nutzen:

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();

Beachten Sie, dass wir keinen direkten Zugriff auf die Befehlsantworten erhalten. Stattdessen erhalten wir eineResponse-Instanz, von der wir die zugrunde liegende Antwort anfordern können, nachdem die Pipeline synchronisiert wurde.

8. Publish/Subscribe

Wir können die Redis Messaging Broker-Funktionalität verwenden, um Nachrichten zwischen den verschiedenen Komponenten unseres Systems zu senden. Stellen Sie sicher, dass die Abonnenten- und Herausgeber-Threads nicht dieselbe Jedis-Verbindung verwenden.

8.1. Teilnehmer

Abonnieren und Anhören von Nachrichten, die an einen Kanal gesendet wurden:

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

Abonnieren ist eine Blockierungsmethode. Sie müssen sich explizit vonJedisPubSubabmelden. Wir haben die MethodeonMessageüberschrieben, aber es stehen noch viel mehruseful methodszum Überschreiben zur Verfügung.

8.2. Verleger

Senden Sie dann einfach Nachrichten aus dem Thread des Herausgebers an denselben Kanal:

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

9. Verbindungspooling

Es ist wichtig zu wissen, dass der Umgang mit unserer Jedis-Instanz naiv ist. In einem realen Szenario möchten Sie keine einzelne Instanz in einer Umgebung mit mehreren Threads verwenden, da eine einzelne Instanz nicht threadsicher ist.

Glücklicherweise können wir auf einfache Weise einen Pool von Verbindungen zu Redis erstellen, den wir bei Bedarf wiederverwenden können. Dieser Pool ist threadsicher und zuverlässig, solange Sie die Ressource an den Pool zurückgeben, wenn Sie damit fertig sind.

Erstellen wir dieJedisPool:

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

Da die Poolinstanz threadsicher ist, können Sie sie statisch speichern. Sie sollten jedoch darauf achten, den Pool zu zerstören, um Leckagen beim Herunterfahren der Anwendung zu vermeiden.

Jetzt können wir bei Bedarf von überall in der Anwendung auf unseren Pool zugreifen:

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

Wir haben die Java-Anweisung try-with-resources verwendet, um zu vermeiden, dass die Jedis-Ressource manuell geschlossen werden muss. Wenn Sie diese Anweisung jedoch nicht verwenden können, können Sie die Ressource auch manuell in der Klauselfinally schließen.

Stellen Sie sicher, dass Sie einen Pool verwenden, wie wir ihn in Ihrer Anwendung beschrieben haben, wenn Sie nicht mit unangenehmen Multithreading-Problemen konfrontiert werden möchten. Sie können natürlich mit den Pool-Konfigurationsparametern spielen, um sie an das beste Setup in Ihrem System anzupassen.

10. Redis Cluster

Diese Redis-Implementierung bietet einfache Skalierbarkeit und hohe Verfügbarkeit. Wir empfehlen Ihnen, derenofficial specificationzu lesen, wenn Sie nicht damit vertraut sind. Wir werden uns nicht mit der Einrichtung von Redis-Clustern befassen, da dies etwas außerhalb des Geltungsbereichs dieses Artikels liegt. Sie sollten jedoch keine Probleme damit haben, wenn Sie mit der Dokumentation fertig sind.

Sobald wir das fertig haben, können wir es in unserer Anwendung verwenden:

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

Wir müssen nur die Host- und Port-Details von einer unserer Master-Instanzen bereitstellen. Die übrigen Instanzen im Cluster werden automatisch erkannt.

Dies ist sicherlich ein sehr mächtiges Feature, aber es ist keine Wunderwaffe. Wenn Sie Redis Cluster verwenden, können Sie weder Transaktionen ausführen noch Pipelines verwenden, zwei wichtige Funktionen, auf die sich viele Anwendungen stützen, um die Datenintegrität zu gewährleisten.

Transaktionen sind deaktiviert, da in einer Clusterumgebung Schlüssel über mehrere Instanzen hinweg beibehalten werden. Für Operationen, bei denen der Befehl in verschiedenen Instanzen ausgeführt wird, können die Atomizität der Operation und die Thread-Sicherheit nicht garantiert werden.

Einige fortschrittliche Strategien zur Schlüsselerstellung stellen sicher, dass Daten, die für Sie interessant sind, in derselben Instanz beibehalten werden. Theoretisch sollte dies Ihnen ermöglichen, Transaktionen mit einer der zugrunde liegenden Jedis-Instanzen des Redis-Clusters erfolgreich durchzuführen.

Leider können Sie derzeit nicht herausfinden, in welcher Redis-Instanz ein bestimmter Schlüssel mit Jedis gespeichert ist (was tatsächlich von Redis nativ unterstützt wird), sodass Sie nicht wissen, welche der Instanzen Sie die Transaktionsoperation ausführen müssen. Wenn Sie daran interessiert sind, finden Sie weitere Informationenhere.

11. Fazit

Die allermeisten Funktionen von Redis sind bereits in Jedis verfügbar, und die Entwicklung schreitet zügig voran.

Es gibt Ihnen die Möglichkeit, eine leistungsstarke In-Memory-Speicher-Engine mit sehr wenig Aufwand in Ihre Anwendung zu integrieren. Vergessen Sie jedoch nicht, das Verbindungspooling einzurichten, um Thread-Sicherheitsprobleme zu vermeiden.

Codebeispiele finden Sie inGitHub project.