Irbisa · Шпаргалка
Брокеры сообщений: Kafka и RabbitMQ
Партиции, consumer groups и оффсеты, репликация и acks, гарантии доставки, идемпотентный продюсер, retention и compaction, exchange и подтверждения в RabbitMQ, схемы событий
Объясните, что такое топик, партиция, consumer group и оффсет в Kafka и чем Kafka отличается от RabbitMQ.
Какие типы exchange есть в RabbitMQ и как биндинги и routing key решают, в какие очереди попадёт сообщение?
func setup(ch *amqp.Channel) error { // durable: the exchange, the queue and the binding survive a broker restart if err := ch.ExchangeDeclare("orders", amqp.ExchangeTopic, true, false, false, false, nil); err != nil { return err } q, err := ch.QueueDeclare("billing", true, false, false, false, nil)…Что делал ZooKeeper для Kafka и что изменилось, когда Kafka 4.0 перешла на KRaft?
Почему Kafka такая быстрая на обычных дисках и что дают append-only лог, page cache, батчинг и zero-copy?
Сколько Kafka хранит сообщения и чем retention по времени или размеру отличается от log compaction?
Где Kafka хранит закоммиченные оффсеты consumer group, что делает
auto.offset.resetи как переиграть топик с заданного момента?// A group reader: StartOffset applies only to partitions with no committed offset.func newBillingReader(brokers []string) *kafka.Reader { return kafka.NewReader(kafka.ReaderConfig{ Brokers: brokers, GroupID: "billing", Topic: "orders",…События одного заказа приходят не по порядку, а рост consumer group с 12 до 24 участников не уменьшил лаг. 12 партиций, сообщения без ключа. Почему и то и другое?
// wrong: &kafka.Writer{Addr: ..., Topic: "orders"} with keyless messages;// the default balancer is RoundRobin, so one order's events scatter. // right: key by the entity whose order matters, with a balancer that hashes itvar w = &kafka.Writer{ Addr: kafka.TCP("kafka-1:9092", "kafka-2:9092"),…Брокер упал, и часть подтверждённых сообщений пропала. Как
acks, фактор репликации,min.insync.replicasи ISR решают, переживёт ли запись сбой?// Topic: three copies, and writes are refused while fewer than two are in sync.func createPayments(ctx context.Context, c *kafka.Client) error { res, err := c.CreateTopics(ctx, &kafka.CreateTopicsRequest{Topics: []kafka.TopicConfig{{ Topic: "payments", NumPartitions: 12, ReplicationFactor: 3,…От чего защищает
enable.idempotenceу продюсера Kafka, как он сохраняет порядок при ретраях и какие дубли не останавливает?Каждый HTTP-запрос публикует одно событие через этот
kafka.Writerи выполняется чуть больше секунды. Почему так и какой компромисс задаютBatchTimeout,BatchSizeиAsync?var w = &kafka.Writer{ Addr: kafka.TCP("kafka-1:9092", "kafka-2:9092"), Topic: "orders", Balancer: &kafka.Hash{},} // Called once per POST /orders, after the order is saved.func publishCreated(ctx context.Context, o Order) error { return w.WriteMessages(ctx, kafka.Message{ Key: []byte(o.ID), Value: o.JSON(), })}Какой Go-клиент Kafka вы бы выбрали (
franz-go,segmentio/kafka-go,confluent-kafka-go,sarama) и чем придётся платить за каждый?Этот консьюмер коммитит каждый оффсет, а потом отдаёт сообщение пулу воркеров. После вытеснения пода сотни событий молча пропали. Куда они делись?
func consume(ctx context.Context, r *kafka.Reader, jobs chan<- kafka.Message) error { for { m, err := r.FetchMessage(ctx) if err != nil { return err } if err := r.CommitMessages(ctx, m); err != nil { return err } jobs <- m // 16 goroutines read jobs and call handle(ctx, m) }}После переключения брокера консьюмер платежей переигрывает минуту сообщений, и часть клиентов платит дважды. Продукт хочет exactly-once доставку. Что вы строите?
func handle(ctx context.Context, db *sql.DB, r *kafka.Reader, m kafka.Message) error { tx, err := db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() // no-op after Commit…Одно битое сообщение роняет консьюмер, он ретраит его бесконечно, и вся партиция стоит. Как обрабатывать poison-сообщения?
func process(ctx context.Context, r *kafka.Reader, dlq *kafka.Writer, m kafka.Message) error { var err error for attempt := 0; attempt < 3; attempt++ { if err = handle(ctx, m); err == nil { return r.CommitMessages(ctx, m) }…Что нужно, чтобы сообщение RabbitMQ пережило рестарт брокера, и что добавляют publisher confirms и quorum-очереди?
// Once at startup: a replicated queue and a channel in confirm mode.func setup(ch *amqp.Channel) error { _, err := ch.QueueDeclare("payments", true, false, false, false, amqp.Table{ amqp.QueueTypeArg: amqp.QueueTypeQuorum, // replicated with Raft, always durable }) if err != nil {…Найдите баг: когда под воркера убивают посреди задачи, сообщения RabbitMQ, которые он обрабатывал, пропадают, хотя очередь durable, а сообщения persistent.
func work(ctx context.Context, ch *amqp.Channel) error { msgs, err := ch.Consume("thumbnails", "", true, false, false, false, nil) if err != nil { return err } for d := range msgs { if err := render(ctx, d.Body); err != nil { log.Printf("render failed: %v", err) } } return nil}Несколько консьюмеров читают одну очередь RabbitMQ. Кто получает какое сообщение, что происходит с порядком и когда нужен single active consumer?
Должно ли событие
OrderUpdatedнести весь заказ или только его id и чем каждый вариант оборачивается для консьюмеров?type OrderUpdated struct { OrderID string `json:"order_id"` Version int64 `json:"version"` // grows with every change at the source Status string `json:"status"` TotalCents int64 `json:"total_cents"`}…Продюсеры и консьюмеры события деплоятся каждый по своему графику. Как безопасно поменять схему события и что проверяет schema registry?