LINUX.ORG.RU

Скрипт на lua за 12 часов работы отъел 1412KB оперативы.

 


0

2

Скрипт на lua слушает events от freeswitch через ESL и обрабатывает их. Скрипт должен непрерывно выполняться пока работает freeswitch. ЗА 12 часов не было ни одного event-а, который нужно было обработать.
Однако при примитивной логике работы скрипта, скрипт разросся в оперативе
цифры из pmap собранных с интервалом 12 часов

# diff /tmp/lua1 /tmp/lua2
5c5
< 000000001ef86000   13308 rw--- 000000001ef86000 000:00000   [ anon ]
---
> 000000001ef86000   14720 rw--- 000000001ef86000 000:00000   [ anon ]
60c60
< mapped: 54848K    writeable/private: 20084K    shared: 0K
---
> mapped: 56260K    writeable/private: 21496K    shared: 0K

Хэш таблица (ключ->значение) с которой работает алгоритм не разрастается по числу ключей. ДА и за 12 часов алгоритм в нее ничего не писал. Алгоритм тупо считывал Event, анализировал поле event а, оно не совпадало и скрипт снова брал новый event. и при этом он разросся!!! Вроде немного, но вопрос. Скрипт планируется повесить на постоянку т.е. за год теоретически скрипт съест 1ГБ оперативы. Может и не съест, скрипт пока работает 12 часов.
Может какой то дамп из lua сбрасывать или из Linuх всего процесса чтобы посмотреть что именно разраствается. ИЛи может просто пустой блок памяти linuх lua резервирует.

★★★★

Наверное где-то копятся ссылки на объекты и от того не подчищаются gc. Может замыкания. В общем, скрипт в студию

makoven ★★★★★
()
Ответ на: комментарий от makoven
#!/usr/local/bin/lua
require("ESL")

operator_prefix = {}
operator_prefix["AAAA"] = 0
operator_prefix["BBBB"] = 0

const_delay = {}
const_delay["AAAA-1"] = 170
const_delay["AAAA-2"] = 170
const_delay["BBBB-1"] = 170
const_delay["BBBB-2"] = 170

hangup_channel_counter = {}
hangup_channel_mduration = {}

for k,v in pairs(operator_prefix) do
    hangup_channel_counter[k] = 0
    hangup_channel_mduration[k] = 0
end
command = ''
args = ''
------------------------
function sleep(n)
  os.execute("sleep " .. tonumber(n))
end
------------------------
-- запихнем в функцию код который работает с freeswitch по ESL
------------------------
function ESL_read_write()

unixtime_start = os.time() -- сохраним время старта коннекта к fs esl
unixtime_start10 = unixtime_start

local con = ESL.ESLconnection("localhost", "8021", "ClueCon");
con:events("plain","CHANNEL_HANGUP_COMPLETE")
fscommand = "version"
e = con:api(fscommand, args);
if e == nil then
    print ("ESL соединение разорвано")
    con = nil
    return  -- покинем ESL_read_write()
else
    print ("ESL соединение установлено")
    event_counter = 0
    mduration_all_calls = 0
    average_mduration = 0
    while con:connected() do  --здесь все плохо, если fs остановить, то статус проверки  con:connected() не меняется
        local e = con:recvEventTimed(100)  -- 100 означает что ждем events указанное время и далее выполняем остальной алгоритм
        unixtime_current = os.time()
        unixtime_delta = unixtime_current - unixtime_start
        unixtime_delta10 = unixtime_current - unixtime_start10

        if unixtime_delta10 > 2 then   -- спецом дергаем fs раз в 2 сек, для определения статуса ESL c fs - отвечает/не отвечает
            unixtime_start10 = os.time()
            fscommand = "version"
            e = con:api(fscommand, args);
            if e == nil then
            print ("ESL соединение разорвано")
                con = nil
                return -- покинем ESL_read_write()
            end
        end
