Compare commits

..

No commits in common. "main" and "basharin_sevastyan_lab_8" have entirely different histories.

1790 changed files with 0 additions and 54680 deletions

View File

@ -1,27 +0,0 @@
version: '3.8'
services:
usr_service:
build:
context: /user_service
dockerfile: Dockerfile
depends_on:
- msg_service
expose:
- 8082
msg_service:
build:
context: /message_service
dockerfile: Dockerfile
expose:
- 8081
nginx:
image: nginx
ports:
- 8086:8086
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- msg_service
- usr_service

View File

@ -1,11 +0,0 @@
FROM python:3.9
EXPOSE 8081
RUN pip install Flask requests
WORKDIR /work
COPY msg_service.py ./
CMD ["python", "msg_service.py"]

View File

@ -1,50 +0,0 @@
from flask import Flask, jsonify, request, Response
from datetime import datetime
app = Flask(__name__)
msgs = {0: {'id': 0, 'from': 0, 'to': 1, 'body': 'qq', 'dt': datetime(2023, 12, 22, 8, 0)},
1: {'id': 1, 'from': 1, 'to': 0, 'body': 'qq', 'dt': datetime(2023, 12, 22, 8, 5)},
2: {'id': 2, 'from': 0, 'to': 1, 'body': 'bye', 'dt': datetime(2023, 12, 22, 8, 10)},
3: {'id': 3, 'from': 1, 'to': 1, 'body': 'bye', 'dt': datetime(2023, 12, 22, 8, 15)}}
@app.route('/', methods=['GET', 'POST'])
def get_all():
if request.method == 'POST':
dto = request.get_json()['dto']
new_id = max(msgs.keys()) + 1
msgs[new_id] = {
'id': new_id,
'from': dto['from'],
'to': dto['to'],
'body': dto['body'],
'dt': datetime.now()
}
return jsonify(msgs[new_id])
return jsonify(msgs)
@app.route('/<int:msg_id>', methods=['GET', 'PUT', 'DELETE'])
def get_by_id(msg_id):
if msg_id not in msgs.keys():
return Response(status=404)
if request.method == 'PUT':
dto = request.get_json()['dto']
msgs[msg_id] = {
'from': dto['from'],
'to': dto['to'],
'body': dto['body'],
'dt': datetime.now()
}
return msgs[msg_id]
elif request.method == 'DELETE':
msgs.pop(msg_id)
return Response(status=200)
return jsonify(msgs[msg_id])
if __name__ == '__main__':
app.run(host='0.0.0.0', use_reloader=False, port=8081)

View File

@ -1,27 +0,0 @@
events {
worker_connections 1024;
}
http {
server {
listen 8086;
listen [::]:8086;
server_name localhost;
location /msg_service/ {
proxy_pass http://msg_service:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Prefix $scheme;
}
location /usr_service/ {
proxy_pass http://usr_service:8082/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Prefix $scheme;
}
}
}

View File

@ -1,32 +0,0 @@
## Задание
1. Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
2. Реализовать механизм синхронного обмена сообщениями между микросервисами.
3. Реализовать шлюз на основе прозрачного прокси-сервера nginx.
Вариант: сообщения и пользователи
## Выполнение
Были написаны два сервиса на языке python с технологией flask:
* Сервис msg_service, хранящий данные о сообщениях и реализующий CRUD операции с ними через HTTP запросы.
* Сервис usr_service, хранящий данные о пользователях и реализующий CRUD операции с ними через HTTP запросы.
Сервисы синхронно сообщены - сервис пользователей запрашивает данные у сервиса сообщений для получения сообщений пользователя.
Для сервисов прописаны файлы Dockerfile, описывающие создание контейнеров:
* Для обоих контейнеров выбирается Python 3.9.
* Оба контейнера проявляют порты, на которых работает приложение: 8081 для сообщений и 8082 для пользователей.
* В оба контейнера устанавливаются пакеты Flask и requests.
* Выбирается рабочая директория /work и туда копируются файлы скриптов.
* Командой запускаются сами скрипты.
Общий yaml-файл развёртки был настроен следующим образом:
* блок services, где перечислены разворачиваемые сервисы.
* для каждого сервиса прописан build, где объявляется его папка и докерфайл создания и зависимости.
* для сервиса nginx прописан порт для отображения вовне.
## Результат
Демонстрация работы в видео.
## Ссылка на видео
https://drive.google.com/file/d/1gmZsbzMmC34Uidi4u_D3nFyPAG0MuPAf/view?usp=drive_link

View File

@ -1,11 +0,0 @@
FROM python:3.9
EXPOSE 8082
RUN pip install Flask requests
WORKDIR /work
COPY usr_service.py ./
CMD ["python", "usr_service.py"]

View File

@ -1,56 +0,0 @@
import requests
from flask import Flask, jsonify, request, Response
app = Flask(__name__)
usrs = {0: {'id': 0, 'name': 'anton', 'role': 'admin', 'online': False, 'msgs_sent': [0, 2], 'msgs_got': [1, 3]},
1: {'id': 1, 'name': 'lioha', 'role': 'user', 'online': False, 'msgs_sent': [1, 3], 'msgs_got': [0, 2]}}
@app.route('/', methods=['GET', 'POST'])
def get_all():
if request.method == 'POST':
dto = request.get_json()['dto']
new_id = max(usrs.keys()) + 1
usrs[new_id] = {
'id': new_id,
'name': dto['name'],
'role': dto['role'],
'online': dto['online']
}
return jsonify(usrs[new_id])
return jsonify(usrs)
@app.route('/<int:usr_id>', methods=['GET', 'PUT', 'DELETE'])
def get_by_id(usr_id):
if usr_id not in usrs.keys():
return Response(status=404)
if request.method == 'PUT':
dto = request.get_json()['dto']
usrs[usr_id] = {
'name': dto['name'],
'role': dto['role'],
'online': dto['online']
}
return usrs[usr_id]
elif request.method == 'DELETE':
usrs.pop(usr_id)
return Response(status=200)
usr = usrs[usr_id]
msgs = []
for msg_id in usr['msgs_sent']:
msgs.append(requests.get("http://msg_service:8081/"+str(msg_id)).json())
usr['msgs_sent'] = msgs
msgs.clear()
for msg_id in usr['msgs_got']:
msgs.append(requests.get("http://msg_service:8081/"+str(msg_id)).json())
usr['msgs_got'] = msgs
return jsonify(usr)
if __name__ == '__main__':
app.run(host='0.0.0.0', use_reloader=False, port=8082)

View File

@ -1,53 +0,0 @@
#ifndef _CLIENT_H
#define _CLIENT_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
using namespace std;
#define HEARBEATS "@@__Control__@@"
class Sender;
class Receiver;
class Client : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<Sender *> *_senders;
QList<Receiver *> *_receivers;
public:
Client();
Client(ClientOption option);
virtual ~Client();
Client& operator=(Client client);
QString getVersion() const;
ClientOption getClientOption() const { return clientOption; }
Sender *createSender(ExchangeOption& option);
void removeSender(Sender *sender);
Receiver *createReceiver(ConsumeOption& option);
void removeReceiver(Receiver *receiver);
signals:
void onStop();
};
Q_DECLARE_METATYPE(string)
#endif // _CLIENT_H

View File

@ -1,124 +0,0 @@
#include "_client_.h"
#include "sender.h"
#include "receiver.h"
// Конструктор для связи с локальным RabbitMQ
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Client::Client() : QObject(nullptr)
{
{
static const int idMsg = qRegisterMetaType<ProduceMessage>();
static const int idMsgPtr = qRegisterMetaType<PtrProduceMessage>();
static const int idString = qRegisterMetaType<string>();
Q_UNUSED(idMsg)
Q_UNUSED(idMsgPtr)
Q_UNUSED(idString)
}
_senders = new QList<Sender *>();
_senders->clear();
_receivers = new QList<Receiver *>();
_receivers->clear();
}
Client::Client(ClientOption option) : Client()
{
clientOption = option;
}
Client::~Client()
{
for (auto &sender : *_senders)
delete sender;
delete _senders;
for (auto &receiver : *_receivers)
delete receiver;
delete _receivers;
}
Client& Client::operator=(Client client)
{
if (this != &client) {
this->clientOption = client.clientOption;
this->_senders = new QList<Sender *>();
this->_senders->clear();
this->_receivers = new QList<Receiver *>();
this->_receivers->clear();
}
return *this;
}
int majorVersion = 1;
int minorVersion = 1;
int releaseVersion = 1;
QString Client::getVersion() const
{
return QString::number(majorVersion) +
"." + QString::number(minorVersion) +
"." + QString::number(releaseVersion);
}
// Создание публикатора (издателя) сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sender *Client::createSender(ExchangeOption& option)
{
Sender *sender = new Sender(clientOption, option);
connect(this, &Client::onStop, sender, &Sender::slotStop);
connect(this, &Client::onStop, sender, &Sender::deleteLater);
_senders->append(sender);
return sender;
}
void Client::removeSender(Sender *sender)
{
if ( !_senders->contains(sender))
return;
sender->slotStop();
_senders->removeOne(sender);
}
// Создание потребителя сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Receiver *Client::createReceiver(ConsumeOption& option)
{
Receiver *receiver = new Receiver(clientOption, option);
connect(this, &Client::onStop, receiver, &Receiver::slotStop);
connect(this, &Client::onStop, receiver, &Receiver::deleteLater);
_receivers->append(receiver);
return receiver;
}
void Client::removeReceiver(Receiver *receiver)
{
if ( !_receivers->contains(receiver))
return;
receiver->slotStop();
_receivers->removeOne(receiver);
}

View File

@ -1,22 +0,0 @@
#ifndef CLIENT_CPP_H
#define CLIENT_CPP_H
#include "clientrbcpp_global.h"
#include "clientrbcpp.h"
#include "_client_.h"
#include "producemessage.h"
#include "properties.h"
#include "cworker.h"
#include "headers.h"
#include "pworker.h"
#include "receiver.h"
#include "sender.h"
#include "validator.h"
#include "vworker.h"
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
#include "options/queueoption.h"
#endif // CLIENT_CPP_H

View File

@ -1,6 +0,0 @@
#include "clientrbcpp.h"
ClientRBcpp::ClientRBcpp()
{
}

View File

@ -1,13 +0,0 @@
#ifndef CLIENTRBCPP_H
#define CLIENTRBCPP_H
#include "clientrbcpp_global.h"
class CLIENTRBCPPSHARED_EXPORT ClientRBcpp
{
public:
ClientRBcpp();
};
#endif // CLIENTRBCPP_H

View File

@ -1,12 +0,0 @@
#ifndef CLIENTRBCPP_GLOBAL_H
#define CLIENTRBCPP_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(CLIENTRBCPP_LIBRARY)
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_EXPORT
#else
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // CLIENTRBCPP_GLOBAL_H

View File

