Jump to content
Калькуляторы

Еще один HTTP-UDP прокси

После мучений с udpxy и борьбы с распуханием xproxy в астре, что сильно обострилось в канун олимпиады, решил по быстрому сделать свое решение. Представляю на суд общественности то, что получилось в итоге и прошу желающих протестировать.

 

Принцип работы - слушание по умолчанию всех udp-каналов, полученных из командной строки, запись их в небольшой буфер, и отдача клиентам по http с небольшим прекешем (2 секунды). Это позволяет клиенту очень быстро выполнить буферизацию и сразу начать просмотр. Особенно шустрое включение происходит на smart tv. Используется epoll, что позволяет улучшить качество работы, однако из-за этого программа собирается только в linux.

 

Запускается так:

 

./inputtcp -p <порт> -u 239.71.91.1:1234 -u 239.71.91.2:1234 …

 

Программа начинает слушать на заданном http-порту и обслуживает группы 239.71.91.1:1234, 239.71.91.2:1234 и остальные, заданные при запуске. Любые другие группы вызовут ошибку 404. Формат запроса - стандартный - /udp/239.71.91.1-1234, статистику можно получить по запросу /stat - по всем клиентам. Особенно понравилась возможность смотреть количество переполнений буфера у клиентов - поле overruns в таблице.

 

Из ограничений - пока все работает в одном треде, но ничего не мешает запустить несколько экземпляров софтины, исходя из количества ядер, для обслуживания разных групп на разных портах, и сформировать плейлист исходя из этого. Также в файле inputtcp.с можно подправить максимальное количество подключений (стоит сейчас 512).

 

По опыту - работает у нас уже 2 недели, пики в 300 пользователей в момент трансляции закрытия олимпиады и прочих событий пережили нормально. Качество не сравнить с udpxy, в котором уже при 50 клиентах начинались затыки и рассыпания, а HD вообще было невозможно смотреть. Здесь же с этим все идеально. Больше у нас по tcp не смотрят. Будет интересно, если кто-нибудь попробует на большем количестве абонентов.

itcp.tar.gz

Share this post


Link to post
Share on other sites

udpxy давно пора закапывать, там уже помочь не чем :)

У меня пока была однопоточная тянула до 3 гигабит на выход при одном гиге входа, на интеле выше средней приличности.

Share this post


Link to post
Share on other sites

закапывайте обратно. скоро выложу http-стример с zero-copy и дающий 4Гбит/с с одного ядра E5620 при 57% cpu usage(этого ядра) и 40k коннектов.

 

epoll/kqueue + sendfile. работает на фре, линуксе и должно на соляре.

Share this post


Link to post
Share on other sites

4 гигабита и 40К соединений? Это же 100 кбит поток.

Число клиентов тут роли не играет(точнее, оно влияет на число контекстсвитчей, т.к. для большего числа файловых дескрипторов придется звать sendfile).

 

Про вашу нелюбовь к sendfile() я помню, но если файл всегда в кеше - проблем никаких. вот nginx очень интересно работает на побочных эффектах AIO(читая через aio 1 байт, а всё остальное - sendfile).

 

тут нашелся человек с 40G в лабе, так что посмотрим.

Edited by ^rage^

Share this post


Link to post
Share on other sites

Что-то не компилиться на убунуту х64 13.04

 

/itcp# make
gcc -lglib-2.0 -lpthread -g -o inputtcp inputtcp.o socket.o packbuf.o helper.o reqhead.o resphead.o resource.o radix-tree.o
inputtcp.o: In function `http_thread':
/home/user/itcp/inputtcp.c:375: undefined reference to `pthread_detach'
inputtcp.o: In function `main':
/home/user/itcp/inputtcp.c:767: undefined reference to `pthread_create'
collect2: ошибка: выполнение ld завершилось с кодом возврата 1
make: *** [inputtcp] Ошибка 1

Share this post


Link to post
Share on other sites

gcc -o inputtcp inputtcp.o socket.o packbuf.o helper.o reqhead.o resphead.o resource.o radix-tree.o -lglib-2.0 -lboost_system -lpthread -g -lrt

Share this post


Link to post
Share on other sites

Первые результаты тестирования показали хорошие результаты, но в статистеке заметил вот такую штуку

225.2.2.129:1234 total packets=18509943 bufpos=1942

х.109.235.134 time=25:31, queue=224 fd=92 in_epoll=0 overruns=166

х.109.235.134 time=182:59, queue=3446 fd=91 in_epoll=0 overruns=1183

х.109.235.134 time=190:9, queue=1397 fd=89 in_epoll=0 overruns=1234

 

