Russian Qt Forum
Ноябрь 23, 2024, 08:49 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Алгоритм высоконагруженного сервера  (Прочитано 19146 раз)
RedDog
Гость
« : Январь 19, 2011, 10:16 »

Прочитал много тем о высоконагруженных серверах, но так и не понял ничего.
Ситуация у меня следующая:
Есть TCP сервер, который по запросу от клиента должен выслать ему файл 20-150мб.
Одномоментно клиентов может быть сотни/тысячи.
На сегодняшний момент на каждого клиента создается свой сокет, по которому пересылается файл.
Код:
class NetworkServer : public QObject
{
    Q_OBJECT
...
private:
...
    QTcpServer *server;
    QList<QTcpSocket*>connections;

private:
    void sendFileToClient(int socketIndex);

public slots:
    void on_newConnection();
    void on_recieveCommand();
};

Добавление нового подключения в массив сокетов:
Код:
void NetworkServer::on_newConnection()
{
    QTcpSocket *socket = server->nextPendingConnection();
    connect(socket, SIGNAL(readyRead()),
            this, SLOT(on_recieveCommand()));
    connections.push_back(socket);   
    emit newConnection(socket->peerAddress().toString());
}
Отправка файла клиенту:
Код:
void NetworkServer::on_recieveCommand()
{
    QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
    if (socket == 0)
        return;
    sendFileToClient(connections.indexOf(socket));
}

void NetworkServer::sendFileToClient(int socketIndex)
{
    QTcpSocket *sock = connections[socketIndex];
    QByteArray outBuf;
    QDataStream out(&outBuf, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_6);
    QFile file(m_SendingFileName);
    file.open(QIODevice::ReadOnly);
    quint32 fileSize = file.size();
    out<<fileSize;
    out<<file.readAll();
    sock->write(outBuf);
    sock->flush();
    sock->close();
    connections.removeAt(socketIndex);
}

Но на каждого клиента создавать свой сокет накладно.
Как быть? Как на один сокет повесить несколько клиентов?
Записан
merke
Гость
« Ответ #1 : Январь 19, 2011, 10:27 »

Тут уж надо уже смотреть не в сторону как повесить на один сокет несколько клиентов, хотя такое мне кажется невозможным, а курить в сторону многопоточности.
Записан
RedDog
Гость
« Ответ #2 : Январь 19, 2011, 10:30 »

Тут уж надо уже смотреть не в сторону как повесить на один сокет несколько клиентов, хотя такое мне кажется невозможным, а курить в сторону многопоточности.
Допустим, я сделаю, что каждый отдельный сокет будет висеть в своем потоке (либо по 10 сокетов на поток), тогда при 1000 клиентах получится 1000 открытых сокетов, думаю система от такого количества заткнется.
Записан
merke
Гость
« Ответ #3 : Январь 19, 2011, 10:38 »

Нет не заткнется) Иначе ни как...
Записан
BRE
Гость
« Ответ #4 : Январь 19, 2011, 10:38 »

думаю система от такого количества заткнется.
А ты не думай - ты проверь. Улыбающийся

Записан
RedDog
Гость
« Ответ #5 : Январь 19, 2011, 10:45 »

Итого получается:
1. При старте сервера создается отдельный поток, в котором слушается порт
2. При подключении нового клиента создается новый сокет, и новый поток, в который сокет перемещается
3. При подключении N-нного (N<M) клиента создается новый сокет, который помещается в работающий отдельный поток
4. При подключении N-нного (N>M) клиента создается новый сокет, и новый поток, сокет перемещается в новый поток
5. "Сканируем" M потоков на количество работающих клиентов, и при отключении одного из них помещаем в этот поток нового N+1 клиента

Я на верном пути?
Записан
brankovic
Гость
« Ответ #6 : Январь 19, 2011, 11:42 »

Итого получается:
1. При старте сервера создается отдельный поток, в котором слушается порт
2. При подключении нового клиента создается новый сокет, и новый поток, в который сокет перемещается
3. При подключении N-нного (N<M) клиента создается новый сокет, который помещается в работающий отдельный поток
4. При подключении N-нного (N>M) клиента создается новый сокет, и новый поток, сокет перемещается в новый поток
5. "Сканируем" M потоков на количество работающих клиентов, и при отключении одного из них помещаем в этот поток нового N+1 клиента

Я на верном пути?

В целом да, только пункты 3-5 лучше убрать. Один сокет -- один поток.
Записан
RedDog
Гость
« Ответ #7 : Январь 19, 2011, 11:48 »

Один сокет -- один поток.
10 000 потоков сервер выдержит!?
Записан
BRE
Гость
« Ответ #8 : Январь 19, 2011, 11:53 »

10 000 потоков сервер выдержит!?
Некоторые выдерживают.
http://ru.wikipedia.org/wiki/Проблема_10000_соединений