@ -1,276 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
#include <QDebug>
CWorker::CWorker(ClientOption& clientOption, ConsumeOption& consumeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
connection = nullptr;
channel = nullptr;
}
CWorker::~CWorker()
{
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
// AMQP::TcpConnection connection(&handler, AMQP::Address(address));
// AMQP::TcpChannel channel(&connection);
connection = new AMQP::TcpConnection(&handler, AMQP::Address(address));
channel = new AMQP::TcpChannel(connection);
channel->setQos(1);
channel->onError([&](const char *message) {
Q_UNUSED(message)
emit onErrorConsume("Channel error!!!");
});
QTimer tm;
tm.stop();
tm.setInterval(30000);
connect(&tm, &QTimer::timeout, this, [&]() {
tm.stop();
connection->heartbeat();
tm.start();
});
tm.start();
// Обработка принятого сообщения
auto messageCb = [&](const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{
Q_UNUSED(redelivered)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onResultReady(rMsg, deliveryTag);
channel->ack(deliveryTag);
};
// объявление точки обмена
if (!consumeOption.receivingExchange.name.isEmpty()) {
string exchange = consumeOption.receivingExchange.name.toStdString();
string type = consumeOption.receivingExchange.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (consumeOption.receivingExchange.durable)
flagsExchange |= AMQP::durable;
if (consumeOption.receivingExchange.auto_delete)
flagsExchange |= AMQP::autodelete;
if (consumeOption.receivingExchange.internal)
flagsExchange |= AMQP::internal;
AMQP::Table tableExch;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (consumeOption.receivingExchange.arguments.contains(alt_e_name)) {
alt_e_value = consumeOption.receivingExchange.arguments[alt_e_name].value<QString>();
tableExch.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel->declareExchange(exchange, typeEx, flagsExchange, tableExch)
.onError([&](const char *description) {
qDebug() << description;
});
}
QMultiMap<QString, QString>::iterator it = consumeOption.bindingArgs.begin();
for(; it != consumeOption.bindingArgs.end(); ++it) {
channel->bindExchange(it.key().toStdString(), exchange, it.value().toStdString()).onError([&](const char *description) {
qDebug() << description;
});
}
}
// объявление очереди
QueueOption option = consumeOption.queueOption;
string exchange = consumeOption.exchange.toStdString();
string queue = option.name.toStdString();
// Подготовка флагов для объявления очереди
int flagsQueue = 0;
if (option.durable)
flagsQueue |= AMQP::durable;
if (option.auto_delete)
flagsQueue |= AMQP::autodelete;
if (option.exclusive)
flagsQueue |= AMQP::exclusive;
channel->declareQueue(queue, flagsQueue)
.onSuccess( [&](const string &name, uint32_t messageCount, uint32_t consumerCount) {
Q_UNUSED(messageCount)
Q_UNUSED(consumerCount)
queue = name;
if (exchange != "")
for (QString rk : consumeOption.bindingKeys) {
channel->bindQueue(exchange, queue, rk.toStdString())
.onError( [&](const char *description) {
qDebug() << description;
});
}
});
// Подготовка флагов потребления сообщений
int flagsConsume = 0;
if (consumeOption.nolocal)
flagsConsume |= AMQP::nolocal;
if (consumeOption.noack)
flagsConsume |= AMQP::noack;
if (consumeOption.exclusive)
flagsConsume |= AMQP::exclusive;
channel->consume(queue, flagsConsume).onReceived(messageCb)
.onSuccess( [&](const string& tag) {
nextTag = tag;
})
.onError( [&](const char *description) {
emit onErrorConsume(description);
markStop = true; // Останов потока
});
//Цикл обработки событий
while(!markStop) {
event_base_loop(evbase, EVLOOP_ONCE);
QCoreApplication::processEvents();
}
// Закроем канал и соединение
channel->close();
connection->close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
delete channel;
delete connection;
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
void CWorker::slotStop()
{
markStop = true;
channel->cancel(nextTag); // Отменить потребление
channel->close();
connection->close();
}
void CWorker::bind(QString exchange, QString key, bool ex)
{
if (ex) channel->bindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->bindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}
void CWorker::unbind(QString exchange, QString key, bool ex)
{
if (ex) channel->unbindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->unbindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}

View File

@ -1,54 +0,0 @@
#ifndef CWORKER_H
#define CWORKER_H
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class CWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ConsumeOption consumeOption;
bool markStop;
AMQP::TcpConnection *connection;
AMQP::TcpChannel *channel;
string nextTag;
public:
CWorker(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~CWorker();
public slots:
void doWork();
void slotStop();
void bind(QString exchange, QString key, bool ex);
void unbind(QString exchange, QString key, bool ex);
signals:
void onResultReady(PtrProduceMessage msg, uint64_t deliveryTag);
void onErrorConsume(const char *description);
void onWorkFinished();
};
#endif // CWORKER_H

View File

@ -1,40 +0,0 @@
#include "headers.h"
Headers::Headers()
{
_headers.clear();
}
Headers::Headers(const Headers& h)
{
this->_headers = h._headers;
}
void Headers::operator=(const Headers& h)
{
this->_headers = h._headers;
}
QMap<QString,QVariant> Headers::getHeaders() const { return _headers; }
QList<QString> Headers::keys() const { return _headers.keys(); }
QList<QVariant> Headers::values() const { return _headers.values(); }
int Headers::size() const { return _headers.size(); }
bool Headers::contains(const QString name) const { return _headers.contains(name); }
void Headers::set(const QString name, bool value) { _headers.insert(name, value); }
void Headers::set(const QString name, int value) { _headers.insert(name, value); }
void Headers::set(const QString name, QString str) { _headers.insert(name, str); }
bool Headers::isBool(const QString name) const { return _headers[name].type() == QVariant::Bool; }
bool Headers::isInteger(const QString name) const { return _headers[name].type() == QVariant::Int; }
bool Headers::isString(const QString name) const { return _headers[name].type() == QVariant::String; }
bool Headers::getBool(const QString name) const { return _headers[name].value<bool>(); }
int Headers::getInteger(const QString name) const { return _headers[name].value<int>(); }
QString Headers::getString(const QString name) const { return _headers[name].value<QString>(); }

View File

@ -1,45 +0,0 @@
#ifndef HEADERS_H
#define HEADERS_H
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
class Headers
{
private:
QMap<QString,QVariant> _headers;
public:
Headers();
Headers(const Headers& h); // Конструктор копирования
~Headers() {}
void operator=(const Headers& h);
QMap<QString,QVariant> getHeaders() const;
QList<QString> keys() const;
QList<QVariant> values() const;
int size() const;
bool contains(const QString name) const;
void set(const QString name, bool value);
void set(const QString name, int value);
void set(const QString name, QString str);
bool isBool(const QString name) const;
bool isInteger(const QString name) const;
bool isString(const QString name) const;
bool getBool(const QString name) const;
int getInteger(const QString name) const;
QString getString(const QString name) const;
};
#endif // HEADERS_H

View File

@ -1,45 +0,0 @@
#ifndef CLIENTOPTION_H
#define CLIENTOPTION_H
#include <QString>
// Значения по умолчанию
const QString DEFAULT_CPP_HOST = "localhost";
const int DEFAULT_CPP_PORT = 5672;
const QString DEFAULT_CPP_USER = "guest";
const QString DEFAULT_CPP_PASSWORD = "guest";
const QString DEFAULT_CPP_VHOST = "/";
struct ClientOption {
QString host;
int port;
QString user;
QString password;
QString vhost;
ClientOption() {
host = DEFAULT_CPP_HOST;
port = DEFAULT_CPP_PORT;
user = DEFAULT_CPP_USER;
password = DEFAULT_CPP_PASSWORD;
vhost = DEFAULT_CPP_VHOST;
}
~ClientOption() {}
ClientOption(const ClientOption& src) = default; // Конструктор копирования
ClientOption(ClientOption&& src) = default; // Конструктор перемещения
ClientOption& operator=(const ClientOption rhs) // Оператор присваивания
{
host = rhs.host;
port = rhs.port;
user = rhs.user;
password = rhs.password;
vhost = rhs.vhost;
return *this;
}
};
#endif // CLIENTOPTION_H

View File

@ -1,52 +0,0 @@
#ifndef CONSUMEOPTION_H
#define CONSUMEOPTION_H
#include <QObject>
#include <QString>
#include <QList>
#include "queueoption.h"
#include "exchangeoption.h"
struct ConsumeOption
{
QString exchange; // Имя точки обмена для связывания
QStringList bindingKeys; // ключи связи точки с очередью
bool nolocal;
bool noack;
bool exclusive;
QueueOption queueOption; // Параметры очереди
ExchangeOption receivingExchange; // Параметры новой принимающей очереди (по умолчанию новой не создаётся)
QMultiMap<QString, QString> bindingArgs; // список связей для точки обмена (если создаётся новая точка)
ConsumeOption() {
exchange = "";
receivingExchange.name = "";
nolocal = false;
noack = false;
exclusive = false;
}
~ConsumeOption() {}
ConsumeOption(const ConsumeOption& src) = default; // Конструктор копирования
ConsumeOption(ConsumeOption&& src) = default; // Конструктор перемещения
ConsumeOption& operator=(const ConsumeOption rhs) // Оператор присваивания
{
exchange = rhs.exchange;
bindingKeys = rhs.bindingKeys;
nolocal = rhs.nolocal;
noack = rhs.noack;
exclusive = rhs.exclusive;
queueOption = rhs.queueOption;
bindingArgs = rhs.bindingArgs;
return *this;
}
};
#endif // CONSUMEOPTION_H

View File

@ -1,55 +0,0 @@
#ifndef EXCHANGEOPTION_H
#define EXCHANGEOPTION_H
#include <QString>
#include <QVariantMap>
#include <QList>
struct ExchangeOption
{
QString name; // уникальное имя точки обмена
QString type; // тип точки обмена (direct, topic,
// fanout или headers)
bool auto_delete; // автоматически удаляемая точка обмена
bool durable; // долгоживущая точка обмена
bool passive; // требуется информация о точке обмена
bool internal; // нельзя вести публикацию из приложения
QVariantMap arguments; // необязательные аргументы
QMap<QString, QString> bindingArgs; // список связей для точки обмена
bool ifunused; // можно удалять, только если точка обмена
// не используется (не имеет потребителей)
ExchangeOption() {
name = "";
type = "";
auto_delete = false;
durable = false;
passive = false;
internal = false;
arguments.clear();
ifunused = false;
}
~ExchangeOption() {}
ExchangeOption(const ExchangeOption& src) = default; // Конструктор копирования
ExchangeOption(ExchangeOption&& src) = default; // Конструктор перемещения
ExchangeOption& operator=(const ExchangeOption rhs) // Оператор присваивания
{
name = rhs.name;
type = rhs.type;
auto_delete = rhs.auto_delete;
durable = rhs.durable;
passive = rhs.passive;
internal = rhs.internal;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
return *this;
}
};
#endif // EXCHANGEOPTION_H

View File

@ -1,63 +0,0 @@
#ifndef QUEUEOPTION_H
#define QUEUEOPTION_H
#include <QObject>
#include <QString>
#include <QVariantMap>
struct QueueOption
{
QString name; // Уникальное имя очереди
bool auto_delete; // Автоматически удаляемая очередь
bool durable; // Долгоживущая очередь
bool passive; // Требуется информация об очереди
bool exclusive; // Исключительная очередь
QVariantMap arguments; // Необязательные аргументы очереди
bool ifunused; // Удалять, только если не используется
bool ifempty; // Удалять, только если очередь пуста
int messageCount; // Число сообщений в очереди
int consumerCount; // Число потребителей очереди
QueueOption() {
name = "";
auto_delete = false;
durable = false;
passive = false;
exclusive = false;
arguments.clear();
ifunused = false;
ifempty = false;
messageCount = 0;
consumerCount = 0;
}
~QueueOption() {}
QueueOption(const QueueOption& src) = default; // Конструктор копирования
QueueOption(QueueOption&& src) = default; // Конструктор перемещения
QueueOption& operator=(const QueueOption rhs) // Оператор присваивания
{
name = rhs.name;
auto_delete = rhs.auto_delete;
passive = rhs.passive;
exclusive = rhs.exclusive;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
ifempty = rhs.ifempty;
messageCount = rhs.messageCount;
consumerCount = rhs.consumerCount;
return *this;
}
};
#endif // QUEUEOPTION_H

View File

@ -1,37 +0,0 @@
#include "producemessage.h"
// Конструктор по умолчанию
ProduceMessage::ProduceMessage()
{
_body.clear();
}
// Конструктор копирования
ProduceMessage::ProduceMessage(const ProduceMessage& msg)
{
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
ProduceMessage& ProduceMessage::operator=(const ProduceMessage& msg)
{
if (this != &msg) {
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
return *this;
}
QByteArray ProduceMessage::getBodyMsg() const { return _body; }
void ProduceMessage::setBodyMsg(const QByteArray &ba) { _body = ba; }
void ProduceMessage::setBodyMsg(const char *body, int size) { _body = QByteArray(body, size); }
Headers ProduceMessage::getHeaders() const { return _headers; }
void ProduceMessage::setHeaders(const Headers &headers) { _headers = headers; }
Properties ProduceMessage::getProperties() const { return _properties; }
void ProduceMessage::setProperties(const Properties &properties) { _properties = properties; }

View File

@ -1,43 +0,0 @@
#ifndef PRODUCEMESSAGE_H
#define PRODUCEMESSAGE_H
#include <QByteArray>
#include "properties.h"
#include "headers.h"
class ProduceMessage
{
public:
ProduceMessage();
ProduceMessage(const ProduceMessage& msg);
~ProduceMessage() {}
ProduceMessage& operator=(const ProduceMessage& msg);
QByteArray getBodyMsg() const;
void setBodyMsg(const QByteArray &ba);
void setBodyMsg(const char *body, int size);
Headers getHeaders() const;
void setHeaders(const Headers &headers);
Properties getProperties() const;
void setProperties(const Properties &properties);
private:
QByteArray _body;
protected:
Properties _properties;
Headers _headers;
};
using PtrProduceMessage = ProduceMessage*;
Q_DECLARE_METATYPE(ProduceMessage)
Q_DECLARE_METATYPE(PtrProduceMessage)
#endif // PRODUCEMESSAGE_H

View File

@ -1,63 +0,0 @@
#include "properties.h"
Properties::Properties()
{
_properties.clear();
setDeliveryMode(1); // не оставлять сообщения
}
// Конструктор копирования
Properties::Properties(const Properties& p)
{
this->_properties = p._properties;
}
void Properties::operator=(const Properties& p)
{
this->_properties = p._properties;
}
const QMap<QString,QVariant> &Properties::getProperties() { return _properties; }
bool Properties::contains(const QString name) const { return _properties.contains(name); }
bool Properties::isContentType() const { return _properties.contains("content-type"); }
bool Properties::isContentEncoding() const { return _properties.contains("content-encoding"); }
bool Properties::isMessageID() const { return _properties.contains("message-id"); }
bool Properties::isCorrelationID() const { return _properties.contains("correlation-id"); }
bool Properties::isTimestamp() const { return _properties.contains("timestamp"); }
bool Properties::isExpiration() const { return _properties.contains("expiration"); }
bool Properties::isDeliveryMode() const { return _properties.contains("delivery-mode"); }
bool Properties::isAppID() const { return _properties.contains("app-id"); }
bool Properties::isUserID() const { return _properties.contains("user-id"); }
bool Properties::isTypeName() const { return _properties.contains("type"); }
bool Properties::isReplyTo() const { return _properties.contains("reply-to"); }
bool Properties::isPriority() const { return _properties.contains("priority"); }
void Properties::setContentType(const QString &str) { _properties.insert("content-type", str); }
void Properties::setContentEncoding(const QString &str) { _properties.insert("content-encoding", str); }
void Properties::setMessageID(const QString &str) { _properties.insert("message-id", str); }
void Properties::setCorrelationID(const QString &str) { _properties.insert("correlation-id", str); }
void Properties::setTimestamp(const quint64 val) { _properties.insert("timestamp", val); }
void Properties::setExpiration(const QString &str) { _properties.insert("expiration", str); }
void Properties::setDeliveryMode(const quint8 val) { _properties.insert("delivery-mode", val); }
void Properties::setAppID(const QString &str) { _properties.insert("app-id", str); }
void Properties::setUserID(const QString &str) { _properties.insert("user-id", str); }
void Properties::setTypeName(const QString &str) { _properties.insert("type", str); }
void Properties::setReplyTo(const QString &str) { _properties.insert("reply-to", str); }
void Properties::setPriority(const quint8 val) { _properties.insert("priority", val); }
QString Properties::getContentType() const { return _properties["content-type"].value<QString>(); }
QString Properties::getContentEncoding() const { return _properties["content-encoding"].value<QString>(); }
QString Properties::getMessageID() const { return _properties["message-id"].value<QString>(); }
QString Properties::getCorrelationID() const { return _properties["correlation-id"].value<QString>(); }
quint64 Properties::getTimestamp() const { return _properties["timestamp"].value<quint64>(); }
QString Properties::getExpiration() const { return _properties["expiration"].value<QString>(); }
quint8 Properties::getDeliveryMode() const { return _properties["delivery-mode"].value<quint8>(); }
QString Properties::getAppID() const { return _properties["app-id"].value<QString>(); }
QString Properties::getUserID() const { return _properties["user-id"].value<QString>(); }
QString Properties::getTypeName() const { return _properties["type"].value<QString>(); }
QString Properties::getReplyTo() const { return _properties["reply-to"].value<QString>(); }
quint8 Properties::getPriority() const { return _properties["priority"].value<quint8>(); }

View File

@ -1,73 +0,0 @@
#ifndef PROPERTIES_H
#define PROPERTIES_H
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QVariant>
class Properties
{
private:
QMap<QString,QVariant> _properties;
public:
Properties();
Properties(const Properties& p);
~Properties() {}
void operator=(const Properties& p);
int size() {return _properties.size(); }
const QMap<QString,QVariant> &getProperties();
bool contains(const QString name) const;
bool isContentType() const;
bool isContentEncoding() const;
bool isMessageID() const;
bool isCorrelationID() const;
bool isTimestamp() const;
bool isExpiration() const;
bool isDeliveryMode() const;
bool isAppID() const;
bool isUserID() const;
bool isTypeName() const;
bool isReplyTo() const;
bool isPriority() const;
void setContentType(const QString &str);
void setContentEncoding(const QString &str);
void setMessageID(const QString &str);
void setCorrelationID(const QString &str);
void setTimestamp(const quint64 val);
void setExpiration(const QString &str);
void setDeliveryMode(const quint8 val);
void setAppID(const QString &str);
void setUserID(const QString &str);
void setTypeName(const QString &str);
void setReplyTo(const QString &str);
void setPriority(const quint8 val);
QString getContentType() const;
QString getContentEncoding() const;
QString getMessageID() const;
QString getCorrelationID() const;
quint64 getTimestamp() const;
QString getExpiration() const;
quint8 getDeliveryMode() const;
QString getAppID() const;
QString getUserID() const;
QString getTypeName() const;
QString getReplyTo() const;
quint8 getPriority() const;
};
#endif // PROPERTIES_H

View File

@ -1,330 +0,0 @@
/**************************************************************************
* PWorker - Publish Worker, - рабочий поток публикации сообщений *
* редакция от 08.06.2022 *
* Принадлежность: библиотека clientRBcpp *
**************************************************************************/
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "pworker.h"
#include <QDebug>
PWorker::PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
qu.clear();
// static const int idE2E = qRegisterMetaType<E2EStruct>();
// Q_UNUSED(idE2E)
}
PWorker::~PWorker()
{
// Освободим очередь сообщений
mutex.lock();
while (! qu.isEmpty()) {
PublishPacket *packet = qu.dequeue();
AMQP::Envelope *envelope = packet->envelope;
delete envelope->body();
delete envelope;
delete packet;
}
mutex.unlock();
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void PWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Обрабатываем аргументы на предмет альтернативной точки обмена
AMQP::Table table;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (exchangeOption.arguments.contains(alt_e_name)) {
alt_e_value = exchangeOption.arguments[alt_e_name].value<QString>();
table.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
string alt_exchange = alt_e_value.toStdString(); // Имя альтернативной точки обмена
AMQP::ExchangeType typeAltEx = AMQP::fanout; // Тип альтернативной точки обмена - всегда fanout
int flagsAltEx = (AMQP::durable | AMQP::internal); // Точка долгоживущая и внутренняя
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onAck([&](uint64_t deliveryTag, bool multiple) {
emit onReceivedAckNack(deliveryTag, true, multiple);
})
.onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) {
Q_UNUSED(requeue)
emit onReceivedAckNack(deliveryTag, false, multiple);
});
// Объявляем альтернативную точку обмена
//--------------------------------------
if (alt_e_value != "") {
channel.declareExchange(alt_exchange, typeAltEx, flagsAltEx)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// Обработка основной точки обмена
//----------------------------------
string exchange = exchangeOption.name.toStdString();
string type = exchangeOption.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (exchangeOption.durable)
flagsExchange |= AMQP::durable;
if (exchangeOption.auto_delete)
flagsExchange |= AMQP::autodelete;
if (exchangeOption.internal)
flagsExchange |= AMQP::internal;
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel.declareExchange(exchange, typeEx, flagsExchange, table)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// обработка mandatory
auto messageCb = [&](const AMQP::Message& message, int16_t code, const std::string &description)
{
Q_UNUSED(code)
Q_UNUSED(description)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onMessageBounced(rMsg);
};
channel.recall().onReceived(messageCb);
// Цикл событий (event loop)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (! markStop) {
// Обрабатываем очередь сообщений на передачу
if (! qu.isEmpty()) {
mutex.lock();
PublishPacket *packet = qu.dequeue();
mutex.unlock();
AMQP::Envelope *envelope = packet->envelope;
int publishFlags = packet->publishFlags;
string routingKey = packet->routingKey;
if (envelope->hasAppID() && envelope->appID() == HEARBEATS)
connection.heartbeat();
else
channel.publish(exchange, routingKey, *envelope, publishFlags);
delete envelope->body();
delete envelope;
delete packet;
}
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
} //while
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
// Прием данных, предразование в формат передачи
// и постановка в очередь на выдачу
void PWorker::sending(ProduceMessage msg, QString routingKey, bool mandatory)
{
mutex.lock();
uint64_t size = msg.getBodyMsg().size();
char *body = new char[size];
memcpy(body, msg.getBodyMsg().data(), size);
AMQP::Envelope *env = new AMQP::Envelope(body, size);
// Готовим сообщение для отправки
Properties p = msg.getProperties();
if (p.contains("content-type"))
env->setContentType(p.getContentType().toStdString().c_str());
if (p.contains("content-encoding"))
env->setContentEncoding(p.getContentEncoding().toStdString().c_str());
if (p.contains("message-id"))
env->setMessageID(p.getMessageID().toStdString().c_str());
if (p.contains("correlation-id"))
env->setCorrelationID(p.getCorrelationID().toStdString().c_str());
if (p.contains("timestamp"))
env->setTimestamp(p.getTimestamp());
if (p.contains("expiration"))
env->setExpiration(p.getExpiration().toStdString().c_str());
if (p.contains("delivery-mode"))
env->setDeliveryMode(p.getDeliveryMode());
if (p.contains("app-id"))
env->setAppID(p.getAppID().toStdString().c_str());
if (p.contains("user-id"))
env->setUserID(p.getUserID().toStdString().c_str());
if (p.contains("type"))
env->setTypeName(p.getTypeName().toStdString().c_str());
if (p.contains("reply-to"))
env->setReplyTo(p.getReplyTo().toStdString().c_str());
if (p.contains("priority"))
env->setPriority(p.getPriority());
AMQP::Table table;
Headers p2 = msg.getHeaders();
QList<QString> k = p2.keys();
QList<QVariant> v = p2.values();
for (int i=0; i < p2.size(); i++) {
QString name = k[i];
QVariant val = v[i];
if (val.type() == QVariant::Int) {
AMQP::Long numb = val.value<int>();
table.set(name.toStdString(), numb.value());
}
else if (val.type() == QVariant::String) {
QString str = val.value<QString>();
AMQP::ShortString s(str.toStdString());
table.set(name.toStdString(), s.value());
}
else if (val.type() == QVariant::Bool) {
bool numb = val.value<bool>();
table.set(name.toStdString(), numb);
}
}
env->setHeaders(table);
int flags = 0; // флаги - в формат AMQP
if (mandatory)
flags |= AMQP::mandatory;
string routing = routingKey.toStdString();
// формируем пакет для постановки в очередь
PublishPacket *pp = new PublishPacket;
pp->envelope = env;
pp->publishFlags = flags;
pp->routingKey = routing;
qu.enqueue(pp);
mutex.unlock();
}
void PWorker::stop()
{
markStop = true; // завершить цикл обработки сообщений
}

View File

@ -1,58 +0,0 @@
#ifndef PWORKER_H
#define PWORKER_H
#include <QMutex>
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
struct PublishPacket {
AMQP::Envelope *envelope;
string routingKey;
int publishFlags;
};
class PWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
bool markStop;
QMutex mutex;
QQueue<PublishPacket *> qu;
public:
PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~PWorker();
public slots:
void doWork();
void sending(ProduceMessage msg, QString routingKey, bool mandatory=false);
void stop();
signals:
void onMessageBounced(PtrProduceMessage msg);
void onError(string msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onWorkFinished();
};
#endif // PWORKER_H

View File

@ -1,65 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
Receiver::Receiver(ClientOption &clientOption, ConsumeOption &consumeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
this->consumeOption.bindingArgs = consumeOption.bindingArgs;
CWorker *worker = new CWorker(this->clientOption, this->consumeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &CWorker::doWork);
connect(&workerThread, &QThread::finished, worker, &CWorker::slotStop);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &CWorker::onWorkFinished, worker, &CWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Receiver::onStop, worker, &CWorker::slotStop, Qt::DirectConnection);
connect(this, &Receiver::doBind, worker, &CWorker::bind, Qt::DirectConnection);
connect(this, &Receiver::doUnbind, worker, &CWorker::unbind, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &CWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &CWorker::onResultReady, this, &Receiver::slotMsgReady, Qt::QueuedConnection);
connect(worker, &CWorker::onErrorConsume, this, &Receiver::slotErrorMsg, Qt::QueuedConnection);
workerThread.start();
}
Receiver::~Receiver()
{
workerThread.quit();
workerThread.wait();
}
// При получении от потока сигнала о приеме сообщения
// выпускаем сигнал дальше
void Receiver::slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag)
{
ProduceMessage message = *msg;
delete msg;
emit onMessage(message, deliveryTag);
}
// При получении сигнала об ошибке транслируем его
void Receiver::slotErrorMsg(const char *description)
{
QString str(description);
emit onError(str);
}
void Receiver::slotStop()
{
emit onStop();
}

View File

@ -1,47 +0,0 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QTimer>
#include <QThread>
#include <QVariant>
#include "_client_.h"
#include "cworker.h"
using namespace std;
class Receiver : public QObject
{
Q_OBJECT
private:
bool stop;
ClientOption clientOption;
ConsumeOption consumeOption;
QThread workerThread;
public:
Receiver(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~Receiver();
public slots:
void slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag);
void slotErrorMsg(const char *description);
void slotStop();
signals:
void onMessage(ProduceMessage msg, uint64_t deliveryTag);
void onError(QString description);
void onStop();
void doBind(QString exchange, QString key, bool ex);
void doUnbind(QString exchange, QString key, bool ex);
};
#endif // RECEIVER_H

View File

@ -1,106 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
Sender::Sender(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
PWorker *worker = new PWorker(this->clientOption, this->exchangeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &PWorker::doWork);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &PWorker::onWorkFinished, worker, &PWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Sender::onSend, worker, &PWorker::sending, Qt::QueuedConnection);
connect(this, &Sender::onStop, worker, &PWorker::stop, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &PWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &PWorker::onReceivedAckNack, this, &Sender::slotAckNack, Qt::QueuedConnection);
connect(worker, &PWorker::onError, this, &Sender::slotError, Qt::QueuedConnection);
connect(worker, &PWorker::onMessageBounced, this, &Sender::slotMsgBounced, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(30000); // 0,5 мин
connect(&tm, &QTimer::timeout, this, &Sender::onTimer);
tm.start();
}
Sender::~Sender()
{
tm.stop();
workerThread.quit();
workerThread.wait();
}
// Периодическое подключение по таймеру (1 мин)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Sender::onTimer()
{
tm.stop();
// Формируем и отправляем служебное сообщение
// на проверку соединения с экземпляром RabbitMQ
string str = "@@"; // содержимое не играет роли
ProduceMessage msg;
msg.setBodyMsg(str.c_str(), str.size());
Properties p;
QString hearbeats(HEARBEATS);
p.setAppID(hearbeats); // маркер служебного сообщения сердцебиения
msg.setProperties(p);
QString routingKey = "";
emit onSend(msg, routingKey); // сообщение передаем в поток для передачи серверу
tm.start();
}
void Sender::slotMsgBounced(PtrProduceMessage msg)
{
ProduceMessage message = *msg;
delete msg;
emit onMsgBounced(message);
}
// Передаем сообщение в поток для выдачи
void Sender::send(ProduceMessage msg, QString routingKey, bool mandatory)
{
emit onSend(msg, routingKey, mandatory);
}
// Прием подтверждения от потока о выдаче (или невыдаче) сообщения
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-------~~~~~~~~~~~~~~------------
void Sender::slotAckNack(uint64_t deliveryTag, bool ack, bool multiple)
{
emit onReceivedAckNack(deliveryTag, ack, multiple);
}
void Sender::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}
void Sender::slotStop()
{
emit onStop();
}

