Mikrotik Router OS, скрипт динамического разделения скорости (Версия 2)
Это вторая версия, первая здесь: здесь.
На днях возникла следующая проблема: поделить скорость поровну между всеми пользователями и так, чтобы скорость распределялась не клиентам, которые в данный момент не пользуются Интернетом, а отдавалась всем остальным, также для того, чтобы получить некую «буферизацию».
» с большим количеством клиентов и узким каналом продаж.
канал.
На основе прошлого опыта был написан совершенно новый сценарий, устранивший недостатки прошлого и доработанный под текущие нужды.
В новой версии все стало намного проще в плане местонахождения пользователей, теперь не имеет значения, как пользователь подключился, главное, что у него есть IP-адрес и нам этого достаточно.
Итак, начнем.
Пример для трех пользователей.
Адреса пользователей 192.168.5.100-192.168.5.102 Добавляем в списки трёх пользователей, и только тех, кто реально существует. Нет необходимости заполнять весь диапазон, если вы им не пользуетесь, потому что.
Каждая дополнительная запись будет иметь огромное влияние на производительность.
/ip firewall address-list add list="users" address=192.168.5.100
/ip firewall address-list add list="users" address=192.168.5.101
/ip firewall address-list add list="users" address=192.168.5.102
Чтобы не добавлять списки вручную, был создан небольшой цикл, который ускорит процесс добавления диапазонов.
Запуск такого сценария один раз приведет к выполнению действий, аналогичных тем, что мы делали выше.
#Settings
######################################################
:local start ("100");
:local stop ("102");
:local net ("192.168.5.");
######################################################
######################################################
:global count ($start);
:for count from=$start to=$stop step=1 do={
/ip firewall address-list add list="users" address=( $net .
$count);};
######################################################
######################################################
# (C) Inlarion icq 429-587 mikrotik.axiom-pro.ru Copyright!
######################################################
Вторым шагом добавляем записи в /queue и /ip firewall mangle, опять же чтобы освободить администратора от рутинной работы по добавлению записей вручную, которая чревата ошибками, так как человеческий фактор не дремлет, был набросан небольшой скрипт. Этот скрипт необходимо запускать каждый раз, когда вы добавляете или удаляете записи в /ip firewall адрес-лист list="users", его основная задача - добавлять новые записи и удалять ненужные старые, очевидно, у нормального сисадмина не должно быть мусора, который забыл удалить.
После завершения работы скрипта в системный журнал будет записана информация о количестве добавленных и удаленных записей.
#Settings
######################################################
:local DownloadParent ("Download");
:local UploadParent ("Upload");
######################################################
#Internal Var
######################################################
:local i;
:local z;
:local userX;
:local enum (" ");
:local mark;
:local qrd;
:local qru;
:local mrd;
:local mru;
:local qrdadd;
:local qruadd;
:local mrdadd;
:local mruadd;
:set qrd (0);
:set qru (0);
:set mrd (0);
:set mru (0);
:set qrdadd (0);
:set qruadd (0);
:set mrdadd (0);
:set mruadd (0);
######################################################
######################################################
:log warning ("Rules Manager Started!");
:if ([/queue type find name="dshaper_down"] = "") do={ /queue type add name="dshaper_down" kind="pcq" pcq-classifier=dst-address pcq-rate=0 pcq-limit=50 pcq-total-limit=2000;};
:if ([/queue type find name="dshaper_up"] = "") do={ /queue type add name="dshaper_up" kind="pcq" pcq-classifier=src-address pcq-rate=0 pcq-limit=50 pcq-total-limit=2000;};
:if ([/queue tree find name=$DownloadParent] = "") do={ /queue tree add name=$DownloadParent parent="global-out" queue="dshaper_down" priority=8;};
:if ([/queue tree find name=$UploadParent] = "") do={ /queue tree add name=$UploadParent parent="global-out" queue="dshaper_up" priority=8;};
:foreach i in=[/ip firewall address-list find list="users"] do={ :set userX [/ip firewall address-list get $i address];
:if ([/queue tree find name=($userX .
"_down")] = "") do={ /queue tree add name=($userX .
"_down") parent=$DownloadParent queue="dshaper_down" packet-mark=($userX .
"_down") priority=8; :set qrdadd ($qrdadd+1); }; :if ([/queue tree find name=($userX .
"_up")] = "") do={ /queue tree add name=($userX .
"_up") parent=$UploadParent queue="dshaper_up" packet-mark=($userX .
"_up") priority=8; :set qruadd ($qruadd+1);}; :set enum (" "); :set enum ([/ip firewall mangle find comment=($userX .
"_up")]); :if ($enum = "") do={ /ip firewall mangle add chain=forward src-address=$userX dst-address=0.0.0.0/0 action=mark-packet new-packet-mark=($userX .
"_up") comment=($userX .
"_up") disabled=no passthrough=yes; :set mruadd ($mruadd+1); }; :set enum (" "); :set enum ([/ip firewall mangle find comment=($userX .
"_down")]); :if ($enum = "") do={ /ip firewall mangle add chain=forward src-address=0.0.0.0/0 dst-address=$userX action=mark-packet new-packet-mark=($userX .
"_down") comment=($userX .
"_down") disabled=no passthrough=yes; :set mrdadd ($mrdadd+1); }; }; :foreach z in=[/queue tree find parent=$DownloadParent] do={ :set mark [/queue tree get $z name]; :if ($mark !="") do={ :set mark ([:tostr $mark]); :set mark ([:pick $mark 0 ([:len $mark]-5)]); :if ([/ip firewall address-list find address=$mark] = "") do={/queue tree remove [/queue tree find name=($mark .
"_down")]; :set qrd ($qrd+1); };};}; :foreach z in=[/queue tree find parent=$UploadParent] do={ :set mark [/queue tree get $z name]; :if ($mark !="") do={ :set mark ([:tostr $mark]); :set mark ([:pick $mark 0 ([:len $mark]-3)]); :if ([/ip firewall address-list find address=$mark] = "") do={/queue tree remove [/queue tree find name=($mark .
"_up")]; :set qru ($qru+1); };};}; :foreach z in=[/ip firewall mangle find src-address="0.0.0.0/0" action="mark-packet" chain="forward"] do={ :set mark [/ ip firewall mangle get $z comment]; :if ($mark !="") do={ :set mark ([:tostr $mark]); :set mark ([:pick $mark 0 ([:len $mark]-5)]); :if ([/ip firewall address-list find address=$mark] = "") do={ :if ([/ip firewall mangle find comment=($mark .
"_down")] != "") do={/ip firewall mangle remove [/ip firewall mangle find comment=($mark .
"_down")]; :set mrd ($mrd+1); }}}} :foreach z in=[/ip firewall mangle find dst-address="0.0.0.0/0" action="mark-packet" chain="forward"] do={ :set mark [/ ip firewall mangle get $z comment]; :if ($mark !="") do={ :set mark ([:tostr $mark]); :set mark ([:pick $mark 0 ([:len $mark]-3)]); :if ([/ip firewall address-list find address=$mark] = "") do={ :if ([/ip firewall mangle find comment=($mark .
"_up")] != "") do={/ip firewall mangle remove [/ ip firewall mangle find comment=($mark .
"_up")]; :set mru ($mru+1); }}}} ###################################################### ###################################################### :log info ("------------------------------------------"); :log warning ("Rules Manager:"); :log info ("Queue Tree Download Records Added: " .
$qrdadd); :log info ("Queue Tree Upload Records Added: " .
$qruadd); :log info ("Mangle Download Records Added: " .
$mrdadd); :log info ("Mangle Upload Records Added: " .
$mruadd); :log info ("Queue Tree Download Records Deleted: " .
$qrd); :log info ("Queue Tree Upload Records Deleted: " .
$qru); :log info ("Mangle Download Records Deleted: " .
$mrd); :log info ("Mangle Upload Records Deleted: " .
$mru);
:log info ("------------------------------------------");
######################################################
######################################################
# (C) Inlarion icq 429-587 mikrotik.axiom-pro.ru Copyright!
######################################################
Теперь скрипт вычислительной и исполнительной частей схемы.
Как работает скрипт. Скрипт определяет, включена ли поддержка ночного времени и проверяет текущее время по временному диапазону, выбирает полосу пропускания канала.
Скрипт измеряет скорость работы клиента и если она превышает ActiveThresholddown, добавляет его как активного на прием.
Это также увеличивает счетчик активных клиентов.
Далее проверяет ActiveThresholdup, если пользователь превысил эту отметку, то добавляет его как активного для возврата.
Это также увеличивает счетчик активных клиентов.
Рассчитывает: MaxRateDownload делится на количество активных пользователей на прием и выводит ставку на пользователя.
MaxRateUpload делится на количество активных пользователей по выходным данным, отображает ставку на пользователя.
Далее он устанавливает лимит для всех пользователей в Дереве очередей по рассчитанной выше схеме.
Далее он вычисляет значение в килобитах и отображает статистику в журнале.
Переменные в скрипте в самом начале: MaxRateDownload -Ширина канала для всех пользователей (прием) Бит/сек.
MaxRateUpload — ширина канала для всех пользователей (загрузка), бит/сек.
MaxRateDownloadNight - Ширина канала для всех пользователей в ночное время (прием) Бит/сек.
MaxRateUploadNight - Ширина канала для всех пользователей в ночное время (загрузка) Бит/сек.
ActiveThresholddown — Порог, при превышении которого пользователь будет считаться активным (прием) Бит/сек.
ActiveThresholdup — Порог, при превышении которого пользователь будет считаться активным (выход) Бит/сек.
usenighttime — может принимать значения «да» и «нет», позволяет скрипту использовать другую ширину канала.
По ночному тарифу.
Nighttimestart — сообщает скрипту о начале ночного тарифа.
Nighttimestop — сообщает скрипту об окончании ночного тарифа.
#Settings
######################################################
:local MaxRateDownload ("15000000");
:local MaxRateUpload ("15000000");
:local MaxRateDownloadNight ("20000000");
:local MaxRateUploadNight ("20000000");
:local ActiveThresholddown ("15000");
:local ActiveThresholdup ("15000");
:local usenighttime ("yes");
:local nighttimestart ("02:00");
:local nighttimestop ("08:00");
######################################################
#Internal Var
######################################################
:local z;
:local i;
:local ii;
:local userX;
:local timedelay (0);
:local startmin;
:local startsec;
:local stopmin;
:local stopsec;
:local scripttimedelay (0);
:local scriptstartmin;
:local scriptstartsec;
:local scriptstopmin;
:local scriptstopsec;
:local userscount ("0");
:local userstmp ("");
:local firstdowntmp ("");
:local firstuptmp ("");
:local twodowntmp ("");
:local twouptmp ("");
:local activedownuserstmp ("");
:local activeupuserstmp ("");
:local activedowncount ("0");
:local activeupcount ("0");
######################################################
######################################################
:set scriptstartmin ([: pick [/system clock get time] 3 5]);
:set scriptstartsec ([: pick [/system clock get time] 6 8]);
:if ($usenighttime = "yes") do={
:set nighttimestart ([: pick $nighttimestart 0 2] .
[: pick $nighttimestart 3 5]); :set nighttimestop ([: pick $nighttimestop 0 2] .
[: pick $nighttimestop 3 5]); :local currenthours ([: pick [/system clock get time] 0 2]); :local currenttime ([: pick [/system clock get time] 0 2] .
[: pick [/system clock get time] 3 5] ); :local acttime ("day"); :local starttime ("day"); :if ($currenthours < 10) do={ :set acttime ("night"); }; :if ( [: pick $nighttimestart 0 2] < 10) do={ :set starttime ("night"); }; :local night ("no"); :if ($starttime = "night") do={ :if ( $currenttime > $nighttimestart && $currenttime < $nighttimestop) do={ :set night ("yes"); }; }; :if ($starttime = "day") do={ :if ( $acttime = "day") do={ :if ( $currenttime >= $nighttimestart) do={ :set night ("yes"); }; }; :if ( $acttime = "night") do={ :if ( $currenttime < $nighttimestop ) do={ :set night ("yes"); }; };}; :if ($night = "yes") do={ :set MaxRateDownload ($MaxRateDownloadNight); :set MaxRateUpload ($MaxRateUploadNight); }; }; :set ActiveThresholddown ($ActiveThresholddown / 8); :set ActiveThresholdup ($ActiveThresholdup / 8); :foreach i in=[/ip firewall address-list find list="users"] do={ :set userX [/ip firewall address-list get $i address]; :set userscount ($userscount+1); :set userstmp ($userstmp .
$userX .
","); }; :local users [:toarray $userstmp]; :set startmin ([: pick [/system clock get time] 3 5]); :set startsec ([: pick [/system clock get time] 6 8]); :global dcount ("1"); :for dcount from=1 to=$userscount step=1 do={ :set firstdowntmp ($firstdowntmp .
[/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] .
"_down"] bytes] .
","); :set firstuptmp ($firstuptmp .
[/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] .
"_up"] bytes] .
","); }; :set stopmin ([: pick [/system clock get time] 3 5]); :set stopsec ([: pick [/system clock get time] 6 8]); :global dcount ("1"); :for dcount from=1 to=$userscount step=1 do={ :set twodowntmp ($twodowntmp .
[/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] .
"_down"] bytes] .
","); :set twouptmp ($twouptmp .
[/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] .
"_up"] bytes] .
","); }; :if ( $stopmin > $startmin) do={ :set timedelay (($stopmin-$startmin) * 60); }; :set timedelay (($timedelay+$stopsec)-$startsec); :local firstdown [:toarray $firstdowntmp]; :local firstup [:toarray $firstuptmp]; :local twodown [:toarray $twodowntmp]; :local twoup [:toarray $twouptmp]; :global dcount ("1"); :for dcount from=1 to=$userscount step=1 do={ :if ( ($ActiveThresholddown * $timedelay) < ([:pick $twodown ($dcount-1)] - [:pick $firstdown ($dcount-1)]) ) do={ :set activedownuserstmp ($activedownuserstmp .
[:pick $users ($dcount-1)] .
","); :set activedowncount ($activedowncount+1); }; :if ( ($ActiveThresholdup * $timedelay) < ([:pick $twoup ($dcount-1)] - [:pick $firstup ($dcount-1)]) ) do={ :set activeupuserstmp ($activeupuserstmp .
[:pick $users ($dcount-1)] .
","); :set activeupcount ($activeupcount+1); }; }; :local activedownusers [:toarray $activedownuserstmp]; :local activeupusers [:toarray $activeupuserstmp]; :local maxlimitdown ("0"); :local maxlimitup ("0"); :if ( $activedowncount > 0 ) do={ :set maxlimitdown ($MaxRateDownload/$activedowncount); :global dcount ("1"); :for dcount from=1 to=$activedowncount step=1 do={ :if ([/queue tree get [find name=[:pick $activedownusers ($dcount-1)] .
"_down"] max-limit] != $maxlimitdown) do={ /queue tree set [/queue tree find name=[:pick $activedownusers ($dcount-1)] .
"_down"] max-limit=$maxlimitdown; }; }; }; :if ( $activeupcount > 0 ) do={ :set maxlimitup ($MaxRateUpload/$activeupcount); :global dcount ("1"); :for dcount from=1 to=$activeupcount step=1 do={ :if ([/queue tree get [find name=[:pick $activeupusers ($dcount-1)] .
"_up"] max-limit] != $maxlimitup) do={ /queue tree set [/queue tree find name=[:pick $activeupusers ($dcount-1)] .
"_up"] max-limit=$maxlimitup; }; }; }; :local kbsmaxdown ($MaxRateDownload/1000); :local kbsmaxup ($MaxRateUpload /1000); :if ( $maxlimitdown = 0 ) do={ :set maxlimitdown ($MaxRateDownload); }; :if ( $maxlimitup = 0 ) do={ :set maxlimitup ($MaxRateUpload); }; :local kbsmaxlimitdown ($maxlimitdown/1024); :local kbsmaxlimitup ($maxlimitup/1024); :set scriptstopmin ([: pick [/system clock get time] 3 5]); :set scriptstopsec ([: pick [/system clock get time] 6 8]); :if ( $scriptstopmin > $scriptstartmin) do={ :set scripttimedelay (($scriptstopmin-$scriptstartmin) * 60); }; :set scripttimedelay (($scripttimedelay+$scriptstopsec)-$scriptstartsec); ###################################################### ###################################################### :log info ("------------------------------------------"); :log warning ("Shaper:"); :log info ("MaxRate Download : " .
$MaxRateDownload .
" bps /" .
$kbsmaxdown .
" kbs / Upload : " .
$MaxRateUpload .
" bps /" .
$kbsmaxup .
" kbs"); :log info ("Active Users : Download : " .
$activedowncount .
" / Upload : " .
$activeupcount ); :log info ("User Speed Download : " .
$maxlimitdown .
" bps /" .
$kbsmaxlimitdown .
" kbs / Upload : " .
$maxlimitdown .
" bps /" .
$kbsmaxlimitup .
" kbs" ); :log warning ("Performance Time: " .
$scripttimedelay .
" seconds.");
:log info ("------------------------------------------");
######################################################
######################################################
# (C) Inlarion icq 429-587 mikrotik.axiom-pro.ru Copyright!
######################################################
Что касается последних желаний
В этой версии можно заметить добавление новой строки в системный журнал «Время производительности»; эта строка позволяет корректно установить интервал выполнения скрипта в планировщике.«Время выполнения» отражает время выполнения скрипта в секундах с точностью +-1 сек.
Это время резко увеличивается при увеличении количества клиентов, добавленных в список адресов /ip firewall list="users", нагрузке на ваш Микротик и неправильно установленном интервале выполнения скриптов, поэтому этот параметр необходимо постоянно контролировать.
Установите интервал выполнения скрипта по формуле: Время выполнения + 10-15 сек.
При первом запуске скрипта установите интервал 1-2 минуты, в идеале, если система в этот момент находится на максимальной нагрузке.
Параметры скрипта: :local MaxRateDownload ("15000000");
:local MaxRateUpload ("15000000");
:local MaxRateDownloadNight ("20000000");
:local MaxRateUploadNight ("20000000");
Вам следует установить ее немного меньше ширины вашего канала, чтобы избежать подтормаживаний при активности другого пользователя, ранее не потреблявшего Интернет. :local ActiveThresholddown ("15000");
:local ActiveThresholdup ("15000");
Установите эти значения в соответствии с вашими потребностями, но исходя из расчета: количество пользователей, умноженное на Thresholddown = не должно превышать MaxRateDownload, иначе ваши пользователи всегда будут неактивны! Естественно пользователи*ActiveThresholdup= не должны превышать MaxRateUpload. Всегда оставляйте некоторый запас.
В дополнение ко всему хочу отметить, что вопрос о нецелесообразности использования скрипта в версиях 5.х закрыт, т.к.
после тестирования версии 5.0rc7 я действительно убедился, что /queue в Микротике стал работать намного лучше по сравнению с на более ранние версии, поэтому в версиях 5.х скрипт не нужен.
А вот вопрос перехода на пятую версию весьма сомнителен, ввиду ошибок, которые допустили разработчики.
Например, в rc3 утилита ping работает коряво и не возвращает параметры, в rc7 не работает маршрутизация при использовании имен в таблице, хотя в более ранних версиях все работает на ура.
Но это всего лишь RC, хотя он уже седьмой по счету и это очень тревожит. Даже если вам не нужна основная часть, а вы используете дерево /очередей, вы можете просто использовать скрипт для создания правил, очень удобная вещь при добавлении/удалении пользователей.
Теги: #Системное администрирование #qos #mikrotik #shaper
-
Птерозавры
19 Oct, 24 -
Штейнталь, Хейман
19 Oct, 24 -
Новая (Новая) Медианомика
19 Oct, 24