- vừa được xem lúc

[MSDP] - Priority Queue Pattern

0 0 15

Người đăng: TheLight

Theo Viblo Asia

Trong một số trường hợp khi các request đến server, chúng ta có thể muốn đưa các request vào hàng đợi và xử lý chúng theo trình tự hoặc theo thứ tự có ưu tiên sau đó thông báo cho người dùng sau khi các tác vụ được hoàn thành. Để các hệ thống của chúng ta có thể được kết hợp một cách mềm dẻo linh hoạt và cung cấp trải nghiệm người dùng tốt hơn vì người dùng sẽ không bị lock lại khi đợi phản hổi từ server.

Đôi khi các nghiệp vụ cần ưu tiên các nhiệm vụ theo thứ tự trước sau dựa vào một số tiêu chí. Ví dụ: yêu cầu của người dùng VIP phải được xử lý trước trước khi xử lý bất kỳ yêu cầu nào của người dùng thông thường. Chúng ta có thể thấy hành vi này trong khi bay trên máy bay, vận chuyển sản phẩm trực tuyến, v.v. Ví dụ trong hình dưới đây, các tác vụ màu đỏ là nhiệm vụ có mức độ ưu tiên cao trong khi các tác vụ màu xanh có mức độ ưu tiên thấp. Mặc dù có một nhiệm vụ màu xanh (ưu tiên thấp) ở vị trí thứ hai trong hàng đợi, một nhiệm vụ màu đỏ (có mức độ ưu tiên cao) ở vị trí thứ tư nhưng nó sẽ được xử lý trước khi xử lý bất kỳ tác vụ nào có mức độ ưu tiên thấp.

Hãy xem cách chúng ta có thể xử lý vấn đề này bằng cách sử dụng Redis như sample application dưới đây:

Sample application

  • Như thường lệ để giữ mọi thứ đơn giản, chúng ta sẽ giả định rằng class Task sẽ tìm số ở vị trí thứ N trong chuỗi Fibonacci.
  • Sẽ có một số danh mục như LOW, HIGH, URGENT để ưu tiên các nhiệm vụ.
  • Các nhiệm vụ URGENT được thực hiện trước HIGH sau đó đến LOW.

Ta sẽ chia ứng dụng thành 3 module: model, task-executor, task-scheduler

Model

public enum Priority implements Serializable { LOW, HIGH, URGENT
}
@Getter
@AllArgsConstructor
public class Task implements Comparable<Task>, Serializable { private final Priority priority; private final int number; @Override public int compareTo(Task o) { return o.getPriority().compareTo(this.getPriority()); } }

Task-executor

  • task-executor là một dịch vụ sẽ thăm dò Redis để nhận các nhiệm vụ. Redis hoạt động giống như một hàng đợi (queue) chứa các nhiệm vụ.
@Service
public class FibService { // intentional - 2^N public long calculate(int n){ if(n < 2) return n; return calculate(n - 1) + calculate(n - 2); } } 

Queue bean

@EnableScheduling
@SpringBootApplication
public class TaskExecutorApplication { @Autowired private RedissonClient redissonClient; public static void main(String[] args) { SpringApplication.run(TaskExecutorApplication.class, args); } @Bean public RPriorityBlockingQueue<Task> getQueue(){ return this.redissonClient.getPriorityBlockingQueue("task"); } }

Executor

@Service
public class Executor { @Autowired private RPriorityBlockingQueue<Task> priorityQueue; @Autowired private FibService fibService; @Scheduled(fixedDelay = 1000) public void runTask() throws InterruptedException { System.out.println("----------------------------------------"); Task task = this.priorityQueue.take(); System.out.println("Priority : " + task.getPriority()); System.out.println("Input : " + task.getNumber()); System.out.println("Result : " + this.fibService.calculate(task.getNumber())); }
}

Chúng ta sử dụng redisson client libarary cho Redis với cấu hình bên dưới.

singleServerConfig: idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 password: null subscriptionsPerConnection: 5 clientName: null address: "redis://master:6379" subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 connectionMinimumIdleSize: 24 connectionPoolSize: 64 database: 0 dnsMonitoringInterval: 5000
threads: 2
nettyThreads: 2
codec: !<org.redisson.codec.FstCodec> {}
transportMode: "NIO"

Task-Scheduler