View File

@ -1,58 +0,0 @@
#ifndef SENDER_H
#define SENDER_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
using namespace std;
class Client;
class PWorker;
class Sender : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
QThread workerThread;
QTimer tm;
public:
Sender(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~Sender();
void send(ProduceMessage msg, QString routingKey, bool mandatory=false);
private slots:
void onTimer();
public slots:
void slotMsgBounced(PtrProduceMessage msg);
void slotStop();
void slotAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void slotError(string msg);
signals:
void onMsgBounced(ProduceMessage msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onSend(ProduceMessage msg, QString routingKey, bool mandatory=false); // Отправка сообщения потоку
void onError(QString &msg);
void onStop();
};
#endif // SENDER_H

View File

@ -1,54 +0,0 @@
#include "validator.h"
#include <QDebug>
Validator::Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr)
{
// класс запускает поток с валидатором, который в течение 5 секунд должен проверить инфраструктуру.
// любая ошибка приведёт к завершению работы программы. если ошибок не было перехвачено, через 5 секунд процесс завершается.
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
VWorker *worker = new VWorker(this->clientOption, this->exchangeOptions);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &VWorker::doWork);
// Автоматическое удаление объектов VWorker и QThread по окончании работы
connect(worker, &VWorker::onWorkFinished, worker, &VWorker::deleteLater);
// Сигналы, принимаемые от потока
connect(worker, &VWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &VWorker::onError, this, &Validator::slotError, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(5000); // 5 сек
connect(&tm, &QTimer::timeout, worker, &VWorker::slotStop);
connect(&tm, &QTimer::timeout, this, &Validator::onTimer);
tm.start();
}
Validator::~Validator()
{
tm.stop();
workerThread.quit();
}
void Validator::onTimer()
{
tm.stop();
workerThread.quit();
}
void Validator::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}

View File

@ -1,41 +0,0 @@
#ifndef VALIDATOR_H
#define VALIDATOR_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
class Validator : public QObject
{
Q_OBJECT
public:
Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~Validator();
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
QThread workerThread;
QTimer tm;
private slots:
void onTimer();
public slots:
void slotError(string msg);
signals:
void onError(QString &msg);
void onStop();
};
#endif // VALIDATOR_H

View File

@ -1,123 +0,0 @@
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "vworker.h"
#include <QDebug>
VWorker::VWorker(ClientOption &clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
}
VWorker::~VWorker()
{
}
void VWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Создаём подключение
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onError([&](const char *message) {
qDebug() << "validator - connecting error: " << message;
});
// Обрабатываем список точек обмена
string exch;
string exType;
AMQP::ExchangeType typeExch;
int flagsExch = 0;
QString ex_alt_e_name = "alternate-exchange";
QString ex_alt_e_value = "";
for(ExchangeOption exOpt : exchangeOptions) {
AMQP::Table exTable;
exch = exOpt.name.toStdString();
exType = exOpt.type.toStdString();
// преобразование типа точки обмена в формат AMQP
if (exType == "" || exType == "direct")
typeExch = AMQP::direct;
else if (exType == "topic")
typeExch = AMQP::topic;
else if (exType == "headers")
typeExch = AMQP::headers;
else
typeExch = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
if (exOpt.durable)
flagsExch |= AMQP::durable;
if (exOpt.auto_delete)
flagsExch |= AMQP::autodelete;
if (exOpt.internal)
flagsExch |= AMQP::internal;
if (exOpt.arguments.contains(ex_alt_e_name)) {
ex_alt_e_value = exOpt.arguments[ex_alt_e_name].value<QString>();
exTable.set(ex_alt_e_name.toStdString(), ex_alt_e_value.toStdString());
}
//Для предопределенных точек обмена их обьявление не производим
if ( exch != "" && exch != "amq.fanout" && exch != "amq.direct" &&
exch != "amq.topic" && exch != "amq.headers") {
channel.declareExchange(exch, typeExch, flagsExch, exTable)
.onError( [&](const char *message) {
qDebug() << "validator - declaring error: " << message;
emit onError(message);
});
}
QMap<QString, QString>::iterator it = exOpt.bindingArgs.begin();
for (; it != exOpt.bindingArgs.end(); ++it) {
channel.bindExchange(exch, it.key().toStdString(), it.value().toStdString())
.onError( [&](const char *message) {
qDebug() << "validator - binding error: " << message;
emit onError(message);
});
}
}
while (! markStop) {
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
}
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
}
void VWorker::slotStop()
{
markStop = true;
}

View File

@ -1,43 +0,0 @@
#ifndef VWORKER_H
#define VWORKER_H
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class VWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
bool markStop;
QMutex mutex;
public:
VWorker(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~VWorker();
public slots:
void doWork();
void slotStop();
signals:
void onError(string msg);
void onWorkFinished();
};
#endif // VWORKER_H

View File

@ -1,41 +0,0 @@
QT += core gui widgets
LIBS += -L/usr/lib -lamqpcpp -L/usr/lib/x86_64-linux-gnu/ -levent -lpthread -ldl
SOURCES += \
clientRBcpp/client.cpp \
clientRBcpp/clientrbcpp.cpp \
clientRBcpp/cworker.cpp \
clientRBcpp/headers.cpp \
clientRBcpp/producemessage.cpp \
clientRBcpp/properties.cpp \
clientRBcpp/pworker.cpp \
clientRBcpp/receiver.cpp \
clientRBcpp/sender.cpp \
clientRBcpp/validator.cpp \
clientRBcpp/vworker.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
clientRBcpp/_client_.h \
clientRBcpp/client_cpp.h \
clientRBcpp/clientrbcpp.h \
clientRBcpp/clientrbcpp_global.h \
clientRBcpp/cworker.h \
clientRBcpp/headers.h \
clientRBcpp/options/clientoption.h \
clientRBcpp/options/consumeoption.h \
clientRBcpp/options/exchangeoption.h \
clientRBcpp/options/queueoption.h \
clientRBcpp/producemessage.h \
clientRBcpp/properties.h \
clientRBcpp/pworker.h \
clientRBcpp/receiver.h \
clientRBcpp/sender.h \
clientRBcpp/validator.h \
clientRBcpp/vworker.h \
mainwindow.h
FORMS += \
mainwindow.ui

View File

@ -1,338 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.3, 2023-12-23T19:03:36. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{77607214-f3f8-45c8-bf65-1a310ea854a8}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">0</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{fa463890-d98c-43fb-aee8-64b3da65bdfc}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Отладка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="int" key="QtQuickCompiler">2</value>
<value type="int" key="SeparateDebugInfo">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">2</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Выпуск</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
<value type="int" key="SeparateDebugInfo">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Профилирование</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
<value type="int" key="SeparateDebugInfo">0</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_fast/consumer_fast.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_fast/consumer_fast.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_fast-Desktop-Debug</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -1,11 +0,0 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@ -1,31 +0,0 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ClientOption clOpt;
ConsumeOption conOpt;
conOpt.exchange = "publisher";
conOpt.bindingKeys << "all";
conOpt.queueOption.name = "queue_fast";
conOpt.queueOption.auto_delete = true;
receiver = new Receiver(clOpt, conOpt);
QObject::connect(receiver, &Receiver::onMessage, this, [&](ProduceMessage msg, uint64_t consumeTag) {
Q_UNUSED(consumeTag)
QString msg_body = "got " + QString::fromLocal8Bit(msg.getBodyMsg());
ui->listWidget->addItem(msg_body);
});
}
MainWindow::~MainWindow()
{
delete ui;
}

View File

@ -1,26 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "clientRBcpp/client_cpp.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Receiver *receiver;
int counter;
};
#endif // MAINWINDOW_H

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>30</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,53 +0,0 @@
#ifndef _CLIENT_H
#define _CLIENT_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
using namespace std;
#define HEARBEATS "@@__Control__@@"
class Sender;
class Receiver;
class Client : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<Sender *> *_senders;
QList<Receiver *> *_receivers;
public:
Client();
Client(ClientOption option);
virtual ~Client();
Client& operator=(Client client);
QString getVersion() const;
ClientOption getClientOption() const { return clientOption; }
Sender *createSender(ExchangeOption& option);
void removeSender(Sender *sender);
Receiver *createReceiver(ConsumeOption& option);
void removeReceiver(Receiver *receiver);
signals:
void onStop();
};
Q_DECLARE_METATYPE(string)
#endif // _CLIENT_H

View File

