Исследование различной семантики доставки при разработке программного обеспечения

При разработке программного обеспечения семантика доставки относится к гарантиям и поведению, связанным с доставкой сообщений или событий между различными компонентами или системами. Крайне важно понимать эту семантику доставки для создания надежных и эффективных распределенных систем. В этой статье мы рассмотрим несколько методов семантики доставки, обычно используемых при разработке программного обеспечения, а также примеры кода.

  1. Семантика двухточечного соединения (запрос-ответ):

Семантика доставки «точка-точка» включает в себя шаблон обмена сообщениями, при котором отправитель (запрашивающая сторона) отправляет сообщение конкретному получателю (ответчику) и ожидает ответа. Этот шаблон обычно используется в системах 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()
  1. Семантика публикации-подписки:

Семантика доставки публикации-подписки включает в себя шаблон обмена сообщениями, при котором отправитель (издатель) отправляет сообщение нескольким получателям (подписчикам), не зная, кто являются подписчиками. Этот шаблон обычно используется в архитектурах, управляемых событиями. Вот пример использования 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());
    }
}
  1. Семантика порядка сообщений:

Семантика доставки сообщений «Упорядочение сообщений» гарантирует, что сообщения будут потребляться подписчиками в том же порядке, в котором они были опубликованы. Это важно для поддержания согласованности в системах, управляемых событиями. Вот пример использования 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]);
    }
});