Russian Qt Forum

Qt => Общие вопросы => Тема начата: Fregloin от Сентябрь 09, 2013, 11:34



Название: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Fregloin от Сентябрь 09, 2013, 11:34
Привет. Нужно как то следить за нашими приложениями, которые написаны на Qt (4.8/5.x) под Ubuntu.
Предложения представляют собой автоматизированные рабочие места, которые должны работать в режиме 24/7.
Проблема в том, что иногда они подвисают. Выяснить причины их зависаний пока не могу (может код кривой, а может глюки системы).
Поэтому возникла мысль написать демона, который бы следил за "жизнью" моих приложений, и при отслеживании что оно зависло, выдавало сообещние пользователю с предлодением о
перезапуске.
Как бы это сделать. Приложения многопоточные, используется сеть, графические сцены (довольно сложные с большим количеством объектов на них), интенсивный парсинг JSON и много чего еще.
Что и где подвешивает их уже не прослдеить. Погнятное дело что я их постоянно оптимизирую, довожу до ума, но на предприятии где они сейчас работают нужно как то логировать их поведение.
Думал в каждом потоке завести сигнал, дергать его по таймеру и остлеживать допустимость таймаутов. Если таймаут больше разрешимого, считать что приложение зависло.
Думаю использовать DBus. Но вот почитав доки Qt толком не понял как его использовать. Есть какие то примеры простые что бы понять, как поднять свой сервис и ловить сообщения. Ну и как подключиться к сервису и слать на него события?


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Bepec от Сентябрь 09, 2013, 13:02
Моё видение:
1) радикальное - разобраться с багами и всё исправить.
2) поднять свой сервер (если терминалы в одной сети) и туда по таймеру тумкать пакеты.
3) в принципе тот же сервер, но локальный в виде службы (самый простой вариант).
4) своя реализация типа modbus, но мне терпения не хватило её освоить :D

Мне 3 вариант импонирует после первого :)


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Igors от Сентябрь 09, 2013, 13:27
Если приложения Ваши - можно через shared memory (ячейками) + глобальный семафор. При запуске создается семвфор и заводится нитка которая постоянно на нем висит. Когда опрашивающий сигналит, нитка снимается с семафора и пишет в shared block статус приложения (напр "идет расчет"). Получается несложно.


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Fregloin от Сентябрь 09, 2013, 13:34
Так я и рассматриваю создание своей службы, которая будет регистрироваться в dbus.
Приложения, при запуске будут искать нужный сервис, и если таковой имеется будут слать на него какие то пакеты, отвечающие о том, как оно живет и что делает.
Если сервис не запущен, то просто будет работать как и раньше.
А сервис будет следить за приложениями (думаю в качестве ключа будет pid) и логировать в файл.

Вот набросал клиент и сервер согласно примерам ping/pong но клиент не может отсладить регистрацию сервера.
Если сервис уже запущен, то он его видит.

Сервис
Код:
#include <QObject>
#include <QtCore/QObject>
#include <QtDBus/QDBusAbstractAdaptor>
#include <QtDBus/QDBusVariant>

class CListener : public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.mycompany.hangapp.service")
    Q_PROPERTY(QString  message_text    READ    messageText WRITE   setMessageText)

    QString fmessageText;

public:
    explicit CListener(QObject *parent = 0);
    
    QString messageText()   const;
    void    setMessageText(const QString & value);

signals:
    
    void    aboutToQuit();

public slots:

    QDBusVariant    query(const QString & query);
    Q_NOREPLY       void    quit();
    
};
Код:
#include "clistener.h"
#include <QTimer>

CListener::CListener(QObject *parent) :
    QDBusAbstractAdaptor(parent)
{
}

QString CListener::messageText() const
{
    return  fmessageText;
}

void CListener::setMessageText(const QString &value)
{
    fmessageText = value;
}


void CListener::quit()
{
//    QTimer::singleShot(0,QCoreApplication::instance(), SLOT(quit()));
}


QDBusVariant CListener::query(const QString &query)
{
    QString answer = "Answer :"+query;
    return  QDBusVariant(answer);
}
main.cpp
Код:
#include <stdio.h>
#include <QCoreApplication>
#include <QtDBus/QtDBus>

#include "clistener.h"
#include "hangapp_service.h"

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QObject object;

    printf("Mycompany hangapp service 0.1\n");

    printf("Try to register in D-Bus ");

    CListener   *   listener = new CListener(&object);
    listener->setProperty("message_text","init_value");

    QDBusConnection::sessionBus().registerObject("/",&object);
    if(!QDBusConnection::sessionBus().registerService(SERVICE_NAME))
    {
        printf("failed with reason '%s'\n",qPrintable(QDBusConnection::sessionBus().lastError().message()));
        exit(1);
    }
    else
    {
        printf("ok\nService name is %s\n",SERVICE_NAME);
    }

    return a.exec();
}

А вот клиент
Код:
#include <QMainWindow>
#include <QtDBus/QDBusInterface>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

    QDBusInterface *iface;

    void    checkDBusInterface();
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void    on_pushButton_clicked();

