rocketmq-client-nodejs
Version:
RocketMQ Node.js Client
450 lines • 41.9 kB
JavaScript
"use strict";
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PushConsumer = void 0;
const definition_pb_1 = require("../../proto/apache/rocketmq/v2/definition_pb");
const service_pb_1 = require("../../proto/apache/rocketmq/v2/service_pb");
const route_1 = require("../route");
const definition_pb_2 = require("../../proto/apache/rocketmq/v2/definition_pb");
const exception_1 = require("../exception");
const util_1 = require("../util");
const Consumer_1 = require("./Consumer");
const FilterExpression_1 = require("./FilterExpression");
const PushSubscriptionSettings_1 = require("./PushSubscriptionSettings");
const StandardConsumeService_1 = require("./StandardConsumeService");
const FifoConsumeService_1 = require("./FifoConsumeService");
const ProcessQueue_1 = require("./ProcessQueue");
const Assignment_1 = require("./Assignment");
const Assignments_1 = require("./Assignments");
const ASSIGNMENT_SCAN_SCHEDULE_DELAY = 1000;
const ASSIGNMENT_SCAN_SCHEDULE_PERIOD = 5000;
class ConsumeMetrics {
receptionTimes = 0;
receivedMessagesQuantity = 0;
consumptionOkQuantity = 0;
consumptionErrorQuantity = 0;
}
class PushConsumer extends Consumer_1.Consumer {
#pushSubscriptionSettings;
#subscriptionExpressions = new Map();
#cacheAssignments = new Map();
#messageListener;
#maxCacheMessageCount;
#maxCacheMessageSizeInBytes;
#enableFifoConsumeAccelerator;
#processQueueTable = new Map();
#metrics = new ConsumeMetrics();
#consumeService;
#scanAssignmentTimer;
constructor(options) {
options.topics = Array.from(options.subscriptions.keys());
super(options);
for (const [topic, filter] of options.subscriptions.entries()) {
if (typeof filter === 'string') {
this.#subscriptionExpressions.set(topic, new FilterExpression_1.FilterExpression(filter));
}
else {
this.#subscriptionExpressions.set(topic, filter);
}
}
this.#messageListener = options.messageListener;
this.#maxCacheMessageCount = options.maxCacheMessageCount ?? 1024;
this.#maxCacheMessageSizeInBytes = options.maxCacheMessageSizeInBytes ?? 64 * 1024 * 1024;
this.#enableFifoConsumeAccelerator = options.enableFifoConsumeAccelerator ?? false;
this.#pushSubscriptionSettings = new PushSubscriptionSettings_1.PushSubscriptionSettings(options.namespace, this.clientId, this.getClientType(), this.endpoints, this.consumerGroup, this.requestTimeout, this.#subscriptionExpressions);
}
async startup() {
this.logger.info('Begin to start the rocketmq push consumer, clientId=%s, consumerGroup=%s', this.clientId, this.consumerGroup);
await super.startup();
this.logger.info('Super startup completed, clientId=%s', this.clientId);
try {
this.#consumeService = this.createConsumeService();
// Start scanning assignments periodically
this.startAssignmentScanning();
this.logger.info('Push consumer started successfully, clientId=%s', this.clientId);
}
catch (err) {
this.logger.error('Failed to start push consumer, cleaning up resources, clientId=%s, error=%s', this.clientId, err);
// Clean up timers if initialization fails
if (this.#scanAssignmentTimer) {
clearInterval(this.#scanAssignmentTimer);
this.#scanAssignmentTimer = undefined;
}
throw err;
}
}
/**
* Start assignment scanning. Can be overridden by subclasses.
*/
startAssignmentScanning() {
this.logger.info('Starting assignment scanning, clientId=%s', this.clientId);
setTimeout(() => this.#scanAssignments(), ASSIGNMENT_SCAN_SCHEDULE_DELAY);
this.#scanAssignmentTimer = setInterval(() => this.#scanAssignments(), ASSIGNMENT_SCAN_SCHEDULE_PERIOD);
}
async shutdown() {
this.logger.info('Begin to shutdown the rocketmq push consumer, clientId=%s', this.clientId);
// Stop scanning assignments
if (this.#scanAssignmentTimer) {
clearInterval(this.#scanAssignmentTimer);
this.#scanAssignmentTimer = undefined;
}
// Drop all process queues
this.logger.info('Dropping all process queues, clientId=%s, queueCount=%d', this.clientId, this.#processQueueTable.size);
for (const { pq } of this.#processQueueTable.values()) {
pq.drop();
pq.abort();
}
this.#processQueueTable.clear();
// Shutdown consume service
if (this.#consumeService) {
this.logger.info('Shutting down consume service, clientId=%s', this.clientId);
this.#consumeService.abort();
}
await super.shutdown();
this.logger.info('Push consumer has been shutdown successfully, clientId=%s', this.clientId);
}
getSettings() {
return this.#pushSubscriptionSettings;
}
/**
* Get the client type for push consumer.
*
* @return The client type identifier
*/
getClientType() {
return definition_pb_1.ClientType.PUSH_CONSUMER;
}
wrapHeartbeatRequest() {
return new service_pb_1.HeartbeatRequest()
.setClientType(this.getClientType())
.setGroup((0, util_1.createResource)(this.consumerGroup));
}
wrapNotifyClientTerminationRequest() {
return new service_pb_1.NotifyClientTerminationRequest()
.setGroup((0, util_1.createResource)(this.consumerGroup));
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onTopicRouteDataUpdate(_topic, _topicRouteData) {
// No-op for push consumer; assignments are queried separately
}
createConsumeService() {
if (this.#pushSubscriptionSettings.isFifo()) {
return new FifoConsumeService_1.FifoConsumeService(this.clientId, this.#messageListener, this.#enableFifoConsumeAccelerator);
}
return new StandardConsumeService_1.StandardConsumeService(this.clientId, this.#messageListener);
}
async subscribe(topic, filterExpression) {
await this.getRouteData(topic);
this.#subscriptionExpressions.set(topic, filterExpression);
}
unsubscribe(topic) {
this.#subscriptionExpressions.delete(topic);
}
// --- Public methods for ProcessQueue access ---
get requestTimeoutValue() {
return this.requestTimeout;
}
getPushConsumerSettings() {
return this.#pushSubscriptionSettings;
}
getConsumerGroup() {
return this.consumerGroup;
}
getConsumeService() {
return this.#consumeService;
}
getRetryPolicy() {
return this.#pushSubscriptionSettings.getRetryPolicy();
}
getClientId() {
return this.clientId;
}
// --- Metrics access ---
getReceptionTimes() {
return this.#metrics.receptionTimes;
}
getReceivedMessagesQuantity() {
return this.#metrics.receivedMessagesQuantity;
}
getConsumptionOkQuantity() {
return this.#metrics.consumptionOkQuantity;
}
getConsumptionErrorQuantity() {
return this.#metrics.consumptionErrorQuantity;
}
incrementReceptionTimes() {
this.#metrics.receptionTimes++;
}
incrementReceivedMessagesQuantity(count) {
this.#metrics.receivedMessagesQuantity += count;
}
incrementConsumptionOkQuantity() {
this.#metrics.consumptionOkQuantity++;
}
incrementConsumptionErrorQuantity() {
this.#metrics.consumptionErrorQuantity++;
}
// --- Statistics ---
doStats() {
const receptionTimes = this.#metrics.receptionTimes;
this.#metrics.receptionTimes = 0;
const receivedMessagesQuantity = this.#metrics.receivedMessagesQuantity;
this.#metrics.receivedMessagesQuantity = 0;
const consumptionOkQuantity = this.#metrics.consumptionOkQuantity;
this.#metrics.consumptionOkQuantity = 0;
const consumptionErrorQuantity = this.#metrics.consumptionErrorQuantity;
this.#metrics.consumptionErrorQuantity = 0;
this.logger.info('clientId=%s, consumerGroup=%s, receptionTimes=%d, receivedMessagesQuantity=%d, '
+ 'consumptionOkQuantity=%d, consumptionErrorQuantity=%d', this.clientId, this.consumerGroup, receptionTimes, receivedMessagesQuantity, consumptionOkQuantity, consumptionErrorQuantity);
}
wrapPushReceiveMessageRequest(batchSize, mq, filterExpression, longPollingTimeout, attemptId) {
const request = new service_pb_1.ReceiveMessageRequest()
.setGroup((0, util_1.createResource)(this.consumerGroup))
.setMessageQueue(mq.toProtobuf())
.setFilterExpression(filterExpression.toProtobuf())
.setLongPollingTimeout((0, util_1.createDuration)(longPollingTimeout))
.setBatchSize(batchSize)
.setAutoRenew(true);
if (attemptId) {
request.setAttemptId(attemptId);
}
return request;
}
async receiveMessage(request, mq, awaitDuration) {
return super.receiveMessage(request, mq, awaitDuration);
}
wrapAckMessageRequest(messageView) {
const request = new service_pb_1.AckMessageRequest()
.setGroup((0, util_1.createResource)(this.consumerGroup))
.setTopic((0, util_1.createResource)(messageView.topic));
const entry = request.addEntries()
.setMessageId(messageView.messageId)
.setReceiptHandle(messageView.receiptHandle);
// For lite consumers, must set liteTopic in ACK request
if (this.isLiteConsumer() && messageView.liteTopic) {
entry.setLiteTopic(messageView.liteTopic);
}
return request;
}
wrapChangeInvisibleDurationRequest(messageView, invisibleDuration) {
const request = new service_pb_1.ChangeInvisibleDurationRequest()
.setGroup((0, util_1.createResource)(this.consumerGroup))
.setTopic((0, util_1.createResource)(messageView.topic))
.setReceiptHandle(messageView.receiptHandle)
.setInvisibleDuration((0, util_1.createDuration)(invisibleDuration))
.setMessageId(messageView.messageId);
// For lite consumers, must set liteTopic and suspend in request
if (this.isLiteConsumer() && messageView.liteTopic) {
request.setLiteTopic(messageView.liteTopic);
request.setSuspend(true);
}
return request;
}
wrapForwardMessageToDeadLetterQueueRequest(messageView) {
const retryPolicy = this.getRetryPolicy();
const request = new service_pb_1.ForwardMessageToDeadLetterQueueRequest()
.setGroup((0, util_1.createResource)(this.consumerGroup))
.setTopic((0, util_1.createResource)(messageView.topic))
.setReceiptHandle(messageView.receiptHandle)
.setMessageId(messageView.messageId)
.setDeliveryAttempt(messageView.deliveryAttempt ?? 0)
.setMaxDeliveryAttempts(retryPolicy?.getMaxAttempts() ?? 1);
// For lite consumers, must set liteTopic in request
if (this.isLiteConsumer() && messageView.liteTopic) {
request.setLiteTopic(messageView.liteTopic);
}
return request;
}
// --- Internal: queue size and cache threshold ---
getQueueSize() {
return this.#processQueueTable.size;
}
cacheMessageBytesThresholdPerQueue() {
const size = this.getQueueSize();
if (size <= 0)
return 0;
return Math.max(1, Math.floor(this.#maxCacheMessageSizeInBytes / size));
}
cacheMessageCountThresholdPerQueue() {
const size = this.getQueueSize();
if (size <= 0)
return 0;
return Math.max(1, Math.floor(this.#maxCacheMessageCount / size));
}
// --- Internal: assignment scanning ---
#scanAssignments() {
try {
this.logger.debug?.('Scanning assignments, clientId=%s, subscriptionCount=%d', this.clientId, this.#subscriptionExpressions.size);
for (const [topic, filterExpression] of this.#subscriptionExpressions) {
const existed = this.#cacheAssignments.get(topic);
this.#queryAssignment(topic)
.then(latest => {
this.logger.debug?.('Query assignment result, topic=%s, clientId=%s, assignmentCount=%d', topic, this.clientId, latest.getAssignmentList().length);
if (latest.getAssignmentList().length === 0) {
if (!existed || existed.getAssignmentList().length === 0) {
this.logger.info('Acquired empty assignments from remote, would scan later, topic=%s, '
+ 'clientId=%s', topic, this.clientId);
return;
}
this.logger.warn('Attention!!! acquired empty assignments from remote, but existed assignments'
+ ' is not empty, topic=%s, clientId=%s', topic, this.clientId);
}
if (!latest.equals(existed)) {
this.logger.info('Assignments of topic=%s has changed, %j => %j, clientId=%s', topic, existed, latest, this.clientId);
this.#syncProcessQueue(topic, latest, filterExpression);
this.#cacheAssignments.set(topic, latest);
return;
}
this.logger.debug?.('Assignments of topic=%s remains the same, assignments=%j, clientId=%s', topic, existed, this.clientId);
// Process queue may be dropped, need to be synchronized anyway.
this.#syncProcessQueue(topic, latest, filterExpression);
})
.catch(err => {
this.logger.error('Exception raised while scanning the assignments, topic=%s, clientId=%s, error=%s', topic, this.clientId, err);
});
}
}
catch (err) {
this.logger.error('Exception raised while scanning the assignments for all topics, clientId=%s, error=%s', this.clientId, err);
}
}
async #queryAssignment(topic) {
const topicRouteData = await this.getRouteData(topic);
const endpointsList = topicRouteData.getTotalEndpoints();
if (endpointsList.length === 0) {
throw new Error(`No endpoints available for topic=${topic}`);
}
const endpoints = endpointsList[0];
const request = new service_pb_1.QueryAssignmentRequest()
.setTopic((0, util_1.createResource)(topic))
.setGroup((0, util_1.createResource)(this.consumerGroup))
.setEndpoints(this.endpoints.toProtobuf());
const response = await this.rpcClientManager.queryAssignment(endpoints, request, this.requestTimeout);
const status = response.getStatus();
if (!status) {
throw new Error('Missing status in query assignment response');
}
exception_1.StatusChecker.check(status.toObject());
const assignmentList = response.getAssignmentsList().map(assignment => {
const mqPb = assignment.getMessageQueue();
const mq = new route_1.MessageQueue(mqPb);
return new Assignment_1.Assignment(mq);
});
return new Assignments_1.Assignments(assignmentList);
}
#syncProcessQueue(topic, assignments, filterExpression) {
const latestMqKeys = new Set();
for (const assignment of assignments.getAssignmentList()) {
latestMqKeys.add(this.#mqKey(assignment.messageQueue));
}
// Find active message queues
const activeMqKeys = new Set();
for (const [mqKey, { mq, pq }] of this.#processQueueTable) {
if (mq.topic.name !== topic) {
continue;
}
if (!latestMqKeys.has(mqKey)) {
this.logger.info('Drop message queue according to the latest assignmentList, mq=%s@%s@%s, clientId=%s', mq.topic.name, mq.broker.name, mq.queueId, this.clientId);
this.#dropProcessQueue(mqKey);
continue;
}
if (pq.expired()) {
this.logger.warn('Drop message queue because it is expired, mq=%s@%s@%s, clientId=%s', mq.topic.name, mq.broker.name, mq.queueId, this.clientId);
this.#dropProcessQueue(mqKey);
continue;
}
activeMqKeys.add(mqKey);
}
// Create new process queues for new assignments
for (const assignment of assignments.getAssignmentList()) {
const mqKey = this.#mqKey(assignment.messageQueue);
if (activeMqKeys.has(mqKey)) {
continue;
}
const processQueue = this.#createProcessQueue(assignment.messageQueue, filterExpression);
if (processQueue) {
this.logger.info('Start to fetch message from remote, mq=%s@%s@%s, clientId=%s', assignment.messageQueue.topic.name, assignment.messageQueue.broker.name, assignment.messageQueue.queueId, this.clientId);
processQueue.fetchMessageImmediately();
}
}
}
#mqKey(mq) {
return `${mq.topic.name}@${mq.broker.name}@${mq.queueId}`;
}
#dropProcessQueue(mqKey) {
const entry = this.#processQueueTable.get(mqKey);
if (entry) {
entry.pq.drop();
this.#processQueueTable.delete(mqKey);
}
}
#createProcessQueue(mq, filterExpression) {
const mqKey = this.#mqKey(mq);
if (this.#processQueueTable.has(mqKey)) {
return null;
}
const processQueue = new ProcessQueue_1.ProcessQueue(this, mq, filterExpression);
this.#processQueueTable.set(mqKey, { mq, pq: processQueue });
return processQueue;
}
/**
* Create a virtual process queue for Lite consumer.
* Lite consumers use a special message queue with queueId=-1 to receive messages.
* This method creates the virtual queue and starts fetching messages immediately.
*
* @param topic - The bind topic name
* @param filterExpression - The filter expression for message filtering
*/
createVirtualProcessQueueForLite(topic, filterExpression) {
// Validate parameters
if (!topic || topic.trim().length === 0) {
throw new Error('topic should not be blank');
}
if (!filterExpression) {
throw new Error('filterExpression should not be null');
}
// Create a virtual broker with default endpoints
const brokerPb = new definition_pb_2.Broker();
brokerPb.setName('virtual-broker');
brokerPb.setId(0);
brokerPb.setEndpoints(this.endpoints.toProtobuf());
const broker = new route_1.Broker(brokerPb.toObject());
// Create a virtual message queue with queueId=-1
const virtualMqPb = new definition_pb_2.MessageQueue();
virtualMqPb.setId(-1); // Virtual queue ID for Lite consumer
virtualMqPb.setTopic((0, util_1.createResource)(topic));
virtualMqPb.setBroker(broker.toProtobuf());
virtualMqPb.setPermission(definition_pb_1.Permission.READ);
virtualMqPb.setAcceptMessageTypesList([definition_pb_1.MessageType.LITE]);
const virtualMq = new route_1.MessageQueue(virtualMqPb);
// Create process queue for the virtual message queue
const mqKey = this.#mqKey(virtualMq);
if (this.#processQueueTable.has(mqKey)) {
this.logger.info('Virtual process queue already exists, mq=%s, clientId=%s', mqKey, this.clientId);
return;
}
const processQueue = new ProcessQueue_1.ProcessQueue(this, virtualMq, filterExpression);
this.#processQueueTable.set(mqKey, { mq: virtualMq, pq: processQueue });
this.logger.info('Created virtual process queue for Lite consumer, mq=%s, clientId=%s', mqKey, this.clientId);
// Start fetching messages immediately
this.logger.info('Start to fetch lite messages from virtual queue, mq=%s, clientId=%s', mqKey, this.clientId);
processQueue.fetchMessageImmediately();
}
}
exports.PushConsumer = PushConsumer;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHVzaENvbnN1bWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbnN1bWVyL1B1c2hDb25zdW1lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHOzs7QUFFSCxnRkFBbUc7QUFDbkcsMEVBUW1EO0FBRW5ELG9DQUFnRTtBQUNoRSxnRkFBa0g7QUFDbEgsNENBQTZDO0FBRTdDLGtDQUF5RDtBQUN6RCx5Q0FBdUQ7QUFDdkQseURBQXNEO0FBQ3RELHlFQUFzRTtBQUV0RSxxRUFBa0U7QUFDbEUsNkRBQTBEO0FBQzFELGlEQUE4QztBQUM5Qyw2Q0FBMEM7QUFDMUMsK0NBQTRDO0FBRzVDLE1BQU0sOEJBQThCLEdBQUcsSUFBSSxDQUFDO0FBQzVDLE1BQU0sK0JBQStCLEdBQUcsSUFBSSxDQUFDO0FBVzdDLE1BQU0sY0FBYztJQUNsQixjQUFjLEdBQUcsQ0FBQyxDQUFDO0lBQ25CLHdCQUF3QixHQUFHLENBQUMsQ0FBQztJQUM3QixxQkFBcUIsR0FBRyxDQUFDLENBQUM7SUFDMUIsd0JBQXdCLEdBQUcsQ0FBQyxDQUFDO0NBQzlCO0FBRUQsTUFBYSxZQUFhLFNBQVEsbUJBQVE7SUFDL0IseUJBQXlCLENBQTJCO0lBQ3BELHdCQUF3QixHQUFHLElBQUksR0FBRyxFQUE0QixDQUFDO0lBQy9ELGlCQUFpQixHQUFHLElBQUksR0FBRyxFQUF1QixDQUFDO0lBQ25ELGdCQUFnQixDQUFrQjtJQUNsQyxxQkFBcUIsQ0FBUztJQUM5QiwyQkFBMkIsQ0FBUztJQUNwQyw2QkFBNkIsQ0FBVTtJQUN2QyxrQkFBa0IsR0FBRyxJQUFJLEdBQUcsRUFBK0QsQ0FBQztJQUM1RixRQUFRLEdBQUcsSUFBSSxjQUFjLEVBQUUsQ0FBQztJQUN6QyxlQUFlLENBQWtCO0lBQ2pDLG9CQUFvQixDQUFrQjtJQUV0QyxZQUFZLE9BQTRCO1FBQ3RDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FB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