Упс. Это я про соединения.
10 000 потоков не надо. Модель - на каждого клиента свой поток - скорее подходит для лабораторной работы, чем для боевых условий.
Посмотри на алгоритм работы, например, nginx.
Кстати, мы здесь несколько раз обсуждали подобные темы. Поищи.
« Последнее редактирование: Январь 19, 2011, 12:07 от BRE » Записан
ufna
Гость
« Ответ #9 : Январь 19, 2011, 12:00 »

Мне кажется, при таком числе соединений, работает не один сервер, а кластер.
Записан
brankovic
Гость
« Ответ #10 : Январь 19, 2011, 12:55 »

Упс. Это я про соединения.
10 000 потоков не надо. Модель - на каждого клиента свой поток - скорее подходит для лабораторной работы, чем для боевых условий.

На работе по 2000 тредов демона отлично работали (под линукс). Но балансировщик nginx стоял и да, кластер.

Посмотри на алгоритм работы, например, nginx.

Если задача просто отдать файл, то можно выпилить а-ля nginx по асинхронной схеме. Как только задача усложняется, то сразу лучше создавать тред на коннект, так удобнее в разы. А ресурсов треды не так много жрут (опять в линуксе).

Мне кажется, при таком числе соединений, работает не один сервер, а кластер.

Согласен, т.к. непонятно, как этот один сервер с 10k коннектами будет отдавать 10k файлов по 150 мб. Какой же канал нужен, чтобы от такой отдачи толк был?
Записан
BRE
Гость
« Ответ #11 : Январь 19, 2011, 13:36 »

Как только задача усложняется, то сразу лучше создавать тред на коннект, так удобнее в разы.
Удобнее для кого, для разработчика? Согласен, реализуется проще.
Эффективней в работе? Сомневаюсь.

А ресурсов треды не так много жрут (опять в линуксе).
Не так много, по сравнению с чем?
Попробуй по-запускать не 2 000 потоков, а 5 000, 10 000. И не на кластере.
Записан
Sahab
Гость
« Ответ #12 : Январь 19, 2011, 13:55 »

каждый поток - на каждый клиент (соединение) - это бред
Цитировать
Нет не заткнется) Иначе ни как...
- улыбнуло

[added]
Кстати подобная тема уже поднималась.
« Последнее редактирование: Январь 19, 2011, 13:57 от Sahab » Записан
brankovic
Гость
« Ответ #13 : Январь 19, 2011, 14:42 »

Удобнее для кого, для разработчика?

Да. Когда задача всё время усложняется, то тащить за собой асинхронную архитектуру становится очень тяжело.

Эффективней в работе? Сомневаюсь.

Не эффективнее, а "не на много хуже". Тред пулу, например, уступает на копейки. Асинхронный же вариант  ещё чуть шустрее, но учитывая возню с разработкой оно того не стоит.

А ресурсов треды не так много жрут (опять в линуксе).
Не так много, по сравнению с чем?
Попробуй по-запускать не 2 000 потоков, а 5 000, 10 000. И не на кластере.

Не так много, как top пишет. Большая часть памяти маппится, но не используется. 10000 тредов на тестах тянет легко, но реальный сервер под нагрузкой просто не отрабатывал больше 1000-2000 коннектов.

edit: т.е. накладные расходы cpu на создание треда были << логики треда, а накладные расходы памяти на тред << размера базы данных в памяти

каждый поток - на каждый клиент (соединение) - это бред

зависит от задачи, но ладно, в данной может и бред. Просто реально прежде чем выпилить убер-асинк-сервер, может попробовать простой вариант и посмотреть, во что упираемся? В гигабитную сетевушку, например, или в неприемлимую скорость для каждого из 10000 клиентов.
« Последнее редактирование: Январь 19, 2011, 14:46 от brankovic » Записан
BRE
Гость
« Ответ #14 : Январь 19, 2011, 18:15 »

Не эффективнее, а "не на много хуже". Тред пулу, например, уступает на копейки. Асинхронный же вариант  ещё чуть шустрее, но учитывая возню с разработкой оно того не стоит.

Не так много, как top пишет. Большая часть памяти маппится, но не используется. 10000 тредов на тестах тянет легко...

А ты сам пробовал запускать или так думаешь?  Подмигивающий

Вот для примера такая программа:
Код
C++ (Qt)
#include <QCoreApplication>
#include <QThread>
 
 
class Thread : public QThread
{
public:
explicit Thread() : QThread( 0 )
{
setStackSize( 64000 );
}
 
protected:
virtual void run()
{
for(;;)
sleep( 1 );
}
 
};
 
int main( int argc, char **argv )
{
QCoreApplication app( argc, argv );
 
for( int i = 0; i < 10000; ++i )
{
Thread *th = new Thread();
th->start();
}
 
return app.exec();
}
 

У меня она откушала 92 Мб реальной (не виртуальной!) памяти и почти 20% процессорного времени (по мнению top'а).
Заметь, проверял это на не самой последней машине (4 ядра x86_64, память DDR3 и т.д.).

Только на переключения контекста между 10 000 потоков мы потратили почти пятую часть процессорного времени, ну и немного памяти на стеки.  Подмигивающий
« Последнее редактирование: Январь 19, 2011, 19:32 от BRE » Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.081 секунд. Запросов: 21.