2019-08-31 23:40:35 +05:30
|
|
|
---
|
|
|
|
layout: pattern
|
|
|
|
title: Priority Queue Pattern
|
|
|
|
folder: priority-queue
|
|
|
|
permalink: /patterns/priority-queue/
|
|
|
|
categories: Behavioral
|
2021-05-19 10:49:05 -06:00
|
|
|
language: en
|
2019-08-31 23:40:35 +05:30
|
|
|
tags:
|
2019-12-13 21:09:28 +02:00
|
|
|
- Decoupling
|
2020-07-26 11:30:42 +03:00
|
|
|
- Cloud distributed
|
2019-08-31 23:40:35 +05:30
|
|
|
---
|
|
|
|
|
|
|
|
## Intent
|
2020-08-30 20:08:34 +03:00
|
|
|
|
|
|
|
Prioritize requests sent to services so that requests with a higher priority are received and
|
|
|
|
processed more quickly than those of a lower priority. This pattern is useful in applications that
|
|
|
|
offer different service level guarantees to individual clients.
|
2019-08-31 23:40:35 +05:30
|
|
|
|
|
|
|
## Explanation
|
2020-08-30 20:08:34 +03:00
|
|
|
|
|
|
|
Applications may delegate specific tasks to other services; for example, to perform background
|
|
|
|
processing or to integrate with other applications or services. In the cloud, a message queue is
|
|
|
|
typically used to delegate tasks to background processing. In many cases the order in which requests
|
|
|
|
are received by a service is not important. However, in some cases it may be necessary to prioritize
|
|
|
|
specific requests. These requests should be processed earlier than others of a lower priority that
|
|
|
|
may have been sent previously by the application.
|
|
|
|
|
|
|
|
Real world example
|
|
|
|
|
|
|
|
> Imagine a video processing service with free and premium customers. The requests coming from the
|
|
|
|
> paying premium customers should be prioritized over the others.
|
|
|
|
|
|
|
|
In plain words
|
|
|
|
|
|
|
|
> Priority Queue enables processing of high priority messages first, regardless of queue size or
|
|
|
|
> message age.
|
|
|
|
|
|
|
|
Wikipedia says
|
|
|
|
|
|
|
|
> In computer science, a priority queue is an abstract data type similar to regular queue or stack
|
|
|
|
> data structure in which each element additionally has a "priority" associated with it. In a
|
|
|
|
> priority queue, an element with high priority is served before an element with low priority.
|
|
|
|
|
|
|
|
**Programmatic Example**
|
|
|
|
|
|
|
|
Looking at the video processing example from above, let's first see the `Message` structure.
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class Message implements Comparable<Message> {
|
|
|
|
|
|
|
|
private final String message;
|
|
|
|
private final int priority; // define message priority in queue
|
|
|
|
|
|
|
|
public Message(String message, int priority) {
|
|
|
|
this.message = message;
|
|
|
|
this.priority = priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int compareTo(Message o) {
|
|
|
|
return priority - o.priority;
|
|
|
|
}
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Here's `PriorityMessageQueue` that handles storing the messages and serving them in priority
|
|
|
|
order.
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class PriorityMessageQueue<T extends Comparable> {
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
public T remove() {
|
|
|
|
if (isEmpty()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
final var root = queue[0];
|
|
|
|
queue[0] = queue[size - 1];
|
|
|
|
size--;
|
|
|
|
maxHeapifyDown();
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void add(T t) {
|
|
|
|
ensureCapacity();
|
|
|
|
queue[size] = t;
|
|
|
|
size++;
|
|
|
|
maxHeapifyUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
`QueueManager` has a `PriorityMessageQueue` and makes it easy to `publishMessage` and
|
|
|
|
`receiveMessage`.
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class QueueManager {
|
|
|
|
|
|
|
|
private final PriorityMessageQueue<Message> messagePriorityMessageQueue;
|
|
|
|
|
|
|
|
public QueueManager(int initialCapacity) {
|
|
|
|
messagePriorityMessageQueue = new PriorityMessageQueue<>(new Message[initialCapacity]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void publishMessage(Message message) {
|
|
|
|
messagePriorityMessageQueue.add(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Message receiveMessage() {
|
|
|
|
if (messagePriorityMessageQueue.isEmpty()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return messagePriorityMessageQueue.remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
`Worker` constantly polls `QueueManager` for highest priority message and processes it.
|
|
|
|
|
|
|
|
```java
|
2021-03-13 13:19:21 +01:00
|
|
|
@Slf4j
|
2020-08-30 20:08:34 +03:00
|
|
|
public class Worker {
|
|
|
|
|
|
|
|
private final QueueManager queueManager;
|
|
|
|
|
|
|
|
public Worker(QueueManager queueManager) {
|
|
|
|
this.queueManager = queueManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void run() throws Exception {
|
|
|
|
while (true) {
|
|
|
|
var message = queueManager.receiveMessage();
|
|
|
|
if (message == null) {
|
|
|
|
LOGGER.info("No Message ... waiting");
|
|
|
|
Thread.sleep(200);
|
|
|
|
} else {
|
|
|
|
processMessage(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void processMessage(Message message) {
|
|
|
|
LOGGER.info(message.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Here's the full example how we create an instance of `QueueManager` and process messages using
|
|
|
|
`Worker`.
|
|
|
|
|
|
|
|
```java
|
|
|
|
var queueManager = new QueueManager(100);
|
|
|
|
|
|
|
|
for (var i = 0; i < 100; i++) {
|
|
|
|
queueManager.publishMessage(new Message("Low Message Priority", 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var i = 0; i < 100; i++) {
|
|
|
|
queueManager.publishMessage(new Message("High Message Priority", 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
var worker = new Worker(queueManager);
|
|
|
|
worker.run();
|
|
|
|
```
|
|
|
|
|
|
|
|
Program output:
|
|
|
|
|
|
|
|
```
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='High Message Priority', priority=1}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
Message{message='Low Message Priority', priority=0}
|
|
|
|
No Message ... waiting
|
|
|
|
No Message ... waiting
|
|
|
|
No Message ... waiting
|
|
|
|
```
|
|
|
|
|
2019-08-31 23:40:35 +05:30
|
|
|
|
2019-12-07 20:01:13 +02:00
|
|
|
## Class diagram
|
2020-08-30 20:08:34 +03:00
|
|
|
|
2019-12-07 20:01:13 +02:00
|
|
|

|
|
|
|
|
2019-08-31 23:40:35 +05:30
|
|
|
## Applicability
|
2020-08-30 20:08:34 +03:00
|
|
|
|
|
|
|
Use the Priority Queue pattern when:
|
2019-08-31 23:40:35 +05:30
|
|
|
|
|
|
|
* The system must handle multiple tasks that might have different priorities.
|
2020-08-30 20:08:34 +03:00
|
|
|
* Different users or tenants should be served with different priority.
|
2019-08-31 23:40:35 +05:30
|
|
|
|
2020-07-26 11:30:42 +03:00
|
|
|
## Credits
|
2019-08-31 23:40:35 +05:30
|
|
|
|
2020-07-26 11:30:42 +03:00
|
|
|
* [Priority Queue pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/priority-queue)
|