-- заходим в эту ветку через интервал времени
        if unixtime_delta > 10 then
            unixtime_start = os.time()
            print ("unixtime_delta:",unixtime_delta,"event_counter:",event_counter)
            for k,v in pairs(operator_prefix) do
--              print (k,v)
                average_mduration = hangup_channel_mduration[k]/hangup_channel_counter[k]
                average_mduration = math.floor (average_mduration)
                print (k,"hangup_channel_mduration", hangup_channel_mduration[k])
                print (k,"hangup_channel_counter", hangup_channel_counter[k])
                print (k,"average_mduration", average_mduration)
--              if average_mduration > 50 and average_mduration < 250 then
                if average_mduration > 10 then
                    fscommand = "hash insert/limit/" ..k .."-FAILURE/1"
                    print (fscommand)
                    e = con:api(fscommand, args);
                    print(e:getBody());
                    f = io.open("/tmp/email_body","w")
                    f:write (fscommand)
                    os.execute("/usr/local/freeswitch/scripts/email_monit_tdm_voip_trunks.sh noc@qwerty.ru /tmp/email_body")
                end
                hangup_channel_mduration[k] = 0
                hangup_channel_counter[k] = 0
            end
            event_counter = 0
        end

        if e ~= nil then  -- если получили event
            call_direction = e:getHeader("Call-Direction")
            if call_direction == "outbound" then
                variable_sip_gateway_name = e:getHeader("variable_sip_gateway_name")
                if variable_sip_gateway_name ~= nil then
                    uduration = e:getHeader("variable_uduration")
                    mduration = e:getHeader("variable_mduration")
                    channel_call_uuid = e:getHeader("Channel-Call-UUID")
                    caller_id_number = e:getHeader("Caller-Caller-ID-Number")
                    callee_id_number = e:getHeader("Caller-Callee-ID-Number")
                    caller_channel_name = e:getHeader("Caller-Channel-Name")
                    for k,v in pairs(operator_prefix) do
                        f,l = string.find(variable_sip_gateway_name, k,1,true)
                        if f ~= nil then
                        print (f,l)
                            mduration = mduration - tonumber(const_delay[variable_sip_gateway_name])
                            hangup_channel_counter[k] = hangup_channel_counter[k] + 1
                            hangup_channel_mduration[k] = hangup_channel_mduration[k] + mduration
                        end
                    end
                    event_counter = event_counter + 1
                    print(caller_channel_name," ",variable_sip_gateway_name," ",caller_id_number," ",callee_id_number," ms ",mduration)
                    print ("===============================================================================================")
--              print (os.time())
                end
            end
        end --if e ~= nil then  -- если получили event
    end --while con:connected() do

end --if e  == nil then

end --function
------------------------
while 1 do
    status, err = pcall(ESL_read_write) -- вызовем функцию в обертке для обработки exeption в случае аварии ESL соединения
    if not status then
        print("Функция ESL_read_write() вылетела с ошибкой:", err)
    end
    print ("Connection to freeswitch ESL failed. Slipping and try again.")
    sleep(5)
end --while 1 do

Vlad-76 ★★★★
() автор топика

Если твой код течёт, то это повод сменить прокладку между креслом и клавиатурой, так как на Lua писать скрипты — это форма наркомании.

А если по делу, то придумай кэш, ибо garbage collector в Lua как таковой отсутствует.

r3lgar ★★★★★
()
Ответ на: комментарий от r3lgar

Что за кэш (через что его реализовать) и для чего он мне в данном случае? lua пока устраивает.
Про замену подумаю. Варианты не подскажете?

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

Однако

[root@freeswitch1 lua]# diff /tmp/lua2 /tmp/lua3
5c5
< 000000001ef86000   14720 rw--- 000000001ef86000 000:00000   [ anon ]
---
> 000000001ef86000   13976 rw--- 000000001ef86000 000:00000   [ anon ]
60c60
< mapped: 56260K    writeable/private: 21496K    shared: 0K
---
> mapped: 55516K    writeable/private: 20752K    shared: 0K

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

