images
04/12/2020 09:44 am

Java với Redis: Key-Value và Publish-Subscribe

Redis là mã nguồn mở, hoạt động như một phần mềm lưu trữ dữ liệu trên bộ nhớ, nó có thể sử dụng như một database, một hệ thống Cache hay một Message Broker.

Trong bài này, Tech Zone sẽ giới thiệu với các bạn cách sử dụng Redis như một Layer dùng để Cache dữ liệu và cũng đồng thời làm Message Broker.

Hiểu đơn giản, Redis là một cấu trúc dữ liệu lưu trữ data dưới dạng Key-Value. Bạn cung cấp Key và Value cần lưu trữ, bạn có thể access được value đó thông qua Key. Value có thể là String, Set, List...


Ở hình vẽ dưới đây, chúng ta thấy khi ứng dụng request dữ liệu, thay vì ứng dụng truy xuất dữ liệu trong database, thì ứng dụng sẽ lấy dữ liệu trong Redis. Do sử dụng Redis là layer để cache, nên dữ liệu sẽ được truy xuất nhanh hơn. Nguyên nhân là data sẽ được lấy trong Memory thay vì cơ chế chạy câu truy vấn trả về dữ liệu từ database. Chỉ khi bước 1 không trả về dữ liệu, ứng dụng mới truy xuất từ Database.



Điều này phù hợp với mô hình ứng dụng chịu được tải cao. Lúc này ta cần thiết kế để database chủ yếu chịu tải từ các transaction: tức là việc ghi dữ liệu. Còn việc đọc dữ liệu, database sẽ không còn phải quan tâm quá nhiều vì cơ bản dữ liệu sẽ được phục vụ thông qua layer cache trên Redis. Về scale, Redis cũng dễ dàng scale hơn so với scale database.


Sử dụng thư viện Java Lettuce.io


Thư viện Java Lettuce là thư viện cho phép chúng ta có thể kết nối với Redis và thực hiện tất cả các lệnh mà Redis cung cấp. Thư viện này hỗ trợ các thao tác dạng Non Blocking IO do được xây trên nền của Netty.



Đầu tiên bạn cần start Redis để lắng nghe trên cổng 6379.

Trong ứng dụng Java, bạn sử dụng Maven hoặc Gradle để add thư viện Lettuce.io như sau:


Với Maven


<dependency>

    <groupId>io.lettuce</groupId>

    <artifactId>lettuce-core</artifactId>

    <version>6.0.1.RELEASE</version>

</dependency>


Với Gradle


dependencies {

 compile 'io.lettuce:lettuce-core:6.0.1.RELEASE

}


Key - Value


Ta sử dụng đoạn code sau để Set/Get key vào Redis


RedisClient redisClient = RedisClient.create("redis://localhost:6379");

RedisCommands commands = redisClient.connect().sync();

commands.set("key1", "Techzonefun");


  • Đầu tiên ta cần khai báo kết nối tới server Redis ở cổng 6379 thông qua RedisClient

  • Tạo kết nối và khởi tạo RedisCommands

  • Chạy câu lệnh set giá trị Techzonefun cho key = key1 trên Redis


Sau khi set giá trị key1, ta có thể lấy giá trị này ra:


RedisClient redisClient = RedisClient.create("redis://localhost:6379");

RedisCommands commands = redisClient.connect().sync();

System.out.println(commands.get("key1"));


  • Chạy câu lệnh get  key1 trả về giá trị Techzonefun


Với Redis, mỗi key sẽ có giá trị timeout. Hết thời gian này key sẽ tự remove. Mặc định nếu bạn không set giá trị timeout thì key sẽ tồn tại mãi mãi.  Để set timeout với Lettuce, ta sử dụng lệnh sau:


commands.set("key1", "techzonefun", SetArgs.Builder.ex(60));



  • SetArgs là class mà Lettuce cung cấp cho phép ta set các arguments cho câu lệnh của Redis. Ở đây ta dùng SetArgs.Builder.ex(60) tức là key này sau 60s sẽ bị clear khỏi hệ thống. Sau khi chạy lệnh này xong, bạn đợi 60s rồi chạy lại script get ở trên:


Giờ ta sẽ lấy dữ liệu từ key1 ra:


System.out.println(commands.get("key1"));



  •  Kết quả là: null


Redis Pub-Sub


Redis Pub-Sub là cơ chế Publish - Subscribe, cho phép Redis có thể gửi một message tới nhiều client đồng thời.

Trong cơ chế này, các Subscriber sẽ đăng kí để nhận một message trên một Channel nào đó của Redis. Bất kỳ khi nào có message được publish vào Key này thì các subscriber đều nhận được. Các subscriber chỉ nhận được message nếu đang kết nối vào Redis. Lưu ý cơ chế này khác với các mô hình publish/subscribe thông thường của các Message Broker khác như: RabbitMQ, ActiveMQ, Kafka.


Đầu tiên bạn tạo ra Subscriber như sau:


RedisClient redisClient = RedisClient.create("redis://localhost:6379");

StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub();

connection.addListener(new RedisPubSubAdapter<String, String>() {

   @Override

   public void message(String channel, String message) {

       System.out.println(String.format("Channel: %s, Message: %s", channel, message));

   }

});


RedisPubSubAsyncCommands<String, String> async = connection.async();

RedisFuture<Void> future = async.subscribe("feeds");


Bạn cần khởi tạo connection: StatefulRedisPubSubConnection. Sau đó sử dụng Listener là RedisPubSubAdapter và override lại method message(). Đây chính là method sẽ được gọi khi subscriber này nhận được message.


Sau khi bật vài subscriber, bạn bắt đầu publish message vào Channel feeds như sau:


RedisClient redisClient = RedisClient.create("redis://localhost:6379");

StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub();

RedisPubSubAsyncCommands<String, String> async = connection.async();

async.publish("feeds", "this is techzonefun");


Bạn cần khởi tạo connection: StatefulRedisPubSubConnection

n. Sau đó tạo ra commands để publish message: This is techzonefun. Sau khi chạy xong dòng code này, bạn sẽ thấy bên của sổ của chương trình subscriber trước đó sẽ nhận được đúng dòng text này.


Redis Pub-Sub với Pattern


Redis cung cấp cơ chế Publish, Subcribe theo pattern với các lệnh psubscribe. Xem minh họa sau:


Khi ta publish vào channel feeds.facebook, sẽ có 2 subcriber: Subscribe1 và Subscribe2 có thể nhận được message này. Khi ta publish message vào channel feeds.google, chỉ có subscriber 1 nhận được vì subscriber1 được subscribe theo cơ chế pattern subscribe.


Để sử dụng Pattern subscribe: bạn code như sau:


RedisClient redisClient = RedisClient.create("redis://localhost:6379");

StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub();

connection.addListener(new RedisPubSubAdapter<String, String>() {

   @Override

   public void message(String pattern, String channel, String message) {

       System.out.println(String.format("Pattern %s on channel %s, message: %s", pattern, channel, message));

   }

});


RedisPubSubAsyncCommands<String, String> async = connection.async();

RedisFuture<Void> future = async.psubscribe("feeds*");


Điểm khác biệt ở đây là bạn cần override method message(String pattern, String channel, String message). Và sử dụng hàm psubscribe()


Sau đó bạn chạy code để publish message vào các channel:


async.publish("feeds.facebook", "this is from fb.com/facebooktechzonefun");


Ở màn hình của consumer, bạn sẽ nhận được dòng sau:


Pattern feeds* on channel feeds.facebook, message: this is from fb.com/facebooktechzonefun


Mời các bạn đọc thêm bài "Java Primitives vs. Object".


- Tech Zone -

Thư giãn chút nào!!!

Bài viết liên quan