@ -1,124 +0,0 @@
#include "_client_.h"
#include "sender.h"
#include "receiver.h"
// Конструктор для связи с локальным RabbitMQ
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Client::Client() : QObject(nullptr)
{
{
static const int idMsg = qRegisterMetaType<ProduceMessage>();
static const int idMsgPtr = qRegisterMetaType<PtrProduceMessage>();
static const int idString = qRegisterMetaType<string>();
Q_UNUSED(idMsg)
Q_UNUSED(idMsgPtr)
Q_UNUSED(idString)
}
_senders = new QList<Sender *>();
_senders->clear();
_receivers = new QList<Receiver *>();
_receivers->clear();
}
Client::Client(ClientOption option) : Client()
{
clientOption = option;
}
Client::~Client()
{
for (auto &sender : *_senders)
delete sender;
delete _senders;
for (auto &receiver : *_receivers)
delete receiver;
delete _receivers;
}
Client& Client::operator=(Client client)
{
if (this != &client) {
this->clientOption = client.clientOption;
this->_senders = new QList<Sender *>();
this->_senders->clear();
this->_receivers = new QList<Receiver *>();
this->_receivers->clear();
}
return *this;
}
int majorVersion = 1;
int minorVersion = 1;
int releaseVersion = 1;
QString Client::getVersion() const
{
return QString::number(majorVersion) +
"." + QString::number(minorVersion) +
"." + QString::number(releaseVersion);
}
// Создание публикатора (издателя) сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sender *Client::createSender(ExchangeOption& option)
{
Sender *sender = new Sender(clientOption, option);
connect(this, &Client::onStop, sender, &Sender::slotStop);
connect(this, &Client::onStop, sender, &Sender::deleteLater);
_senders->append(sender);
return sender;
}
void Client::removeSender(Sender *sender)
{
if ( !_senders->contains(sender))
return;
sender->slotStop();
_senders->removeOne(sender);
}
// Создание потребителя сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Receiver *Client::createReceiver(ConsumeOption& option)
{
Receiver *receiver = new Receiver(clientOption, option);
connect(this, &Client::onStop, receiver, &Receiver::slotStop);
connect(this, &Client::onStop, receiver, &Receiver::deleteLater);
_receivers->append(receiver);
return receiver;
}
void Client::removeReceiver(Receiver *receiver)
{
if ( !_receivers->contains(receiver))
return;
receiver->slotStop();
_receivers->removeOne(receiver);
}

View File

@ -1,22 +0,0 @@
#ifndef CLIENT_CPP_H
#define CLIENT_CPP_H
#include "clientrbcpp_global.h"
#include "clientrbcpp.h"
#include "_client_.h"
#include "producemessage.h"
#include "properties.h"
#include "cworker.h"
#include "headers.h"
#include "pworker.h"
#include "receiver.h"
#include "sender.h"
#include "validator.h"
#include "vworker.h"
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
#include "options/queueoption.h"
#endif // CLIENT_CPP_H

View File

@ -1,6 +0,0 @@
#include "clientrbcpp.h"
ClientRBcpp::ClientRBcpp()
{
}

View File

@ -1,13 +0,0 @@
#ifndef CLIENTRBCPP_H
#define CLIENTRBCPP_H
#include "clientrbcpp_global.h"
class CLIENTRBCPPSHARED_EXPORT ClientRBcpp
{
public:
ClientRBcpp();
};
#endif // CLIENTRBCPP_H

View File

@ -1,12 +0,0 @@
#ifndef CLIENTRBCPP_GLOBAL_H
#define CLIENTRBCPP_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(CLIENTRBCPP_LIBRARY)
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_EXPORT
#else
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // CLIENTRBCPP_GLOBAL_H

View File