ESL модуль для python весит 5730702 байта, для lua 404207 байта.
глядя на аналогичный пример скрипта на pyton который идет в комплекте с fs, волосы дыбом встают. Пожалуй не в этот раз.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

Что за кэш (через что его реализовать)

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

Про замену подумаю. Варианты не подскажете?

Выше уже предложили Python, других не знаю.

r3lgar ★★★★★
()
Ответ на: комментарий от r3lgar

Не могу понять для чего в моем случае кэш? Что именно в нем хранить?
неужели несколько таблиц и переменных могут так сильно течь?

Vlad-76 ★★★★
() автор топика
Последнее исправление: Vlad-76 (всего исправлений: 2)
Ответ на: комментарий от r3lgar

так как на Lua писать скрипты — это форма наркомании

Почему?

ибо garbage collector в Lua как таковой отсутствует

Как же так? Его даже можно тюнинговать и вызывать вручную. Еще есть Weak Tables

makoven ★★★★★
()
Ответ на: комментарий от Vlad-76

На кой чёрт у тебя все переменные и функции — глобальные?

while 1 do

3.14-здец!

Нет, видимо, я пишу код немного лучше. xD

r3lgar ★★★★★
()
Ответ на: комментарий от Vlad-76
[root@freeswitch1 lua]# diff /tmp/lua3 /tmp/lua4
5c5
< 000000001ef86000   13976 rw--- 000000001ef86000 000:00000   [ anon ]
---
[quote] 000000001ef86000   13636 rw--- 000000001ef86000 000:00000   [ anon ][br][/quote]60c60
< mapped: 55516K    writeable/private: 20752K    shared: 0K
---
[quote] mapped: 55176K    writeable/private: 20412K    shared: 0K[/quote]
Vlad-76 ★★★★
() автор топика
Последнее исправление: Vlad-76 (всего исправлений: 1)
Ответ на: комментарий от makoven

так как на Lua писать скрипты — это форма наркомании

Почему?

Потому, что пользоваться им как standalone interpreter — это боль.

ибо garbage collector в Lua как таковой отсутствует

Как же так? Его даже можно тюнинговать и вызывать вручную.

Вручную он работает, сам у меня ещё ни разу не приходил, что наводит на мысль, что он ещё более мифичен, чем OOM Killer в Linux, лол.

Еще есть Weak Tables

Они тоже не являются панацеей.

r3lgar ★★★★★
()
Ответ на: комментарий от r3lgar

с областью видимости переменных разберусь
что касается «while 1 do» какой более простой способ есть на lua для создания бесконечного цикла?

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

Функция в 90 строк и 7 уровнями вложенности. Сложна. Разбил бы на маленькие функции

Еще вижу кучу глобальных переменных и функций. Не факт что FS изолирует глобальное окружение при вызове твоего скрипта. На всякий случай перед переменными и функциями можно добавить local

Возможно для ESL.ESLconnection есть функция, закрывающая этот коннекшн?

makoven ★★★★★
()
Ответ на: комментарий от makoven

90 строк меня не напрягает, тут норм.

перенес переменные в функцию, local подставил

есть метод disconnet который закрывает ESL соединение. Переделал.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

после перекидывания переменных и добавления local

mapped: 41936K    writeable/private: 7172K    shared: 0K
через нексолько минут
mapped: 43872K    writeable/private: 9108K    shared: 0K
ладно, понаблюдаю подольше

Vlad-76 ★★★★
() автор топика
Последнее исправление: Vlad-76 (всего исправлений: 1)
Ответ на: комментарий от Vlad-76

перенес переменные в функцию

Это не обязательно. Каждый файл компилится в анонимную функцию. Поэтому переменные можно оставить снаружи, главное подписать к ним local.

Перед функциями тоже local подписать, т.к. «function foo()» это сахар для «foo = function()»

makoven ★★★★★
()
Ответ на: комментарий от makoven

