@aitickets123654/common-kafka 1.0.20 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/events/base-listener.js +54 -17
- package/package.json +1 -1
|
@@ -13,26 +13,63 @@ class Listener {
|
|
|
13
13
|
});
|
|
14
14
|
console.log(`Listener connected to Kafka (topic: ${this.topic})`);
|
|
15
15
|
await this.consumer.run({
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
eachBatchAutoResolve: false, // Отключаем автокоммиты — всё контролируем вручную
|
|
17
|
+
eachBatch: async ({ batch, resolveOffset, heartbeat, commitOffsetsIfNecessary, uncommittedOffsets, }) => {
|
|
18
18
|
var _a;
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
if (!value)
|
|
22
|
-
return;
|
|
19
|
+
const { topic, partition } = batch;
|
|
20
|
+
let lastProcessedOffset = null; // запоминаем, до какого оффсета дошли
|
|
23
21
|
try {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
for (const message of batch.messages) {
|
|
23
|
+
const offset = message.offset;
|
|
24
|
+
const value = (_a = message.value) === null || _a === void 0 ? void 0 : _a.toString();
|
|
25
|
+
// Пустое сообщение — пропускаем и фиксируем
|
|
26
|
+
if (!value) {
|
|
27
|
+
resolveOffset(offset);
|
|
28
|
+
lastProcessedOffset = offset;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
let data;
|
|
32
|
+
try {
|
|
33
|
+
data = JSON.parse(value);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.error(`Невалидный JSON ${topic}/${partition}@${offset}`, err);
|
|
37
|
+
resolveOffset(offset); // не мешает прогрессу
|
|
38
|
+
lastProcessedOffset = offset;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
await this.onMessage(data, { topic, partition, message });
|
|
43
|
+
// Помечаем оффсет как обработанный
|
|
44
|
+
resolveOffset(offset);
|
|
45
|
+
lastProcessedOffset = offset;
|
|
46
|
+
// Даём знать брокеру, что живы
|
|
47
|
+
await heartbeat();
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.warn(`Ошибка при обработке ${topic}/${partition}@${offset}:`, err);
|
|
51
|
+
// Откатываемся на оффсет с ошибкой
|
|
52
|
+
// Kafka при следующем цикле снова отдаст это сообщение
|
|
53
|
+
this.consumer.seek({ topic, partition, offset });
|
|
54
|
+
// даём кластерам немного времени (редко, но помогает при гонках)
|
|
55
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
56
|
+
// Выходим из цикла, чтобы не трогать следующие сообщения в батче
|
|
57
|
+
// Отсюда управление перейдет в finally и для успешно обработанных
|
|
58
|
+
// оффсетов выполнится commit
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
33
62
|
}
|
|
34
|
-
|
|
35
|
-
|
|
63
|
+
finally {
|
|
64
|
+
// Коммитим все успешно обработанные оффсеты (если были)
|
|
65
|
+
try {
|
|
66
|
+
if (lastProcessedOffset !== null) {
|
|
67
|
+
await commitOffsetsIfNecessary();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (commitErr) {
|
|
71
|
+
console.error(`commitOffsetsIfNecessary не сработал (${topic}/${partition})`, commitErr);
|
|
72
|
+
}
|
|
36
73
|
}
|
|
37
74
|
},
|
|
38
75
|
});
|