possible fixes for the blocking heartbeat problem

This commit is contained in:
Daniel Sommer 2022-05-03 12:26:15 +02:00
parent f8eb00e04e
commit cb011c903b
6 changed files with 32 additions and 45 deletions

View file

@ -4,8 +4,7 @@ const Message = require('./Message');
class Audiostream { class Audiostream {
constructor(eventParser) { constructor() {
this.eventParser = eventParser;
this.#handleEvents(); this.#handleEvents();
} }
@ -33,23 +32,23 @@ class Audiostream {
} }
#handleEvents() { #handleEvents() {
this.eventParser.on('audio:initialize', (data) => { eventparser.on('audio:initialize', (data) => {
logger.debug('handling event \'audio:initialize\'...'); logger.debug('handling event \'audio:initialize\'...');
this.#initialize(data); this.#initialize(data);
}); });
this.eventParser.on(constants.AUDIO_PLAY, () => { eventparser.on(constants.AUDIO_PLAY, () => {
logger.debug('handling event \'' + constants.AUDIO_PLAY + '\'...'); logger.debug('handling event \'' + constants.AUDIO_PLAY + '\'...');
global.player.play(); global.player.play();
}); });
this.eventParser.on(constants.AUDIO_RESUME, () => { eventparser.on(constants.AUDIO_RESUME, () => {
logger.debug('handling event \'' + constants.AUDIO_RESUME + '\'...'); logger.debug('handling event \'' + constants.AUDIO_RESUME + '\'...');
global.player.resume(); global.player.resume();
}); });
this.eventParser.on(constants.AUDIO_PAUSE, () => { eventparser.on(constants.AUDIO_PAUSE, () => {
logger.debug('handling event \'' + constants.AUDIO_PAUSE + '\'...'); logger.debug('handling event \'' + constants.AUDIO_PAUSE + '\'...');
global.player.pause(); global.player.pause();
}); });
this.eventParser.on(constants.AUDIO_STOP, () => { eventparser.on(constants.AUDIO_STOP, () => {
logger.debug('handling event \'' + constants.AUDIO_STOP + '\'...'); logger.debug('handling event \'' + constants.AUDIO_STOP + '\'...');
global.player.stop(); global.player.stop();
}); });
@ -71,9 +70,6 @@ class Audiostream {
socket.on('error', (error) => { socket.on('error', (error) => {
logger.error('error connecting to audio server \'' + this.getTag() + '\': ' + error); logger.error('error connecting to audio server \'' + this.getTag() + '\': ' + error);
}); });
socket.on('close', () => {
logger.info('connection to audio server \'' + this.getTag() + '\' closed');
});
} }
destroy() { destroy() {

View file

@ -2,7 +2,6 @@ const util = require('../libs/util.js');
const net = require('net'); const net = require('net');
const Heartbeat = require('./Heartbeat.js'); const Heartbeat = require('./Heartbeat.js');
const EventParser = require('./EventParser.js');
const Audiostream = require('./Audiostream.js'); const Audiostream = require('./Audiostream.js');
class Connection { class Connection {
@ -16,8 +15,7 @@ class Connection {
} }
this.host = config?.server?.host || "127.0.0.1"; this.host = config?.server?.host || "127.0.0.1";
this.port = config?.server?.port || "3000"; this.port = config?.server?.port || "3000";
this.eventParser = new EventParser(); this.audiostream = new Audiostream();
this.audiostream = new Audiostream(this.eventParser);
} }
getHost() { getHost() {
@ -53,7 +51,7 @@ class Connection {
#handleEventConnect(resolve, socket) { #handleEventConnect(resolve, socket) {
logger.info('connected to communication server \'' + this.getTag() + '\'...'); logger.info('connected to communication server \'' + this.getTag() + '\'...');
this.socket = socket; this.socket = socket;
this.heartbeat = new Heartbeat(this.eventParser); this.heartbeat = new Heartbeat();
this.#handleHeartbeat(); this.#handleHeartbeat();
socket.on('timeout', () => { socket.on('timeout', () => {
this.#handleEventTimeout(); this.#handleEventTimeout();
@ -67,7 +65,7 @@ class Connection {
} }
async #handleEventData(data) { async #handleEventData(data) {
this.eventParser.parse(data); eventparser.parse(data);
} }
#handleEventTimeout() { #handleEventTimeout() {
@ -93,16 +91,12 @@ class Connection {
destroy() { destroy() {
if (this.heartbeat !== undefined) { if (this.heartbeat !== undefined) {
this.heartbeat.removeAllListeners();
this.heartbeat.destroy(); this.heartbeat.destroy();
this.heartbeat.removeAllListeners('timeout');
this.heartbeat = undefined; this.heartbeat = undefined;
} }
if (this.socket !== undefined) { if (this.socket !== undefined) {
this.socket.removeAllListeners('connect'); this.socket.removeAllListeners();
this.socket.removeAllListeners('error');
this.socket.removeAllListeners('timeout');
this.socket.removeAllListeners('close');
this.socket.removeAllListeners('data');
this.socket.end(); this.socket.end();
this.socket.destroy(); this.socket.destroy();
this.socket = undefined; this.socket = undefined;

View file

@ -1,52 +1,47 @@
const { sleep } = require('../libs/util.js');
const EventEmitter = require('events'); const EventEmitter = require('events');
const Message = require('./Message.js'); const Message = require('./Message.js');
class Heartbeat extends EventEmitter { class Heartbeat extends EventEmitter {
constructor(eventParser) { constructor() {
super(); super();
this.interval = config?.server?.heartbeat || 10000; this.interval = config?.server?.heartbeat || 10000;
this.eventParser = eventParser;
this.#listenForPingPong(); this.#listenForPingPong();
this.#sendPing(); this.#sendPing();
} }
async #sendPing() { async #sendPing() {
if (this.timeout !== undefined) { if (this.destroyed === true) {
clearTimeout(this.timeout); return;
} }
if (this.alive === false) { if (this.alive === false) {
this.emit('timeout'); this.emit('timeout');
return; }
} else if (this.alive === undefined) { if (this.alive === undefined) {
await new Promise((resolve, reject) => { await sleep(this.interval);
setTimeout(resolve, this.interval);
})
} }
this.alive = false; this.alive = false;
await new Message('ping').send(); await new Message('ping').send();
this.timeout = setTimeout(() => { await sleep(this.interval);
this.#sendPing(); this.#sendPing();
}, this.interval);
} }
async #listenForPingPong() { async #listenForPingPong() {
this.eventParser.on('ping', () => { eventparser.on('ping', () => {
logger.debug('handling event \'ping\', responding with \'pong\'...'); logger.debug('handling event \'ping\', responding with \'pong\'...');
new Message('pong').send(); new Message('pong').send();
}); });
this.eventParser.on('pong', () => { eventparser.on('pong', () => {
logger.debug('handling event \'pong\'...'); logger.debug('handling event \'pong\'...');
this.alive = true; this.alive = true;
}); });
} }
destroy() { destroy() {
if (this.timeout !== undefined) { this.destroyed = true;
clearTimeout(this.timeout); eventparser.removeAllListeners('ping');
} eventparser.removeAllListeners('pong');
this.eventParser.removeAllListeners('ping');
this.eventParser.removeAllListeners('pong');
} }
} }

View file

@ -25,7 +25,7 @@ class Message {
return; return;
} }
const data = this.toString(); const data = this.toString();
logger.debug('sending data to \'' + socket.remoteAddress + ':' + socket.remotePort + '\': ' + data); // logger.debug('sending data to \'' + socket.remoteAddress + ':' + socket.remotePort + '\': ' + data);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
socket.write(data + constants.EVENT_DELIMITER, resolve); socket.write(data + constants.EVENT_DELIMITER, resolve);
}); });

View file

@ -1,9 +1,10 @@
const packageJSON = require('./package.json'); const packageJSON = require('./package.json');
const path = require('path'); const path = require('path');
const Connection = require('./classes/Connection.js');
const Logger = require('./classes/Logger.js'); const Logger = require('./classes/Logger.js');
const EventParser = require('./classes/EventParser.js');
const Player = require('./classes/Player.js'); const Player = require('./classes/Player.js');
const Connection = require('./classes/Connection.js');
const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM']; const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM'];
@ -23,6 +24,7 @@ async function main() {
handleExit(); handleExit();
global.constants = require('./libs/constants.js'); global.constants = require('./libs/constants.js');
global.logger.info("launching " + packageJSON.name + " " + packageJSON.version + "..."); global.logger.info("launching " + packageJSON.name + " " + packageJSON.version + "...");
global.eventparser = new EventParser();
global.player = new Player(); global.player = new Player();
global.connection = new Connection(); global.connection = new Connection();
while (true) { while (true) {