Skip to main content

Sử dụng Jedis làm việc với Redis trong Java

Bài viết này mình sẽ giới thiệu về Jedis, một thư viện client Java cho Redis.

1. Tại sao lại là Jedis?
Redis liệt kê các thư viện client nổi tiếng nhất trên trang web chính thức của họ Có nhiều lựa chọn thay thế cho Jedis, nhưng chỉ có hai lựa chọn khác xứng đáng để đề xuất đó là lettuce và Redisson.
Hai clients này có một số tính năng độc đáo như an toàn luồng, xử lý kết nối lại trong suốt và API không đồng bộ, tất cả các tính năng mà Jedis thiếu.
Tuy nhiên, Jedis nhỏ và nhanh hơn đáng kể so với hai loại kia. Bên cạnh đó, nó là thư viện client được lựa chọn của các nhà phát triển Spring Framework, và nó có cộng đồng lớn nhất trong cả ba.
2. Maven Dependencies
Hãy bắt đầu bằng cách khai báo dependency trong file pom.xml :
1
2
3
4
5
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.1</version>
</dependency>
Xem trang này để tìm version mới nhất.
3. Cài đặt Redis
Bạn sẽ cần cài đặt và kích hoạt một trong các phiên bản mới nhất của Redis. Mình đang chạy phiên bản ổn định (3.2.1).
Tìm ở đây biết thêm thông tin về Redis cho Linux và Macintosh, họ có các bước cài đặt cơ bản rất giống nhau.
Sau đó chúng ta có thể trực tiếp đi sâu vào và kết nối với nó từ code Java như sau:
1
Jedis jedis = new Jedis();
Hàm khởi tạo mặc định sẽ hoạt động tốt trừ khi bạn đã khởi động dịch vụ trên cổng không mặc định hoặc máy từ xa, trong trường hợp đó bạn có thể cấu hình nó một cách chính xác bằng cách chuyển các giá trị đúng như tham số vào hàm tạo.
4. Cấu trúc dữ liệu Redis
Hầu hết các lệnh hoạt động tự nhiên được hỗ trợ và đủ thuận tiện, chúng thường chia sẻ cùng một tên phương thức.
4.1. Strings
Các chuỗi là loại giá trị Redis cơ bản nhất, hữu ích khi bạn cần phải duy trì các kiểu dữ liệu key-value đơn giản:
1
2
jedis.set("city/london", "32,15,223,828");
String cachedResponse = jedis.get("city/london");
Biến cachedResponse sẽ giữ giá trị 32,15,223,828 .
4.2. Lists
List trong Redis chỉ đơn giản là danh sách các chuỗi, được sắp xếp theo thứ tự inserted:
1
2
3
4
jedis.lpush("queue#tasks", "firstTask");
jedis.lpush("queue#tasks", "secondTask");
 
String task = jedis.rpop("queue#tasks");
biến task sẽ giữ giá trị firstTask . Hãy nhớ rằng bạn có thể tuần tự hóa bất kỳ đối tượng nào và lấy nó ra như một chuỗi.
4.3. Sets
Redis Sets là collection string không có thứ tự có ích khi bạn muốn loại trừ các phần tử lặp lại:
1
2
3
4
5
6
jedis.sadd("nicknames", "nickname#1");
jedis.sadd("nicknames", "nickname#2");
jedis.sadd("nicknames", "nickname#1");
 
Set<String> nicknames = jedis.smembers("nicknames");
boolean exists = jedis.sismember("nicknames", "nickname#1");
Set nicknames sẽ có kích thước là 2, add thêm nickname#1  đã bị bỏ qua. Ngoài ra, biến exists sẽ có giá trị true , phương thức sismember cho phép bạn kiểm tra sự tồn tại của một phần tử cụ thể một cách nhanh chóng.
4.4. Hashes
Redis Hashes map giữa  String fields và String values:
1
2
3
4
5
6
7
jedis.hset("user#1", "name", "Peter");
jedis.hset("user#1", "job", "politician");
         
String name = jedis.hget("user#1", "name");
         
Map<String, String> fields = jedis.hgetAll("user#1");
String job = fields.get("job");
Như bạn có thể thấy, hashes là một kiểu dữ liệu rất tiện lợi khi bạn muốn truy cập các thuộc tính của đối tượng một cách riêng lẻ vì bạn không cần lấy toàn bộ đối tượng.
4.5. Sorted Sets
Các Sorted Sets giống như một Set trong đó mỗi phần tử có rank liên quan, được sử dụng để sắp xếp chúng:
1
2
3
4
5
6
7
8
9
10
11
12
Map<String, Double> scores = new HashMap<>();
 
scores.put("PlayerOne", 3000.0);
scores.put("PlayerTwo", 1500.0);
scores.put("PlayerThree", 8200.0);
 
scores.keySet().forEach(player -> {
    jedis.zadd("ranking", scores.get(player), player);
});
         
