UNPKG

rocketmq-client-nodejs

Version:
282 lines (255 loc) 9.85 kB
/** * 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. */ import { ClientType } from '../../proto/apache/rocketmq/v2/definition_pb'; import { NotifyUnsubscribeLiteCommand } from '../../proto/apache/rocketmq/v2/service_pb'; import { Resource } from '../route/Resource'; import { Endpoints, TopicRouteData } from '../route'; import { PushConsumer, PushConsumerOptions } from './PushConsumer'; import { LitePushConsumer } from './LitePushConsumer'; import { OffsetOption } from './OffsetOption'; import { LiteSubscriptionManager } from './LiteSubscriptionManager'; import { FilterExpression } from './FilterExpression'; import { LiteFifoConsumeService } from './LiteFifoConsumeService'; import { LiteStandardConsumeService } from './LiteStandardConsumeService'; import { ConsumeService } from './ConsumeService'; import { MessageListener } from './MessageListener'; export interface LitePushConsumerOptions extends PushConsumerOptions { bindTopic: string; } /** * Implementation of LitePushConsumer. * * <p>LitePushConsumer extends PushConsumer to provide lightweight message consumption * with reduced metadata and storage overhead. It supports dynamic subscription * management for lite topics.</p> * * <p>Key features:</p> * <ul> * <li>Dynamic lite topic subscription/unsubscription</li> * <li>Quota management for lite subscriptions</li> * <li>Reduced resource consumption compared to standard PushConsumer</li> * <li>Automatic synchronization with server settings</li> * </ul> * * <p>Note: Unlike LiteSimpleConsumer, LitePushConsumer does not perform route optimization * because it uses the assignment-based message delivery mechanism managed by the server.</p> */ export class LitePushConsumerImpl extends PushConsumer implements LitePushConsumer { private readonly liteSubscriptionManager: LiteSubscriptionManager; private readonly bindTopic: Resource; readonly #messageListener: MessageListener; readonly #enableFifoConsumeAccelerator: boolean; constructor(options: LitePushConsumerOptions) { // Create subscription expressions with bind topic const subscriptions = new Map<string, FilterExpression>(); subscriptions.set(options.bindTopic, FilterExpression.SUB_ALL); super({ ...options, subscriptions, } as PushConsumerOptions); this.#messageListener = options.messageListener; this.#enableFifoConsumeAccelerator = options.enableFifoConsumeAccelerator ?? false; this.bindTopic = new Resource(options.namespace, options.bindTopic); const groupResource = new Resource(options.namespace, options.consumerGroup); this.liteSubscriptionManager = new LiteSubscriptionManager( this, this.bindTopic, groupResource, ); } /** * Create the consume service for lite push consumer. * * <p>Lite push consumer uses lite-topic-specific consume services to ensure * correct grouping and retry behavior.</p> */ protected createConsumeService(): ConsumeService { if (!this.#messageListener) { throw new Error('messageListener should not be null'); } if (this.getPushConsumerSettings().isFifo()) { this.logger.info( 'Create lite FIFO consume service, consumerGroup=%s, clientId=%s, enableFifoConsumeAccelerator=%s', this.consumerGroup, this.clientId, this.#enableFifoConsumeAccelerator, ); return new LiteFifoConsumeService(this.clientId, this.#messageListener, this.#enableFifoConsumeAccelerator); } this.logger.info('Create lite standard consume service, consumerGroup=%s, clientId=%s', this.consumerGroup, this.clientId); return new LiteStandardConsumeService(this.clientId, this.#messageListener); } /** * Get the client type. * * @return The client type identifier for lite push consumer */ protected getClientType(): ClientType { return ClientType.LITE_PUSH_CONSUMER; } /** * Override to disable standard assignment scanning for lite consumers. * LitePushConsumer uses Lite Subscription mechanism instead of assignments. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars protected onTopicRouteDataUpdate(_topic: string, _topicRouteData: TopicRouteData) { // Lite consumers do not use standard assignment mechanism // They rely on LiteSubscriptionManager for message delivery } /** * Start up the consumer. * * <p>This method initializes the consumer and starts the lite subscription manager. * It must be called before the consumer can receive messages.</p> */ async startup() { await super.startup(); this.liteSubscriptionManager.startUp(); } /** * Shutdown the consumer. * * <p>This method gracefully shuts down the consumer, releasing all resources * and stopping the lite subscription manager.</p> */ async shutdown() { this.liteSubscriptionManager.shutdown(); await super.shutdown(); } /** * Subscribe to a lite topic. * * <p>The subscribeLite() method initiates network requests and performs quota verification, * so it may fail. It's important to handle potential errors. Possible failure scenarios include:</p> * <ul> * <li>Network request errors, which can be retried.</li> * <li>Quota verification failures (LiteSubscriptionQuotaExceededException). * In this case, evaluate whether the quota is insufficient and promptly unsubscribe * from unused subscriptions using unsubscribeLite() to free up resources.</li> * </ul> * * @param liteTopic - The name of the lite topic to subscribe */ async subscribeLite(liteTopic: string): Promise<void>; /** * Subscribe to a lite topic with offset option to specify the consume from offset. * * @param liteTopic - The name of the lite topic to subscribe * @param offsetOption - The consume from offset option */ async subscribeLite(liteTopic: string, offsetOption: OffsetOption): Promise<void>; async subscribeLite(liteTopic: string, offsetOption?: OffsetOption): Promise<void> { if (!liteTopic || liteTopic.trim().length === 0) { throw new Error('liteTopic should not be blank'); } await this.liteSubscriptionManager.subscribeLite(liteTopic, offsetOption ?? null); } /** * Unsubscribe from a lite topic. * * <p>This method removes the subscription and notifies the server. * After unsubscribing, the consumer will no longer receive messages from this topic.</p> * * @param liteTopic - The name of the lite topic to unsubscribe from */ async unsubscribeLite(liteTopic: string): Promise<void> { if (!liteTopic || liteTopic.trim().length === 0) { throw new Error('liteTopic should not be blank'); } await this.liteSubscriptionManager.unsubscribeLite(liteTopic); } /** * Get the lite topic set. * * <p>Returns an immutable set of currently subscribed lite topics. * This set reflects the current state of subscriptions managed by the lite subscription manager.</p> * * @return Set of lite topic names */ getLiteTopicSet(): Set<string> { return this.liteSubscriptionManager.getLiteTopicSet(); } /** * Get the load balancing group for the consumer. * * <p>The consumer group is used for load balancing across multiple consumer instances. * All consumers in the same group will share the message load.</p> * * @return Consumer group name */ getConsumerGroup(): string { return this.liteSubscriptionManager.getConsumerGroupName(); } /** * Handle notify unsubscribe lite command from server. * * <p>This method is called when the server sends a notification to unsubscribe * from a lite topic, typically due to quota violations or administrative actions.</p> * * @param command - The unsubscribe command from the server */ onNotifyUnsubscribeLiteCommand(command: NotifyUnsubscribeLiteCommand) { this.liteSubscriptionManager.onNotifyUnsubscribeLiteCommand(command); } /** * Handle settings command from server. * * <p>This method processes configuration updates from the server and synchronizes * the lite subscription manager with the latest settings.</p> * * @param endpoints - The server endpoints * @param settings - The settings configuration */ onSettingsCommand(endpoints: any, settings: any) { super.onSettingsCommand(endpoints, settings); this.liteSubscriptionManager.sync(settings); } /** * Close the consumer. */ async close(): Promise<void> { await this.shutdown(); } /** * Get the logger. */ getLogger() { return this.logger; } /** * Get the RPC client manager (protected access for internal use). * * @internal */ getRpcClientManager() { return this.rpcClientManager; } /** * Get the endpoints (protected access for internal use). * * @internal */ getEndpoints(): Endpoints { return this.endpoints; } /** * Get the request timeout (protected access for internal use). * * @internal */ getRequestTimeout(): number { return this.requestTimeout; } }