Panduan untuk Redis dengan Redisson

1. Gambaran keseluruhan

Redisson adalah pelanggan Redis untuk Java . Dalam artikel ini, kami akan meneroka beberapa ciri-cirinya, dan menunjukkan bagaimana ia dapat memudahkan pembangunan aplikasi perniagaan yang diedarkan.

Redisson merupakan grid data dalam memori yang menawarkan objek dan perkhidmatan Java yang diedarkan yang disokong oleh Redis . Model data dalam memori yang diedarkan membolehkan perkongsian objek dan perkhidmatan domain merentasi aplikasi dan pelayan.

Dalam artikel ini kita akan melihat penyediaan Redisson, memahami bagaimana ia beroperasi, dan meneroka beberapa objek dan perkhidmatan Redisson.

2. Pergantungan Maven

Mari mulakan dengan mengimport Redisson ke projek kami dengan menambahkan bahagian di bawah ke pom.xml kami :

 org.redisson redisson 3.13.1 

Versi terbaru dari ketergantungan ini boleh didapati di sini.

3. Konfigurasi

Sebelum kita memulakan, kita mesti memastikan bahawa kita mempunyai versi dan persediaan Redis terkini dan berjalan. Sekiranya anda tidak mempunyai Redis dan menggunakan Linux atau Macintosh, anda boleh mengikuti maklumat di sini untuk mendapatkannya. Sekiranya anda pengguna Windows, anda boleh menyiapkan Redis menggunakan port tidak rasmi ini.

Kita perlu mengkonfigurasi Redisson untuk menyambung ke Redis. Redisson menyokong sambungan ke konfigurasi Redis berikut:

  • Node tunggal
  • Tuan dengan nod hamba
  • Nod sentinel
  • Nod berkumpulan
  • Nod yang direplikasi

Redisson menyokong Amazon Web Services (AWS) ElastiCache Cluster dan Azure Redis Cache untuk Clustered and Replicate Nodes.

Mari sambungkan ke contoh simpul tunggal Redis. Contoh ini dijalankan secara tempatan pada port lalai, 6379:

RedissonClient client = Redisson.create();

Anda boleh meneruskan konfigurasi yang berbeza ke kaedah membuat objek Redisson . Ini boleh menjadi konfigurasi untuk menghubungkannya ke port lain, atau mungkin, untuk menyambung ke kluster Redis. Ini konfigurasi boleh menjadi kod Java atau dimuatkan dari fail konfigurasi luar .

3.1. Konfigurasi Java

Mari konfigurasikan Redisson dalam kod Java:

Config config = new Config(); config.useSingleServer() .setAddress("redis://127.0.0.1:6379"); RedissonClient client = Redisson.create(config);

Kami menentukan konfigurasi Redisson dalam contoh objek Config dan kemudian meneruskannya ke kaedah buat . Di atas, kami menyatakan kepada Redisson bahawa kami ingin menyambung ke satu contoh simpul Redis. Untuk melakukan ini, kami menggunakan kaedah useSingleServer objek Config . Ini mengembalikan rujukan ke objek SingleServerConfig .

The SingleServerConfig objek mempunyai tetapan yang Redisson menggunakan untuk menyambung kepada contoh nod tunggal Redis. Di sini, kami menggunakan kaedah setAddress untuk mengkonfigurasi tetapan alamat . Ini menetapkan alamat nod yang kita sambungkan. Beberapa tetapan lain termasuk percubaan semula Percubaan , sambungan Timeout dan nama pelanggan . Tetapan ini dikonfigurasi menggunakan kaedah setter yang sesuai.

Kita boleh mengkonfigurasi Redisson untuk konfigurasi Redis yang berbeza dengan cara yang sama menggunakan kaedah berikut objek Config :

  • useSingleServer - untuk contoh nod tunggal. Dapatkan tetapan nod tunggal di sini
  • useMasterSlaveServers - untuk master dengan nod hamba. Dapatkan tetapan nod master-slave di sini
  • useSentinelServers - untuk nod sentinel. Dapatkan tetapan nod sentinel di sini
  • useClusterServers - untuk nod berkelompok. Dapatkan tetapan nod berkelompok di sini
  • useReplicatedServers - untuk nod yang direplikasi. Dapatkan tetapan nod yang direplikasi di sini

3.2. Konfigurasi Fail