перед функциями тоже подписал.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от makoven

в это место скрипт попадет если соединение по какой либо причине ESL соединение разорвется. А этого не было ни разу. соотв collectgarbage() не вызовется

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от r3lgar

Потому, что пользоваться им как standalone interpreter — это боль.

Почему?

сам у меня ещё ни разу не приходил, что наводит на мысль, что он ещё более мифичен, чем OOM Killer в Linux, лол

Приходит )

local counter = 1

local function sleep_1s()
  print(counter)
  os.execute("sleep 1")
  counter = counter + 1
end

local mt = {
  __gc = function()
    print("Me clean")
  end
}

local o = setmetatable({}, mt)
o = {}

for _ = 1, 1000 do sleep_1s() end
...
871
Me clean
872
makoven ★★★★★
()
Ответ на: комментарий от makoven

Возможно для ESL.ESLconnection есть функция, закрывающая этот коннекшн?

А она ведь есть, ESLconnection:disconnect(). Может, её вызывать перед con = nil?

Ещё вот насчёт этого:

f = io.open(«/tmp/email_body»,«w»)

Этот файл следовало бы закрыть после записи. Хотя, впрочем, сборщик мусора, если он работает, сам должен это сделать. Однако закрыть следует ещё и потому, что данные после f:write() не обязательно запишутся на диск, а не останутся в буфере.

Но что тут течёт особенно если event'ы никогда не приходят и соединения никогда не разрываются... хм. Может, ESL?

proud_anon ★★★★★
()
Ответ на: комментарий от proud_anon

Может, ESL?

Значит надо написать минимальный скрипт, вызывающий ESL connect/disconnect/etc и погонять его

makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от proud_anon

Уже внес изменения - вместо con=nil, сделал con:disconnect()
про закрытие файла проглядел ((, добавил закрытие.
event`ы приходят, парсятся, только в таблицы ничего не пишется, т.к. event`ы не удовлетворяют условиям. Соединения да не разрываются.
ХЗ может и ESL, особенно после того как fs остановить, а метод con:connected() все равно единицу выдает. Что угодно с ним может быть.
Понаблюдаю за ростом потребления памяти на дистанции, память то занимается, то освобождается.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от makoven

А можете вкратце рассказать что вот эта часть кода делает?

local mt = {
  __gc = function()
    print("Me clean")
  end
}

local o = setmetatable({}, mt)
o = {}

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

mt - это метатаблица. Функция __gc запустится перед сборкой мусора на объекте, на котором установлена эта метатаблица

local o = setmetatable({}, mt) - устанавливает метатаблицу mt объекту {} и возвращает этот объект прямо в переменную «o»

o = {} - присваиваем новое значение «o». Старое значение больше недоступно в области видимости, а значит подлежит уничтожению сборщиком мусора

Дальше сидим и ждем когда придет уборщица

https://www.lua.org/manual/5.3/manual.html#2.5

makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 3)
Ответ на: комментарий от makoven
ESLconnection   function: 0x13442950
ESLevent        function: 0x134447c0
eslSetLogLevel  function: 0x134428f0

а как вывести методы объекта con после создания ESLconnection?

local con = ESL.ESLconnection("localhost", "8021", "ClueCon");

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

что касается «while 1 do» какой более простой способ есть на lua для создания бесконечного цикла?

И скрипт ты будешь останавливать 13 сигналом?

rezedent12 ☆☆☆
()
Ответ на: комментарий от Vlad-76

Перенеси содержимое главного цикла в функцию. Назови её например Iteration. А затем все переменные которые возможно, сделай локальными для этой функции.

rezedent12 ☆☆☆
()
Ответ на: комментарий от makoven

Так ты раму-то чего не показываешь?

Если он пришёл и отработал, это не значит, что он что-то толковое сделал. У меня, например, после его отработки обычно накипает пару килобайт, хотя по уму должно быть наоборот, лол.

r3lgar ★★★★★
()
Ответ на: комментарий от rezedent12

Да там можно всё сделать локальным, и return main_func.

r3lgar ★★★★★
()
Ответ на: комментарий от r3lgar

так как на Lua писать скрипты — это форма наркомании.

/0

Dron ★★★★★
()

Однако при примитивной логике работы скрипта, скрипт разросся в оперативе

Lua запускает свой gc только по ооочень большим праздникам. Прогони вручную если хочешь память высвободить.

lua_gc (L, LUAGC_COLLECT, 0);

morse ★★★★★
()
Ответ на: комментарий от rezedent12

Если Вы про это, то не понимаю каким образом это для меня нужно.

SIGPIPE	13	Завершение	Запись в разорванное соединение (пайп, сокет)	Уведомление
Я не против чем бы его останавливать, или есть способы лучше чем kill -13 ? kill -9 скрипт прекрасно останавливает.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

Если Вы про это, то не понимаю каким образом это для меня нужно.

Так обычно поступают быдлокодеры.

Я не против чем бы его останавливать, или есть способы лучше чем kill -13 ? kill -9 скрипт прекрасно останавливает.

Скрипт имеет какой либо способ связи с окружением и функциями ОС кроме «events от freeswitch через ESL»? Файл-флаг, сокет?

rezedent12 ☆☆☆
()
Ответ на: комментарий от rezedent12

текст скрипта приведен выше. КМК он не имеет явной (через код) связи с функциями ОС. Что встраивает в него или в его окружение lua мне не очевидно. Скорее всего то что сам lua встраивает при запуске в окружение скрипта то и окружает скрипт.
Про быдло кодеров не понял )). Поясните что нужно сделать чтобы не быть быдло кодером.

