rocketmq-client-nodejs-beta
Version:
RocketMQ Node.js Client
313 lines • 27.5 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.BaseClient = void 0;
const node_util_1 = require("node:util");
const node_crypto_1 = require("node:crypto");
const grpc_js_1 = require("@grpc/grpc-js");
const definition_pb_1 = require("../../proto/apache/rocketmq/v2/definition_pb");
const service_pb_1 = require("../../proto/apache/rocketmq/v2/service_pb");
const util_1 = require("../util");
const route_1 = require("../route");
const exception_1 = require("../exception");
const UserAgent_1 = require("./UserAgent");
const Logger_1 = require("./Logger");
const RpcClientManager_1 = require("./RpcClientManager");
const TelemetrySession_1 = require("./TelemetrySession");
const ClientId_1 = require("./ClientId");
const debug = (0, node_util_1.debuglog)('rocketmq-client-nodejs:client:BaseClient');
/**
* RocketMQ Base Client, Consumer and Producer should extends this class
*
* it handle:
* - RpcClient lifecycle, e.g: cleanup the idle clients
* - startup flow
* - periodic Task
*/
class BaseClient {
clientId = ClientId_1.ClientId.create();
clientType = definition_pb_1.ClientType.CLIENT_TYPE_UNSPECIFIED;
sslEnabled;
#sessionCredentials;
namespace;
endpoints;
isolated = new Map();
requestTimeout;
topics = new Set();
topicRouteCache = new Map();
logger;
rpcClientManager;
#telemetrySessions = new Map();
#startupResolve;
#startupReject;
#timers = [];
constructor(options) {
this.logger = options.logger ?? (0, Logger_1.getDefaultLogger)();
this.sslEnabled = options.sslEnabled === true;
this.endpoints = new route_1.Endpoints(options.endpoints);
this.namespace = options.namespace;
this.#sessionCredentials = options.sessionCredentials;
// https://rocketmq.apache.org/docs/introduction/03limits/
// Default request timeout is 3000ms
this.requestTimeout = options.requestTimeout ?? 3000;
this.rpcClientManager = new RpcClientManager_1.RpcClientManager(this, this.logger);
if (options.topics) {
for (const topic of options.topics) {
this.topics.add(topic);
}
}
}
/**
* Startup flow
* https://github.com/apache/rocketmq-clients/blob/master/docs/workflow.md#startup
*/
async startup() {
this.logger.info('Begin to startup the rocketmq client, clientId=%s', this.clientId);
try {
await this.#startup();
}
catch (e) {
const err = new Error(`Startup the rocketmq client failed, clientId=${this.clientId}, error=${e}`);
this.logger.error(err);
err.cause = e;
throw err;
}
this.logger.info('Startup the rocketmq client successfully, clientId=%s', this.clientId);
}
async #startup() {
// fetch topic route
await this.updateRoutes();
// update topic route every 30s
this.#timers.push(setInterval(async () => {
this.updateRoutes();
}, 30000));
// sync settings every 5m
this.#timers.push(setInterval(async () => {
this.#syncSettings();
}, 5 * 60000));
// heartbeat every 10s
this.#timers.push(setInterval(async () => {
this.#doHeartbeat();
}, 10000));
// doStats every 60s
// doStats()
if (this.topics.size > 0) {
// wait for this first onSettingsCommand call
// eslint-disable-next-line @typescript-eslint/no-unused-vars
await new Promise((resolve, reject) => {
this.#startupReject = reject;
this.#startupResolve = resolve;
});
this.#startupReject = undefined;
this.#startupResolve = undefined;
}
}
async shutdown() {
this.logger.info('Begin to shutdown the rocketmq client, clientId=%s', this.clientId);
while (this.#timers.length > 0) {
const timer = this.#timers.pop();
clearInterval(timer);
}
await this.#notifyClientTermination();
this.logger.info('Begin to release all telemetry sessions, clientId=%s', this.clientId);
this.#releaseTelemetrySessions();
this.logger.info('Release all telemetry sessions successfully, clientId=%s', this.clientId);
this.rpcClientManager.close();
this.logger.info('Shutdown the rocketmq client successfully, clientId=%s', this.clientId);
this.logger.close && this.logger.close();
}
async #doHeartbeat() {
const request = this.wrapHeartbeatRequest();
for (const endpoints of this.getTotalRouteEndpoints()) {
await this.rpcClientManager.heartbeat(endpoints, request, this.requestTimeout);
}
}
#getTotalRouteEndpointsMap() {
const endpointsMap = new Map();
for (const topicRoute of this.topicRouteCache.values()) {
for (const endpoints of topicRoute.getTotalEndpoints()) {
endpointsMap.set(endpoints.facade, endpoints);
}
}
return endpointsMap;
}
getTotalRouteEndpoints() {
const endpointsMap = this.#getTotalRouteEndpointsMap();
return Array.from(endpointsMap.values());
}
findNewRouteEndpoints(endpointsList) {
const endpointsMap = this.#getTotalRouteEndpointsMap();
const newEndpoints = [];
for (const endpoints of endpointsList) {
if (!endpointsMap.has(endpoints.facade)) {
newEndpoints.push(endpoints);
}
}
return newEndpoints;
}
async updateRoutes() {
for (const topic of this.topics) {
await this.#fetchTopicRoute(topic);
}
}
async getRouteData(topic) {
let topicRouteData = this.topicRouteCache.get(topic);
if (!topicRouteData) {
this.topics.add(topic);
topicRouteData = await this.#fetchTopicRoute(topic);
}
return topicRouteData;
}
async #fetchTopicRoute(topic) {
const req = new service_pb_1.QueryRouteRequest();
req.setTopic((0, util_1.createResource)(topic));
req.setEndpoints(this.endpoints.toProtobuf());
const response = await this.rpcClientManager.queryRoute(this.endpoints, req, this.requestTimeout);
exception_1.StatusChecker.check(response.getStatus()?.toObject());
const topicRouteData = new route_1.TopicRouteData(response.getMessageQueuesList());
const newEndpoints = this.findNewRouteEndpoints(topicRouteData.getTotalEndpoints());
for (const endpoints of newEndpoints) {
// sync current settings to new endpoints
this.getTelemetrySession(endpoints).syncSettings();
}
this.topicRouteCache.set(topic, topicRouteData);
this.onTopicRouteDataUpdate(topic, topicRouteData);
debug('fetchTopicRoute topic=%o topicRouteData=%j', topic, topicRouteData);
return topicRouteData;
}
#syncSettings() {
const command = this.settingsCommand();
for (const endpoints of this.getTotalRouteEndpoints()) {
this.telemetry(endpoints, command);
}
}
settingsCommand() {
const command = new service_pb_1.TelemetryCommand();
command.setSettings(this.getSettings().toProtobuf());
return command;
}
getTelemetrySession(endpoints) {
let session = this.#telemetrySessions.get(endpoints.facade);
if (!session) {
session = new TelemetrySession_1.TelemetrySession(this, endpoints, this.logger);
this.#telemetrySessions.set(endpoints.facade, session);
}
return session;
}
createTelemetryStream(endpoints) {
const metadata = this.getRequestMetadata();
return this.rpcClientManager.telemetry(endpoints, metadata);
}
telemetry(endpoints, command) {
this.getTelemetrySession(endpoints).write(command);
}
getRequestMetadata() {
// https://github.com/apache/rocketmq-clients/blob/master/docs/transport.md
// Transport Header
const metadata = new grpc_js_1.Metadata();
// version of protocol
metadata.set('x-mq-protocol', 'v2');
// client unique identifier: mbp@78774@2@3549a8wsr
metadata.set('x-mq-client-id', this.clientId);
// current timestamp: 20210309T195445Z, DATE_TIME_FORMAT = "yyyyMMdd'T'HHmmss'Z'"
const dateTime = (0, util_1.getRequestDateTime)();
metadata.set('x-mq-date-time', dateTime);
// request id for each gRPC header: f122a1e0-dbcf-4ca4-9db7-221903354be7
metadata.set('x-mq-request-id', (0, node_crypto_1.randomUUID)());
// language of client
// FIXME: java.lang.IllegalArgumentException: No enum constant org.apache.rocketmq.remoting.protocol.LanguageCode.nodejs
// https://github.com/apache/rocketmq/blob/master/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/LanguageCode.java
metadata.set('x-mq-language', 'NODE_JS');
// version of client
metadata.set('x-mq-client-version', UserAgent_1.UserAgent.INSTANCE.version);
if (this.namespace) {
metadata.set('x-mq-namespace', this.namespace);
}
if (this.#sessionCredentials) {
if (this.#sessionCredentials.securityToken) {
metadata.set('x-mq-session-token', this.#sessionCredentials.securityToken);
}
const signature = (0, util_1.sign)(this.#sessionCredentials.accessSecret, dateTime);
const authorization = `MQv2-HMAC-SHA1 Credential=${this.#sessionCredentials.accessKey}, SignedHeaders=x-mq-date-time, Signature=${signature}`;
metadata.set('authorization', authorization);
}
return metadata;
}
#releaseTelemetrySessions() {
for (const session of this.#telemetrySessions.values()) {
session.release();
}
this.#telemetrySessions.clear();
}
/**
* Notify remote that current client is prepared to be terminated.
*/
async #notifyClientTermination() {
this.logger.info('Notify remote that client is terminated, clientId=%s', this.clientId);
const request = this.wrapNotifyClientTerminationRequest();
for (const endpoints of this.getTotalRouteEndpoints()) {
await this.rpcClientManager.notifyClientTermination(endpoints, request, this.requestTimeout);
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onTopicRouteDataUpdate(_topic, _topicRouteData) {
// sub class can monitor topic route data change here
}
onUnknownCommand(endpoints, status) {
try {
exception_1.StatusChecker.check(status);
}
catch (err) {
this.logger.error('Get error status from telemetry session, status=%j, endpoints=%j, clientId=%s', status, endpoints, this.clientId);
this.#startupReject && this.#startupReject(err);
}
}
onSettingsCommand(_endpoints, settings) {
// final Metric metric = new Metric(settings.getMetric());
// clientMeterManager.reset(metric);
this.getSettings().sync(settings);
this.logger.info('Sync settings=%j, clientId=%s', this.getSettings(), this.clientId);
this.#startupResolve && this.#startupResolve();
}
onRecoverOrphanedTransactionCommand(_endpoints, command) {
this.logger.warn('Ignore orphaned transaction recovery command from remote, which is not expected, clientId=%s, command=%j', this.clientId, command.toObject());
// const telemetryCommand = new TelemetryCommand();
// telemetryCommand.setStatus(new Status().setCode(Code.NOT_IMPLEMENTED));
// telemetryCommand.setRecoverOrphanedTransactionCommand(new RecoverOrphanedTransactionCommand());
// this.telemetry(endpoints, telemetryCommand);
}
onVerifyMessageCommand(endpoints, command) {
const obj = command.toObject();
this.logger.warn('Ignore verify message command from remote, which is not expected, clientId=%s, command=%j', this.clientId, obj);
const telemetryCommand = new service_pb_1.TelemetryCommand();
telemetryCommand.setStatus(new definition_pb_1.Status().setCode(definition_pb_1.Code.NOT_IMPLEMENTED));
telemetryCommand.setVerifyMessageCommand(new service_pb_1.VerifyMessageCommand().setNonce(obj.nonce));
this.telemetry(endpoints, telemetryCommand);
}
onPrintThreadStackTraceCommand(endpoints, command) {
const obj = command.toObject();
this.logger.warn('Ignore orphaned transaction recovery command from remote, which is not expected, clientId=%s, command=%j', this.clientId, obj);
const nonce = obj.nonce;
const telemetryCommand = new service_pb_1.TelemetryCommand();
telemetryCommand.setThreadStackTrace(new service_pb_1.ThreadStackTrace().setThreadStackTrace('mock stack').setNonce(nonce));
telemetryCommand.setStatus(new definition_pb_1.Status().setCode(definition_pb_1.Code.OK));
this.telemetry(endpoints, telemetryCommand);
}
}
exports.BaseClient = BaseClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmFzZUNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGllbnQvQmFzZUNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHOzs7QUFFSCx5Q0FBcUM7QUFDckMsNkNBQXlDO0FBQ3pDLDJDQUF5QztBQUN6QyxnRkFLc0Q7QUFDdEQsMEVBU21EO0FBQ25ELGtDQUFtRTtBQUNuRSxvQ0FBcUQ7QUFDckQsNENBQThEO0FBRTlELDJDQUF3QztBQUN4QyxxQ0FBcUQ7QUFFckQseURBQXNEO0FBQ3RELHlEQUFzRDtBQUN0RCx5Q0FBc0M7QUFFdEMsTUFBTSxLQUFLLEdBQUcsSUFBQSxvQkFBUSxFQUFDLDBDQUEwQyxDQUFDLENBQUM7QUFtQm5FOzs7Ozs7O0dBT0c7QUFDSCxNQUFzQixVQUFVO0lBQ3JCLFFBQVEsR0FBRyxtQkFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQzdCLFVBQVUsR0FBRywwQkFBVSxDQUFDLHVCQUF1QixDQUFDO0lBQ2hELFVBQVUsQ0FBVTtJQUNwQixtQkFBbUIsQ0FBc0I7SUFDekMsU0FBUyxDQUFTO0lBQ1IsU0FBUyxDQUFZO0lBQ3JCLFFBQVEsR0FBRyxJQUFJLEdBQUcsRUFBcUIsQ0FBQztJQUN4QyxjQUFjLENBQVM7SUFDdkIsTUFBTSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDM0IsZUFBZSxHQUFHLElBQUksR0FBRyxFQUEwQixDQUFDO0lBQ3BELE1BQU0sQ0FBVTtJQUNoQixnQkFBZ0IsQ0FBbUI7SUFDN0Msa0JBQWtCLEdBQUcsSUFBSSxHQUFHLEVBQTRCLENBQUM7SUFDbEUsZUFBZSxDQUFjO0lBQzdCLGNBQWMsQ0FBd0I7SUFDdEMsT0FBTyxHQUFxQixFQUFFLENBQUM7SUFFL0IsWUFBWSxPQUEwQjtRQUNwQyxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBQSx5QkFBZ0IsR0FBRSxDQUFDO1FBQ25E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