String player = jedis.zrevrange("ranking", 0, 1).iterator().next();
long rank = jedis.zrevrank("ranking", "PlayerOne");
Biến player sẽ giữ giá trị PlayerThree vì mình đang tìm cầu thủ top 1 và anh ấy là người có số điểm cao nhất. Biến rank sẽ có giá trị là 1 vì PlayerOne là thứ hai trong bảng xếp hạng và xếp hạng là 0.
5. Transactions
Chắc các bạn cũng hiểu transactions là gì rồi, nó sẽ giúp đảm bảo tính toàn vẹn của dữ liệu.
1
2
3
4
5
6
7
8
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();
Bạn thậm chí có thể làm cho một transaction thành công phụ thuộc vào một khóa cụ thể bằng cách "watch" nó ngay trước khi bạn thực hiện transaction của bạn :
1
jedis.watch("friends#deleted#" + userOneId);
Nếu giá trị của khóa đó thay đổi trước khi transaction được thực hiện, transaction sẽ không được hoàn tất thành công.
6. Pipelining
Khi chúng ta phải gửi nhiều lệnh, chúng ta có thể đóng gói chúng lại với nhau trong một request và tiết kiệm kết nối bằng cách sử dụng pipelines, về cơ bản nó giúp tối ưu hóa băng thông mạng. Miễn là các hoạt động độc lập với nhau, chúng ta có thể tận dụng kỹ thuật này:
1
2
3
4
5
6
7
8
9
10
11
12
13
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<Boolean> pipeExists = p.sismember("searched#" + userOneId, "paris");
Response<Set<String>> pipeRanking = p.zrange("ranking", 0, -1);
p.sync();
 
String exists = pipeExists.get();
Set<String> ranking = pipeRanking.get();

7. Publish/Subscribe

Chúng ta có thể sử dụng Redis messaging broker để gửi messages giữa các components khác nhau trong hệ thống. Bạn cần chắc chắn rằng subscriber và publisher threads không chia sẻ chung 1 Jedis connection.
7.1. Subscriber
Đăng ký và lắng nghe messages được gửi:
1
2
3
4
5
6
7
Jedis jSubscriber = new Jedis();
jSubscriber.subscribe(new JedisPubSub() {
    @Override
    public void onMessage(String channel, String message) {
        // handle message
    }
}, "channel");
Subscribe là một phương pháp chặn, bạn sẽ cần phải hủy đăng ký khỏi JedisPubSub một cách rõ ràng. Mình đã override phương thức onMessage nhưng có nhiều phương thức hữu ích hơn để ghi đè.
7.2. Publisher
Sau đó đơn giản là chỉ cần gửi 1 message với cùng 1 channel từ publisher’s thread:
1
2
Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");

8. Kết luận

Phần lớn các tính năng từ Redis đã có sẵn trong Jedis và sự phát triển của nó tiến về phía trước với tốc độ tốt.
Nó cung cấp cho bạn khả năng tích hợp một công cụ lưu trữ trong bộ nhớ mạnh mẽ trong ứng dụng của bạn với rất ít rắc rối, chỉ cần đừng quên thiết lập  connection pooling để tránh các vấn đề thread safety.

Comments

Popular posts from this blog

Ứng dụng giải thuật MiniMax trong trò chơi cờ tướng - Tìm hiểu về trí tuệ nhân tạo (Phần 2)

Chắc hẳn mọi người đều biết về trò chơi thú vị như cờ tướng. Tiếp theo loạt bài về trí tuệ nhân tạo, bài viết này mình sẽ nói về cụ thể giải thuật Minimax ứng dụng trong trò chơi trí tuệ cờ tướng như thế nào. OK! Let's go. 1. Ý tưởng Cờ tướng là trò chơi đối kháng, trong đó hai người luôn phiên nhau đi nước đi của mình. Trạng thái bắt đầu là trạng thái khởi tạo bàn cờ, sau mỗi nước đi của một bên, trạng thái bàn cờ sẽ được thay đổi thành một trạng thái mới hiện hành. Cờ tướng có luật của nó, và trò chơi sẽ kết thúc khi một người có được trạng thái phản ánh sự thắng cuộc hoặc hai người rơi vào trạng thái hòa cờ. Ta tìm cách phân tích xem từ một trạng thái nào đó sẽ dẫn đến đấu thủ nào sẽ thắng với điều kiện cả hai có trình độ như nhau. Giải thuật Minimax sẽ được áp dụng vào trong trò chơi cờ tướng. Hai đấu thủ trong trò chơi sẽ được gọi là MIN và MAX và hai đấu

Sử dụng Jenkins để Build Docker Images

Khởi chạy Jenkins Khởi chạy Jenkins như một Docker Container với lệnh sau: docker run -d -u root --name jenkins \ -p 8080:8080 -p 50000:50000 \ -v /root/jenkins_2112:/var/jenkins_home \ jenkins/jenkins:2.112-alpine Load Dashboard Tên người dùng  admin có mật khẩu mặc định là  344827fbdbfb40d5aac067c7a07b9230 Trên hệ thống của bạn, bạn có thể tìm mật khẩu bằng docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword Có thể mất vài giây để Jenkins hoàn thành việc bắt đầu và có sẵn.  Trong các bước tiếp theo, bạn sẽ sử dụng trang Dashboard để định cấu hình các plugin và bắt đầu tạo Image Docker. Cấu hình Plugin Docker Bước đầu tiên là cấu hình  plugin Docker  .  Plugin này dựa trên plugin Jenkins Cloud.  Khi build Docker Image, nó sẽ tạo ra một "Cloud Agent" thông qua plugin.  Tác nhân sẽ là Docker Container được cấu hình để giao tiếp với Docker Daemon Job build của Jenkins sẽ sử dụng vùng chứa nà