Intro to Jedis - Library Klien Java Redis

1. Ikhtisar

Artikel ini adalah pengantar Jedis , pustaka klien di Java untuk Redis - penyimpanan struktur data dalam memori populer yang juga dapat bertahan di disk. Ini didorong oleh struktur data berbasis keystore untuk menyimpan data dan dapat digunakan sebagai database, cache, perantara pesan, dll.

Pertama, kami akan menjelaskan dalam situasi seperti apa Jedi berguna dan tentang apa Jedi itu.

Pada bagian selanjutnya kami menguraikan berbagai struktur data dan menjelaskan transaksi, pipelining, dan fitur publikasikan / langganan. Kami menyimpulkan dengan penggabungan koneksi dan Redis Cluster.

2. Mengapa Jedis?

Redis mencantumkan pustaka klien paling terkenal di situs resmi mereka. Ada beberapa alternatif selain Jedi, tetapi hanya dua lagi yang saat ini layak mendapatkan bintang rekomendasi mereka, selada, dan Redisson.

Kedua klien ini memang memiliki beberapa fitur unik seperti keamanan thread, penanganan koneksi ulang transparan dan API asinkron, semua fitur yang tidak dimiliki Jedis.

Namun, ini kecil dan jauh lebih cepat daripada dua lainnya. Selain itu, ini adalah pustaka klien pilihan pengembang Spring Framework, dan memiliki komunitas terbesar dari ketiganya.

3. Ketergantungan Maven

Mari kita mulai dengan mendeklarasikan satu-satunya ketergantungan yang kita perlukan di pom.xml :

 redis.clients jedis 2.8.1  

Jika Anda mencari versi terbaru dari perpustakaan ini, lihat halaman ini.

4. Instalasi Redis

Anda perlu menginstal dan menjalankan salah satu versi Redis terbaru. Kami sedang menjalankan versi stabil terbaru saat ini (3.2.1), tetapi versi 3.x posting apa pun seharusnya baik-baik saja.

Temukan di sini informasi lebih lanjut tentang Redis untuk Linux dan Macintosh, mereka memiliki langkah-langkah instalasi dasar yang sangat mirip. Windows tidak didukung secara resmi, tetapi port ini dipertahankan dengan baik.

Setelah itu kita bisa langsung menyelami dan menghubungkannya dari kode Java kita:

Jedis jedis = new Jedis();

Konstruktor default akan berfungsi dengan baik kecuali Anda telah memulai layanan pada port non-default atau mesin jarak jauh, dalam hal ini Anda dapat mengonfigurasinya dengan benar dengan meneruskan nilai yang benar sebagai parameter ke dalam konstruktor.

5. Redis Struktur Data

Sebagian besar perintah operasi asli didukung dan, cukup nyaman, biasanya berbagi nama metode yang sama.

5.1. String

String adalah jenis nilai Redis paling dasar, berguna saat Anda perlu mempertahankan tipe data nilai kunci sederhana:

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

Variabel cachedResponse akan memiliki nilai 32,15,223,828 . Ditambah dengan dukungan kedaluwarsa, yang akan dibahas nanti, ini dapat berfungsi secepat kilat dan mudah menggunakan lapisan cache untuk permintaan HTTP yang diterima di aplikasi web Anda dan persyaratan caching lainnya.

5.2. Daftar

Daftar Redis hanyalah daftar string, diurutkan berdasarkan urutan penyisipan dan menjadikannya alat yang ideal untuk diterapkan, misalnya, antrean pesan:

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

Tugas variabel akan menyimpan nilai firstTask . Ingatlah bahwa Anda dapat membuat serial objek apa pun dan mempertahankannya sebagai string, sehingga pesan dalam antrean dapat membawa data yang lebih kompleks saat diperlukan.

5.3. Set

Kumpulan Redis adalah kumpulan String yang tidak diurutkan yang berguna saat Anda ingin mengecualikan anggota 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 dari nama panggilan # 1 telah diabaikan. Juga, ada variabel akan memiliki nilai yang benar , metode sismember memungkinkan Anda untuk memeriksa keberadaan anggota tertentu dengan cepat.

5.4. Hash

Redis kilatan pemetaan antara String bidang dan String nilai-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 tipe data yang sangat nyaman ketika Anda ingin mengakses properti objek secara individual karena Anda tidak perlu mengambil keseluruhan objek.

5.5. Set yang Diurutkan

Set yang Diurutkan adalah seperti Set di mana setiap anggota memiliki peringkat terkait, yang digunakan untuk menyortirnya:

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

Variabel pemain akan memegang nilai PlayerThree karena kita mengambil bagian atas 1 pemain dan dia adalah satu dengan nilai tertinggi. The peringkat variabel akan memiliki nilai 1 karena PlayerOne adalah kedua dalam peringkat dan peringkat adalah nol berbasis.

6. Transaksi

Transaksi menjamin operasi atomicity dan keamanan benang, yang berarti bahwa permintaan dari klien lain tidak akan pernah ditangani secara bersamaan selama 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 dapat membuat transaksi sukses bergantung pada kunci tertentu dengan "melihatnya" tepat sebelum Anda membuat contoh Transaksi Anda :

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

Sebagian besar fitur dari Redis sudah tersedia di Jedis dan perkembangannya bergerak maju dengan kecepatan yang baik.

Ini memberi Anda kemampuan untuk mengintegrasikan mesin penyimpanan dalam memori yang kuat dalam aplikasi Anda dengan sedikit kerumitan, namun jangan lupa untuk mengatur penyatuan koneksi untuk menghindari masalah keamanan thread.

Anda dapat menemukan contoh kode di proyek GitHub.