rocketmq-client-nodejs
Version:
RocketMQ Node.js Client
343 lines • 35.4 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.
*/
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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