понедельник, 10 марта 2008 г.

Управление KTorrent'ом при изменении сетевой активности браузера

По сегодняшним меркам у меня довольно медленное соединение с Интернетом - 150 Кбит/с днем и 300 Кбит/с ночью. Компьютер работает круглосуточно. Ночью для локальной сети зеркалируются репозитории Ubuntu 7.10 и 8.04, и все время включен KTorrent, который постоянно что-то раздает и довольно часто что-то качает. В итоге получается, что мой канал забит практически круглосуточно.

Зеркалирование мне никак не мешает, т. к. работает ночью. А вот ходить по Интернету при работающем KTorrent'e, который качает в несколько потоков, занятие не из приятных. Вручную останавливать/запускать закачки/раздачи - не выход.

Ранее в небольшой заметке я упоминал, как можно управлять KTorrent'ом из консоли. Теперь же я хочу показать вам, как у меня это реализовано на практике в виде небольшого Bash скрипта.

Задача скрипта сводится к следующему:
Скрипт запускается при старте системы и работает постоянно. Каждые 10 секунд он проверяет, не запущено ли зеркалирование репозиториев, и не работаю ли я в данный момент с браузером. Если запущено зеркалирование репозиториев, то все закачки останавливаются, а раздачи остаются активными т. к. скрипту, выкачивающему репозитории, исходящий трафик не нужен. Как только выкачивание репозиториев завершится, скрипт запустит все активные закачки. Если же скрипт обнаруживает, что я в данный момент брожу по Интернету в браузере, то он останавливает закачки и раздачи и переходит в режим проверки "моей активности", каждую секунду записывая при этом время, в которое была зафиксирована последняя сетевая активность браузера. Если в течение 5 минут браузер ни разу не проявит сетевой активности, то скрипт возобновляет все закачки и раздачи.

Примечание:
В данном скрипте для остановки и запуска закачек/раздач я использую циклы по идентификаторам торрентов, хотя мог бы останавливать/запускать их одной командой dcop ktorrent KTorrent stopAll/dcop ktorrent KTorrent startAll. Я не поступаю так потому, что после выполнения этой команды KTorrent время от времени падает.

Цель данной заметки - показать, как можно организовать свою работу, повысив при этом свою производительность (скорость загрузки страниц в браузере), практически не потеряв при этом мегабайт, гигабайт и терабайт :), выкачиваемых вами с BitTorrent трекеров. Сам я довольно долго мучился с медленной загрузкой страниц в браузере, пока не узнал об утилите netstat, которая легла в основу данного скрипта.

Также хочу заметить, что данный скрипт является именно примером, на основе которого вы можете написать для себя собственный, полностью удовлетворяющий вашим требованиям. Прошу обратить внимание, что, возможно, в вашей системе мой скрипт будет работать не так, как вы ожидаете. Например, если у вас в Firefox'e установлено расширение, которое периодически получает информацию о погоде с удаленного сервера, то скрипт будет расценивать эти обращения как вашу активность в браузере. В таком случае вам просто нужно добавить дополнительный фильтр на вывод netstat, исключающий строки, содержащие IP адрес сервера, с которого расширение берет информацию о погоде.

Так что удачи! Надеюсь, кому-то этот совет пригодится, а, может быть, просто заставит задуматься над тем, что, оказывается, есть очень много операций, которые вы можете автоматизировать, облегчив тем самым себе жизнь (а может и не только себе), но просто пока не подозреваете об этом. :)

ktorrent_scheduler:
#!/bin/bash
#
# Cкрипт управления KTorrent'ом в зависимости от активности браузера и прочих факторов.
#

# Интервал в секундах, через который скрипт проверяет, активность Firefox,
# если в данный момент он не активен.
check_interval=10

# Время в секундах, которое ждет скрипт после обнаружения сетевой
# активности браузера, по истечению которого будет считаться, что браузер
# прекратил сетевую активность.
browser_timeout=300

# Файлы, при появлении которых скрипт независмо от активности браузера и
# прочих факторов будет переключаться в требуемый режим.
# Файл активности имеет более высокий приоритет.
activity_file=/my_files/temp/ktorrent_user_active
idle_file=/my_files/temp/ktorrent_user_idle