@ -1,278 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
#include <QDebug>
CWorker::CWorker(ClientOption& clientOption, ConsumeOption& consumeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
connection = nullptr;
channel = nullptr;
}
CWorker::~CWorker()
{
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
// AMQP::TcpConnection connection(&handler, AMQP::Address(address));
// AMQP::TcpChannel channel(&connection);
connection = new AMQP::TcpConnection(&handler, AMQP::Address(address));
channel = new AMQP::TcpChannel(connection);
channel->setQos(1);
channel->onError([&](const char *message) {
Q_UNUSED(message)
emit onErrorConsume("Channel error!!!");
});
QTimer tm;
tm.stop();
tm.setInterval(30000);
connect(&tm, &QTimer::timeout, this, [&]() {
tm.stop();
connection->heartbeat();
tm.start();
});
tm.start();
// Обработка принятого сообщения
auto messageCb = [&](const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{
Q_UNUSED(redelivered)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
QThread::sleep(3);
emit onResultReady(rMsg, deliveryTag);
channel->ack(deliveryTag);
};
// объявление точки обмена
if (!consumeOption.receivingExchange.name.isEmpty()) {
string exchange = consumeOption.receivingExchange.name.toStdString();
string type = consumeOption.receivingExchange.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (consumeOption.receivingExchange.durable)
flagsExchange |= AMQP::durable;
if (consumeOption.receivingExchange.auto_delete)
flagsExchange |= AMQP::autodelete;
if (consumeOption.receivingExchange.internal)
flagsExchange |= AMQP::internal;
AMQP::Table tableExch;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (consumeOption.receivingExchange.arguments.contains(alt_e_name)) {
alt_e_value = consumeOption.receivingExchange.arguments[alt_e_name].value<QString>();
tableExch.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel->declareExchange(exchange, typeEx, flagsExchange, tableExch)
.onError([&](const char *description) {
qDebug() << description;
});
}
QMultiMap<QString, QString>::iterator it = consumeOption.bindingArgs.begin();
for(; it != consumeOption.bindingArgs.end(); ++it) {
channel->bindExchange(it.key().toStdString(), exchange, it.value().toStdString()).onError([&](const char *description) {
qDebug() << description;
});
}
}
// объявление очереди
QueueOption option = consumeOption.queueOption;
string exchange = consumeOption.exchange.toStdString();
string queue = option.name.toStdString();
// Подготовка флагов для объявления очереди
int flagsQueue = 0;
if (option.durable)
flagsQueue |= AMQP::durable;
if (option.auto_delete)
flagsQueue |= AMQP::autodelete;
if (option.exclusive)
flagsQueue |= AMQP::exclusive;
channel->declareQueue(queue, flagsQueue)
.onSuccess( [&](const string &name, uint32_t messageCount, uint32_t consumerCount) {
Q_UNUSED(messageCount)
Q_UNUSED(consumerCount)
queue = name;
if (exchange != "")
for (QString rk : consumeOption.bindingKeys) {
channel->bindQueue(exchange, queue, rk.toStdString())
.onError( [&](const char *description) {
qDebug() << description;
});
}
});
// Подготовка флагов потребления сообщений
int flagsConsume = 0;
if (consumeOption.nolocal)
flagsConsume |= AMQP::nolocal;
if (consumeOption.noack)
flagsConsume |= AMQP::noack;
if (consumeOption.exclusive)
flagsConsume |= AMQP::exclusive;
channel->consume(queue, flagsConsume).onReceived(messageCb)
.onSuccess( [&](const string& tag) {
nextTag = tag;
})
.onError( [&](const char *description) {
emit onErrorConsume(description);
markStop = true; // Останов потока
});
//Цикл обработки событий
while(!markStop) {
event_base_loop(evbase, EVLOOP_ONCE);
QCoreApplication::processEvents();
}
// Закроем канал и соединение
channel->close();
connection->close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
delete channel;
delete connection;
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
void CWorker::slotStop()
{
markStop = true;
channel->cancel(nextTag); // Отменить потребление
channel->close();
connection->close();
}
void CWorker::bind(QString exchange, QString key, bool ex)
{
if (ex) channel->bindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->bindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}
void CWorker::unbind(QString exchange, QString key, bool ex)
{
if (ex) channel->unbindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->unbindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}

View File

@ -1,54 +0,0 @@
#ifndef CWORKER_H
#define CWORKER_H
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class CWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ConsumeOption consumeOption;
bool markStop;
AMQP::TcpConnection *connection;
AMQP::TcpChannel *channel;
string nextTag;
public:
CWorker(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~CWorker();
public slots:
void doWork();
void slotStop();
void bind(QString exchange, QString key, bool ex);
void unbind(QString exchange, QString key, bool ex);
signals:
void onResultReady(PtrProduceMessage msg, uint64_t deliveryTag);
void onErrorConsume(const char *description);
void onWorkFinished();
};
#endif // CWORKER_H

View File

@ -1,40 +0,0 @@
#include "headers.h"
Headers::Headers()
{
_headers.clear();
}
Headers::Headers(const Headers& h)
{
this->_headers = h._headers;
}
void Headers::operator=(const Headers& h)
{
this->_headers = h._headers;
}
QMap<QString,QVariant> Headers::getHeaders() const { return _headers; }
QList<QString> Headers::keys() const { return _headers.keys(); }
QList<QVariant> Headers::values() const { return _headers.values(); }
int Headers::size() const { return _headers.size(); }
bool Headers::contains(const QString name) const { return _headers.contains(name); }
void Headers::set(const QString name, bool value) { _headers.insert(name, value); }
void Headers::set(const QString name, int value) { _headers.insert(name, value); }
void Headers::set(const QString name, QString str) { _headers.insert(name, str); }
bool Headers::isBool(const QString name) const { return _headers[name].type() == QVariant::Bool; }
bool Headers::isInteger(const QString name) const { return _headers[name].type() == QVariant::Int; }
bool Headers::isString(const QString name) const { return _headers[name].type() == QVariant::String; }
bool Headers::getBool(const QString name) const { return _headers[name].value<bool>(); }
int Headers::getInteger(const QString name) const { return _headers[name].value<int>(); }
QString Headers::getString(const QString name) const { return _headers[name].value<QString>(); }

View File

@ -1,45 +0,0 @@
#ifndef HEADERS_H
#define HEADERS_H
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
class Headers
{
private:
QMap<QString,QVariant> _headers;
public:
Headers();
Headers(const Headers& h); // Конструктор копирования
~Headers() {}
void operator=(const Headers& h);
QMap<QString,QVariant> getHeaders() const;
QList<QString> keys() const;
QList<QVariant> values() const;
int size() const;
bool contains(const QString name) const;
void set(const QString name, bool value);
void set(const QString name, int value);
void set(const QString name, QString str);
bool isBool(const QString name) const;
bool isInteger(const QString name) const;
bool isString(const QString name) const;
bool getBool(const QString name) const;
int getInteger(const QString name) const;
QString getString(const QString name) const;
};
#endif // HEADERS_H

View File

@ -1,45 +0,0 @@
#ifndef CLIENTOPTION_H
#define CLIENTOPTION_H
#include <QString>
// Значения по умолчанию
const QString DEFAULT_CPP_HOST = "localhost";
const int DEFAULT_CPP_PORT = 5672;
const QString DEFAULT_CPP_USER = "guest";
const QString DEFAULT_CPP_PASSWORD = "guest";
const QString DEFAULT_CPP_VHOST = "/";
struct ClientOption {
QString host;
int port;
QString user;
QString password;
QString vhost;
ClientOption() {
host = DEFAULT_CPP_HOST;
port = DEFAULT_CPP_PORT;
user = DEFAULT_CPP_USER;
password = DEFAULT_CPP_PASSWORD;
vhost = DEFAULT_CPP_VHOST;
}
~ClientOption() {}
ClientOption(const ClientOption& src) = default; // Конструктор копирования
ClientOption(ClientOption&& src) = default; // Конструктор перемещения
ClientOption& operator=(const ClientOption rhs) // Оператор присваивания
{
host = rhs.host;
port = rhs.port;
user = rhs.user;
password = rhs.password;
vhost = rhs.vhost;
return *this;
}
};
#endif // CLIENTOPTION_H

View File

@ -1,52 +0,0 @@
#ifndef CONSUMEOPTION_H
#define CONSUMEOPTION_H
#include <QObject>
#include <QString>
#include <QList>
#include "queueoption.h"
#include "exchangeoption.h"
struct ConsumeOption
{
QString exchange; // Имя точки обмена для связывания
QStringList bindingKeys; // ключи связи точки с очередью
bool nolocal;
bool noack;
bool exclusive;
QueueOption queueOption; // Параметры очереди
ExchangeOption receivingExchange; // Параметры новой принимающей очереди (по умолчанию новой не создаётся)
QMultiMap<QString, QString> bindingArgs; // список связей для точки обмена (если создаётся новая точка)
ConsumeOption() {
exchange = "";
receivingExchange.name = "";
nolocal = false;
noack = false;
exclusive = false;
}
~ConsumeOption() {}
ConsumeOption(const ConsumeOption& src) = default; // Конструктор копирования
ConsumeOption(ConsumeOption&& src) = default; // Конструктор перемещения
ConsumeOption& operator=(const ConsumeOption rhs) // Оператор присваивания
{
exchange = rhs.exchange;
bindingKeys = rhs.bindingKeys;
nolocal = rhs.nolocal;
noack = rhs.noack;
exclusive = rhs.exclusive;
queueOption = rhs.queueOption;
bindingArgs = rhs.bindingArgs;
return *this;
}
};
#endif // CONSUMEOPTION_H

View File

@ -1,55 +0,0 @@
#ifndef EXCHANGEOPTION_H
#define EXCHANGEOPTION_H
#include <QString>
#include <QVariantMap>
#include <QList>
struct ExchangeOption
{
QString name; // уникальное имя точки обмена
QString type; // тип точки обмена (direct, topic,
// fanout или headers)
bool auto_delete; // автоматически удаляемая точка обмена
bool durable; // долгоживущая точка обмена
bool passive; // требуется информация о точке обмена
bool internal; // нельзя вести публикацию из приложения
QVariantMap arguments; // необязательные аргументы
QMap<QString, QString> bindingArgs; // список связей для точки обмена
bool ifunused; // можно удалять, только если точка обмена
// не используется (не имеет потребителей)
ExchangeOption() {
name = "";
type = "";
auto_delete = false;
durable = false;
passive = false;
internal = false;
arguments.clear();
ifunused = false;
}
~ExchangeOption() {}
ExchangeOption(const ExchangeOption& src) = default; // Конструктор копирования
ExchangeOption(ExchangeOption&& src) = default; // Конструктор перемещения
ExchangeOption& operator=(const ExchangeOption rhs) // Оператор присваивания
{
name = rhs.name;
type = rhs.type;
auto_delete = rhs.auto_delete;
durable = rhs.durable;
passive = rhs.passive;
internal = rhs.internal;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
return *this;
}
};
#endif // EXCHANGEOPTION_H

View File

@ -1,63 +0,0 @@
#ifndef QUEUEOPTION_H
#define QUEUEOPTION_H
#include <QObject>
#include <QString>
#include <QVariantMap>
struct QueueOption
{
QString name; // Уникальное имя очереди
bool auto_delete; // Автоматически удаляемая очередь
bool durable; // Долгоживущая очередь
bool passive; // Требуется информация об очереди
bool exclusive; // Исключительная очередь
QVariantMap arguments; // Необязательные аргументы очереди
bool ifunused; // Удалять, только если не используется
bool ifempty; // Удалять, только если очередь пуста
int messageCount; // Число сообщений в очереди
int consumerCount; // Число потребителей очереди
QueueOption() {
name = "";
auto_delete = false;
durable = false;
passive = false;
exclusive = false;
arguments.clear();
ifunused = false;
ifempty = false;
messageCount = 0;
consumerCount = 0;
}
~QueueOption() {}
QueueOption(const QueueOption& src) = default; // Конструктор копирования
QueueOption(QueueOption&& src) = default; // Конструктор перемещения
QueueOption& operator=(const QueueOption rhs) // Оператор присваивания
{
name = rhs.name;
auto_delete = rhs.auto_delete;
passive = rhs.passive;
exclusive = rhs.exclusive;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
ifempty = rhs.ifempty;
messageCount = rhs.messageCount;
consumerCount = rhs.consumerCount;
return *this;
}
};
#endif // QUEUEOPTION_H

View File

@ -1,37 +0,0 @@
#include "producemessage.h"
// Конструктор по умолчанию
ProduceMessage::ProduceMessage()
{
_body.clear();
}
// Конструктор копирования
ProduceMessage::ProduceMessage(const ProduceMessage& msg)
{
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
ProduceMessage& ProduceMessage::operator=(const ProduceMessage& msg)
{
if (this != &msg) {
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
return *this;
}
QByteArray ProduceMessage::getBodyMsg() const { return _body; }
void ProduceMessage::setBodyMsg(const QByteArray &ba) { _body = ba; }
void ProduceMessage::setBodyMsg(const char *body, int size) { _body = QByteArray(body, size); }
Headers ProduceMessage::getHeaders() const { return _headers; }
void ProduceMessage::setHeaders(const Headers &headers) { _headers = headers; }
Properties ProduceMessage::getProperties() const { return _properties; }
void ProduceMessage::setProperties(const Properties &properties) { _properties = properties; }

View File

@ -1,43 +0,0 @@
#ifndef PRODUCEMESSAGE_H
#define PRODUCEMESSAGE_H
#include <QByteArray>
#include "properties.h"
#include "headers.h"
class ProduceMessage
{
public:
ProduceMessage();
ProduceMessage(const ProduceMessage& msg);
~ProduceMessage() {}
ProduceMessage& operator=(const ProduceMessage& msg);
QByteArray getBodyMsg() const;
void setBodyMsg(const QByteArray &ba);
void setBodyMsg(const char *body, int size);
Headers getHeaders() const;
void setHeaders(const Headers &headers);
Properties getProperties() const;
void setProperties(const Properties &properties);
private:
QByteArray _body;
protected:
Properties _properties;
Headers _headers;
};
using PtrProduceMessage = ProduceMessage*;
Q_DECLARE_METATYPE(ProduceMessage)
Q_DECLARE_METATYPE(PtrProduceMessage)
#endif // PRODUCEMESSAGE_H

View File

@ -1,63 +0,0 @@
#include "properties.h"
Properties::Properties()
{
_properties.clear();
setDeliveryMode(1); // не оставлять сообщения
}
// Конструктор копирования
Properties::Properties(const Properties& p)
{
this->_properties = p._properties;
}
void Properties::operator=(const Properties& p)
{
this->_properties = p._properties;
}
const QMap<QString,QVariant> &Properties::getProperties() { return _properties; }
bool Properties::contains(const QString name) const { return _properties.contains(name); }
bool Properties::isContentType() const { return _properties.contains("content-type"); }
bool Properties::isContentEncoding() const { return _properties.contains("content-encoding"); }
bool Properties::isMessageID() const { return _properties.contains("message-id"); }
bool Properties::isCorrelationID() const { return _properties.contains("correlation-id"); }
bool Properties::isTimestamp() const { return _properties.contains("timestamp"); }
bool Properties::isExpiration() const { return _properties.contains("expiration"); }
bool Properties::isDeliveryMode() const { return _properties.contains("delivery-mode"); }
bool Properties::isAppID() const { return _properties.contains("app-id"); }
bool Properties::isUserID() const { return _properties.contains("user-id"); }
bool Properties::isTypeName() const { return _properties.contains("type"); }
bool Properties::isReplyTo() const { return _properties.contains("reply-to"); }
bool Properties::isPriority() const { return _properties.contains("priority"); }
void Properties::setContentType(const QString &str) { _properties.insert("content-type", str); }
void Properties::setContentEncoding(const QString &str) { _properties.insert("content-encoding", str); }
void Properties::setMessageID(const QString &str) { _properties.insert("message-id", str); }
void Properties::setCorrelationID(const QString &str) { _properties.insert("correlation-id", str); }
void Properties::setTimestamp(const quint64 val) { _properties.insert("timestamp", val); }
void Properties::setExpiration(const QString &str) { _properties.insert("expiration", str); }
void Properties::setDeliveryMode(const quint8 val) { _properties.insert("delivery-mode", val); }
void Properties::setAppID(const QString &str) { _properties.insert("app-id", str); }
void Properties::setUserID(const QString &str) { _properties.insert("user-id", str); }
void Properties::setTypeName(const QString &str) { _properties.insert("type", str); }
void Properties::setReplyTo(const QString &str) { _properties.insert("reply-to", str); }
void Properties::setPriority(const quint8 val) { _properties.insert("priority", val); }
QString Properties::getContentType() const { return _properties["content-type"].value<QString>(); }
QString Properties::getContentEncoding() const { return _properties["content-encoding"].value<QString>(); }
QString Properties::getMessageID() const { return _properties["message-id"].value<QString>(); }
QString Properties::getCorrelationID() const { return _properties["correlation-id"].value<QString>(); }
quint64 Properties::getTimestamp() const { return _properties["timestamp"].value<quint64>(); }
QString Properties::getExpiration() const { return _properties["expiration"].value<QString>(); }
quint8 Properties::getDeliveryMode() const { return _properties["delivery-mode"].value<quint8>(); }
QString Properties::getAppID() const { return _properties["app-id"].value<QString>(); }
QString Properties::getUserID() const { return _properties["user-id"].value<QString>(); }
QString Properties::getTypeName() const { return _properties["type"].value<QString>(); }
QString Properties::getReplyTo() const { return _properties["reply-to"].value<QString>(); }
quint8 Properties::getPriority() const { return _properties["priority"].value<quint8>(); }

View File

@ -1,73 +0,0 @@
#ifndef PROPERTIES_H
#define PROPERTIES_H
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QVariant>
class Properties
{
private:
QMap<QString,QVariant> _properties;
public:
Properties();
Properties(const Properties& p);
~Properties() {}
void operator=(const Properties& p);
int size() {return _properties.size(); }
const QMap<QString,QVariant> &getProperties();
bool contains(const QString name) const;
bool isContentType() const;
bool isContentEncoding() const;
bool isMessageID() const;
bool isCorrelationID() const;
bool isTimestamp() const;
bool isExpiration() const;
bool isDeliveryMode() const;
bool isAppID() const;
bool isUserID() const;
bool isTypeName() const;
bool isReplyTo() const;
bool isPriority() const;
void setContentType(const QString &str);
void setContentEncoding(const QString &str);
void setMessageID(const QString &str);
void setCorrelationID(const QString &str);
void setTimestamp(const quint64 val);
void setExpiration(const QString &str);
void setDeliveryMode(const quint8 val);
void setAppID(const QString &str);
void setUserID(const QString &str);
void setTypeName(const QString &str);
void setReplyTo(const QString &str);
void setPriority(const quint8 val);
QString getContentType() const;
QString getContentEncoding() const;
QString getMessageID() const;
QString getCorrelationID() const;
quint64 getTimestamp() const;
QString getExpiration() const;
quint8 getDeliveryMode() const;
QString getAppID() const;
QString getUserID() const;
QString getTypeName() const;
QString getReplyTo() const;
quint8 getPriority() const;
};
#endif // PROPERTIES_H

View File

@ -1,330 +0,0 @@
/**************************************************************************
* PWorker - Publish Worker, - рабочий поток публикации сообщений *
* редакция от 08.06.2022 *
* Принадлежность: библиотека clientRBcpp *
**************************************************************************/
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "pworker.h"
#include <QDebug>
PWorker::PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
qu.clear();
// static const int idE2E = qRegisterMetaType<E2EStruct>();
// Q_UNUSED(idE2E)
}
PWorker::~PWorker()
{
// Освободим очередь сообщений
mutex.lock();
while (! qu.isEmpty()) {
PublishPacket *packet = qu.dequeue();
AMQP::Envelope *envelope = packet->envelope;
delete envelope->body();
delete envelope;
delete packet;
}
mutex.unlock();
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void PWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Обрабатываем аргументы на предмет альтернативной точки обмена
AMQP::Table table;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (exchangeOption.arguments.contains(alt_e_name)) {
alt_e_value = exchangeOption.arguments[alt_e_name].value<QString>();
table.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
string alt_exchange = alt_e_value.toStdString(); // Имя альтернативной точки обмена
AMQP::ExchangeType typeAltEx = AMQP::fanout; // Тип альтернативной точки обмена - всегда fanout
int flagsAltEx = (AMQP::durable | AMQP::internal); // Точка долгоживущая и внутренняя
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onAck([&](uint64_t deliveryTag, bool multiple) {
emit onReceivedAckNack(deliveryTag, true, multiple);
})
.onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) {
Q_UNUSED(requeue)
emit onReceivedAckNack(deliveryTag, false, multiple);
});
// Объявляем альтернативную точку обмена
//--------------------------------------
if (alt_e_value != "") {
channel.declareExchange(alt_exchange, typeAltEx, flagsAltEx)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// Обработка основной точки обмена
//----------------------------------
string exchange = exchangeOption.name.toStdString();
string type = exchangeOption.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (exchangeOption.durable)
flagsExchange |= AMQP::durable;
if (exchangeOption.auto_delete)
flagsExchange |= AMQP::autodelete;
if (exchangeOption.internal)
flagsExchange |= AMQP::internal;
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel.declareExchange(exchange, typeEx, flagsExchange, table)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// обработка mandatory
auto messageCb = [&](const AMQP::Message& message, int16_t code, const std::string &description)
{
Q_UNUSED(code)
Q_UNUSED(description)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onMessageBounced(rMsg);
};
channel.recall().onReceived(messageCb);
// Цикл событий (event loop)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (! markStop) {
// Обрабатываем очередь сообщений на передачу
if (! qu.isEmpty()) {
mutex.lock();
PublishPacket *packet = qu.dequeue();
mutex.unlock();
AMQP::Envelope *envelope = packet->envelope;
int publishFlags = packet->publishFlags;
string routingKey = packet->routingKey;
if (envelope->hasAppID() && envelope->appID() == HEARBEATS)
connection.heartbeat();
else
channel.publish(exchange, routingKey, *envelope, publishFlags);
delete envelope->body();
delete envelope;
delete packet;
}
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
} //while
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
// Прием данных, предразование в формат передачи
// и постановка в очередь на выдачу
void PWorker::sending(ProduceMessage msg, QString routingKey, bool mandatory)
{
mutex.lock();
uint64_t size = msg.getBodyMsg().size();
char *body = new char[size];
memcpy(body, msg.getBodyMsg().data(), size);
AMQP::Envelope *env = new AMQP::Envelope(body, size);
// Готовим сообщение для отправки
Properties p = msg.getProperties();
if (p.contains("content-type"))
env->setContentType(p.getContentType().toStdString().c_str());
if (p.contains("content-encoding"))
env->setContentEncoding(p.getContentEncoding().toStdString().c_str());
if (p.contains("message-id"))
env->setMessageID(p.getMessageID().toStdString().c_str());
if (p.contains("correlation-id"))
env->setCorrelationID(p.getCorrelationID().toStdString().c_str());
if (p.contains("timestamp"))
env->setTimestamp(p.getTimestamp());
if (p.contains("expiration"))
env->setExpiration(p.getExpiration().toStdString().c_str());
if (p.contains("delivery-mode"))
env->setDeliveryMode(p.getDeliveryMode());
if (p.contains("app-id"))
env->setAppID(p.getAppID().toStdString().c_str());
if (p.contains("user-id"))
env->setUserID(p.getUserID().toStdString().c_str());
if (p.contains("type"))
env->setTypeName(p.getTypeName().toStdString().c_str());
if (p.contains("reply-to"))
env->setReplyTo(p.getReplyTo().toStdString().c_str());
if (p.contains("priority"))
env->setPriority(p.getPriority());
AMQP::Table table;
Headers p2 = msg.getHeaders();
QList<QString> k = p2.keys();
QList<QVariant> v = p2.values();
for (int i=0; i < p2.size(); i++) {
QString name = k[i];
QVariant val = v[i];
if (val.type() == QVariant::Int) {
AMQP::Long numb = val.value<int>();
table.set(name.toStdString(), numb.value());
}
else if (val.type() == QVariant::String) {
QString str = val.value<QString>();
AMQP::ShortString s(str.toStdString());
table.set(name.toStdString(), s.value());
}
else if (val.type() == QVariant::Bool) {
bool numb = val.value<bool>();
table.set(name.toStdString(), numb);
}
}
env->setHeaders(table);
int flags = 0; // флаги - в формат AMQP
if (mandatory)
flags |= AMQP::mandatory;
string routing = routingKey.toStdString();
// формируем пакет для постановки в очередь
PublishPacket *pp = new PublishPacket;
pp->envelope = env;
pp->publishFlags = flags;
pp->routingKey = routing;
qu.enqueue(pp);
mutex.unlock();
}
void PWorker::stop()
{
markStop = true; // завершить цикл обработки сообщений
}

View File

@ -1,58 +0,0 @@
#ifndef PWORKER_H
#define PWORKER_H
#include <QMutex>
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
struct PublishPacket {
AMQP::Envelope *envelope;
string routingKey;
int publishFlags;
};
class PWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
bool markStop;
QMutex mutex;
QQueue<PublishPacket *> qu;
public:
PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~PWorker();
public slots:
void doWork();
void sending(ProduceMessage msg, QString routingKey, bool mandatory=false);
void stop();
signals:
void onMessageBounced(PtrProduceMessage msg);
void onError(string msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onWorkFinished();
};
#endif // PWORKER_H

View File

@ -1,65 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
Receiver::Receiver(ClientOption &clientOption, ConsumeOption &consumeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
this->consumeOption.bindingArgs = consumeOption.bindingArgs;
CWorker *worker = new CWorker(this->clientOption, this->consumeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &CWorker::doWork);
connect(&workerThread, &QThread::finished, worker, &CWorker::slotStop);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &CWorker::onWorkFinished, worker, &CWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Receiver::onStop, worker, &CWorker::slotStop, Qt::DirectConnection);
connect(this, &Receiver::doBind, worker, &CWorker::bind, Qt::DirectConnection);
connect(this, &Receiver::doUnbind, worker, &CWorker::unbind, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &CWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &CWorker::onResultReady, this, &Receiver::slotMsgReady, Qt::QueuedConnection);
connect(worker, &CWorker::onErrorConsume, this, &Receiver::slotErrorMsg, Qt::QueuedConnection);
workerThread.start();
}
Receiver::~Receiver()
{
workerThread.quit();
workerThread.wait();
}
// При получении от потока сигнала о приеме сообщения
// выпускаем сигнал дальше
void Receiver::slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag)
{
ProduceMessage message = *msg;
delete msg;
emit onMessage(message, deliveryTag);
}
// При получении сигнала об ошибке транслируем его
void Receiver::slotErrorMsg(const char *description)
{
QString str(description);
emit onError(str);
}
void Receiver::slotStop()
{
emit onStop();
}

View File

@ -1,47 +0,0 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QTimer>
#include <QThread>
#include <QVariant>
#include "_client_.h"
#include "cworker.h"
using namespace std;
class Receiver : public QObject
{
Q_OBJECT
private:
bool stop;
ClientOption clientOption;
ConsumeOption consumeOption;
QThread workerThread;
public:
Receiver(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~Receiver();
public slots:
void slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag);
void slotErrorMsg(const char *description);
void slotStop();
signals:
void onMessage(ProduceMessage msg, uint64_t deliveryTag);
void onError(QString description);
void onStop();
void doBind(QString exchange, QString key, bool ex);
void doUnbind(QString exchange, QString key, bool ex);
};
#endif // RECEIVER_H

View File

@ -1,106 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
Sender::Sender(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
PWorker *worker = new PWorker(this->clientOption, this->exchangeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &PWorker::doWork);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &PWorker::onWorkFinished, worker, &PWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Sender::onSend, worker, &PWorker::sending, Qt::QueuedConnection);
connect(this, &Sender::onStop, worker, &PWorker::stop, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &PWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &PWorker::onReceivedAckNack, this, &Sender::slotAckNack, Qt::QueuedConnection);
connect(worker, &PWorker::onError, this, &Sender::slotError, Qt::QueuedConnection);
connect(worker, &PWorker::onMessageBounced, this, &Sender::slotMsgBounced, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(30000); // 0,5 мин
connect(&tm, &QTimer::timeout, this, &Sender::onTimer);
tm.start();
}
Sender::~Sender()
{
tm.stop();
workerThread.quit();
workerThread.wait();
}
// Периодическое подключение по таймеру (1 мин)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Sender::onTimer()
{
tm.stop();
// Формируем и отправляем служебное сообщение
// на проверку соединения с экземпляром RabbitMQ
string str = "@@"; // содержимое не играет роли
ProduceMessage msg;
msg.setBodyMsg(str.c_str(), str.size());
Properties p;
QString hearbeats(HEARBEATS);
p.setAppID(hearbeats); // маркер служебного сообщения сердцебиения
msg.setProperties(p);
QString routingKey = "";
emit onSend(msg, routingKey); // сообщение передаем в поток для передачи серверу
tm.start();
}
void Sender::slotMsgBounced(PtrProduceMessage msg)
{
ProduceMessage message = *msg;
delete msg;
emit onMsgBounced(message);
}
// Передаем сообщение в поток для выдачи
void Sender::send(ProduceMessage msg, QString routingKey, bool mandatory)
{
emit onSend(msg, routingKey, mandatory);
}
// Прием подтверждения от потока о выдаче (или невыдаче) сообщения
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-------~~~~~~~~~~~~~~------------
void Sender::slotAckNack(uint64_t deliveryTag, bool ack, bool multiple)
{
emit onReceivedAckNack(deliveryTag, ack, multiple);
}
void Sender::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}
void Sender::slotStop()
{
emit onStop();
}

