Pengenalan kepada Jedis - Perpustakaan Pelanggan Java Redis

1. Gambaran keseluruhan

Artikel ini adalah pengenalan kepada Jedis , pustaka pelanggan di Java untuk Redis - kedai struktur data dalam memori yang popular yang dapat bertahan dalam cakera juga. Ia didorong oleh struktur data berasaskan kunci untuk menyimpan data dan dapat digunakan sebagai pangkalan data, cache, broker mesej, dll.

Pertama, kita akan menerangkan situasi seperti mana yang berguna bagi Jedis dan bagaimana keadaannya.

Pada bahagian berikutnya, kami menguraikan pelbagai struktur data dan menerangkan urus niaga, pipelining dan ciri penerbitan / langganan. Kami menyimpulkan dengan penyatuan sambungan dan Redis Cluster.

2. Mengapa Jedis?

Redis menyenaraikan perpustakaan pelanggan yang paling terkenal di laman rasmi mereka. Terdapat banyak alternatif untuk Jedis, tetapi hanya dua lagi yang layak untuk bintang cadangan mereka, selada, dan Redisson.

Kedua-dua pelanggan ini mempunyai beberapa ciri unik seperti keselamatan thread, pengendalian penyambungan semula yang telus dan API tak segerak, semua ciri yang tidak dimiliki Jedis.

Walau bagaimanapun, ia kecil dan jauh lebih cepat daripada dua yang lain. Selain itu, ia adalah perpustakaan pelanggan pilihan pemaju Spring Framework, dan ia mempunyai komuniti terbesar dari ketiga-tiganya.

3. Pergantungan Maven

Mari mulakan dengan menyatakan satu-satunya pergantungan yang kita perlukan dalam pom.xml :

 redis.clients jedis 2.8.1  

Sekiranya anda mencari versi terbaru perpustakaan, lihat halaman ini.

4. Pemasangan semula

Anda perlu memasang dan menghidupkan salah satu versi Redis terkini. Kami sedang menjalankan versi stabil terkini pada masa ini (3.2.1), tetapi versi post 3.x mana-mana sahaja yang baik.

Dapatkan maklumat lebih lanjut mengenai Redis untuk Linux dan Macintosh di sini, mereka mempunyai langkah pemasangan asas yang serupa. Windows tidak disokong secara rasmi, tetapi port ini dijaga dengan baik.

Selepas itu kita boleh langsung masuk dan menyambung dari kod Java kita:

Jedis jedis = new Jedis();

Pembina lalai akan berfungsi dengan baik melainkan anda telah memulakan perkhidmatan di port bukan lalai atau mesin jarak jauh, dalam hal ini anda dapat mengkonfigurasinya dengan betul dengan meneruskan nilai yang betul sebagai parameter ke dalam konstruktor.

5. Rediskan Struktur Data

Sebilangan besar arahan operasi asli disokong dan, cukup mudah, mereka biasanya berkongsi nama kaedah yang sama.

5.1. Rentetan

String adalah jenis nilai Redis yang paling asas, berguna apabila anda perlu mengekalkan jenis data kunci-nilai sederhana:

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

Pemboleh ubah cachedResponse akan menyimpan nilai 32,15,223,828 . Ditambah dengan dukungan kadaluarsa, dibahas kemudian, ia dapat berfungsi sebagai lapisan cache yang cepat dan mudah digunakan untuk permintaan HTTP yang diterima di aplikasi web anda dan keperluan cache lain.

5.2. Senarai

Daftar Redis hanyalah senarai rentetan, disusun mengikut susunan penyisipan dan menjadikannya alat yang ideal untuk melaksanakan, misalnya, antrian pesanan:

jedis.lpush("queue#tasks", "firstTask"); jedis.lpush("queue#tasks", "secondTask"); String task = jedis.rpop("queue#tasks");

Tugas pemboleh ubah akan menyimpan nilai FirstTask . Ingatlah bahawa anda dapat membuat siri objek apa pun dan meneruskannya sebagai rentetan, sehingga pesan dalam barisan dapat membawa data yang lebih kompleks bila diperlukan.

5.3. Set

Set Redis adalah koleksi Strings yang tidak tersusun yang sangat berguna apabila anda ingin mengecualikan ahli berulang:

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

Nama panggilan Java Set akan berukuran 2, penambahan kedua nama panggilan # 1 diabaikan. Selain itu, wujud pembolehubah akan mempunyai nilai benar , kaedah sismember membolehkan anda untuk memeriksa kewujudan ahli tertentu dengan cepat.

5.4. Labuh