Vlad-76 ★★★★
() автор топика
Последнее исправление: Vlad-76 (всего исправлений: 1)
Ответ на: комментарий от Vlad-76

кстати да, обработчик sigpipe можно встроить, т.к. непонятно почему функция con:connected() не меняет своего статуса при отвале fs.
в принципе алгоритм это детектит, но для правильности можно попробовать.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от rezedent12

не понимаю, поясните. Что значит связь с внешним миром.
Скрипт делает системный вызов - отправляет почту, функция sleep через системный вызов работает и все.

Vlad-76 ★★★★
() автор топика
Последнее исправление: Vlad-76 (всего исправлений: 2)
Ответ на: комментарий от Vlad-76

не понимаю, поясните. Что значит связь с внешним миром.

Ты можешь скрипту послать какое нибудь сообщение штатным образом? Например через сокет?

rezedent12 ☆☆☆
()
Ответ на: комментарий от rezedent12

Главный цикл вы имеете ввиду это?

while 1 do
    status, err = pcall(ESL_read_write) -- вызовем функцию в обертке для обработки exeption в случае аварии ESL соединения
    if not status then
        print("Функция ESL_read_write() вылетела с ошибкой:", err)
    end
    print ("Connection to freeswitch ESL failed. Slipping and try again.")
    sleep(5)
end --while 1 do
Если перенесу этот код в функцию, то как вызывать эту функцию?
Все переменные сделал local.

Vlad-76 ★★★★
() автор топика
Последнее исправление: Vlad-76 (всего исправлений: 1)
Ответ на: комментарий от rezedent12

ХЗ. Скорее нет - мне это с точки зрения его работы не нужно, и не предусмотрено это.
Никаких сокетов специальных скрипт не делает.

Vlad-76 ★★★★
() автор топика
Ответ на: комментарий от Vlad-76

Если перенесу этот код в функцию, то как вызывать эту функцию?

В функцию нужно перенести

status, err = pcall(ESL_read_write) -- вызовем функцию в обертке для обработки exeption в случае аварии ESL соединения
    if not status then
        print("Функция ESL_read_write() вылетела с ошибкой:", err)
    end
    print ("Connection to freeswitch ESL failed. Slipping and try again.")
И вызывать эту функцию в цикле.

rezedent12 ☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.