Redisson dapat memuat konfigurasi dari fail JSON atau YAML luaran :

Config config = Config.fromJSON(new File("singleNodeConfig.json")); RedissonClient client = Redisson.create(config);

Kaedah Config objek dariJSON dapat memuat konfigurasi dari rentetan, fail, aliran input atau URL.

Berikut adalah contoh konfigurasi dalam fail singleNodeConfig.json :

{ "singleServerConfig": { "idleConnectionTimeout": 10000, "connectTimeout": 10000, "timeout": 3000, "retryAttempts": 3, "retryInterval": 1500, "password": null, "subscriptionsPerConnection": 5, "clientName": null, "address": "redis://127.0.0.1:6379", "subscriptionConnectionMinimumIdleSize": 1, "subscriptionConnectionPoolSize": 50, "connectionMinimumIdleSize": 10, "connectionPoolSize": 64, "database": 0, "dnsMonitoringInterval": 5000 }, "threads": 0, "nettyThreads": 0, "codec": null }

Berikut adalah fail konfigurasi YAML yang sepadan:

singleServerConfig: idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 password: null subscriptionsPerConnection: 5 clientName: null address: "redis://127.0.0.1:6379" subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 connectionMinimumIdleSize: 10 connectionPoolSize: 64 database: 0 dnsMonitoringInterval: 5000 threads: 0 nettyThreads: 0 codec: ! {} 

Kami dapat mengkonfigurasi konfigurasi Redis lain dari fail dengan cara yang serupa menggunakan tetapan yang khas untuk konfigurasi tersebut. Untuk rujukan anda, berikut adalah format fail JSON dan YAML mereka:

  • Node tunggal - format
  • Master dengan nod hamba - format
  • Nod sentinel - format
  • Node berkelompok - format
  • Nod - format yang direplikasi

Untuk menyimpan konfigurasi Java untuk JSON atau YAML format, kita boleh menggunakan toJSON atau toYAML kaedah yang Config objek:

Config config = new Config(); // ... we configure multiple settings here in Java String jsonFormat = config.toJSON(); String yamlFormat = config.toYAML();

Setelah kita mengetahui cara mengkonfigurasi Redisson, mari kita lihat bagaimana Redisson melaksanakan operasi.

4. Operasi

Redisson menyokong antara muka segerak, tak segerak dan reaktif . Operasi di antara muka ini selamat digunakan .

All entities (objects, collections, locks and services) generated by a RedissonClient have synchronous and asynchronous methods. Synchronous methods bear asynchronous variants. These methods normally bear the same method name of their synchronous variants appended with “Async”. Let's look at a synchronous method of the RAtomicLong object:

RedissonClient client = Redisson.create(); RAtomicLong myLong = client.getAtomicLong('myLong'); 

The asynchronous variant of the synchronous compareAndSet method would be:

RFuture isSet = myLong.compareAndSetAsync(6, 27);

The asynchronous variant of the method returns an RFuture object. We can set listeners on this object to get back the result when it becomes available:

isSet.handle((result, exception) -> { // handle the result or exception here. });

To generate reactive objects, we would need to use the RedissonReactiveClient:

RedissonReactiveClient client = Redisson.createReactive(); RAtomicLongReactive myLong = client.getAtomicLong("myLong"); Publisher isSetPublisher = myLong.compareAndSet(5, 28);

This method returns reactive objects based on the Reactive Streams Standard for Java 9.

Let's explore some of the distributed objects provided by Redisson.

5. Objects

An individual instance of a Redisson object is serialized and stored in any of the available Redis nodes backing Redisson. These objects could be distributed in a cluster across multiple nodes and can be accessed by a single application or multiple applications/servers.

These distributed objects follow specifications from the java.util.concurrent.atomic package. They support lock-free, thread-safe and atomic operations on objects stored in Redis. Data consistency between applications/servers is ensured as values are not updated while another application is reading the object.

Redisson objects are bound to Redis keys. We can manage these keys through the RKeys interface. And then, we access our Redisson objects using these keys.

There are several options we may use to get the Redis keys.

We can simple get all the keys:

RKeys keys = client.getKeys();

Alternatively, we can extract only the names:

Iterable allKeys = keys.getKeys();

And finally, we're able to get the keys conforming to a pattern:

Iterable keysByPattern = keys.getKeysByPattern('key*')

The RKeys interface also allows deleting keys, deleting keys by pattern and other useful key-based operations that we could use to manage our keys and objects.

Distributed objects provided by Redisson include:

  • ObjectHolder
  • BinaryStreamHolder
  • GeospatialHolder
  • BitSet
  • AtomicLong
  • AtomicDouble
  • Topic
  • BloomFilter
  • HyperLogLog

Let's take a look at three of these objects: ObjectHolder, AtomicLong, and Topic.

5.1. Object Holder

Represented by the RBucket class, this object can hold any type of object. This object has a maximum size of 512MB:

RBucket bucket = client.getBucket("ledger"); bucket.set(new Ledger()); Ledger ledger = bucket.get();

The RBucket object can perform atomic operations such as compareAndSet andgetAndSet on objects it holds.

5.2. AtomicLong

Represented by the RAtomicLong class, this object closely resembles the java.util.concurrent.atomic.AtomicLong class and represents a long value that can be updated atomically:

RAtomicLong atomicLong = client.getAtomicLong("myAtomicLong"); atomicLong.set(5); atomicLong.incrementAndGet();

5.3. Topic

The Topic object supports the Redis' “publish and subscribe” mechanism. To listen for published messages:

RTopic subscribeTopic = client.getTopic("baeldung"); subscribeTopic.addListener(CustomMessage.class, (channel, customMessage) -> future.complete(customMessage.getMessage()));

Above, the Topic is registered to listen to messages from the “baeldung” channel. We then add a listener to the topic to handle incoming messages from that channel. We can add multiple listeners to a channel.

Let's publish messages to the “baeldung” channel:

RTopic publishTopic = client.getTopic("baeldung"); long clientsReceivedMessage = publishTopic.publish(new CustomMessage("This is a message"));

This could be published from another application or server. The CustomMessage object will be received by the listener and processed as defined in the onMessage method.

We can learn more about other Redisson objects here.

6. Collections

We handle Redisson collections in the same fashion we handle objects.

Distributed collections provided by Redisson include:

  • Map
  • Multimap
  • Set
  • SortedSet
  • ScoredSortedSet
  • LexSortedSet
  • List
  • Queue
  • Deque
  • BlockingQueue
  • BoundedBlockingQueue
  • BlockingDeque
  • BlockingFairQueue
  • DelayedQueue
  • PriorityQueue
  • PriorityDeque

Let's take a look at three of these collections: Map, Set, and List.

6.1. Map

Redisson based maps implement the java.util.concurrent.ConcurrentMap and java.util.Map interfaces. Redisson has four map implementations. These are RMap, RMapCache, RLocalCachedMap and RClusteredMap.

Let's create a map with Redisson:

RMap map = client.getMap("ledger"); Ledger newLedger = map.put("123", new Ledger());map

RMapCache supports map entry eviction. RLocalCachedMap allows local caching of map entries. RClusteredMap allows data from a single map to be split across Redis cluster master nodes.

We can learn more about Redisson maps here.

6.2. Set

Redisson based Set implements the java.util.Set interface.

Redisson has three Set implementations, RSet, RSetCache, and RClusteredSet with similar functionality as their map counterparts.

Let's create a Set with Redisson:

RSet ledgerSet = client.getSet("ledgerSet"); ledgerSet.add(new Ledger());

We can learn more about Redisson sets here.

6.3. List

Redisson-based Lists implement the java.util.List interface.

Let's create a List with Redisson:

RList ledgerList = client.getList("ledgerList"); ledgerList.add(new Ledger());

We can learn more about other Redisson collections here.

7. Locks and Synchronizers

Redisson's distributed locks allow for thread synchronization across applications/servers. Redisson's list of locks and synchronizers include:

  • Lock
  • FairLock
  • MultiLock
  • ReadWriteLock
  • Semaphore
  • PermitExpirableSemaphore
  • CountDownLatch

Let's take a look at Lock and MultiLock.

7.1. Lock

Redisson's Lock implements java.util.concurrent.locks.Lock interface.

Let's implement a lock, represented by the RLock class:

RLock lock = client.getLock("lock"); lock.lock(); // perform some long operations... lock.unlock();

7.2. MultiLock

Redisson's RedissonMultiLock groups multiple RLock objects and treats them as a single lock:

RLock lock1 = clientInstance1.getLock("lock1"); RLock lock2 = clientInstance2.getLock("lock2"); RLock lock3 = clientInstance3.getLock("lock3"); RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3); lock.lock(); // perform long running operation... lock.unlock();

We can learn more about other locks here.

8. Services

Redisson exposes 4 types of distributed services. These are: Remote Service, Live Object Service, Executor Service and Scheduled Executor Service. Let's look at the Remote Service and Live Object Service.

8.1. Remote Service

This service provides Java remote method invocation facilitated by Redis. A Redisson remote service consists of a server-side (worker instance) and client-side implementation. The server-side implementation executes a remote method invoked by the client. Calls from a remote service can be synchronous or asynchronous.

The server-side registers an interface for remote invocation:

RRemoteService remoteService = client.getRemoteService(); LedgerServiceImpl ledgerServiceImpl = new LedgerServiceImpl(); remoteService.register(LedgerServiceInterface.class, ledgerServiceImpl);

The client-side calls a method of the registered remote interface:

RRemoteService remoteService = client.getRemoteService(); LedgerServiceInterface ledgerService = remoteService.get(LedgerServiceInterface.class); List entries = ledgerService.getEntries(10);

We can learn more about remote services here.

8.2. Live Object Service

Redisson Live Objects extend the concept of standard Java objects that could only be accessed from a single JVM to enhanced Java objects that could be shared between different JVMs in different machines. This is accomplished by mapping an object's fields to a Redis hash. This mapping is made through a runtime-constructed proxy class. Field getters and setters are mapped to Redis hget/hset commands.

Redisson Live Objects support atomic field access as a result of Redis' single-threaded nature.

Creating a Live Object is simple:

@REntity public class LedgerLiveObject { @RId private String name; // getters and setters... }

We annotate our class with @REntity and a unique or identifying field with @RId. Once we have done this, we can use our Live Object in our application:

RLiveObjectService service = client.getLiveObjectService(); LedgerLiveObject ledger = new LedgerLiveObject(); ledger.setName("ledger1"); ledger = service.persist(ledger);

We create our Live Object like standard Java objects using the new keyword. We then use an instance of RLiveObjectService to save the object to Redis using its persist method.

If the object has previously been persisted to Redis, we can retrieve the object:

LedgerLiveObject returnLedger = service.get(LedgerLiveObject.class, "ledger1");

We use the RLiveObjectService to get our Live Object using the field annotated with @RId.

Here we can find more about Redisson Live Objects, and other Redisson services are described here.

9. Pipelining

Redisson supports pipelining. Multiple operations can be batched as a single atomic operation. This is facilitated by the RBatch class. Multiple commands are aggregated against an RBatch object instance before they are executed:

RBatch batch = client.createBatch(); batch.getMap("ledgerMap").fastPutAsync("1", "2"); batch.getMap("ledgerMap").putAsync("2", "5"); BatchResult batchResult = batch.execute();

10. Scripting

Redisson supports LUA scripting. We can execute LUA scripts against Redis:

client.getBucket("foo").set("bar"); String result = client.getScript().eval(Mode.READ_ONLY, "return redis.call('get', 'foo')", RScript.ReturnType.VALUE);

11. Low-Level Client

It's possible that we might want to perform Redis operations not yet supported by Redisson. Redisson provides a low-level client that allows execution of native Redis commands:

RedisClientConfig redisClientConfig = new RedisClientConfig(); redisClientConfig.setAddress("localhost", 6379); RedisClient client = RedisClient.create(redisClientConfig); RedisConnection conn = client.connect(); conn.sync(StringCodec.INSTANCE, RedisCommands.SET, "test", 0); conn.closeAsync(); client.shutdown();

The low-level client also supports asynchronous operations.

12. Conclusion

Artikel ini memaparkan Redisson dan beberapa ciri yang menjadikannya sesuai untuk mengembangkan aplikasi yang diedarkan. Kami meneroka objek, koleksi, kunci dan perkhidmatannya yang diedarkan. Kami juga meneroka beberapa ciri lain seperti pipelining, scripting dan pelanggan tahap rendahnya.

Redisson juga menyediakan integrasi dengan kerangka lain seperti JCache API, Spring Cache, Hibernate Cache dan Spring Sessions. Kami dapat mengetahui lebih lanjut mengenai penyatuannya dengan kerangka kerja lain di sini.

Anda boleh mendapatkan sampel kod dalam projek GitHub.