При разработке программного обеспечения семантика доставки относится к гарантиям и поведению, связанным с доставкой сообщений или событий между различными компонентами или системами. Крайне важно понимать эту семантику доставки для создания надежных и эффективных распределенных систем. В этой статье мы рассмотрим несколько методов семантики доставки, обычно используемых при разработке программного обеспечения, а также примеры кода.
- Семантика двухточечного соединения (запрос-ответ):
Семантика доставки «точка-точка» включает в себя шаблон обмена сообщениями, при котором отправитель (запрашивающая сторона) отправляет сообщение конкретному получателю (ответчику) и ожидает ответа. Этот шаблон обычно используется в системах RPC (удаленный вызов процедур). Вот пример использования Python и RabbitMQ:
# Requester code
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='request_queue')
channel.basic_publish(
exchange='',
routing_key='request_queue',
body='Hello, World!',
properties=pika.BasicProperties(reply_to='response_queue')
)
# Responder code
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='response_queue')
def on_request(ch, method, props, body):
response = 'Hello, back!'
ch.basic_publish(
exchange='',
routing_key=props.reply_to,
body=response
)
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue='request_queue', on_message_callback=on_request)
channel.start_consuming()
- Семантика публикации-подписки:
Семантика доставки публикации-подписки включает в себя шаблон обмена сообщениями, при котором отправитель (издатель) отправляет сообщение нескольким получателям (подписчикам), не зная, кто являются подписчиками. Этот шаблон обычно используется в архитектурах, управляемых событиями. Вот пример использования Java и Apache Kafka:
// Publisher code
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
ProducerRecord<String, String> record = new ProducerRecord<>("topic_name", "Hello, World!");
producer.send(record);
producer.close();
// Subscriber code
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("group.id", "group_name");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("topic_name"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.println(record.value());
}
}
- Семантика порядка сообщений:
Семантика доставки сообщений «Упорядочение сообщений» гарантирует, что сообщения будут потребляться подписчиками в том же порядке, в котором они были опубликованы. Это важно для поддержания согласованности в системах, управляемых событиями. Вот пример использования Node.js и Redis Streams:
// Publisher code
const redis = require('redis');
const client = redis.createClient();
const streamKey = 'stream_key';
const messages = ['Message 1', 'Message 2', 'Message 3'];
for (const message of messages) {
client.xadd(streamKey, '*', 'message', message);
}
// Subscriber code
const redis = require('redis');
const client = redis.createClient();
const streamKey = 'stream_key';
const consumerGroup = 'consumer_group';
const consumerName = 'consumer_name';
client.xgroupCreate(streamKey, consumerGroup, '$', true);
client.xreadgroup('GROUP', consumerGroup, consumerName, 'BLOCK', 0, 'STREAMS', streamKey, '>');
client.on('xreadgroup', (key, group, id, messages) => {
for (const message of messages) {
console.log(message);
client.xack(streamKey, consumerGroup, message[0]);
}
});