Redis Hash adalah pemetaan antara String bidang dan String nilai:

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

Seperti yang anda lihat, hash adalah jenis data yang sangat mudah apabila anda ingin mengakses sifat objek secara individu kerana anda tidak perlu mengambil keseluruhan objek.

5.5. Set yang disusun

Kumpulan Diurut adalah seperti Set di mana setiap anggota mempunyai kedudukan yang berkaitan, yang digunakan untuk menyusunnya:

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

Pemain berubah-ubah akan memegang nilai PlayerThree kerana kami mendapatkan pemain 1 teratas dan dia adalah orang dengan skor tertinggi. The kedudukan berubah-ubah akan mempunyai nilai 1 kerana PlayerOne adalah yang kedua dalam ranking dan kedudukan adalah berasaskan sifar.

6. Urus Niaga

Urus niaga menjamin operasi keselamatan dan keselamatan atom, yang bermaksud bahawa permintaan dari pelanggan lain tidak akan ditangani secara serentak semasa transaksi 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();

Anda bahkan boleh membuat kejayaan transaksi bergantung pada kunci tertentu dengan "menonton" tepat sebelum anda membuat Transaksi :

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

If the value of that key changes before the transaction is executed, the transaction will not be completed successfully.

7. Pipelining

When we have to send multiple commands, we can pack them together in one request and save connection overhead by using pipelines, it is essentially a network optimization. As long as the operations are mutually independent, we can take advantage of this 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();
    

Notice we do not get direct access to the command responses, instead, we're given a Response instance from which we can request the underlying response after the pipeline has been synced.

8. Publish/Subscribe

We can use the Redis messaging broker functionality to send messages between the different components of our system. Make sure the subscriber and publisher threads do not share the same Jedis connection.

8.1. Subscriber

Subscribe and listen to messages sent to a channel:

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

Subscribe is a blocking method, you will need to unsubscribe from the JedisPubSub explicitly. We have overridden the onMessage method but there are many more useful methods available to override.

8.2. Publisher

Then simply send messages to that same channel from the publisher's thread:

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

9. Connection Pooling

It is important to know that the way we have been dealing with our Jedis instance is naive. In a real-world scenario, you do not want to use a single instance in a multi-threaded environment as a single instance is not thread-safe.

Luckily enough we can easily create a pool of connections to Redis for us to reuse on demand, a pool that is thread safe and reliable as long as you return the resource to the pool when you are done with it.

Let's create the JedisPool:

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

Since the pool instance is thread safe, you can store it somewhere statically but you should take care of destroying the pool to avoid leaks when the application is being shutdown.

Now we can make use of our pool from anywhere in the application when needed:

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

We used the Java try-with-resources statement to avoid having to manually close the Jedis resource, but if you cannot use this statement you can also close the resource manually in the finally clause.

Make sure you use a pool like we have described in your application if you do not want to face nasty multi-threading issues. You can obviously play with the pool configuration parameters to adapt it to the best setup in your system.

10. Redis Cluster

This Redis implementation provides easy scalability and high availability, we encourage you to read their official specification if you are not familiar with it. We will not cover Redis cluster setup since that is a bit out of the scope for this article, but you should have no problems in doing so when you are done with its documentation.

Once we have that ready, we can start using it from our 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) {}

We only need to provide the host and port details from one of our master instances, it will auto-discover the rest of the instances in the cluster.

This is certainly a very powerful feature but it is not a silver bullet. When using Redis Cluster you cannot perform transactions nor use pipelines, two important features on which many applications rely for ensuring data integrity.

Transactions are disabled because, in a clustered environment, keys will be persisted across multiple instances. Operation atomicity and thread safety cannot be guaranteed for operations that involve command execution in different instances.

Some advanced key creation strategies will ensure that data that is interesting for you to be persisted in the same instance will get persisted that way. In theory, that should enable you to perform transactions successfully using one of the underlying Jedis instances of the Redis Cluster.

Unfortunately, currently you cannot find out in which Redis instance a particular key is saved using Jedis (which is actually supported natively by Redis), so you do not know which of the instances you must perform the transaction operation. If you are interested about this, you can find more information here.

11. Conclusion

Sebilangan besar ciri dari Redis sudah tersedia di Jedis dan pengembangannya bergerak maju dengan pantas.

Ini memberi anda kemampuan untuk mengintegrasikan enjin penyimpanan memori yang kuat dalam aplikasi anda dengan sedikit kerumitan, jangan lupa untuk mengatur penyatuan sambungan untuk mengelakkan masalah keselamatan benang.

Anda boleh mendapatkan sampel kod dalam projek GitHub.