UNPKG

rocketmq-client-nodejs

Version:
450 lines 41.9 kB
"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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