  • task-Scheduler là một ứng dụng web mà thông qua đó chúng gửi các nhiệm vụ đến hàng đợi Redis queue.
  • Nó cung cấp một REST API để gửi nhiệm vụ
@RestController
@RequestMapping("/task")
public class TaskController { @Autowired private RPriorityBlockingQueue<Task> priorityBlockingQueue; @GetMapping("/{priority}/{number}") public void schedule(@PathVariable String priority, @PathVariable int number){ this.priorityBlockingQueue.add(this.getTask(priority, number)); } private Task getTask(final String priority, final int number){ return new Task( Priority.valueOf(priority.toUpperCase()), number ); } }

docker-compose file

version: '3'
services: master: container_name: master image: redis ports: - 6379:6379 task-scheduler: build: ./task-scheduler image: vinsdocker/task-scheduler ports: - 8080:8080 task-executor: build: ./task-executor image: vinsdocker/task-executor redis-commander: container_name: redis-commander hostname: redis-commander image: rediscommander/redis-commander:latest restart: always environment: - REDIS_HOSTS=master:master ports: - 8081:8081
  • Khi chúng ta chạy lệnh docker-compose up, docker sẽ build các file docker image và thiết lập chạy ứng dụng. Chúng ta gửi 100 nhiệm vụ ưu tiên thấp với input đầu vào là vị trí 46 trong dãy số Fibonacci. Phải mất một khoảng thời gian đáng kể để xử lý từng nhiệm vụ. Khi chúng đã có 98 nhiệm vụ trong hàng đợi, chúng ta tiếp tục gửi một yêu cầu ưu tiên với input đầu vào là vị trí 33 trong dãy số Fibonacci. Kết quả là nó sẽ được thực thi ngay lập tức và sau đó các tác vụ thấp còn lại được tiếp tục.
task-executor_1 | Priority : LOW
task-executor_1 | Input : 46
task-executor_1 | Result : 1836311903
task-executor_1 | ----------------------------------------
task-executor_1 | Priority : LOW
task-executor_1 | Input : 46
task-executor_1 | Result : 1836311903
task-executor_1 | ----------------------------------------
task-executor_1 | Priority : HIGH
task-executor_1 | Input : 33
task-executor_1 | Result : 3524578
task-executor_1 | ----------------------------------------
task-executor_1 | Priority : LOW
task-executor_1 | Input : 46
task-executor_1 | Result : 1836311903
task-executor_1 | ----------------------------------------

Tổng kết

Chúng ta vừa hoàn thành một ví dụ nhỏ sử dụng mẫu hàng đợi ưu tiên (priority-queue pattern) và design pattern này sẽ rất hữu ích khi hệ thống cần xử lý nhiều tác vụ với mức độ ưu tiên khác nhau. Hi vọng bài viết hữu ích với mọi người.

Thanks for watching!

Nguồn: https://thenewstack.wordpress.com/2021/10/27/msdp-priority-queue-pattern/

Follow me: thenewstack.wordpress.com

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tổng hợp các bài hướng dẫn về Design Pattern - 23 mẫu cơ bản của GoF

Link bài viết gốc: https://gpcoder.com/4164-gioi-thieu-design-patterns/. Design Patterns là gì. Design Patterns không phải là ngôn ngữ cụ thể nào cả.

0 0 277

- vừa được xem lúc

Giới thiệu về Builder Design Pattern

Nguồn: refactoring.guru. Builder. Ý đồ.

0 0 34

- vừa được xem lúc

Một ví dụ nhỏ về Factory method

Trong bài viết trước mình đã giới thiệu tới các bạn về Abstract Factory pattern, các bạn quan tâm có thể theo dõi lại tại đây. Để tiếp tục về chủ đề design pattern trong bài viết này mình sẽ trình bày những khái niệm, ưu nhược điểm và các sử dụng của một creational design pattern khác đó là Factory

0 0 27

- vừa được xem lúc

Tôi đã dùng Service Pattern trong NuxtJS như thế nào ?

Giới thiệu. Trong quá trình làm VueJS NuxtJS hay thậm chí là Laravel mình cũng hay áp dụng các pattern như Service hoặc Repository.

0 0 59

- vừa được xem lúc

Hướng dẫn Adapter Design Pattern

Trong bài viết này, chúng ta sẽ cùng tìm hiểu về Adapter Design Pattern qua cấu trúc, cánh triển khai, ví dụ, ưu điểm nhược điểm và ứng dụng của nó. Đây là bài viết đầu tiên của mình nên sẽ không trán

1 1 53

- vừa được xem lúc

Giới thiệu về Prototype Design Pattern

Ý đồ. Prototype là một creational design pattern cho phép bạn sao chép các object hiện có mà không làm cho code của bạn phụ thuộc vào các class của chúng.

0 0 44