View File

@ -1,58 +0,0 @@
#ifndef SENDER_H
#define SENDER_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
using namespace std;
class Client;
class PWorker;
class Sender : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
QThread workerThread;
QTimer tm;
public:
Sender(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~Sender();
void send(ProduceMessage msg, QString routingKey, bool mandatory=false);
private slots:
void onTimer();
public slots:
void slotMsgBounced(PtrProduceMessage msg);
void slotStop();
void slotAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void slotError(string msg);
signals:
void onMsgBounced(ProduceMessage msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onSend(ProduceMessage msg, QString routingKey, bool mandatory=false); // Отправка сообщения потоку
void onError(QString &msg);
void onStop();
};
#endif // SENDER_H

View File

@ -1,54 +0,0 @@
#include "validator.h"
#include <QDebug>
Validator::Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr)
{
// класс запускает поток с валидатором, который в течение 5 секунд должен проверить инфраструктуру.
// любая ошибка приведёт к завершению работы программы. если ошибок не было перехвачено, через 5 секунд процесс завершается.
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
VWorker *worker = new VWorker(this->clientOption, this->exchangeOptions);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &VWorker::doWork);
// Автоматическое удаление объектов VWorker и QThread по окончании работы
connect(worker, &VWorker::onWorkFinished, worker, &VWorker::deleteLater);
// Сигналы, принимаемые от потока
connect(worker, &VWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &VWorker::onError, this, &Validator::slotError, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(5000); // 5 сек
connect(&tm, &QTimer::timeout, worker, &VWorker::slotStop);
connect(&tm, &QTimer::timeout, this, &Validator::onTimer);
tm.start();
}
Validator::~Validator()
{
tm.stop();
workerThread.quit();
}
void Validator::onTimer()
{
tm.stop();
workerThread.quit();
}
void Validator::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}

View File

@ -1,41 +0,0 @@
#ifndef VALIDATOR_H
#define VALIDATOR_H
#include <QMap>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include "client_cpp.h"
class Validator : public QObject
{
Q_OBJECT
public:
Validator(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~Validator();
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
QThread workerThread;
QTimer tm;
private slots:
void onTimer();
public slots:
void slotError(string msg);
signals:
void onError(QString &msg);
void onStop();
};
#endif // VALIDATOR_H

View File

@ -1,123 +0,0 @@
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "vworker.h"
#include <QDebug>
VWorker::VWorker(ClientOption &clientOption, QList<ExchangeOption>& exchangeOptions)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOptions = exchangeOptions;
}
VWorker::~VWorker()
{
}
void VWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Создаём подключение
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onError([&](const char *message) {
qDebug() << "validator - connecting error: " << message;
});
// Обрабатываем список точек обмена
string exch;
string exType;
AMQP::ExchangeType typeExch;
int flagsExch = 0;
QString ex_alt_e_name = "alternate-exchange";
QString ex_alt_e_value = "";
for(ExchangeOption exOpt : exchangeOptions) {
AMQP::Table exTable;
exch = exOpt.name.toStdString();
exType = exOpt.type.toStdString();
// преобразование типа точки обмена в формат AMQP
if (exType == "" || exType == "direct")
typeExch = AMQP::direct;
else if (exType == "topic")
typeExch = AMQP::topic;
else if (exType == "headers")
typeExch = AMQP::headers;
else
typeExch = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
if (exOpt.durable)
flagsExch |= AMQP::durable;
if (exOpt.auto_delete)
flagsExch |= AMQP::autodelete;
if (exOpt.internal)
flagsExch |= AMQP::internal;
if (exOpt.arguments.contains(ex_alt_e_name)) {
ex_alt_e_value = exOpt.arguments[ex_alt_e_name].value<QString>();
exTable.set(ex_alt_e_name.toStdString(), ex_alt_e_value.toStdString());
}
//Для предопределенных точек обмена их обьявление не производим
if ( exch != "" && exch != "amq.fanout" && exch != "amq.direct" &&
exch != "amq.topic" && exch != "amq.headers") {
channel.declareExchange(exch, typeExch, flagsExch, exTable)
.onError( [&](const char *message) {
qDebug() << "validator - declaring error: " << message;
emit onError(message);
});
}
QMap<QString, QString>::iterator it = exOpt.bindingArgs.begin();
for (; it != exOpt.bindingArgs.end(); ++it) {
channel.bindExchange(exch, it.key().toStdString(), it.value().toStdString())
.onError( [&](const char *message) {
qDebug() << "validator - binding error: " << message;
emit onError(message);
});
}
}
while (! markStop) {
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
}
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
}
void VWorker::slotStop()
{
markStop = true;
}

View File

@ -1,43 +0,0 @@
#ifndef VWORKER_H
#define VWORKER_H
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class VWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<ExchangeOption> exchangeOptions;
bool markStop;
QMutex mutex;
public:
VWorker(ClientOption& clientOption, QList<ExchangeOption>& exchangeOptions);
virtual ~VWorker();
public slots:
void doWork();
void slotStop();
signals:
void onError(string msg);
void onWorkFinished();
};
#endif // VWORKER_H

View File

@ -1,41 +0,0 @@
QT += core gui widgets
LIBS += -L/usr/lib -lamqpcpp -L/usr/lib/x86_64-linux-gnu/ -levent -lpthread -ldl
SOURCES += \
clientRBcpp/client.cpp \
clientRBcpp/clientrbcpp.cpp \
clientRBcpp/cworker.cpp \
clientRBcpp/headers.cpp \
clientRBcpp/producemessage.cpp \
clientRBcpp/properties.cpp \
clientRBcpp/pworker.cpp \
clientRBcpp/receiver.cpp \
clientRBcpp/sender.cpp \
clientRBcpp/validator.cpp \
clientRBcpp/vworker.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
clientRBcpp/_client_.h \
clientRBcpp/client_cpp.h \
clientRBcpp/clientrbcpp.h \
clientRBcpp/clientrbcpp_global.h \
clientRBcpp/cworker.h \
clientRBcpp/headers.h \
clientRBcpp/options/clientoption.h \
clientRBcpp/options/consumeoption.h \
clientRBcpp/options/exchangeoption.h \
clientRBcpp/options/queueoption.h \
clientRBcpp/producemessage.h \
clientRBcpp/properties.h \
clientRBcpp/pworker.h \
clientRBcpp/receiver.h \
clientRBcpp/sender.h \
clientRBcpp/validator.h \
clientRBcpp/vworker.h \
mainwindow.h
FORMS += \
mainwindow.ui

View File

@ -1,338 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.3, 2023-12-23T19:03:36. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{77607214-f3f8-45c8-bf65-1a310ea854a8}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">0</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{fa463890-d98c-43fb-aee8-64b3da65bdfc}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Отладка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="int" key="QtQuickCompiler">2</value>
<value type="int" key="SeparateDebugInfo">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">2</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Release</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Выпуск</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
<value type="int" key="SeparateDebugInfo">2</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="bool">true</value>
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Profile</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Профилирование</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="int" key="QtQuickCompiler">0</value>
<value type="int" key="SeparateDebugInfo">0</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_slow/consumer_slow.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/consumer_slow/consumer_slow.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/user/work/DAS_2023_1/alexandrov_dmitrii_lab_4/build-consumer_slow-Desktop-Debug</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -1,11 +0,0 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@ -1,33 +0,0 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QUuid>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ClientOption clOpt;
ConsumeOption conOpt;
conOpt.exchange = "publisher";
conOpt.bindingKeys << "all";
conOpt.queueOption.name = "queue_slow";
conOpt.queueOption.auto_delete = true;
receiver = new Receiver(clOpt, conOpt);
QObject::connect(receiver, &Receiver::onMessage, this, [&](ProduceMessage msg, uint64_t consumeTag) {
Q_UNUSED(consumeTag)
QString msg_body = "got " + QString::fromLocal8Bit(msg.getBodyMsg());
ui->listWidget->addItem(msg_body);
});
}
MainWindow::~MainWindow()
{
delete ui;
}

View File

@ -1,25 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "clientRBcpp/client_cpp.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Receiver *receiver;
int counter;
};
#endif // MAINWINDOW_H

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>30</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,53 +0,0 @@
#ifndef _CLIENT_H
#define _CLIENT_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QString>
#include <QVariant>
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
using namespace std;
#define HEARBEATS "@@__Control__@@"
class Sender;
class Receiver;
class Client : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
QList<Sender *> *_senders;
QList<Receiver *> *_receivers;
public:
Client();
Client(ClientOption option);
virtual ~Client();
Client& operator=(Client client);
QString getVersion() const;
ClientOption getClientOption() const { return clientOption; }
Sender *createSender(ExchangeOption& option);
void removeSender(Sender *sender);
Receiver *createReceiver(ConsumeOption& option);
void removeReceiver(Receiver *receiver);
signals:
void onStop();
};
Q_DECLARE_METATYPE(string)
#endif // _CLIENT_H

View File

@ -1,124 +0,0 @@
#include "_client_.h"
#include "sender.h"
#include "receiver.h"
// Конструктор для связи с локальным RabbitMQ
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Client::Client() : QObject(nullptr)
{
{
static const int idMsg = qRegisterMetaType<ProduceMessage>();
static const int idMsgPtr = qRegisterMetaType<PtrProduceMessage>();
static const int idString = qRegisterMetaType<string>();
Q_UNUSED(idMsg)
Q_UNUSED(idMsgPtr)
Q_UNUSED(idString)
}
_senders = new QList<Sender *>();
_senders->clear();
_receivers = new QList<Receiver *>();
_receivers->clear();
}
Client::Client(ClientOption option) : Client()
{
clientOption = option;
}
Client::~Client()
{
for (auto &sender : *_senders)
delete sender;
delete _senders;
for (auto &receiver : *_receivers)
delete receiver;
delete _receivers;
}
Client& Client::operator=(Client client)
{
if (this != &client) {
this->clientOption = client.clientOption;
this->_senders = new QList<Sender *>();
this->_senders->clear();
this->_receivers = new QList<Receiver *>();
this->_receivers->clear();
}
return *this;
}
int majorVersion = 1;
int minorVersion = 1;
int releaseVersion = 1;
QString Client::getVersion() const
{
return QString::number(majorVersion) +
"." + QString::number(minorVersion) +
"." + QString::number(releaseVersion);
}
// Создание публикатора (издателя) сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sender *Client::createSender(ExchangeOption& option)
{
Sender *sender = new Sender(clientOption, option);
connect(this, &Client::onStop, sender, &Sender::slotStop);
connect(this, &Client::onStop, sender, &Sender::deleteLater);
_senders->append(sender);
return sender;
}
void Client::removeSender(Sender *sender)
{
if ( !_senders->contains(sender))
return;
sender->slotStop();
_senders->removeOne(sender);
}
// Создание потребителя сообщений
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Receiver *Client::createReceiver(ConsumeOption& option)
{
Receiver *receiver = new Receiver(clientOption, option);
connect(this, &Client::onStop, receiver, &Receiver::slotStop);
connect(this, &Client::onStop, receiver, &Receiver::deleteLater);
_receivers->append(receiver);
return receiver;
}
void Client::removeReceiver(Receiver *receiver)
{
if ( !_receivers->contains(receiver))
return;
receiver->slotStop();
_receivers->removeOne(receiver);
}

View File

@ -1,22 +0,0 @@
#ifndef CLIENT_CPP_H
#define CLIENT_CPP_H
#include "clientrbcpp_global.h"
#include "clientrbcpp.h"
#include "_client_.h"
#include "producemessage.h"
#include "properties.h"
#include "cworker.h"
#include "headers.h"
#include "pworker.h"
#include "receiver.h"
#include "sender.h"
#include "validator.h"
#include "vworker.h"
#include "options/clientoption.h"
#include "options/consumeoption.h"
#include "options/exchangeoption.h"
#include "options/queueoption.h"
#endif // CLIENT_CPP_H

View File

@ -1,6 +0,0 @@
#include "clientrbcpp.h"
ClientRBcpp::ClientRBcpp()
{
}

View File

@ -1,13 +0,0 @@
#ifndef CLIENTRBCPP_H
#define CLIENTRBCPP_H
#include "clientrbcpp_global.h"
class CLIENTRBCPPSHARED_EXPORT ClientRBcpp
{
public:
ClientRBcpp();
};
#endif // CLIENTRBCPP_H

View File

@ -1,12 +0,0 @@
#ifndef CLIENTRBCPP_GLOBAL_H
#define CLIENTRBCPP_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(CLIENTRBCPP_LIBRARY)
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_EXPORT
#else
# define CLIENTRBCPPSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // CLIENTRBCPP_GLOBAL_H

View File