Как понимаю не закрываються конекты :( overruns только увеличиваються

Можно ли установить timeout если overruns превысит какого то значения?

Share this post


Link to post
Share on other sites

> pthread_create(&http_thread_t, NULL, http_thread, harg);

 

 

Вы серьезно? На каждого клиента тред?

Share this post


Link to post
Share on other sites

gcc -o inputtcp inputtcp.o socket.o packbuf.o helper.o reqhead.o resphead.o resource.o radix-tree.o -lglib-2.0 -lboost_system -lpthread -g -lrt

 

собрал без -lboost_system

запускается, но все равно не работает. Иногда картинка появиться, но дальше не показывает...

 

=ERROR REPORT==== 25-Mar-2014::13:41:08 ===
** Generic server <0.46.0> terminating
** Last message in was {http,#Port<0.956>,
                          {http_request,'GET',
                              {abs_path,"/udp/239.0.100.1:1234"},
                              {1,0}}}
** When Server state == {state,#Port<0.956>,undefined}
** Reason for termination ==
** {unhandled_path,"/udp/239.0.100.1:1234"}
>> ~n
=ERROR REPORT==== 25-Mar-2014::13:41:08 ===
** Generic server <0.47.0> terminating
** Last message in was {http,#Port<0.957>,
                          {http_request,'GET',
                              {abs_path,"/udp/239.0.100.1:1234"},
                              {1,0}}}
** When Server state == {state,#Port<0.957>,undefined}
** Reason for termination ==
** {unhandled_path,"/udp/239.0.100.1:1234"}

Share this post


Link to post
Share on other sites

запускается, но все равно не работает. Иногда картинка появиться, но дальше не показывает...

 

=ERROR REPORT==== 25-Mar-2014::13:41:08 ===
** Generic server <0.46.0> terminating
** Last message in was {http,#Port<0.956>,
                          {http_request,'GET',
                              {abs_path,"/udp/239.0.100.1:1234"},
                              {1,0}}}
** When Server state == {state,#Port<0.956>,undefined}
** Reason for termination ==
** {unhandled_path,"/udp/239.0.100.1:1234"}
>> ~n
=ERROR REPORT==== 25-Mar-2014::13:41:08 ===
** Generic server <0.47.0> terminating
** Last message in was {http,#Port<0.957>,
                          {http_request,'GET',
                              {abs_path,"/udp/239.0.100.1:1234"},
                              {1,0}}}
** When Server state == {state,#Port<0.957>,undefined}
** Reason for termination ==
** {unhandled_path,"/udp/239.0.100.1:1234"}

 

подскажу немного: у вас работает сервер на эрланге, а не на C

Share this post


Link to post
Share on other sites

запускается, но все равно не работает. Иногда картинка появиться, но дальше не показывает...

 

=ERROR REPORT==== 25-Mar-2014::13:41:08 ===
** Generic server <0.46.0> terminating
** Last message in was {http,#Port<0.956>,
                          {http_request,'GET',
                              {abs_path,"/udp/239.0.100.1:1234"},
                              {1,0}}}
** When Server state == {state,#Port<0.956>,undefined}
** Reason for termination ==
** {unhandled_path,"/udp/239.0.100.1:1234"}
>> ~n
=ERROR REPORT==== 25-Mar-2014::13:41:08 ===
** Generic server <0.47.0> terminating
** Last message in was {http,#Port<0.957>,
                          {http_request,'GET',
                              {abs_path,"/udp/239.0.100.1:1234"},
                              {1,0}}}
** When Server state == {state,#Port<0.957>,undefined}
** Reason for termination ==
** {unhandled_path,"/udp/239.0.100.1:1234"}

 

подскажу немного: у вас работает сервер на эрланге, а не на C

Удалил эрланге, толку нету все равно(

 

./inputtcp -p 8899 -u 239.0.100.1:1234
accepted http connection from 172.16.1.248:59745
Status ok
172.16.1.248:59745 requested: </udp/239.0.100.1:1234>
Preparing sender thread
sender thread for http found
skipping 1 packets

 

и в влц не показывает

Share this post


Link to post
Share on other sites

> pthread_create(&http_thread_t, NULL, http_thread, harg);

 

Вы серьезно? На каждого клиента тред?

 

Тред на http-сессию, для обработки запроса. После этого fd клиента передается в общий пул и тред убивается.

 

skipping 1 packets

 

и в влц не показывает

 

Судя по выделенному, не идет udp-поток. Посмотри несколько раз /stat - увеличивается ли атрибут total packets?

Share this post


Link to post
Share on other sites

 

skipping 1 packets

 

и в влц не показывает

 

Судя по выделенному, не идет udp-поток. Посмотри несколько раз /stat - увеличивается ли атрибут total packets?

 

Неа, не увеличивается total packets, картинка в влц появилась и зависла.

Share this post


Link to post
Share on other sites

закапывайте обратно. скоро выложу http-стример с zero-copy и дающий 4Гбит/с с одного ядра E5620 при 57% cpu usage(этого ядра) и 40k коннектов.

 

epoll/kqueue + sendfile. работает на фре, линуксе и должно на соляре.

 

День добрый ! А когда выложите то? Хочется пощупать ))) Уж больно аппетитно выглядит. ))

Share this post


Link to post
Share on other sites

Тред на http-сессию, для обработки запроса. После этого fd клиента передается в общий пул и тред убивается.

и что, при 8k запросов в секунду будете создавать/убивать 8k тредов в секунду? может, лучше пул тредов?

Share this post


Link to post
Share on other sites

Это же не HLS сервер, а UDP -> HTTP прокси. 8К подключений в секунду — это нерассчетная нагрузка.

 

Вопрос в другом: каждый клиент будет стоить в такой системе порядка 2 мегабайт памяти. Т.е. 4000 клиентов обойдутся чисто в 8 Гб только на стеки тредов.

 

Архитектура с тредом на клиента имеет кучу изъянов и странно её видеть в чем-то, что делается сегодня.

Share this post


Link to post
Share on other sites

Еще раз - треда на клиента нет, есть тред только на время обработки запроса GET. Потом тред убивается и работа идет только с клиентским fd. На 500 клиентах количество занимаемой памяти не изменилось ни на мегабайт.

 

и что, при 8k запросов в секунду будете создавать/убивать 8k тредов в секунду? может, лучше пул тредов?

 

При таких нагрузках уже будут ресурсы допилить либо эту программу, либо купить что-то готовое. Пока же у нас 300-400 клиентов, и не в секунду, а в час пик одновременно смотрящих, под такие запросы и сделал софтину. Бесплатные решения не устраивали, а коммерческие - были неоправданно дорогие.

 

Неа, не увеличивается total packets, картинка в влц появилась и зависла.

Значит 100% не идет мультикаст-поток, или прекращает идти после запуска. Точно не включен fast leave, ограничение количества потоков или еще что-нибудь в этом роде? Программа запрашивает все потоки сразу, и должна получать их даже если никто не смотрит.

Share this post


Link to post
Share on other sites

Вспомнился Courier Mail Server 1.56 и ранее, он тоже создавал по потоку на клиента.

А досить его было легко: нужно было только создать одновременно пачку соединений (порядка 2к под х32) и всё, память кончалась и он высыпался.

Здесь такая же история: цепляемся несколько тыщ коннектов, и не шлём данных или по одному байту можно цедить запрос, и где то кончится память :)

Share this post


Link to post
Share on other sites

Вспомнился Courier Mail Server 1.56 и ранее, он тоже создавал по потоку на клиента.

Третий раз - по потоку на клиента не создается. Тред создается только на время обработки запроса GET, потом убивается и дальше клиент обслуживается главным процессом через epoll. На сотни тысяч одновременных подключений оно и не рассчитано, для этого есть другие решения. Свою задачу предложенное решение выполняет более, чем хорошо.

 

Если у вас во внутренней сети есть проблема c ddos-ерами, то это можно решить парой строчек в iptables. Или доработать софтину, и выложить изменения тут :)

Share this post


Link to post
Share on other sites

Третий раз - по потоку на клиента не создается. Тред создается только на время обработки запроса GET, потом убивается и дальше клиент обслуживается главным процессом через epoll.

Речь о том, что клиент подключается и шлет букву "G". Через минуту - букву "E"... к 30й минуте подходим к CRLF первой строки запроса...

Share this post


Link to post
Share on other sites
Третий раз - по потоку на клиента не создается. Тред создается только на время обработки запроса GET, потом убивается и дальше клиент обслуживается главным процессом через epoll.

Ещё раз перечитайте внимательно предыдущий пост.

По шагам:

1. Подключаемся и не шлём запрос, просто висим, итого: потоки +1

2. Подключаемся ещё раз и не шлём запрос, просто висим, итого: потоки +2

...

Все эти треды будут висеть и ждать гет запрос.

 

Если у вас во внутренней сети есть проблема c ddos-ерами, то это можно решить парой строчек в iptables. Или доработать софтину, и выложить изменения тут :)

Я свою лучше ещё доработаю, начинать с начала как то не интересно :)

Там уже дошло до разгребания MPEG2-TS: она научилась собирать у себя в памяти все нужные PSI таблицы, умеет их обратно выдавать в самом начале потока новому клиенту, чтобы он не дропал до пол секунды потока пока дождётся этих самых таблиц.

Отсюда до HLS рукой подать :)

И заодно EPG (EIT) у меня тоже собирается (тк это часть PSI), научить отдавать через веб в любом формате дело пары вечеров.

zerocopy тоже не проблема, совсем немного менять, дольше вникать в особенности линуха и искать стенд для тестов на 10г или более.

Share this post


Link to post
Share on other sites

1. Подключаемся и не шлём запрос, просто висим, итого: потоки +1

Если такая проблема есть, доработать не сложно - либо в iptables, либо ограничением количества подключений с одного IP в самой софтине. Это явно не тянет на фатальный баг. У нас внутри сети ддосеров не наблюдалось, а в открытый интернет сервер не светим.

 

Я свою лучше ещё доработаю, начинать с начала как то не интересно :)

Я знаю, что у тебя хорошее решение, только для 200-300 пользователей дороговато, особенно с ежегодными платежами.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this