UNPKG

rocketmq-client-nodejs

Version:
343 lines 35.4 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. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Producer = void 0; const node_assert_1 = __importDefault(require("node:assert")); const promises_1 = require("node:timers/promises"); const definition_pb_1 = require("../../proto/apache/rocketmq/v2/definition_pb"); const service_pb_1 = require("../../proto/apache/rocketmq/v2/service_pb"); const retry_1 = require("../retry"); const exception_1 = require("../exception"); const client_1 = require("../client"); const message_1 = require("../message"); const PublishingSettings_1 = require("./PublishingSettings"); const PublishingLoadBalancer_1 = require("./PublishingLoadBalancer"); const SendReceipt_1 = require("./SendReceipt"); const Transaction_1 = require("./Transaction"); const util_1 = require("../util"); const RecallReceipt_1 = require("./RecallReceipt"); const service_pb_2 = require("../../proto/apache/rocketmq/v2/service_pb"); class Producer extends client_1.BaseClient { #publishingSettings; #checker; #publishingRouteDataCache = new Map(); constructor(options) { if (!options.topics && options.topic) { options.topics = Array.isArray(options.topic) ? options.topic : [options.topic]; } super(options); // https://rocketmq.apache.org/docs/introduction/03limits/ // Default max number of message sending retries is 3 const retryPolicy = retry_1.ExponentialBackoffRetryPolicy.immediatelyRetryPolicy(options.maxAttempts ?? 3); this.#publishingSettings = new PublishingSettings_1.PublishingSettings(options.namespace, this.clientId, this.endpoints, retryPolicy, this.requestTimeout, this.topics); this.#checker = options.checker; } async startup() { this.logger.info('Begin to start the rocketmq producer, clientId=%s', this.clientId); await super.startup(); this.logger.info('The rocketmq producer starts successfully, clientId=%s', this.clientId); } async shutdown() { this.logger.info('Begin to shutdown the rocketmq producer, clientId=%s', this.clientId); await super.shutdown(); this.logger.info('Shutdown the rocketmq producer successfully, clientId=%s', this.clientId); } get publishingSettings() { return this.#publishingSettings; } beginTransaction() { (0, node_assert_1.default)(this.#checker, 'Transaction checker should not be null'); // Check producer status before beginning transaction if (!this.isRunning()) { this.logger.error('Unable to begin a transaction because producer is not running, clientId=%s', this.clientId); throw new Error('Producer is not running now'); } return new Transaction_1.Transaction(this); } async endTransaction(endpoints, message, messageId, transactionId, resolution, source = definition_pb_1.TransactionSource.SOURCE_CLIENT) { const resolutionStr = resolution === definition_pb_1.TransactionResolution.COMMIT ? 'COMMIT' : 'ROLLBACK'; const sourceStr = definition_pb_1.TransactionSource[source]; this.logger.debug?.('Begin to end transaction, messageId=%s, transactionId=%s, resolution=%s, source=%s, clientId=%s', messageId, transactionId, resolutionStr, sourceStr, this.clientId); const request = new service_pb_1.EndTransactionRequest() .setMessageId(messageId) .setTransactionId(transactionId) .setTopic((0, util_1.createResource)(message.topic).setResourceNamespace(this.namespace)) .setResolution(resolution) .setSource(source); const response = await this.rpcClientManager.endTransaction(endpoints, request, this.requestTimeout); exception_1.StatusChecker.check(response.getStatus()?.toObject()); this.logger.debug?.('End transaction successfully, messageId=%s, transactionId=%s, resolution=%s, source=%s, clientId=%s', messageId, transactionId, resolutionStr, sourceStr, this.clientId); } async onRecoverOrphanedTransactionCommand(endpoints, command) { const transactionId = command.getTransactionId(); const messagePB = command.getMessage(); const messageId = messagePB.getSystemProperties().getMessageId(); if (!this.#checker) { this.logger.error('No transaction checker registered, ignore it, messageId=%s, transactionId=%s, endpoints=%s, clientId=%s', messageId, transactionId, endpoints, this.clientId); return; } let messageView; try { messageView = new message_1.MessageView(messagePB); } catch (err) { this.logger.error('[Bug] Failed to decode message during orphaned transaction message recovery, messageId=%s, transactionId=%s, endpoints=%s, clientId=%s, error=%s', messageId, transactionId, endpoints, this.clientId, err); return; } try { const resolution = await this.#checker.check(messageView); if (resolution === null || resolution === definition_pb_1.TransactionResolution.TRANSACTION_RESOLUTION_UNSPECIFIED) { return; } // Use SOURCE_SERVER_CHECK for transaction recovery await this.endTransaction(endpoints, messageView, messageId, transactionId, resolution, definition_pb_1.TransactionSource.SOURCE_SERVER_CHECK); this.logger.info('Recover orphaned transaction message success, transactionId=%s, resolution=%s, messageId=%s, clientId=%s', transactionId, resolution, messageId, this.clientId); } catch (err) { this.logger.error('Exception raised while checking the transaction, messageId=%s, transactionId=%s, endpoints=%s, clientId=%s, error=%s', messageId, transactionId, endpoints, this.clientId, err); return; } } getSettings() { return this.#publishingSettings; } /** * Get the client type. * * @return The client type identifier for producer */ getClientType() { return definition_pb_1.ClientType.PRODUCER; } wrapHeartbeatRequest() { return new service_pb_1.HeartbeatRequest() .setClientType(this.getClientType()); } wrapNotifyClientTerminationRequest() { return new service_pb_1.NotifyClientTerminationRequest(); } async send(message, transaction) { if (!transaction) { const sendReceipts = await this.#send([message], false); return sendReceipts[0]; } // Send transactional message try { const publishingMessage = transaction.tryAddMessage(message); const sendReceipts = await this.#send([message], true); const sendReceipt = sendReceipts[0]; transaction.tryAddReceipt(publishingMessage, sendReceipt); return sendReceipt; } catch (err) { this.logger.error('Failed to send transactional message, clientId=%s, error=%s', this.clientId, err); throw err; } } async #send(messages, txEnabled) { // Check producer status before message publishing if (!this.isRunning()) { this.logger.error('Unable to send message because producer is not running, clientId=%s', this.clientId); throw new Error('Producer is not running now'); } const pubMessages = []; const topics = new Set(); for (const message of messages) { pubMessages.push(new message_1.PublishingMessage(message, this.#publishingSettings, txEnabled)); topics.add(message.topic); } if (topics.size > 1) { throw new TypeError(`Messages to send have different topics=${JSON.stringify(Array.from(topics))}`); } const topic = pubMessages[0].topic; const messageType = pubMessages[0].messageType; const messageGroup = pubMessages[0].messageGroup; const liteTopic = pubMessages[0].liteTopic; const messageTypes = new Set(pubMessages.map(m => m.messageType)); if (messageTypes.size > 1) { throw new TypeError(`Messages to send have different types=${JSON.stringify(Array.from(messageTypes))}`); } // Log Lite Topic message sending if (liteTopic) { this.logger.debug?.('Sending Lite Topic message, topic=%s, liteTopic=%s, messageType=%s, clientId=%s', topic, liteTopic, definition_pb_1.MessageType[messageType], this.clientId); } // Message group must be same if message type is FIFO, or no need to proceed. if (messageType === definition_pb_1.MessageType.FIFO) { const messageGroups = new Set(pubMessages.map(m => m.messageGroup)); if (messageGroups.size > 1) { throw new TypeError(`FIFO messages to send have message groups, messageGroups=${JSON.stringify(Array.from(messageGroups))}`); } } // Get publishing topic route. const loadBalancer = await this.#getPublishingLoadBalancer(topic); // Prepare the candidate message queue(s) for retry-sending in advance. const candidates = messageGroup ? [loadBalancer.takeMessageQueueByMessageGroup(messageGroup)] : this.#takeMessageQueues(loadBalancer); return await this.#send0(topic, messageType, candidates, pubMessages, 1); } #wrapSendMessageRequest(pubMessages, mq) { const request = new service_pb_1.SendMessageRequest(); for (const pubMessage of pubMessages) { if (this.namespace) { request.addMessages(pubMessage.toProtobuf(this.namespace, mq)); } else { request.addMessages(pubMessage.toProtobuf('', mq)); } } return request; } /** * Isolate specified Endpoints */ #isolate(endpoints) { this.isolated.set(endpoints.facade, endpoints); } async #send0(topic, messageType, candidates, messages, attempt) { // Calculate the current message queue. const index = (attempt - 1) % candidates.length; const mq = candidates[index]; const acceptMessageTypes = mq.acceptMessageTypesList; if (this.#publishingSettings.isValidateMessageType() && !acceptMessageTypes.includes(messageType)) { throw new TypeError('Current message type not match with ' + 'topic accept message types, topic=' + topic + ', actualMessageType=' + messageType + ', ' + 'acceptMessageTypes=' + JSON.stringify(acceptMessageTypes)); } const endpoints = mq.broker.endpoints; const maxAttempts = this.#getRetryPolicy().getMaxAttempts(); const request = this.#wrapSendMessageRequest(messages, mq); let sendReceipts = []; try { const response = await this.rpcClientManager.sendMessage(endpoints, request, this.requestTimeout); sendReceipts = SendReceipt_1.SendReceipt.processResponseInvocation(mq, response); } catch (err) { const messageIds = messages.map(m => m.messageId); // Isolate endpoints because of sending failure this.#isolate(endpoints); if (attempt >= maxAttempts) { // No more attempts this.logger.error('Failed to send message(s) finally, run out of attempt times, maxAttempts=%d, attempt=%d, topic=%s, messageId(s)=%s, endpoints=%s, clientId=%s, error=%s', maxAttempts, attempt, topic, messageIds, endpoints, this.clientId, err); throw err; } // No more attempts for transactional message if (messageType === definition_pb_1.MessageType.TRANSACTION) { this.logger.error('Failed to send transactional message finally, maxAttempts=%d, attempt=%d, topic=%s, messageId(s)=%s, endpoints=%s, clientId=%s, error=%s', maxAttempts, attempt, topic, messageIds, endpoints, this.clientId, err); throw err; } // Try next attempt const nextAttempt = 1 + attempt; // Retry immediately if the request is not throttled if (!(err instanceof exception_1.TooManyRequestsException)) { this.logger.warn('Failed to send message, would attempt to resend right now, maxAttempts=%d, attempt=%d, topic=%s, messageId(s)=%s, endpoints=%s, clientId=%s, error=%s', maxAttempts, attempt, topic, messageIds, endpoints, this.clientId, err); return this.#send0(topic, messageType, candidates, messages, nextAttempt); } const delay = this.#getRetryPolicy().getNextAttemptDelay(nextAttempt); this.logger.warn('Failed to send message due to too many requests, would attempt to resend after %dms, maxAttempts=%d, attempt=%d, topic=%s, messageId(s)=%s, endpoints=%s, clientId=%s, error=%s', delay, maxAttempts, attempt, topic, messageIds, endpoints, this.clientId, err); await (0, promises_1.setTimeout)(delay); return this.#send0(topic, messageType, candidates, messages, nextAttempt); } // Resend message(s) successfully if (attempt > 1) { const messageIds = sendReceipts.map(r => r.messageId); this.logger.info('Resend message successfully, topic=%s, messageId(s)=%s, maxAttempts=%d, attempt=%d, endpoints=%s, clientId=%s', topic, messageIds, maxAttempts, attempt, endpoints, this.clientId); } // Log Lite Topic message sent successfully if (sendReceipts.length > 0 && messages[0].liteTopic) { this.logger.debug?.('Lite Topic message sent successfully, topic=%s, liteTopic=%s, messageId=%s, clientId=%s', topic, messages[0].liteTopic, sendReceipts[0].messageId, this.clientId); } // Send message(s) successfully on first attempt, return directly return sendReceipts; } async #getPublishingLoadBalancer(topic) { let loadBalancer = this.#publishingRouteDataCache.get(topic); if (!loadBalancer) { const topicRouteData = await this.getRouteData(topic); loadBalancer = this.#updatePublishingLoadBalancer(topic, topicRouteData); } return loadBalancer; } #updatePublishingLoadBalancer(topic, topicRouteData) { let loadBalancer = this.#publishingRouteDataCache.get(topic); if (loadBalancer) { loadBalancer = loadBalancer.update(topicRouteData); } else { loadBalancer = new PublishingLoadBalancer_1.PublishingLoadBalancer(topicRouteData); } this.#publishingRouteDataCache.set(topic, loadBalancer); return loadBalancer; } /** * Take message queue(s) from route for message publishing. */ #takeMessageQueues(loadBalancer) { return loadBalancer.takeMessageQueues(this.isolated, this.#getRetryPolicy().getMaxAttempts()); } #getRetryPolicy() { return this.#publishingSettings.getRetryPolicy(); } /** * Recalls a scheduled/delayed message based on the topic and recall handle. * This operation requires server support and can only be performed before the message is delivered. * * @param topic - The topic associated with the scheduled message to be canceled. * @param recallHandle - A unique handle to identify the message to recall (obtained from SendReceipt). * @return Promise resolving to RecallReceipt containing the recalled message ID. * @throws Error if producer is not running or recall handle is invalid. */ async recallMessage(topic, recallHandle) { if (!this.isRunning()) { this.logger.error('Unable to recall message because producer is not running, clientId=%s', this.clientId); throw new Error('Producer is not running now'); } // Validate topic if (!topic || typeof topic !== 'string' || topic.trim().length === 0) { this.logger.error('Topic is invalid for recall message, clientId=%s', this.clientId); throw new Error('Topic is invalid'); } if (!recallHandle) { this.logger.error('Recall handle is invalid, clientId=%s', this.clientId); throw new Error('Recall handle is invalid'); } this.logger.info('Begin to recall message, topic=%s, recallHandle=%s, clientId=%s', topic, recallHandle, this.clientId); const request = new service_pb_2.RecallMessageRequest() .setTopic((0, util_1.createResource)(topic).setResourceNamespace(this.namespace)) .setRecallHandle(recallHandle); const response = await this.rpcClientManager.recallMessage(this.endpoints, request, this.requestTimeout); const status = response.getStatus(); if (!status) { throw new Error('Recall message response status is null'); } // Check status code const statusCode = status.getCode(); if (statusCode !== definition_pb_1.Code.OK) { const errorMessage = status.getMessage() || 'Unknown error'; throw new Error(`Failed to recall message: ${errorMessage} (code: ${statusCode})`); } const messageId = response.getMessageId(); const receipt = new RecallReceipt_1.RecallReceipt(messageId); this.logger.info('Recall message successfully, topic=%s, recallHandle=%s, messageId=%s, clientId=%s', topic, recallHandle, messageId, this.clientId); return receipt; } } exports.Producer = Producer; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJvZHVjZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvZHVjZXIvUHJvZHVjZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRzs7Ozs7O0FBRUgsOERBQWlDO0FBQ2pDLG1EQUFrRDtBQUNsRCxnRkFNc0Q7QUFDdEQsMEVBTW1EO0FBTW5ELG9DQUVrQjtBQUNsQiw0Q0FBdUU7QUFDdkUsc0NBQW9FO0FBQ3BFLHdDQUFxRjtBQUNyRiw2REFBMEQ7QUFFMUQscUVBQWtFO0FBQ2xFLCtDQUE0QztBQUM1QywrQ0FBNEM7QUFDNUMsa0NBQXlDO0FBQ3pDLG1EQUFnRDtBQUNoRCwwRUFBaUY7QUFRakYsTUFBYSxRQUFTLFNBQVEsbUJBQVU7SUFDdEMsbUJBQW1CLENBQXFCO0lBQ3hDLFFBQVEsQ0FBc0I7SUFDOUIseUJBQXlCLEdBQUcsSUFBSSxHQUFHLEVBQWtDLENBQUM7SUFFdEUsWUFBWSxPQUF3QjtRQUNsQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDckMsT0FBTyxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBRSxPQUFPLENBQUMsS0FBSyxDQUFFLENBQUM7UUFDcEYsQ0FBQztRQUNELEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLDBEQUEwRDtRQUMxRCxxREFBcUQ7UUFDckQsTUFBTSxXQUFXLEdBQUcscUNBQTZCLENBQUMsc0JBQXNCLENBQUMsT0FBTyxDQUFDLFdBQVcsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNuRyxJQUFJLENBQUMsbUJBQW1CLEdBQUcsSUFBSSx1Q0FBa0IsQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxD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