Sau khi đã cài đặt thành công dự án với spring boot và redis, giờ là lúc chúng ta có thể tìm hiểu các phần nâng cao hơn mà spring boot đã cung cấp cho Redis. Trong bài này Dũng sẽ cùng các bạn tìm hiểu về CRUD Repository và Pub/Sub message nhé.

CRUD Repository

Để thuận tiện hơn cho lập trình viên khi làm việc với Redis thì spring boot đã cung cấp cho chúng ta một giao diện cầu nối có tên CrudRepository. Trước hết chúng ta sẽ cần thay đổi lớp User một chút:
Tương ứng chúng ta cũng cần cập nhật lớp User để bổ sung thông tin @RedisHash(“users”) và @Id.

package vn.techmaster.redis.data;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

import java.io.Serializable;

@Getter
@Setter
@ToString
@RedisHash("users")
public class User implements Serializable {
    @Id
    private String username;
    private String displayName;
}

users là tiền tố của key sẽ được lưu vào redis map, và ở đây chúng ta quy định key sẽ là username. Để hình dung rõ hơn bạn có thể truy cập vào lớp RedisKeyValueAdapter và đoạn mã nguồn:

byte[] key = toBytes(rdo.getId());
byte[] objectKey = createKey(rdo.getKeyspace(), rdo.getId());

boolean isNew = connection.del(objectKey) == 0;

connection.hMSet(objectKey, rdo.getBucket().rawMap());

Bạn sẽ thấy rằng ví dụ một người dùng có dữ liệu:

username: techmaster
displayName: Tech Master

Sẽ được lưu vào redis map kiểu:

map[("users:" + user.username).toByteArray()] = user.toMapOfByteArrays();

Tiếp theo chúng ta có thể tạo ra một giao diện cầu nối cho lớp User như sau:

package vn.techmaster.redis.repo;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import vn.techmaster.redis.data.User;

@Repository
public interface UserCrudRepository extends CrudRepository<User, String> {}

Lưu ý rằng chúng ta chỉ cần tạo ra giao diện mà thôi, còn việc cài đặt sẽ do spring boot lo. Để làm được điều này thì spring boot phải sử dụng thư viện java byte code để tạo và biên dịch mã nguồn java thành lớp và đối tượng trong runtime. Có vẻ như hiện tại spring boot đang sử dụng thư viện java byte code bytebuddy.
Về phía sâu bên trong thì spring boot cũng làm tương tự những công việc chúng ta đã làm trong lớp UserRepository trước đó, cũng sẽ là các công việc chuyển đổi đối tượng java thành byte array sau đó gọi đến thư viện tương tác với redis để lưu trữ hoặc lấy ra dữ liệu dạng byte array từ redis sau đó chuyển đổi sang đối tượng java khi lấy ra.

Pub/Sub

Thực tế thì Redis Server cũng là một socket server nên nó cũng có thể được tận dụng luôn để làm một message broker được.
Để có thể cho phép pub/sub chúng ta có cần cập nhật lớp RedisConfig như sau:

package vn.techmaster.redis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import vn.techmaster.redis.pubsub.RedisMessageSubscriber;

@Configuration
public class RedisConfig {

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        return new JedisConnectionFactory();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory());
        return template;
    }

    @Bean
    public RedisMessageListenerContainer redisContainer(
        RedisMessageSubscriber messageSubscriber
    ) {
        RedisMessageListenerContainer container
            = new RedisMessageListenerContainer();
        container.setConnectionFactory(jedisConnectionFactory());
        container.addMessageListener(messageSubscriber, topic());
        return container;
    }

    @Bean
    public ChannelTopic topic() {
        return new ChannelTopic("hello-world");
    }
}

Chúng ta vừa bổ sung thêm hai hàm redisContainer và topic để đăng ký đối tượng lắng nghe và xử lý khi có message được gửi đến. Mã nguồn của lớp RedisMessageSubscriber sẽ như sau:

package vn.techmaster.redis.pubsub;

import lombok.AllArgsConstructor;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

@Component
@AllArgsConstructor
public class RedisMessageSubscriber implements MessageListener {

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        RedisSerializer<?> serializer = redisTemplate.getDefaultSerializer();
        assert serializer != null;
        Object messageBody = serializer.deserialize(message.getBody());
        System.out.println("Message received: " + messageBody);
    }
}

Ở đây chúng ta đang sử dụng đối tượng serializer mặc định của spring boot redis, chúng ta sẽ deserialize dữ liệu nhận được trong message dưới dạng byte array sang dữ liệu đối tượng cũng chính là dữ liệu mà chúng ta đã publish.
Tiếp theo chúng ta sẽ cài đặt lớp RedisMessagePublisher để publish message:

package vn.techmaster.redis.pubsub;

import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.stereotype.Component;

@Component
@AllArgsConstructor
public class RedisMessagePublisher {

    private final RedisTemplate<String, Object> redisTemplate;
    private final ChannelTopic topic;

    public void publish(String message) {
        redisTemplate.convertAndSend(topic.getTopic(), message);
    }
}

Lớp này cũng chỉ đơn giản là publish message dạng string thông qua đối tượng redisTemplate để gửi đến redis server, sau đó redis server sẽ phân phối đến các client đang kết nối đến nó.

Khởi chạy chương trình

Để khởi chạy chương trình chúng ta sẽ cần update lớp RedisStartUp thành như sau:

package vn.techmaster.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import vn.techmaster.redis.data.User;
import vn.techmaster.redis.pubsub.RedisMessagePublisher;
import vn.techmaster.redis.repo.UserCrudRepository;
import vn.techmaster.redis.repo.UserRepository;

@SpringBootApplication
public class RedisStartUp {

    public static void main(String[] args) throws Exception {
        ApplicationContext applicationContext = SpringApplication
            .run(RedisStartUp.class, args);
        UserRepository userRepository = applicationContext
            .getBean(UserRepository.class);
        UserCrudRepository userCrudRepository = applicationContext
            .getBean(UserCrudRepository.class);
        User user = new User();
        user.setUsername("techmaster");
        user.setDisplayName("Tech Master");
        // userRepository.saveUser(user);
        userCrudRepository.save(user);

        // User fetchedUser = userRepository.getUserByUsername(
            // "techmaster"
        // );
        User fetchedUser = userCrudRepository.findById("techmaster")
            .get();
        System.out.println(fetchedUser);

        RedisMessagePublisher messagePublisher = applicationContext
            .getBean(RedisMessagePublisher.class);
        messagePublisher.publish("Hello TechMaster");

        while(true) {
            Thread.sleep(100);
        }
    }
}

Chúng ta sẽ sử dụng lớp UserCrudRepository để lưu trữ và lấy ra thông tin ngừoi dùng từ redis. Chúng ta cũng publish một message “Hello Techmaster” thông qua lớp RedisMessagePublisher.
Kết quả chúng ta nhận được sẽ là:

User(username=techmaster, displayName=Tech Master)
Message received: Hello TechMaster

Tổng kết lại

Chúng ta đã tìm hiểu hai tính năng chính hay được sử dụng với redis là thao tác với dữ liệu và pub/sub message.

Sách tham khảo

Bạn có thể tìm hiểu thêm về bridge design pattern thông qua cuốn làm chủ các mẫu thiết kế kinh điển trong lập trình nhé.


Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a