# Выводит список идентификаторов закачек
get_downloads_torrents_list()
{
dcop ktorrent KTorrent getTorrentNumbers 1
}


# Выводит список идентификаторов раздач
get_uploads_torrents_list()
{
dcop ktorrent KTorrent getTorrentNumbers 2
}


# Останавливает закачки
stop_downloads_torrents()
{
echo `date` 'Stopping downloads...'

for torrent_number in `get_downloads_torrents_list`
do
dcop ktorrent KTorrent stop $torrent_number 1
done
}


# Останавливает раздачи
stop_uploads_torrents()
{
echo `date` 'Stopping uploads...'

for torrent_number in `get_uploads_torrents_list`
do
dcop ktorrent KTorrent stop $torrent_number 1
done
}


# Запускает закачки
start_download_torrents()
{
echo `date` 'Starting downloads...'

for torrent_number in `get_downloads_torrents_list`
do
dcop ktorrent KTorrent start $torrent_number
done
}


# Запускает раздачи
start_upload_torrents()
{
echo `date` 'Starting uploads...'

for torrent_number in `get_uploads_torrents_list`
do
sudo -u ktorrent_internet dcop ktorrent KTorrent start $torrent_number
done
}


# Устанавливает режим полной активности юзера
# (все закачки и раздачи должны быть остановлены)
set_active_state()
{
if [[ "$cur_downloads_state" != 'active' ]]
then
stop_downloads_torrents
cur_downloads_state='active'
fi

if [[ "$cur_uploads_state" != 'active' ]]
then
stop_uploads_torrents
cur_uploads_state='active'
fi
}


# Устанавливает режим частичной активности юзера
# (все закачки должны быть остановлены)
set_partially_active_state()
{
if [[ "$cur_downloads_state" != 'active' ]]
then
stop_downloads_torrents
cur_downloads_state='active'
fi

if [[ "$cur_uploads_state" != 'idle' ]]
then
start_upload_torrents
cur_uploads_state='idle'
fi
}


# Устанавливает режим простоя
# (все закачки и раздачи должны быть запущены)
set_idle_state()
{
if [[ "$cur_downloads_state" != 'idle' ]]
then
start_download_torrents
cur_downloads_state='idle'
fi

if [[ "$cur_uploads_state" != 'idle' ]]
then
start_upload_torrents
cur_uploads_state='idle'
fi
}


# Возвращает 0, если браузер или proxy сервер, через который в Интернет выходят другие компьютеры, проявляют сетевую активность
# (обращение к локальным адресам 10.100.*.*, 10.110.*.* и 192.168.*.* за сетевую активность не считается)
is_browser_active()
{
sudo netstat -p --inet -n --tcp 2>/dev/null | awk '{ print $5, $6, $7 }' | egrep 'firefox-bin|ffproxy' | egrep -v '^10\.1[01]0\.[[:digit:]]+\.[[:digit:]]+:' | egrep -v '^192\.168\.[[:digit:]]+\.[[:digit:]]+:' > /dev/null
}


# Возвращает 0, если запущено зеркалирование репозиториев
is_repository_mirroring_active()
{
ps ax | grep -v grep | grep debmirror > /dev/null
}



# При старте состояние не определено
cur_downloads_state=''
cur_uploads_state=''

while true
do
if [[ -e "$activity_file" ]]
then
set_active_state
elif [[ -e "$idle_file" ]]
then
set_idle_state
elif is_browser_active
then
set_active_state

timeout=$browser_timeout
while [[ $timeout -ne 0 ]]
do
sleep 1

if is_browser_active
then
timeout=$browser_timeout
else
((timeout--))
fi
done

continue
elif is_repository_mirroring_active
then
set_partially_active_state
else
set_idle_state
fi

sleep $check_interval
done

12 комментариев:

Анонимный комментирует...

Хорошо...
Кроме ktorent есть ещё wget....
А почему бы не ограничивать пропускную способность заместо остановки?

Dmitry Konishchev комментирует...

Ну, например, потому, что KTorrent во время своей работы устанавливает большое количество соединений, и если ограничить пропускную способность, то они все равно останутся открытыми.