public  slots:
    void    dbus_start_notify(const QString & dbus_name);
    void    dbus_stop_notify(const QString & dbus_name);
};
Код:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtDBus/QtDBus>
#include "hangapp_service.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    iface = NULL;
    connect(QDBusConnection::sessionBus().interface(),SIGNAL(serviceRegistered(QString)),this,SLOT(dbus_start_notify(QString)));
    connect(QDBusConnection::sessionBus().interface(),SIGNAL(serviceUnregistered(QString)),this,SLOT(dbus_stop_notify(QString)));
    checkDBusInterface();
}

void    MainWindow::checkDBusInterface()
{
    iface = new QDBusInterface(SERVICE_NAME, "/", "com.mycompany.hangapp.service",
                               QDBusConnection::sessionBus(), this);
    if (!iface->isValid()) {
        fprintf(stderr, "%s\n",
                qPrintable(QDBusConnection::sessionBus().lastError().message()));
        ui->pushButton->setEnabled(false);
        ui->lineEdit->setEnabled(false);
        ui->listWidget->setEnabled(false);
        delete iface;
        iface = NULL;
    }
    else
    {
        ui->pushButton->setEnabled(true);
        ui->lineEdit->setEnabled(true);
        ui->listWidget->setEnabled(true);
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QDBusReply<QDBusVariant> reply = iface->call("query", ui->lineEdit->text());
    if(reply.isValid())
        ui->listWidget->addItem(qPrintable(reply.value().variant().toString()));
}

void MainWindow::dbus_start_notify(const QString &dbus_name)
{
    if(dbus_name==SERVICE_NAME)
    {
        checkDBusInterface();
    }
}

void MainWindow::dbus_stop_notify(const QString &dbus_name)
{
    if(dbus_name==SERVICE_NAME)
    {
       ui->pushButton->setEnabled(false);
       ui->lineEdit->setEnabled(false);
       ui->listWidget->setEnabled(false);
       delete iface;
       iface = NULL;
    }
}
main.cpp
Код:
#include "mainwindow.h"
#include <QApplication>
#include <QtDBus>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    if (!QDBusConnection::sessionBus().isConnected())
    {
        fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"
                     "To start it, run:\n"
                     "\teval `dbus-launch --auto-syntax`\n");
        return 1;
    }

    MainWindow w;

    w.show();
    
    return a.exec();
}


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Fregloin от Сентябрь 09, 2013, 13:36
На одной машине обычно запускается одна копия приложения, но одновременно может быть запущено еще 1-2, с разным функционалом но общим ядром. Нужно их различать.


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Alex Custov от Сентябрь 09, 2013, 13:38
стандартная практика - watchdog. Локальный процесс, который по каким-либо условиям определяет, что другой процесс завис и перезапускает его. Например, watchdog открывает локальный сокет и передаёт путь к нему программе, за которой нужно следить. Программа раз в период пишет туда данные, watchdog их читает. Если данных нет - программа зависла, её нужно перезапустить.


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Fregloin от Сентябрь 09, 2013, 13:53
Вот такой ватчдог хочу сделать, только централизированный, который будет собирать данные со всех запущенных моих приложений на машине(машинах в сети в будущем), который по мимо того что будет перезапускать программы, будет писать когда запустилась, когда зависла (какой поток). Т.к. приезжаю на объект не часто (раз в 2-3 месяца), а потом слышу - прога зависла, а когда и почему не знают. Сначала думал что прога сама будет писать логи, но при зависании записать уже ничего не получалось. + Сервер будет слать событе о необходимости обновления клиента на новую версию + какое то дополнительное сопровождение на будущее. Поэтому dbus рассматриваю. Пока все крутится на ubuntu (есть версии под винду, но они сделаны для некритичных участков, т.е. там как бы остлеживания не важны, но не помешали бы).

Какая у меня идея.
Есть прога. Есть потоки. Потоку впринципе можно задать имя через objectName. В каждом потоке (и в гуи тоже) будет таймер, который преиодически будет слать импульсы.
Будет отдельный поток, который все эти импульсы будет перенаправлять уже в DBUS с названием потоков. Что бы можно было более детально понять, какой именно поток подвисает, например сетевой, поток парсинга json, поток гуи и т.д.


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Fregloin от Сентябрь 09, 2013, 17:07
Разобрался с подключением к сервису и отправкой сообщений.
Я так понял что при создании системного процесса нужно записать конфигурацию в /etc/dbus-1/system.d
Вопрос, что туда писать, посмотрел несколько файлов, но не совсем понял как и что туда прописывать.
Ну и скорее всего сервис нужно запускать под рутом? Из среды когда запускаю, выдает ошибку что невозможно зарегестрировать системный процесс.


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Alex Custov от Сентябрь 09, 2013, 19:44
системный сервис должны быть явно разрешён политиками в указанном каталоге. Если политика для сервиса отсутствует или неверна, то сервис не сможет зарегистрироваться. Сессионные сервисы этого не требуют, зачем использовать именно системные?


Название: Re: Перезапуск зависающих приложений/слежение за зависаниями
Отправлено: Fregloin от Сентябрь 10, 2013, 11:28
Ну хотя бы потому, что пользователь может случайно выйти с системы (было уже не раз). Т.е. что бы прога работала между сессиями.