@ -1,276 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
#include <QDebug>
CWorker::CWorker(ClientOption& clientOption, ConsumeOption& consumeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
connection = nullptr;
channel = nullptr;
}
CWorker::~CWorker()
{
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void CWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
// AMQP::TcpConnection connection(&handler, AMQP::Address(address));
// AMQP::TcpChannel channel(&connection);
connection = new AMQP::TcpConnection(&handler, AMQP::Address(address));
channel = new AMQP::TcpChannel(connection);
channel->setQos(1);
channel->onError([&](const char *message) {
Q_UNUSED(message)
emit onErrorConsume("Channel error!!!");
});
QTimer tm;
tm.stop();
tm.setInterval(30000);
connect(&tm, &QTimer::timeout, this, [&]() {
tm.stop();
connection->heartbeat();
tm.start();
});
tm.start();
// Обработка принятого сообщения
auto messageCb = [&](const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{
Q_UNUSED(redelivered)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onResultReady(rMsg, deliveryTag);
channel->ack(deliveryTag);
};
// объявление точки обмена
if (!consumeOption.receivingExchange.name.isEmpty()) {
string exchange = consumeOption.receivingExchange.name.toStdString();
string type = consumeOption.receivingExchange.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (consumeOption.receivingExchange.durable)
flagsExchange |= AMQP::durable;
if (consumeOption.receivingExchange.auto_delete)
flagsExchange |= AMQP::autodelete;
if (consumeOption.receivingExchange.internal)
flagsExchange |= AMQP::internal;
AMQP::Table tableExch;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (consumeOption.receivingExchange.arguments.contains(alt_e_name)) {
alt_e_value = consumeOption.receivingExchange.arguments[alt_e_name].value<QString>();
tableExch.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel->declareExchange(exchange, typeEx, flagsExchange, tableExch)
.onError([&](const char *description) {
qDebug() << description;
});
}
QMultiMap<QString, QString>::iterator it = consumeOption.bindingArgs.begin();
for(; it != consumeOption.bindingArgs.end(); ++it) {
channel->bindExchange(it.key().toStdString(), exchange, it.value().toStdString()).onError([&](const char *description) {
qDebug() << description;
});
}
}
// объявление очереди
QueueOption option = consumeOption.queueOption;
string exchange = consumeOption.exchange.toStdString();
string queue = option.name.toStdString();
// Подготовка флагов для объявления очереди
int flagsQueue = 0;
if (option.durable)
flagsQueue |= AMQP::durable;
if (option.auto_delete)
flagsQueue |= AMQP::autodelete;
if (option.exclusive)
flagsQueue |= AMQP::exclusive;
channel->declareQueue(queue, flagsQueue)
.onSuccess( [&](const string &name, uint32_t messageCount, uint32_t consumerCount) {
Q_UNUSED(messageCount)
Q_UNUSED(consumerCount)
queue = name;
if (exchange != "")
for (QString rk : consumeOption.bindingKeys) {
channel->bindQueue(exchange, queue, rk.toStdString())
.onError( [&](const char *description) {
qDebug() << description;
});
}
});
// Подготовка флагов потребления сообщений
int flagsConsume = 0;
if (consumeOption.nolocal)
flagsConsume |= AMQP::nolocal;
if (consumeOption.noack)
flagsConsume |= AMQP::noack;
if (consumeOption.exclusive)
flagsConsume |= AMQP::exclusive;
channel->consume(queue, flagsConsume).onReceived(messageCb)
.onSuccess( [&](const string& tag) {
nextTag = tag;
})
.onError( [&](const char *description) {
emit onErrorConsume(description);
markStop = true; // Останов потока
});
//Цикл обработки событий
while(!markStop) {
event_base_loop(evbase, EVLOOP_ONCE);
QCoreApplication::processEvents();
}
// Закроем канал и соединение
channel->close();
connection->close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
delete channel;
delete connection;
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
void CWorker::slotStop()
{
markStop = true;
channel->cancel(nextTag); // Отменить потребление
channel->close();
connection->close();
}
void CWorker::bind(QString exchange, QString key, bool ex)
{
if (ex) channel->bindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->bindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}
void CWorker::unbind(QString exchange, QString key, bool ex)
{
if (ex) channel->unbindExchange(exchange.toStdString(), consumeOption.exchange.toStdString(), key.toStdString());
else channel->unbindQueue(exchange.toStdString(), consumeOption.queueOption.name.toStdString(), key.toStdString());
}

View File

@ -1,54 +0,0 @@
#ifndef CWORKER_H
#define CWORKER_H
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
class CWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ConsumeOption consumeOption;
bool markStop;
AMQP::TcpConnection *connection;
AMQP::TcpChannel *channel;
string nextTag;
public:
CWorker(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~CWorker();
public slots:
void doWork();
void slotStop();
void bind(QString exchange, QString key, bool ex);
void unbind(QString exchange, QString key, bool ex);
signals:
void onResultReady(PtrProduceMessage msg, uint64_t deliveryTag);
void onErrorConsume(const char *description);
void onWorkFinished();
};
#endif // CWORKER_H

View File

@ -1,40 +0,0 @@
#include "headers.h"
Headers::Headers()
{
_headers.clear();
}
Headers::Headers(const Headers& h)
{
this->_headers = h._headers;
}
void Headers::operator=(const Headers& h)
{
this->_headers = h._headers;
}
QMap<QString,QVariant> Headers::getHeaders() const { return _headers; }
QList<QString> Headers::keys() const { return _headers.keys(); }
QList<QVariant> Headers::values() const { return _headers.values(); }
int Headers::size() const { return _headers.size(); }
bool Headers::contains(const QString name) const { return _headers.contains(name); }
void Headers::set(const QString name, bool value) { _headers.insert(name, value); }
void Headers::set(const QString name, int value) { _headers.insert(name, value); }
void Headers::set(const QString name, QString str) { _headers.insert(name, str); }
bool Headers::isBool(const QString name) const { return _headers[name].type() == QVariant::Bool; }
bool Headers::isInteger(const QString name) const { return _headers[name].type() == QVariant::Int; }
bool Headers::isString(const QString name) const { return _headers[name].type() == QVariant::String; }
bool Headers::getBool(const QString name) const { return _headers[name].value<bool>(); }
int Headers::getInteger(const QString name) const { return _headers[name].value<int>(); }
QString Headers::getString(const QString name) const { return _headers[name].value<QString>(); }

View File

@ -1,45 +0,0 @@
#ifndef HEADERS_H
#define HEADERS_H
#include <QList>
#include <QMap>
#include <QString>
#include <QVariant>
class Headers
{
private:
QMap<QString,QVariant> _headers;
public:
Headers();
Headers(const Headers& h); // Конструктор копирования
~Headers() {}
void operator=(const Headers& h);
QMap<QString,QVariant> getHeaders() const;
QList<QString> keys() const;
QList<QVariant> values() const;
int size() const;
bool contains(const QString name) const;
void set(const QString name, bool value);
void set(const QString name, int value);
void set(const QString name, QString str);
bool isBool(const QString name) const;
bool isInteger(const QString name) const;
bool isString(const QString name) const;
bool getBool(const QString name) const;
int getInteger(const QString name) const;
QString getString(const QString name) const;
};
#endif // HEADERS_H

View File

@ -1,45 +0,0 @@
#ifndef CLIENTOPTION_H
#define CLIENTOPTION_H
#include <QString>
// Значения по умолчанию
const QString DEFAULT_CPP_HOST = "localhost";
const int DEFAULT_CPP_PORT = 5672;
const QString DEFAULT_CPP_USER = "guest";
const QString DEFAULT_CPP_PASSWORD = "guest";
const QString DEFAULT_CPP_VHOST = "/";
struct ClientOption {
QString host;
int port;
QString user;
QString password;
QString vhost;
ClientOption() {
host = DEFAULT_CPP_HOST;
port = DEFAULT_CPP_PORT;
user = DEFAULT_CPP_USER;
password = DEFAULT_CPP_PASSWORD;
vhost = DEFAULT_CPP_VHOST;
}
~ClientOption() {}
ClientOption(const ClientOption& src) = default; // Конструктор копирования
ClientOption(ClientOption&& src) = default; // Конструктор перемещения
ClientOption& operator=(const ClientOption rhs) // Оператор присваивания
{
host = rhs.host;
port = rhs.port;
user = rhs.user;
password = rhs.password;
vhost = rhs.vhost;
return *this;
}
};
#endif // CLIENTOPTION_H

View File

@ -1,52 +0,0 @@
#ifndef CONSUMEOPTION_H
#define CONSUMEOPTION_H
#include <QObject>
#include <QString>
#include <QList>
#include "queueoption.h"
#include "exchangeoption.h"
struct ConsumeOption
{
QString exchange; // Имя точки обмена для связывания
QStringList bindingKeys; // ключи связи точки с очередью
bool nolocal;
bool noack;
bool exclusive;
QueueOption queueOption; // Параметры очереди
ExchangeOption receivingExchange; // Параметры новой принимающей очереди (по умолчанию новой не создаётся)
QMultiMap<QString, QString> bindingArgs; // список связей для точки обмена (если создаётся новая точка)
ConsumeOption() {
exchange = "";
receivingExchange.name = "";
nolocal = false;
noack = false;
exclusive = false;
}
~ConsumeOption() {}
ConsumeOption(const ConsumeOption& src) = default; // Конструктор копирования
ConsumeOption(ConsumeOption&& src) = default; // Конструктор перемещения
ConsumeOption& operator=(const ConsumeOption rhs) // Оператор присваивания
{
exchange = rhs.exchange;
bindingKeys = rhs.bindingKeys;
nolocal = rhs.nolocal;
noack = rhs.noack;
exclusive = rhs.exclusive;
queueOption = rhs.queueOption;
bindingArgs = rhs.bindingArgs;
return *this;
}
};
#endif // CONSUMEOPTION_H

View File

@ -1,55 +0,0 @@
#ifndef EXCHANGEOPTION_H
#define EXCHANGEOPTION_H
#include <QString>
#include <QVariantMap>
#include <QList>
struct ExchangeOption
{
QString name; // уникальное имя точки обмена
QString type; // тип точки обмена (direct, topic,
// fanout или headers)
bool auto_delete; // автоматически удаляемая точка обмена
bool durable; // долгоживущая точка обмена
bool passive; // требуется информация о точке обмена
bool internal; // нельзя вести публикацию из приложения
QVariantMap arguments; // необязательные аргументы
QMap<QString, QString> bindingArgs; // список связей для точки обмена
bool ifunused; // можно удалять, только если точка обмена
// не используется (не имеет потребителей)
ExchangeOption() {
name = "";
type = "";
auto_delete = false;
durable = false;
passive = false;
internal = false;
arguments.clear();
ifunused = false;
}
~ExchangeOption() {}
ExchangeOption(const ExchangeOption& src) = default; // Конструктор копирования
ExchangeOption(ExchangeOption&& src) = default; // Конструктор перемещения
ExchangeOption& operator=(const ExchangeOption rhs) // Оператор присваивания
{
name = rhs.name;
type = rhs.type;
auto_delete = rhs.auto_delete;
durable = rhs.durable;
passive = rhs.passive;
internal = rhs.internal;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
return *this;
}
};
#endif // EXCHANGEOPTION_H

View File

@ -1,63 +0,0 @@
#ifndef QUEUEOPTION_H
#define QUEUEOPTION_H
#include <QObject>
#include <QString>
#include <QVariantMap>
struct QueueOption
{
QString name; // Уникальное имя очереди
bool auto_delete; // Автоматически удаляемая очередь
bool durable; // Долгоживущая очередь
bool passive; // Требуется информация об очереди
bool exclusive; // Исключительная очередь
QVariantMap arguments; // Необязательные аргументы очереди
bool ifunused; // Удалять, только если не используется
bool ifempty; // Удалять, только если очередь пуста
int messageCount; // Число сообщений в очереди
int consumerCount; // Число потребителей очереди
QueueOption() {
name = "";
auto_delete = false;
durable = false;
passive = false;
exclusive = false;
arguments.clear();
ifunused = false;
ifempty = false;
messageCount = 0;
consumerCount = 0;
}
~QueueOption() {}
QueueOption(const QueueOption& src) = default; // Конструктор копирования
QueueOption(QueueOption&& src) = default; // Конструктор перемещения
QueueOption& operator=(const QueueOption rhs) // Оператор присваивания
{
name = rhs.name;
auto_delete = rhs.auto_delete;
passive = rhs.passive;
exclusive = rhs.exclusive;
arguments = rhs.arguments;
ifunused = rhs.ifunused;
ifempty = rhs.ifempty;
messageCount = rhs.messageCount;
consumerCount = rhs.consumerCount;
return *this;
}
};
#endif // QUEUEOPTION_H

View File

@ -1,37 +0,0 @@
#include "producemessage.h"
// Конструктор по умолчанию
ProduceMessage::ProduceMessage()
{
_body.clear();
}
// Конструктор копирования
ProduceMessage::ProduceMessage(const ProduceMessage& msg)
{
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
ProduceMessage& ProduceMessage::operator=(const ProduceMessage& msg)
{
if (this != &msg) {
this->_body = msg._body;
this->_headers = msg._headers;
this->_properties = msg._properties;
}
return *this;
}
QByteArray ProduceMessage::getBodyMsg() const { return _body; }
void ProduceMessage::setBodyMsg(const QByteArray &ba) { _body = ba; }
void ProduceMessage::setBodyMsg(const char *body, int size) { _body = QByteArray(body, size); }
Headers ProduceMessage::getHeaders() const { return _headers; }
void ProduceMessage::setHeaders(const Headers &headers) { _headers = headers; }
Properties ProduceMessage::getProperties() const { return _properties; }
void ProduceMessage::setProperties(const Properties &properties) { _properties = properties; }

View File

@ -1,43 +0,0 @@
#ifndef PRODUCEMESSAGE_H
#define PRODUCEMESSAGE_H
#include <QByteArray>
#include "properties.h"
#include "headers.h"
class ProduceMessage
{
public:
ProduceMessage();
ProduceMessage(const ProduceMessage& msg);
~ProduceMessage() {}
ProduceMessage& operator=(const ProduceMessage& msg);
QByteArray getBodyMsg() const;
void setBodyMsg(const QByteArray &ba);
void setBodyMsg(const char *body, int size);
Headers getHeaders() const;
void setHeaders(const Headers &headers);
Properties getProperties() const;
void setProperties(const Properties &properties);
private:
QByteArray _body;
protected:
Properties _properties;
Headers _headers;
};
using PtrProduceMessage = ProduceMessage*;
Q_DECLARE_METATYPE(ProduceMessage)
Q_DECLARE_METATYPE(PtrProduceMessage)
#endif // PRODUCEMESSAGE_H

View File

@ -1,63 +0,0 @@
#include "properties.h"
Properties::Properties()
{
_properties.clear();
setDeliveryMode(1); // не оставлять сообщения
}
// Конструктор копирования
Properties::Properties(const Properties& p)
{
this->_properties = p._properties;
}
void Properties::operator=(const Properties& p)
{
this->_properties = p._properties;
}
const QMap<QString,QVariant> &Properties::getProperties() { return _properties; }
bool Properties::contains(const QString name) const { return _properties.contains(name); }
bool Properties::isContentType() const { return _properties.contains("content-type"); }
bool Properties::isContentEncoding() const { return _properties.contains("content-encoding"); }
bool Properties::isMessageID() const { return _properties.contains("message-id"); }
bool Properties::isCorrelationID() const { return _properties.contains("correlation-id"); }
bool Properties::isTimestamp() const { return _properties.contains("timestamp"); }
bool Properties::isExpiration() const { return _properties.contains("expiration"); }
bool Properties::isDeliveryMode() const { return _properties.contains("delivery-mode"); }
bool Properties::isAppID() const { return _properties.contains("app-id"); }
bool Properties::isUserID() const { return _properties.contains("user-id"); }
bool Properties::isTypeName() const { return _properties.contains("type"); }
bool Properties::isReplyTo() const { return _properties.contains("reply-to"); }
bool Properties::isPriority() const { return _properties.contains("priority"); }
void Properties::setContentType(const QString &str) { _properties.insert("content-type", str); }
void Properties::setContentEncoding(const QString &str) { _properties.insert("content-encoding", str); }
void Properties::setMessageID(const QString &str) { _properties.insert("message-id", str); }
void Properties::setCorrelationID(const QString &str) { _properties.insert("correlation-id", str); }
void Properties::setTimestamp(const quint64 val) { _properties.insert("timestamp", val); }
void Properties::setExpiration(const QString &str) { _properties.insert("expiration", str); }
void Properties::setDeliveryMode(const quint8 val) { _properties.insert("delivery-mode", val); }
void Properties::setAppID(const QString &str) { _properties.insert("app-id", str); }
void Properties::setUserID(const QString &str) { _properties.insert("user-id", str); }
void Properties::setTypeName(const QString &str) { _properties.insert("type", str); }
void Properties::setReplyTo(const QString &str) { _properties.insert("reply-to", str); }
void Properties::setPriority(const quint8 val) { _properties.insert("priority", val); }
QString Properties::getContentType() const { return _properties["content-type"].value<QString>(); }
QString Properties::getContentEncoding() const { return _properties["content-encoding"].value<QString>(); }
QString Properties::getMessageID() const { return _properties["message-id"].value<QString>(); }
QString Properties::getCorrelationID() const { return _properties["correlation-id"].value<QString>(); }
quint64 Properties::getTimestamp() const { return _properties["timestamp"].value<quint64>(); }
QString Properties::getExpiration() const { return _properties["expiration"].value<QString>(); }
quint8 Properties::getDeliveryMode() const { return _properties["delivery-mode"].value<quint8>(); }
QString Properties::getAppID() const { return _properties["app-id"].value<QString>(); }
QString Properties::getUserID() const { return _properties["user-id"].value<QString>(); }
QString Properties::getTypeName() const { return _properties["type"].value<QString>(); }
QString Properties::getReplyTo() const { return _properties["reply-to"].value<QString>(); }
quint8 Properties::getPriority() const { return _properties["priority"].value<quint8>(); }

View File

@ -1,73 +0,0 @@
#ifndef PROPERTIES_H
#define PROPERTIES_H
#include <QtGlobal>
#include <QMap>
#include <QString>
#include <QVariant>
class Properties
{
private:
QMap<QString,QVariant> _properties;
public:
Properties();
Properties(const Properties& p);
~Properties() {}
void operator=(const Properties& p);
int size() {return _properties.size(); }
const QMap<QString,QVariant> &getProperties();
bool contains(const QString name) const;
bool isContentType() const;
bool isContentEncoding() const;
bool isMessageID() const;
bool isCorrelationID() const;
bool isTimestamp() const;
bool isExpiration() const;
bool isDeliveryMode() const;
bool isAppID() const;
bool isUserID() const;
bool isTypeName() const;
bool isReplyTo() const;
bool isPriority() const;
void setContentType(const QString &str);
void setContentEncoding(const QString &str);
void setMessageID(const QString &str);
void setCorrelationID(const QString &str);
void setTimestamp(const quint64 val);
void setExpiration(const QString &str);
void setDeliveryMode(const quint8 val);
void setAppID(const QString &str);
void setUserID(const QString &str);
void setTypeName(const QString &str);
void setReplyTo(const QString &str);
void setPriority(const quint8 val);
QString getContentType() const;
QString getContentEncoding() const;
QString getMessageID() const;
QString getCorrelationID() const;
quint64 getTimestamp() const;
QString getExpiration() const;
quint8 getDeliveryMode() const;
QString getAppID() const;
QString getUserID() const;
QString getTypeName() const;
QString getReplyTo() const;
quint8 getPriority() const;
};
#endif // PROPERTIES_H

View File

@ -1,330 +0,0 @@
/**************************************************************************
* PWorker - Publish Worker, - рабочий поток публикации сообщений *
* редакция от 08.06.2022 *
* Принадлежность: библиотека clientRBcpp *
**************************************************************************/
#include <QCoreApplication>
#include "_client_.h"
#include "sender.h"
#include "pworker.h"
#include <QDebug>
PWorker::PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr), markStop(false)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
qu.clear();
// static const int idE2E = qRegisterMetaType<E2EStruct>();
// Q_UNUSED(idE2E)
}
PWorker::~PWorker()
{
// Освободим очередь сообщений
mutex.lock();
while (! qu.isEmpty()) {
PublishPacket *packet = qu.dequeue();
AMQP::Envelope *envelope = packet->envelope;
delete envelope->body();
delete envelope;
delete packet;
}
mutex.unlock();
}
// Здесь реализуется основная деятельность потока
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void PWorker::doWork()
{
string host = clientOption.host.toStdString();
string port = QString::number(clientOption.port).toStdString();
string user = clientOption.user.toStdString();
string password = clientOption.password.toStdString();
string vhost = clientOption.vhost.toStdString();
string address = "amqp://" + user + ":" + password + "@" + host + ":" + port + vhost;
// Обрабатываем аргументы на предмет альтернативной точки обмена
AMQP::Table table;
QString alt_e_name = "alternate-exchange";
QString alt_e_value = "";
if (exchangeOption.arguments.contains(alt_e_name)) {
alt_e_value = exchangeOption.arguments[alt_e_name].value<QString>();
table.set(alt_e_name.toStdString(), alt_e_value.toStdString());
}
string alt_exchange = alt_e_value.toStdString(); // Имя альтернативной точки обмена
AMQP::ExchangeType typeAltEx = AMQP::fanout; // Тип альтернативной точки обмена - всегда fanout
int flagsAltEx = (AMQP::durable | AMQP::internal); // Точка долгоживущая и внутренняя
auto evbase = event_base_new();
AMQP::LibEventHandler handler(evbase);
AMQP::TcpConnection connection(&handler, AMQP::Address(address));
AMQP::TcpChannel channel(&connection);
channel.setQos(1);
channel.confirmSelect()
.onAck([&](uint64_t deliveryTag, bool multiple) {
emit onReceivedAckNack(deliveryTag, true, multiple);
})
.onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) {
Q_UNUSED(requeue)
emit onReceivedAckNack(deliveryTag, false, multiple);
});
// Объявляем альтернативную точку обмена
//--------------------------------------
if (alt_e_value != "") {
channel.declareExchange(alt_exchange, typeAltEx, flagsAltEx)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// Обработка основной точки обмена
//----------------------------------
string exchange = exchangeOption.name.toStdString();
string type = exchangeOption.type.toStdString();
// преобразование типа точки обмена в формат AMQP
AMQP::ExchangeType typeEx;
if (type == "" || type == "direct")
typeEx = AMQP::direct;
else if (type == "topic")
typeEx = AMQP::topic;
else if (type == "headers")
typeEx = AMQP::headers;
else
typeEx = AMQP::fanout;
// предобразование флагов точки обмена в формат AMQP
int flagsExchange = 0;
if (exchangeOption.durable)
flagsExchange |= AMQP::durable;
if (exchangeOption.auto_delete)
flagsExchange |= AMQP::autodelete;
if (exchangeOption.internal)
flagsExchange |= AMQP::internal;
// Для предопределенных точек обмена их обьявление не производим
if ( exchange != "" && exchange != "amq.fanout" && exchange != "amq.direct" &&
exchange != "amq.topic" && exchange != "amq.headers") {
channel.declareExchange(exchange, typeEx, flagsExchange, table)
.onError( [&](const char *message) {
string msg(message);
emit onError(msg);
});
}
// обработка mandatory
auto messageCb = [&](const AMQP::Message& message, int16_t code, const std::string &description)
{
Q_UNUSED(code)
Q_UNUSED(description)
// Формируем принятое сообщениев формате ReceivedMessage
ProduceMessage *rMsg = new ProduceMessage;
rMsg->setBodyMsg(message.body(), message.bodySize());
// Формируем таблицу Properties свойств сообщения
Properties p;
if (message.hasContentType())
p.setContentType(QString::fromStdString(message.contentType()));
if (message.hasContentEncoding())
p.setContentEncoding(QString::fromStdString(message.contentEncoding()));
if (message.hasMessageID())
p.setMessageID(QString::fromStdString(message.messageID()));
if (message.hasCorrelationID())
p.setCorrelationID(QString::fromStdString(message.correlationID()));
if (message.timestamp())
p.setTimestamp(message.timestamp());
if (message.hasExpiration())
p.setExpiration(QString::fromStdString(message.expiration()));
if (message.hasDeliveryMode())
p.setDeliveryMode(message.deliveryMode());
if (message.hasAppID())
p.setAppID(QString::fromStdString(message.appID()));
if (message.hasUserID())
p.setUserID(QString::fromStdString(message.userID()));
if (message.hasTypeName())
p.setTypeName(QString::fromStdString(message.typeName()));
if (message.hasReplyTo())
p.setReplyTo(QString::fromStdString(message.replyTo()));
if (message.hasPriority())
p.setPriority(message.priority());
rMsg->setProperties(p);
// Работа со свойствами Headers
Headers h;
AMQP::Table table = message.headers();
vector<string> keys = table.keys();
string name;
for(uint i = 0; i < keys.size(); i++) {
name = keys[i];
if (table.get(name).isInteger()) {
int value = table.get(name);
h.set(QString::fromStdString(name), value);
}
else if (table.get(name).isString()) {
QString str = QString::fromStdString(table.get(name));
h.set(QString::fromStdString(name), str);
}
else if (table.get(name).isBoolean()) {
AMQP::Field &b = const_cast<AMQP::Field &>(table.get(name));
AMQP::BooleanSet &b1 = dynamic_cast<AMQP::BooleanSet &>(b);
bool value = b1.get(0);
h.set(QString::fromStdString(name), value);
}
}
rMsg->setHeaders(h);
emit onMessageBounced(rMsg);
};
channel.recall().onReceived(messageCb);
// Цикл событий (event loop)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (! markStop) {
// Обрабатываем очередь сообщений на передачу
if (! qu.isEmpty()) {
mutex.lock();
PublishPacket *packet = qu.dequeue();
mutex.unlock();
AMQP::Envelope *envelope = packet->envelope;
int publishFlags = packet->publishFlags;
string routingKey = packet->routingKey;
if (envelope->hasAppID() && envelope->appID() == HEARBEATS)
connection.heartbeat();
else
channel.publish(exchange, routingKey, *envelope, publishFlags);
delete envelope->body();
delete envelope;
delete packet;
}
event_base_loop(evbase, EVLOOP_NONBLOCK);
QCoreApplication::processEvents();
QThread::msleep(0);
} //while
// Закроем канал и соединение
channel.close();
connection.close();
event_base_loopbreak(evbase);
event_base_loopexit(evbase, 0);
event_base_free(evbase);
emit onWorkFinished(); // Посылаем сигнал о завершении работы
}
// Прием данных, предразование в формат передачи
// и постановка в очередь на выдачу
void PWorker::sending(ProduceMessage msg, QString routingKey, bool mandatory)
{
mutex.lock();
uint64_t size = msg.getBodyMsg().size();
char *body = new char[size];
memcpy(body, msg.getBodyMsg().data(), size);
AMQP::Envelope *env = new AMQP::Envelope(body, size);
// Готовим сообщение для отправки
Properties p = msg.getProperties();
if (p.contains("content-type"))
env->setContentType(p.getContentType().toStdString().c_str());
if (p.contains("content-encoding"))
env->setContentEncoding(p.getContentEncoding().toStdString().c_str());
if (p.contains("message-id"))
env->setMessageID(p.getMessageID().toStdString().c_str());
if (p.contains("correlation-id"))
env->setCorrelationID(p.getCorrelationID().toStdString().c_str());
if (p.contains("timestamp"))
env->setTimestamp(p.getTimestamp());
if (p.contains("expiration"))
env->setExpiration(p.getExpiration().toStdString().c_str());
if (p.contains("delivery-mode"))
env->setDeliveryMode(p.getDeliveryMode());
if (p.contains("app-id"))
env->setAppID(p.getAppID().toStdString().c_str());
if (p.contains("user-id"))
env->setUserID(p.getUserID().toStdString().c_str());
if (p.contains("type"))
env->setTypeName(p.getTypeName().toStdString().c_str());
if (p.contains("reply-to"))
env->setReplyTo(p.getReplyTo().toStdString().c_str());
if (p.contains("priority"))
env->setPriority(p.getPriority());
AMQP::Table table;
Headers p2 = msg.getHeaders();
QList<QString> k = p2.keys();
QList<QVariant> v = p2.values();
for (int i=0; i < p2.size(); i++) {
QString name = k[i];
QVariant val = v[i];
if (val.type() == QVariant::Int) {
AMQP::Long numb = val.value<int>();
table.set(name.toStdString(), numb.value());
}
else if (val.type() == QVariant::String) {
QString str = val.value<QString>();
AMQP::ShortString s(str.toStdString());
table.set(name.toStdString(), s.value());
}
else if (val.type() == QVariant::Bool) {
bool numb = val.value<bool>();
table.set(name.toStdString(), numb);
}
}
env->setHeaders(table);
int flags = 0; // флаги - в формат AMQP
if (mandatory)
flags |= AMQP::mandatory;
string routing = routingKey.toStdString();
// формируем пакет для постановки в очередь
PublishPacket *pp = new PublishPacket;
pp->envelope = env;
pp->publishFlags = flags;
pp->routingKey = routing;
qu.enqueue(pp);
mutex.unlock();
}
void PWorker::stop()
{
markStop = true; // завершить цикл обработки сообщений
}