Это не хорошо как минимум по двум причинам:
1) Если провайдер ограничивает количество открытых соединений, то получится, что все равно KTorrent будет занимать значительную их часть.
2) Т. к. соединения останутся открытыми, то, если я, к примеру, раздаю какой-то торрент, то у пользователя, скачивающего этот торрент, я буду числиться раздающим, и если в настройках его BitTorrent клиента стоит ограничение на количество сидеров, с которых он качает торрент, то я своим открытым соединением, через которое данные практически не идут, буду занимать место в очереди раздающих тогда, когда его мог бы занять раздающий с более высокой скоростью отдачи. (Аналогично и для случая, когда я качаю с кого-то)

Анонимный комментирует...

Спасибо тебе, спаситель!
Побежал сервак настраивать!

Анонимный комментирует...

Можно же настроить приоритизацию трафика, и отдать no-delay для ssh, icq, etc... maxim-data сделать для http. остальному - что получится или попросту отдать пол канала для торрента.

Dmitry Konishchev комментирует...

Анонимный, а разве можно задавать приоритет для входящего трафика? По-моему приоритезировать можно только исходящий - входящий контролирует провайдер. Как он узнает, какой трафик для меня приоритетный, а каким я согласен пожертвовать?

Или я отстал от жизни, и это можно сделать?

Анонимный комментирует...

Нет, не отстали, просто можно раздать приоритеты трафику, и резать полосу для определенных приложений на своей машине, то есть быть для себя же "сервером провайдера". На машине настраивается HTB.

Или может я отстал и это сделать нельзя?

Dmitry Konishchev комментирует...

Сразу скажу, что ни разу не занимался приоритезированием трафика, поэтому исхожу чисто из логических рассуждений. По-моему, как бы я ни резал трафик на своей стороне, это не поможет, когда при скачивании торрента из интернета ко мне польется трафик с сотни пиров, раздающих данный торрент. Как я могу резать входящий трафик на своей машине - он же уже порезан провайдером, что там отрезать-то? :) Разве можно в таких условиях сделать так, чтобы провайдер отбросил весь torrent-трафик и пропустил только браузерный http трафик? Честно, просто не представляю, как это можно сделать. Разве что в каждый отправляемый мной пакет будет зашиваться приоритет, а пиры будут копировать этот приоритет в свои пакеты, чтобы провайдер правильно нарезал трафик.

Анонимный комментирует...

Ваша логика работает только по количеству сессий и то вроде как оно зависит от скорости канала(500 сессий по пол килобайта в сек - бред, это про торренты).

Что же касается трафика от провайдера - он тоже ограничен скоростью вашего соединения. То есть вы не запросите ни у кого больше чем потянет Ваш канал, провайдер такого не допустит, протоколы межсетевого общения тоже такого не допустят. Вы же договариваетесь, что и сколько передать по TCP и UDP.

При приоритезации трафика у Вас договариваться об установлении коннекта и быстром пропуске данных через свой канал именно нужного трафика будете именно Вы. Остальные сессии, в следствии ограничений на канал, будут устанавливаться реже и скорость будет меньше.

Во всяком случае с VoIP это проходит очень хорошо. Разницы в данном случае с торрентом я особой не вижу... И в том и в том случае получаем множество udp сессий.

Dmitry Konishchev комментирует...

Да, похоже, вы правы. Я как-то совсем забыл про такое понятие TCP, как окно. С его помощью как раз можно (пусть и с небольшой задержкой) запретить пирам отсылать пакеты. Спасибо за то, что вразумили. Надо будет как-нибудь попробовать в свободное время...

Анонимный комментирует...

Да, в линухе так всегда, сколько не копай в глубь и в ширь, а всегда найдется что-то новое...

Dmitry Konishchev комментирует...

Как же вы правы... Я когда-то тоже думал, что заведу блог, напишу пару статей, и больше не о чем будет писать. =)

Dmitry Konishchev комментирует...

Вы не против, если я ваше высказывание, поставлю в описание блога? :) Уж больно оно мне понравилось... Наверное потому, что с того момента, как пересел на Linux, эту фразу можно было повторять чуть ли не ежедневно, за что я его и полюбил. =)