@cargolift-cdi/util-rabbitmq 0.2.21 → 0.2.23
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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/rabbitmq-retry.helper.d.ts +21 -0
- package/dist/rabbitmq-retry.helper.d.ts.map +1 -0
- package/dist/rabbitmq-retry.helper.js +64 -0
- package/dist/rabbitmq-retry.helper.js.map +1 -0
- package/dist/rabbitmq-topology.service.d.ts +5 -6
- package/dist/rabbitmq-topology.service.d.ts.map +1 -1
- package/dist/rabbitmq-topology.service.js +48 -28
- package/dist/rabbitmq-topology.service.js.map +1 -1
- package/dist/rabbitmq.module.d.ts.map +1 -1
- package/dist/rabbitmq.module.js +3 -2
- package/dist/rabbitmq.module.js.map +1 -1
- package/dist/rmq-connection.service.d.ts +56 -0
- package/dist/rmq-connection.service.d.ts.map +1 -0
- package/dist/rmq-connection.service.js +245 -0
- package/dist/rmq-connection.service.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,4 +9,6 @@ export * from './rabbitmq-publisher-dlq.service.js';
|
|
|
9
9
|
export * from './rabbitmq-event-publisher.service.js';
|
|
10
10
|
export * from './rabbitmq.module.js';
|
|
11
11
|
export * from './rabbitmq-topology.service.js';
|
|
12
|
+
export * from './rmq-connection.service.js';
|
|
13
|
+
export * from './rabbitmq-retry.helper.js';
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,4 +9,6 @@ export * from './rabbitmq-publisher-dlq.service.js';
|
|
|
9
9
|
export * from './rabbitmq-event-publisher.service.js';
|
|
10
10
|
export * from './rabbitmq.module.js';
|
|
11
11
|
export * from './rabbitmq-topology.service.js';
|
|
12
|
+
export * from './rmq-connection.service.js';
|
|
13
|
+
export * from './rabbitmq-retry.helper.js';
|
|
12
14
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ConsumeMessage } from "amqplib";
|
|
2
|
+
import type { LoggerContextService } from "@cargolift-cdi/common";
|
|
3
|
+
type ChannelLike = {
|
|
4
|
+
publish: (exchange: string, routingKey: string, content: Buffer, options?: any) => boolean;
|
|
5
|
+
ack: (msg: any) => void;
|
|
6
|
+
};
|
|
7
|
+
export type RetrySchedule = "short" | "long";
|
|
8
|
+
export interface ScheduleRetryOptions {
|
|
9
|
+
inboundExchange: string;
|
|
10
|
+
routingKey?: string;
|
|
11
|
+
maxShort?: number;
|
|
12
|
+
maxLong?: number;
|
|
13
|
+
headersBase?: Record<string, any>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Agenda retry curto/long para mensagens transitórias. Retorna true se reagendou; false caso contrário.
|
|
17
|
+
* Se reagendado, faz ack da mensagem original.
|
|
18
|
+
*/
|
|
19
|
+
export declare function scheduleTransientRetry(channel: ChannelLike, msg: ConsumeMessage, logger: LoggerContextService, opts: ScheduleRetryOptions): boolean;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=rabbitmq-retry.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rabbitmq-retry.helper.d.ts","sourceRoot":"","sources":["../src/rabbitmq-retry.helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;IAC3F,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,oBAAoB,GACzB,OAAO,CA2ET"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agenda retry curto/long para mensagens transitórias. Retorna true se reagendou; false caso contrário.
|
|
3
|
+
* Se reagendado, faz ack da mensagem original.
|
|
4
|
+
*/
|
|
5
|
+
export function scheduleTransientRetry(channel, msg, logger, opts) {
|
|
6
|
+
const inbound = opts.inboundExchange || process.env.RABBITMQ_EXCHANGE_INBOUND || "inbound";
|
|
7
|
+
const maxShort = Number(process.env.RETRY_POLICY_MAX_SHORT) || opts.maxShort || 3;
|
|
8
|
+
const maxLong = Number(process.env.RETRY_POLICY_MAX_LONG) || opts.maxLong || 3;
|
|
9
|
+
const baseHeaders = (msg.properties?.headers ?? {});
|
|
10
|
+
const headers = { ...baseHeaders };
|
|
11
|
+
if (opts.headersBase)
|
|
12
|
+
Object.assign(headers, opts.headersBase);
|
|
13
|
+
headers["x-trace"] = logger.getContext()?.trace || headers["x-trace"]; // preserva ou injeta trace
|
|
14
|
+
const currentRetry = Number(headers["x-retry-count"] ?? 0) || 0;
|
|
15
|
+
const routingKey = opts.routingKey || msg.fields?.routingKey;
|
|
16
|
+
if (!routingKey) {
|
|
17
|
+
logger.error(`[retry] routingKey ausente para agendamento de retry.`, {
|
|
18
|
+
data: { rabbitmq: { exchange: inbound } },
|
|
19
|
+
});
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
let targetExchange = "";
|
|
23
|
+
let schedule = "";
|
|
24
|
+
if (currentRetry < maxShort) {
|
|
25
|
+
targetExchange = `${inbound}.retry.short`;
|
|
26
|
+
schedule = "short";
|
|
27
|
+
}
|
|
28
|
+
else if (currentRetry < maxShort + maxLong) {
|
|
29
|
+
targetExchange = `${inbound}.retry.long`;
|
|
30
|
+
schedule = "long";
|
|
31
|
+
}
|
|
32
|
+
if (!targetExchange)
|
|
33
|
+
return false;
|
|
34
|
+
const nextHeaders = {
|
|
35
|
+
...headers,
|
|
36
|
+
"x-retry-count": currentRetry + 1,
|
|
37
|
+
"x-retry-schedule": schedule,
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
const ok = channel.publish(targetExchange, routingKey, msg.content, {
|
|
41
|
+
headers: nextHeaders,
|
|
42
|
+
persistent: true,
|
|
43
|
+
contentType: msg.properties.contentType,
|
|
44
|
+
contentEncoding: msg.properties.contentEncoding,
|
|
45
|
+
correlationId: msg.properties.correlationId,
|
|
46
|
+
messageId: msg.properties.messageId,
|
|
47
|
+
timestamp: msg.properties.timestamp,
|
|
48
|
+
});
|
|
49
|
+
if (!ok) {
|
|
50
|
+
// backpressure sinalizado; sem callback em channel normal
|
|
51
|
+
logger.warn?.(`[retry] Backpressure ao publicar retry (${schedule}). Mensagem será confirmada pelo broker de forma assíncrona.`);
|
|
52
|
+
}
|
|
53
|
+
logger.warn?.(`[retry] Agendando retry (${schedule}). Tentativa ${currentRetry + 1}/${maxShort + maxLong}.`, {
|
|
54
|
+
data: { rabbitmq: { exchange: targetExchange, routingKey } },
|
|
55
|
+
});
|
|
56
|
+
channel.ack(msg);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
logger.error?.(`[retry] Falha ao publicar retry (${schedule}). Motivo: ${e?.message}`, { data: { rabbitmq: { exchange: targetExchange, routingKey } } });
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=rabbitmq-retry.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rabbitmq-retry.helper.js","sourceRoot":"","sources":["../src/rabbitmq-retry.helper.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAoB,EACpB,GAAmB,EACnB,MAA4B,EAC5B,IAA0B;IAE1B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,SAAS,CAAC;IAC3F,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAClF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;IAE/E,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,IAAI,EAAE,CAAwB,CAAC;IAC3E,MAAM,OAAO,GAAwB,EAAE,GAAG,WAAW,EAAE,CAAC;IACxD,IAAI,IAAI,CAAC,WAAW;QAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC/D,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,2BAA2B;IAClG,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAK,GAAG,CAAC,MAAc,EAAE,UAAU,CAAC;IAEtE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,uDAAuD,EAAE;YACpE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;SACnC,CAAC,CAAC;QACV,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,cAAc,GAAG,EAAE,CAAC;IACxB,IAAI,QAAQ,GAAuB,EAAE,CAAC;IACtC,IAAI,YAAY,GAAG,QAAQ,EAAE,CAAC;QAC5B,cAAc,GAAG,GAAG,OAAO,cAAc,CAAC;QAC1C,QAAQ,GAAG,OAAO,CAAC;IACrB,CAAC;SAAM,IAAI,YAAY,GAAG,QAAQ,GAAG,OAAO,EAAE,CAAC;QAC7C,cAAc,GAAG,GAAG,OAAO,aAAa,CAAC;QACzC,QAAQ,GAAG,MAAM,CAAC;IACpB,CAAC;IAED,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAElC,MAAM,WAAW,GAAwB;QACvC,GAAG,OAAO;QACV,eAAe,EAAE,YAAY,GAAG,CAAC;QACjC,kBAAkB,EAAE,QAAQ;KAC7B,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CACxB,cAAc,EACd,UAAU,EACV,GAAG,CAAC,OAAO,EACX;YACE,OAAO,EAAE,WAAW;YACpB,UAAU,EAAE,IAAI;YAChB,WAAW,EAAE,GAAG,CAAC,UAAU,CAAC,WAAW;YACvC,eAAe,EAAE,GAAG,CAAC,UAAU,CAAC,eAAe;YAC/C,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,aAAa;YAC3C,SAAS,EAAE,GAAG,CAAC,UAAU,CAAC,SAAS;YACnC,SAAS,EAAE,GAAG,CAAC,UAAU,CAAC,SAAS;SACpC,CACF,CAAC;QAEF,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,0DAA0D;YAC1D,MAAM,CAAC,IAAI,EAAE,CACX,2CAA2C,QAAQ,8DAA8D,CAClH,CAAC;QACJ,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,CACX,4BAA4B,QAAQ,gBAAgB,YAAY,GAAG,CAAC,IAAI,QAAQ,GAAG,OAAO,GAAG,EAC7F;YACE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE;SACtD,CACT,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,EAAE,CACZ,oCAAoC,QAAQ,cAAe,CAAS,EAAE,OAAO,EAAE,EAC/E,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,EAAS,CACxE,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -4,16 +4,15 @@ interface RabbitMQTopologyRetry {
|
|
|
4
4
|
}
|
|
5
5
|
export interface RabbitMQTopology {
|
|
6
6
|
exchange: string;
|
|
7
|
-
type: string;
|
|
8
7
|
queues: RabbitMQTopologyQueue[];
|
|
9
8
|
alternateExchange?: {
|
|
10
9
|
exchange: string;
|
|
11
10
|
queue: string;
|
|
12
11
|
};
|
|
13
|
-
deadLetterExchange
|
|
12
|
+
deadLetterExchange?: {
|
|
14
13
|
exchange: string;
|
|
15
14
|
};
|
|
16
|
-
retry
|
|
15
|
+
retry?: {
|
|
17
16
|
exchanges: RabbitMQTopologyRetry;
|
|
18
17
|
policy_short_ms: number;
|
|
19
18
|
policy_long_ms: number;
|
|
@@ -22,11 +21,11 @@ export interface RabbitMQTopology {
|
|
|
22
21
|
export interface RabbitMQTopologyQueue {
|
|
23
22
|
queue: string;
|
|
24
23
|
routingKey: string;
|
|
25
|
-
deadLetterQueue
|
|
24
|
+
deadLetterQueue?: {
|
|
26
25
|
name: string;
|
|
27
26
|
routingKey: string;
|
|
28
27
|
};
|
|
29
|
-
retryQueues
|
|
28
|
+
retryQueues?: RabbitMQTopologyRetry;
|
|
30
29
|
}
|
|
31
30
|
/**
|
|
32
31
|
* RabbitMQTopologyService
|
|
@@ -38,7 +37,7 @@ export declare class RabbitMQTopologyService {
|
|
|
38
37
|
/**
|
|
39
38
|
* Cria exchanges, AE (fanout), DLX (topic), unrouteable queue e exchanges de retry (inbound).
|
|
40
39
|
*/
|
|
41
|
-
|
|
40
|
+
setupExchange(topology: RabbitMQTopology): Promise<void>;
|
|
42
41
|
/**
|
|
43
42
|
* Cria filas por sistema e faz binds necessários. Para inbound, cria filas de retry com TTLs.
|
|
44
43
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rabbitmq-topology.service.d.ts","sourceRoot":"","sources":["../src/rabbitmq-topology.service.ts"],"names":[],"mappings":"AAIA,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,
|
|
1
|
+
{"version":3,"file":"rabbitmq-topology.service.d.ts","sourceRoot":"","sources":["../src/rabbitmq-topology.service.ts"],"names":[],"mappings":"AAIA,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,iBAAiB,CAAC,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,kBAAkB,CAAC,EAAE;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IAEF,KAAK,CAAC,EAAE;QACN,SAAS,EAAE,qBAAqB,CAAC;QACjC,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CAGH;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACrC;AAED;;;;GAIG;AACH,qBACa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;IAEnE;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+C9D;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;CAuF9D"}
|
|
@@ -19,7 +19,7 @@ let RabbitMQTopologyService = RabbitMQTopologyService_1 = class RabbitMQTopology
|
|
|
19
19
|
/**
|
|
20
20
|
* Cria exchanges, AE (fanout), DLX (topic), unrouteable queue e exchanges de retry (inbound).
|
|
21
21
|
*/
|
|
22
|
-
async
|
|
22
|
+
async setupExchange(topology) {
|
|
23
23
|
const ch = await getRabbitConnectionManager().createChannel();
|
|
24
24
|
try {
|
|
25
25
|
// Alternate Exchange (inbound.ae)
|
|
@@ -47,7 +47,7 @@ let RabbitMQTopologyService = RabbitMQTopologyService_1 = class RabbitMQTopology
|
|
|
47
47
|
await ch.assertQueue(topology.alternateExchange.queue, { durable: true });
|
|
48
48
|
await ch.bindQueue(topology.alternateExchange.queue, topology.alternateExchange.exchange, "");
|
|
49
49
|
}
|
|
50
|
-
this.logger.log(`[
|
|
50
|
+
this.logger.log(`[RabbitMQ] Topology Queue = queue exchange: ${topology.exchange} ` +
|
|
51
51
|
(topology.alternateExchange ? ` | AE: ${topology.alternateExchange.exchange}` : "") +
|
|
52
52
|
(topology.deadLetterExchange ? ` | DLX: ${topology.deadLetterExchange.exchange}` : ""));
|
|
53
53
|
await this.setupQueue(topology);
|
|
@@ -67,32 +67,18 @@ let RabbitMQTopologyService = RabbitMQTopologyService_1 = class RabbitMQTopology
|
|
|
67
67
|
try {
|
|
68
68
|
for (const queue of topology.queues) {
|
|
69
69
|
// Dead Letter Queue (inbound.sistema.dlq)
|
|
70
|
-
await ch.assertQueue(queue.deadLetterQueue.name, { durable: true });
|
|
71
|
-
// Fila principal (inbound.sistema)
|
|
72
|
-
// x-dead-letter-exchange: (inbound.dlx)
|
|
73
|
-
// x-dead-letter-routing-key: (integration.sistema.dead)
|
|
74
|
-
await ch.assertQueue(queue.queue, {
|
|
75
|
-
durable: true,
|
|
76
|
-
arguments: {
|
|
77
|
-
"x-dead-letter-exchange": topology.deadLetterExchange?.exchange,
|
|
78
|
-
"x-dead-letter-routing-key": queue.deadLetterQueue?.routingKey,
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
await ch.bindQueue(queue.queue, topology.exchange, queue.routingKey);
|
|
82
|
-
await ch.bindQueue(queue.deadLetterQueue.name, topology.deadLetterExchange.exchange, queue.deadLetterQueue.routingKey);
|
|
83
70
|
try {
|
|
84
|
-
|
|
85
|
-
durable: true
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
await ch.assertQueue(queue.retryQueues.long, {
|
|
71
|
+
if (queue.deadLetterQueue) {
|
|
72
|
+
await ch.assertQueue(queue.deadLetterQueue.name, { durable: true });
|
|
73
|
+
}
|
|
74
|
+
// Fila principal (inbound.sistema)
|
|
75
|
+
// x-dead-letter-exchange: (inbound.dlx)
|
|
76
|
+
// x-dead-letter-routing-key: (integration.sistema.dead)
|
|
77
|
+
await ch.assertQueue(queue.queue, {
|
|
92
78
|
durable: true,
|
|
93
79
|
arguments: {
|
|
94
|
-
"x-
|
|
95
|
-
"x-dead-letter-
|
|
80
|
+
"x-dead-letter-exchange": topology.deadLetterExchange?.exchange || "",
|
|
81
|
+
"x-dead-letter-routing-key": queue.deadLetterQueue?.routingKey,
|
|
96
82
|
},
|
|
97
83
|
});
|
|
98
84
|
}
|
|
@@ -104,9 +90,43 @@ let RabbitMQTopologyService = RabbitMQTopologyService_1 = class RabbitMQTopology
|
|
|
104
90
|
}
|
|
105
91
|
throw e;
|
|
106
92
|
}
|
|
107
|
-
await ch.bindQueue(queue.
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
await ch.bindQueue(queue.queue, topology.exchange, queue.routingKey);
|
|
94
|
+
if (topology.deadLetterExchange && queue.deadLetterQueue) {
|
|
95
|
+
await ch.bindQueue(queue.deadLetterQueue.name, topology.deadLetterExchange.exchange || "", queue.deadLetterQueue.routingKey);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
if (topology.retry && queue.retryQueues) {
|
|
99
|
+
await ch.assertQueue(queue.retryQueues.short, {
|
|
100
|
+
durable: true,
|
|
101
|
+
arguments: {
|
|
102
|
+
"x-message-ttl": topology.retry.policy_short_ms,
|
|
103
|
+
"x-dead-letter-exchange": topology.exchange,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
await ch.assertQueue(queue.retryQueues.long, {
|
|
107
|
+
durable: true,
|
|
108
|
+
arguments: {
|
|
109
|
+
"x-message-ttl": topology.retry.policy_long_ms,
|
|
110
|
+
"x-dead-letter-exchange": topology.exchange,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
const msg = String(e?.message ?? e ?? "");
|
|
117
|
+
if (/PRECONDITION[_-]FAILED|inequivalent arg/i.test(msg)) {
|
|
118
|
+
// Sinaliza claramente drift de topologia
|
|
119
|
+
throw new Error(`RabbitMQ topology drift: ${msg}. Filas de retry existentes para '${queue.queue}' com TTL/args diferentes dos configurados. Alinhe variáveis ou remova filas manualmente.`);
|
|
120
|
+
}
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
if (topology.retry && queue.retryQueues) {
|
|
124
|
+
await ch.bindQueue(queue.retryQueues.short, topology.retry.exchanges.short, queue.routingKey);
|
|
125
|
+
await ch.bindQueue(queue.retryQueues.long, topology.retry.exchanges.long, queue.routingKey);
|
|
126
|
+
}
|
|
127
|
+
this.logger.log(`[RabbitMQ] Topology Queue => queue ${queue.queue}` +
|
|
128
|
+
(queue.deadLetterQueue ? ` | DLQ: ${queue.deadLetterQueue.name}` : "") +
|
|
129
|
+
` | exchange: ${topology.exchange} | bind: ${queue.routingKey}`);
|
|
110
130
|
}
|
|
111
131
|
return "topology.queue";
|
|
112
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rabbitmq-topology.service.js","sourceRoot":"","sources":["../src/rabbitmq-topology.service.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"rabbitmq-topology.service.js","sourceRoot":"","sources":["../src/rabbitmq-topology.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rabbitmq.module.d.ts","sourceRoot":"","sources":["../src/rabbitmq.module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rabbitmq.module.d.ts","sourceRoot":"","sources":["../src/rabbitmq.module.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,qBAIa,cAAc;CAAG"}
|
package/dist/rabbitmq.module.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Module } from '@nestjs/common';
|
|
|
8
8
|
import { RabbitMQPublisherService } from './rabbitmq-publisher.service.js';
|
|
9
9
|
import { RabbitMQEventPublisherService } from './rabbitmq-event-publisher.service.js';
|
|
10
10
|
import { LoggerContextService } from '@cargolift-cdi/common';
|
|
11
|
+
import { RmqConnectionService } from './rmq-connection.service.js';
|
|
11
12
|
/**
|
|
12
13
|
* RabbitmqModule
|
|
13
14
|
* Fornece publishers de RabbitMQ (baixo e alto nível) prontos para injeção.
|
|
@@ -17,8 +18,8 @@ let RabbitmqModule = class RabbitmqModule {
|
|
|
17
18
|
};
|
|
18
19
|
RabbitmqModule = __decorate([
|
|
19
20
|
Module({
|
|
20
|
-
providers: [LoggerContextService, RabbitMQPublisherService, RabbitMQEventPublisherService],
|
|
21
|
-
exports: [RabbitMQEventPublisherService, RabbitMQPublisherService]
|
|
21
|
+
providers: [LoggerContextService, RmqConnectionService, RabbitMQPublisherService, RabbitMQEventPublisherService],
|
|
22
|
+
exports: [RmqConnectionService, RabbitMQEventPublisherService, RabbitMQPublisherService]
|
|
22
23
|
})
|
|
23
24
|
], RabbitmqModule);
|
|
24
25
|
export { RabbitmqModule };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rabbitmq.module.js","sourceRoot":"","sources":["../src/rabbitmq.module.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"rabbitmq.module.js","sourceRoot":"","sources":["../src/rabbitmq.module.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE;;;;GAIG;AAKI,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,cAAc;IAJ1B,MAAM,CAAC;QACN,SAAS,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,6BAA6B,CAAC;QAChH,OAAO,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,EAAE,wBAAwB,CAAC;KACzF,CAAC;GACW,cAAc,CAAG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { OnModuleDestroy } from "@nestjs/common";
|
|
2
|
+
import { Channel, ChannelModel } from "amqplib";
|
|
3
|
+
type CreateChannelOptions = {
|
|
4
|
+
prefetch: number;
|
|
5
|
+
onClose?: (err?: any) => void;
|
|
6
|
+
/** Rótulo opcional para logs de canal (ex.: 'consumer inbound.erp', 'publisher') */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Se true, suprime WARN ao fechar canal intencionalmente (mantém DEBUG). */
|
|
9
|
+
silentClose?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Serviço singleton para gerenciar conexão RabbitMQ com lógica de reconexão robusta,
|
|
13
|
+
* detecção de falhas de topologia (PRECONDITION_FAILED) e controle de canais.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RmqConnectionService implements OnModuleDestroy {
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private connection?;
|
|
18
|
+
private connecting;
|
|
19
|
+
private closed;
|
|
20
|
+
private unexpectedChannelCloses;
|
|
21
|
+
constructor();
|
|
22
|
+
/** Pequeno delay utilitário */
|
|
23
|
+
private delay;
|
|
24
|
+
/** Gera número aleatório entre min e max (inclusive) */
|
|
25
|
+
private rand;
|
|
26
|
+
/**
|
|
27
|
+
* Detecta se o erro é um PRECONDITION_FAILED do RabbitMQ
|
|
28
|
+
*/
|
|
29
|
+
private isPreconditionFailed;
|
|
30
|
+
/**
|
|
31
|
+
* Registra um fechamento inesperado de canal e aborta se exceder limiar em janela T.
|
|
32
|
+
*/
|
|
33
|
+
private registerUnexpectedChannelCloseAndMaybeAbort;
|
|
34
|
+
/**
|
|
35
|
+
* Fail-fast shutdown when topology drift is detected (e.g., PRECONDITION_FAILED).
|
|
36
|
+
* Controlled by env RABBITMQ_ABORT_ON_PRECONDITION === 'true'.
|
|
37
|
+
*/
|
|
38
|
+
private abortOnPrecondition;
|
|
39
|
+
/**
|
|
40
|
+
* Obtém a conexão RabbitMQ, aguardando reconexão se necessário.
|
|
41
|
+
* @returns Canal RabbitMQ confirmável (ChannelModel)
|
|
42
|
+
*/
|
|
43
|
+
getConnection(): Promise<ChannelModel>;
|
|
44
|
+
/**
|
|
45
|
+
* Cria um canal RabbitMQ com prefetch e handlers de erro/fechamento.
|
|
46
|
+
* @param opts Opções de criação de canal (CreateChannelOptions)
|
|
47
|
+
* @returns Canal RabbitMQ (Channel)
|
|
48
|
+
*/
|
|
49
|
+
createChannel(opts: CreateChannelOptions): Promise<Channel>;
|
|
50
|
+
/**
|
|
51
|
+
* Fecha a conexão RabbitMQ e todos os canais.
|
|
52
|
+
*/
|
|
53
|
+
onModuleDestroy(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=rmq-connection.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rmq-connection.service.d.ts","sourceRoot":"","sources":["../src/rmq-connection.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAW,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzD,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,qBACa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAEhE,OAAO,CAAC,UAAU,CAAC,CAAe;IAClC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,uBAAuB,CAAgB;;IAI/C,+BAA+B;YACjB,KAAK;IAInB,wDAAwD;IACxD,OAAO,CAAC,IAAI;IAIZ;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,OAAO,CAAC,2CAA2C;IA0BnD;;;OAGG;YACW,mBAAmB;IAqBjC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,YAAY,CAAC;IAkF5C;;;;OAIG;IACG,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA8CjE;;OAEG;IACG,eAAe;CAYtB"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var RmqConnectionService_1;
|
|
11
|
+
import { ApplicationError } from "@cargolift-cdi/common";
|
|
12
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
13
|
+
import { connect } from "amqplib";
|
|
14
|
+
/**
|
|
15
|
+
* Serviço singleton para gerenciar conexão RabbitMQ com lógica de reconexão robusta,
|
|
16
|
+
* detecção de falhas de topologia (PRECONDITION_FAILED) e controle de canais.
|
|
17
|
+
*/
|
|
18
|
+
let RmqConnectionService = RmqConnectionService_1 = class RmqConnectionService {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.logger = new Logger(RmqConnectionService_1.name);
|
|
21
|
+
this.connecting = false;
|
|
22
|
+
this.closed = false;
|
|
23
|
+
// Janela deslizante de fechamentos inesperados de canal (timestamps em ms)
|
|
24
|
+
this.unexpectedChannelCloses = [];
|
|
25
|
+
}
|
|
26
|
+
/** Pequeno delay utilitário */
|
|
27
|
+
async delay(ms) {
|
|
28
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
29
|
+
}
|
|
30
|
+
/** Gera número aleatório entre min e max (inclusive) */
|
|
31
|
+
rand(min, max) {
|
|
32
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Detecta se o erro é um PRECONDITION_FAILED do RabbitMQ
|
|
36
|
+
*/
|
|
37
|
+
isPreconditionFailed(err) {
|
|
38
|
+
const code = err?.code;
|
|
39
|
+
const msg = String(err?.message ?? err ?? "");
|
|
40
|
+
return code === 406 || /PRECONDITION[_-]FAILED/i.test(msg) || /inequivalent arg/i.test(msg);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Registra um fechamento inesperado de canal e aborta se exceder limiar em janela T.
|
|
44
|
+
*/
|
|
45
|
+
registerUnexpectedChannelCloseAndMaybeAbort(label) {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const maxCount = Number(process.env.RABBITMQ_CHANNEL_CLOSE_BURST_COUNT) || 5;
|
|
48
|
+
const windowMs = Number(process.env.RABBITMQ_CHANNEL_CLOSE_BURST_WINDOW_MS) || 30000;
|
|
49
|
+
// adiciona e limpa fora da janela
|
|
50
|
+
this.unexpectedChannelCloses.push(now);
|
|
51
|
+
const threshold = now - windowMs;
|
|
52
|
+
this.unexpectedChannelCloses = this.unexpectedChannelCloses.filter((t) => t >= threshold);
|
|
53
|
+
if (this.unexpectedChannelCloses.length >= maxCount) {
|
|
54
|
+
this.logger.error(`[RabbitMQ] Fechamentos inesperados de canal: ${this.unexpectedChannelCloses.length}/${maxCount} em ${windowMs}ms` +
|
|
55
|
+
(label ? ` (ex.: ${label})` : "") +
|
|
56
|
+
`. Abortando processo por instabilidade persistente.`);
|
|
57
|
+
// Marcamos shutdown para evitar reconexões em background
|
|
58
|
+
this.closed = true;
|
|
59
|
+
try {
|
|
60
|
+
void this.connection?.close();
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
setTimeout(() => process.exit(1), 25);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fail-fast shutdown when topology drift is detected (e.g., PRECONDITION_FAILED).
|
|
68
|
+
* Controlled by env RABBITMQ_ABORT_ON_PRECONDITION === 'true'.
|
|
69
|
+
*/
|
|
70
|
+
async abortOnPrecondition(reason, err) {
|
|
71
|
+
const shouldAbort = String(process.env.RABBITMQ_ABORT_ON_PRECONDITION || "true").toLowerCase() === "true";
|
|
72
|
+
this.logger.error(`[RabbitMQ] PRECONDITION-FAILED detectado. ${reason}`);
|
|
73
|
+
if (!shouldAbort) {
|
|
74
|
+
this.logger.warn("[RabbitMQ] Abortar está desabilitado (RABBITMQ_ABORT_ON_PRECONDITION != true). Mantendo processo vivo para tentativa de reconexão.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Evita novas conexões e tenta fechar a atual
|
|
78
|
+
this.closed = true;
|
|
79
|
+
try {
|
|
80
|
+
await this.connection?.close();
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
// Pequeno atraso para flush de logs e então encerra processo
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
// Use exit(1) para sinalizar falha de inicialização/configuração
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}, 50);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Obtém a conexão RabbitMQ, aguardando reconexão se necessário.
|
|
91
|
+
* @returns Canal RabbitMQ confirmável (ChannelModel)
|
|
92
|
+
*/
|
|
93
|
+
async getConnection() {
|
|
94
|
+
if (this.closed)
|
|
95
|
+
throw new ApplicationError("[RabbitMQ] Conexão requisitada após shutdown");
|
|
96
|
+
if (this.connection)
|
|
97
|
+
return this.connection;
|
|
98
|
+
if (this.connecting) {
|
|
99
|
+
// aguarda até conectar
|
|
100
|
+
while (!this.connection && !this.closed) {
|
|
101
|
+
await this.delay(50);
|
|
102
|
+
}
|
|
103
|
+
if (this.connection)
|
|
104
|
+
return this.connection;
|
|
105
|
+
throw new ApplicationError("[RabbitMQ] Não foi possível conectar ao RabbitMQ");
|
|
106
|
+
}
|
|
107
|
+
this.connecting = true;
|
|
108
|
+
const url = process.env.RABBITMQ_URL;
|
|
109
|
+
if (!url)
|
|
110
|
+
throw new ApplicationError("[RabbitMQ] RABBITMQ_URL não configurada");
|
|
111
|
+
// Aplica heartbeat via querystring se não definido, para detectar quedas de rede mais rápido
|
|
112
|
+
const hbSec = Number(process.env.RABBITMQ_HEARTBEAT_SECONDS) || 15;
|
|
113
|
+
const urlHasHb = /([?&])heartbeat=/.test(url);
|
|
114
|
+
let finalUrl = url;
|
|
115
|
+
if (!urlHasHb) {
|
|
116
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
117
|
+
finalUrl = `${url}${sep}heartbeat=${hbSec}`;
|
|
118
|
+
}
|
|
119
|
+
let retryAttempts = 0;
|
|
120
|
+
while (!this.connection && !this.closed) {
|
|
121
|
+
retryAttempts++;
|
|
122
|
+
if (retryAttempts >= 10) {
|
|
123
|
+
this.connecting = false;
|
|
124
|
+
this.logger.log(`[RabbitMQ] Não foi possível conectar ao RabbitMQ após múltiplas tentativas`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
this.logger.log(`[RabbitMQ] Conectando ao RabbitMQ em ${finalUrl}...`);
|
|
129
|
+
const conn = await connect(finalUrl);
|
|
130
|
+
conn.on("error", (err) => {
|
|
131
|
+
if (this.isPreconditionFailed(err)) {
|
|
132
|
+
void this.abortOnPrecondition("Erro de conexão relacionado a precondition (verifique TTL/args de filas/exchanges).", err);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.logger.error(`[RabbitMQ] Erro de Conexão: ${err?.message ?? err}`);
|
|
136
|
+
// Em alguns cenários de perda de rede, apenas 'error' dispara e 'close' pode demorar.
|
|
137
|
+
// Proativamente iniciamos reconexão.
|
|
138
|
+
if (!this.closed && this.connection === conn) {
|
|
139
|
+
this.connection = undefined;
|
|
140
|
+
this.connecting = false;
|
|
141
|
+
this.getConnection().catch((e) => this.logger.error(`[RabbitMQ] Falha reconectar (via error): ${e?.message ?? e}.`));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
conn.on("close", () => {
|
|
145
|
+
this.logger.warn("[RabbitMQ] Conexão fechada, iniciando reconexão...");
|
|
146
|
+
this.connection = undefined;
|
|
147
|
+
if (!this.closed) {
|
|
148
|
+
// reconectar em background
|
|
149
|
+
this.connecting = false;
|
|
150
|
+
this.getConnection().catch((e) => this.logger.error(`[RabbitMQ] Falha reconectar: ${e?.message ?? e}.`));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
this.connection = conn;
|
|
154
|
+
this.logger.log("[RabbitMQ] Conectado ao RabbitMQ.");
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
const minMs = Number(process.env.RABBITMQ_RECONNECT_MIN_MS) || 1000;
|
|
158
|
+
const maxMs = Number(process.env.RABBITMQ_RECONNECT_MAX_MS) || 5000;
|
|
159
|
+
const wait = this.rand(minMs, maxMs);
|
|
160
|
+
this.logger.error(`[RabbitMQ] Falha ao conectar: ${e?.message ?? e}. Tentando em ${wait}ms. Tentativa #${retryAttempts}/10`);
|
|
161
|
+
await this.delay(wait);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
this.connecting = false;
|
|
165
|
+
if (!this.connection)
|
|
166
|
+
throw new ApplicationError("[RabbitMQ] Não foi possível conectar ao RabbitMQ");
|
|
167
|
+
return this.connection;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Cria um canal RabbitMQ com prefetch e handlers de erro/fechamento.
|
|
171
|
+
* @param opts Opções de criação de canal (CreateChannelOptions)
|
|
172
|
+
* @returns Canal RabbitMQ (Channel)
|
|
173
|
+
*/
|
|
174
|
+
async createChannel(opts) {
|
|
175
|
+
const connection = await this.getConnection();
|
|
176
|
+
const ch = await connection.createChannel();
|
|
177
|
+
const parsedPrefetch = Number(opts.prefetch);
|
|
178
|
+
const safePrefetch = Number.isFinite(parsedPrefetch) && parsedPrefetch > 0 ? Math.floor(parsedPrefetch) : 1;
|
|
179
|
+
await ch.prefetch(safePrefetch);
|
|
180
|
+
const label = opts.label ? ` (${opts.label})` : "";
|
|
181
|
+
// Detecta fechamento intencional para reduzir ruído de log
|
|
182
|
+
let closedByUs = false;
|
|
183
|
+
const originalClose = ch.close.bind(ch);
|
|
184
|
+
// sobrescrevemos método para marcar fechamento intencional (tipagem flexível)
|
|
185
|
+
ch.close = async (...args) => {
|
|
186
|
+
closedByUs = true;
|
|
187
|
+
return originalClose(...args);
|
|
188
|
+
};
|
|
189
|
+
ch.on("error", (err) => {
|
|
190
|
+
if (this.isPreconditionFailed(err)) {
|
|
191
|
+
// Falha de topologia: aborta (opcional) com mensagem clara
|
|
192
|
+
void this.abortOnPrecondition("Canal fechado por PRECONDITION_FAILED (ex.: inequivalent arg). Alinhar configuração do RabbitMQ e reiniciar o serviço.", err);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
this.logger.error(`[RabbitMQ] Erro no canal${label}: ${err?.message ?? err}`);
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
opts.onClose?.(err);
|
|
199
|
+
}
|
|
200
|
+
catch { }
|
|
201
|
+
// Evita throw dentro de event handler (pode causar unhandled exception sem controle)
|
|
202
|
+
});
|
|
203
|
+
ch.on("close", () => {
|
|
204
|
+
if (closedByUs || opts.silentClose) {
|
|
205
|
+
// Fechamento esperado/intencional (ex.: publisher/topologia). Rebaixa para DEBUG.
|
|
206
|
+
if (process.env.LOG_LEVEL === "debug") {
|
|
207
|
+
this.logger.debug(`[RabbitMQ] Canal fechado${label} (fechamento intencional)`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.logger.warn(`[RabbitMQ] Canal fechado${label}`);
|
|
212
|
+
// Registra para possível abort se for uma rajada de fechamentos
|
|
213
|
+
this.registerUnexpectedChannelCloseAndMaybeAbort(opts.label);
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
opts.onClose?.();
|
|
217
|
+
}
|
|
218
|
+
catch { }
|
|
219
|
+
});
|
|
220
|
+
return ch;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Fecha a conexão RabbitMQ e todos os canais.
|
|
224
|
+
*/
|
|
225
|
+
async onModuleDestroy() {
|
|
226
|
+
this.closed = true;
|
|
227
|
+
if (this.connection) {
|
|
228
|
+
try {
|
|
229
|
+
await this.connection.close();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// ignora
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
this.connection = undefined;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
RmqConnectionService = RmqConnectionService_1 = __decorate([
|
|
241
|
+
Injectable(),
|
|
242
|
+
__metadata("design:paramtypes", [])
|
|
243
|
+
], RmqConnectionService);
|
|
244
|
+
export { RmqConnectionService };
|
|
245
|
+
//# sourceMappingURL=rmq-connection.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rmq-connection.service.js","sourceRoot":"","sources":["../src/rmq-connection.service.ts"],"names":[],"mappings":"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"}
|