UNPKG

roach-storm

Version:

Apache Kafka to Google Pub/Sub Gateway, API controlled

149 lines (148 loc) 5.91 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Debug = require("debug"); const debug = Debug("roach:roach"); const MongoWrapper_1 = require("./db/MongoWrapper"); const MongoPoller_1 = require("./db/MongoPoller"); const Discovery_1 = require("./kafka/Discovery"); const HttpServer_1 = require("./api/HttpServer"); const MessageHandler_1 = require("./MessageHandler"); const Consumer_1 = require("./kafka/Consumer"); const Producer_1 = require("./kafka/Producer"); const Metrics_1 = require("./Metrics"); const PubSubHandler_1 = require("./PubSubHandler"); const PubSubToKafka_1 = require("./PubSubToKafka"); const PubSubToMetrics_1 = require("./PubSubToMetrics"); const GRACE_EXIT_MS = 1250; class RoachStorm { constructor(config) { this.alive = true; this.ready = false; if (!config || typeof config !== "object") { throw new Error("Config must be an object: {kafka,discovery,mongo,http}"); } this.config = config; this.metrics = new Metrics_1.Metrics("roach", ["topic"], ["topic"]); if (this.config.gcf && this.config.gcf.metrics && this.config.gcf.metrics.pubSubMetricTopic) { this.gcfMetrics = new Metrics_1.Metrics(this.config.gcf.metrics.prefix || "gcf_roach", this.config.gcf.metrics.counterLabels, this.config.gcf.metrics.gaugeLabels); this.pubSubToMetrics = new PubSubToMetrics_1.default(this); } else { this.gcfMetrics = null; this.pubSubToMetrics = null; } this.discovery = new Discovery_1.default(this.config.discovery, this.metrics); this.mongoWrapper = new MongoWrapper_1.default(this.config.mongo, this); this.mongoPoller = new MongoPoller_1.default(this.mongoWrapper, this.metrics); this.httpServer = new HttpServer_1.default(this.config.http, this); this.consumer = new Consumer_1.default(this.config.kafka, this); this.producer = new Producer_1.default(this.config.kafka, this); this.pubSubHandler = new PubSubHandler_1.default(this.config, this); this.messageHandler = new MessageHandler_1.default(this); this.pubSubToKafka = new PubSubToKafka_1.default(this); } shutdownOnErrorIfNotProduction() { if (!RoachStorm.isProduction()) { debug("Shutting down (because of error) in", GRACE_EXIT_MS, "ms"); this.close(); setTimeout(() => { process.exit(1); }, GRACE_EXIT_MS); } } shutdownGracefully() { debug("\nShutting down gracefully in", GRACE_EXIT_MS, "ms"); this.close(); debug("Bye.."); setTimeout(() => { process.exit(0); }, GRACE_EXIT_MS); } init() { process.on("SIGINT", this.shutdownGracefully.bind(this)); process.on("SIGUSR1", this.shutdownGracefully.bind(this)); process.on("SIGUSR2", this.shutdownGracefully.bind(this)); process.on("uncaughtException", (error) => { debug("Unhandled Exception: ", error.message, error.stack); this.shutdownOnErrorIfNotProduction(); }); process.on("unhandledRejection", (reason, promise) => { debug("Unhandled Rejection: ", reason); this.shutdownOnErrorIfNotProduction(); }); if (RoachStorm.isProduction()) { debug("Running production."); } else { debug("Running NOT in production."); } } async run() { this.init(); debug("Starting.."); this.metrics.registerDefault(); // its okay to start these first, as consumer not subscribe to anything until the // poller told him about the configured topics // NOTE: this is necessary, because consumer requires connection before adjusting subscriptions await this.mongoWrapper.start(); await this.consumer.start(); if (this.config.pubSubToKafkaTopicName) { await this.producer.start(); } this.mongoPoller.on("error", (error) => { debug("MongoDB polling error: " + error.message, error.stack); }); this.mongoPoller.on("topic-config-changed", (topics) => { debug("Topic Configuration changed, adjusting subscription of consumer accordingly..", topics.length); this.consumer.adjustSubscriptions(topics); }); await this.discovery.start(this.consumer.getKafkaClient()); await this.mongoPoller.start(30000); await this.httpServer.start(); if (this.config.pubSubToKafkaTopicName) { await this.pubSubToKafka.start(); } if (this.pubSubToMetrics) { await this.pubSubToMetrics.start(); } this.setReadyState(true); debug("Running.."); } async close() { debug("Closing.."); this.setAliveState(false); this.setReadyState(false); await this.pubSubToKafka.close(); if (this.pubSubToMetrics) { await this.pubSubToMetrics.close(); } this.mongoPoller.close(); this.discovery.close(); this.httpServer.close(); await this.consumer.close(); await this.producer.close(); this.mongoWrapper.close(); this.metrics.close(); if (this.gcfMetrics) { this.gcfMetrics.close(); } } static isProduction() { return process.env.NODE_ENV === "production"; } setAliveState(state) { debug("Setting alive state from", this.alive, "to", state); this.alive = state; } isAlive() { return this.alive; } setReadyState(state) { debug("Setting ready state from", this.ready, "to", state); this.ready = state; } isReady() { return this.ready; } } exports.default = RoachStorm;