View File

@ -1,58 +0,0 @@
#ifndef PWORKER_H
#define PWORKER_H
#include <QMutex>
#include <QObject>
#include <QQueue>
#include <QString>
#include <QVariant>
#include <iostream>
#include <amqpcpp.h>
#include <amqpcpp/libevent.h>
#include "client_cpp.h"
using namespace std;
struct PublishPacket {
AMQP::Envelope *envelope;
string routingKey;
int publishFlags;
};
class PWorker : public QObject
{
Q_OBJECT
private:
ClientOption clientOption;
ExchangeOption exchangeOption;
bool markStop;
QMutex mutex;
QQueue<PublishPacket *> qu;
public:
PWorker(ClientOption& clientOption, ExchangeOption& exchangeOption);
virtual ~PWorker();
public slots:
void doWork();
void sending(ProduceMessage msg, QString routingKey, bool mandatory=false);
void stop();
signals:
void onMessageBounced(PtrProduceMessage msg);
void onError(string msg);
void onReceivedAckNack(uint64_t deliveryTag, bool ack, bool multiple);
void onWorkFinished();
};
#endif // PWORKER_H

View File

@ -1,65 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
Receiver::Receiver(ClientOption &clientOption, ConsumeOption &consumeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->consumeOption = consumeOption;
this->consumeOption.receivingExchange = consumeOption.receivingExchange;
this->consumeOption.bindingArgs = consumeOption.bindingArgs;
CWorker *worker = new CWorker(this->clientOption, this->consumeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &CWorker::doWork);
connect(&workerThread, &QThread::finished, worker, &CWorker::slotStop);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &CWorker::onWorkFinished, worker, &CWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Receiver::onStop, worker, &CWorker::slotStop, Qt::DirectConnection);
connect(this, &Receiver::doBind, worker, &CWorker::bind, Qt::DirectConnection);
connect(this, &Receiver::doUnbind, worker, &CWorker::unbind, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &CWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &CWorker::onResultReady, this, &Receiver::slotMsgReady, Qt::QueuedConnection);
connect(worker, &CWorker::onErrorConsume, this, &Receiver::slotErrorMsg, Qt::QueuedConnection);
workerThread.start();
}
Receiver::~Receiver()
{
workerThread.quit();
workerThread.wait();
}
// При получении от потока сигнала о приеме сообщения
// выпускаем сигнал дальше
void Receiver::slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag)
{
ProduceMessage message = *msg;
delete msg;
emit onMessage(message, deliveryTag);
}
// При получении сигнала об ошибке транслируем его
void Receiver::slotErrorMsg(const char *description)
{
QString str(description);
emit onError(str);
}
void Receiver::slotStop()
{
emit onStop();
}

View File

@ -1,47 +0,0 @@
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QTimer>
#include <QThread>
#include <QVariant>
#include "_client_.h"
#include "cworker.h"
using namespace std;
class Receiver : public QObject
{
Q_OBJECT
private:
bool stop;
ClientOption clientOption;
ConsumeOption consumeOption;
QThread workerThread;
public:
Receiver(ClientOption& clientOption, ConsumeOption& consumeOption);
virtual ~Receiver();
public slots:
void slotMsgReady(PtrProduceMessage msg, uint64_t deliveryTag);
void slotErrorMsg(const char *description);
void slotStop();
signals:
void onMessage(ProduceMessage msg, uint64_t deliveryTag);
void onError(QString description);
void onStop();
void doBind(QString exchange, QString key, bool ex);
void doUnbind(QString exchange, QString key, bool ex);
};
#endif // RECEIVER_H

View File

@ -1,106 +0,0 @@
#include <QCoreApplication>
#include "client_cpp.h"
Sender::Sender(ClientOption& clientOption, ExchangeOption& exchangeOption)
: QObject(nullptr)
{
this->clientOption = clientOption;
this->exchangeOption = exchangeOption;
PWorker *worker = new PWorker(this->clientOption, this->exchangeOption);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &PWorker::doWork);
// Автоматическое удаление объектов PWorker и QThread по окончании работы
connect(worker, &PWorker::onWorkFinished, worker, &PWorker::deleteLater);
// Посылаемые потоку сигналы
connect(this, &Sender::onSend, worker, &PWorker::sending, Qt::QueuedConnection);
connect(this, &Sender::onStop, worker, &PWorker::stop, Qt::DirectConnection);
// Сигналы, принимаемые от потока
connect(worker, &PWorker::onWorkFinished, &workerThread, &QThread::quit, Qt::QueuedConnection);
connect(worker, &PWorker::onReceivedAckNack, this, &Sender::slotAckNack, Qt::QueuedConnection);
connect(worker, &PWorker::onError, this, &Sender::slotError, Qt::QueuedConnection);
connect(worker, &PWorker::onMessageBounced, this, &Sender::slotMsgBounced, Qt::QueuedConnection);
workerThread.start();
// Запуск таймера для механизма сердцебиения
tm.stop();
tm.setInterval(30000); // 0,5 мин
connect(&tm, &QTimer::timeout, this, &Sender::onTimer);
tm.start();
}
Sender::~Sender()
{
tm.stop();
workerThread.quit();
workerThread.wait();
}
// Периодическое подключение по таймеру (1 мин)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Sender::onTimer()
{
tm.stop();
// Формируем и отправляем служебное сообщение
// на проверку соединения с экземпляром RabbitMQ
string str = "@@"; // содержимое не играет роли
ProduceMessage msg;
msg.setBodyMsg(str.c_str(), str.size());
Properties p;
QString hearbeats(HEARBEATS);
p.setAppID(hearbeats); // маркер служебного сообщения сердцебиения
msg.setProperties(p);
QString routingKey = "";
emit onSend(msg, routingKey); // сообщение передаем в поток для передачи серверу
tm.start();
}
void Sender::slotMsgBounced(PtrProduceMessage msg)
{
ProduceMessage message = *msg;
delete msg;
emit onMsgBounced(message);
}
// Передаем сообщение в поток для выдачи
void Sender::send(ProduceMessage msg, QString routingKey, bool mandatory)
{
emit onSend(msg, routingKey, mandatory);
}
// Прием подтверждения от потока о выдаче (или невыдаче) сообщения
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-------~~~~~~~~~~~~~~------------
void Sender::slotAckNack(uint64_t deliveryTag, bool ack, bool multiple)
{
emit onReceivedAckNack(deliveryTag, ack, multiple);
}
void Sender::slotError(string msg)
{
QString message = QString::fromStdString(msg);
emit onError(message);
}
void Sender::slotStop()
{
emit onStop();
}

Some files were not shown because too many files have changed in this diff Show More