Yuriy
client_prototype.quit = function(client)
request.multibulk(client, 'QUIT')
client.network.socket:shutdown()
return true
end
client_prototype.shutdown = function(client)
request.multibulk(client, 'SHUTDOWN')
client.network.socket:shutdown()
end
Snusmumriken
Ну понятное дело что сокет закрывается в обоих функциях!
В обоих случаях редиска завершает работу КОНКРЕТНО С НАШИМ КЛИЕНТОМ.
Snusmumriken
Просто в первом случае, закрывается конкретно НАШ клиент, а во втором - ВСЕ, плюс сама редиска.
Yuriy
Аха
Точно. mutlibulk пересылает второй аргумент в redis
Yuriy
и только потом закрывает сокет
Snusmumriken
Карочи, если дергать shutdown, редиска будет падать после каждого запроса : )
А так - на проде, обычно, не используют даже quit, потому что соединение закешировано.
Snusmumriken
То есть, условно, сколько потоков у нгинкса, или сколько потоков в принципе общается с редиской - столько и постоянных клиентов.
Yuriy
Ну nginx Все несколкьо проще в этом плане
Там есть механизм котороый позволят переиспользовать соединения - там же nginx руководствуется своими cosocket
А по поводу обычного скрипта lua - хз. Не вникал - будет ли сохраняться коннект после завершения скрипта
Yuriy
В этом как раз была проблема работы lua В asterisk
Он запускает скрипт при каждом звонке - и создает новый сокет на соединение с БД
Yuriy
соответсвенно если не настроить таймауты то в один пректрасный ммоент при большой нагрузке на севрер все упирается в количество файловых дескрипторов или разрешенное количество портов (на сервере БД)
Snusmumriken
Ну в целом, луасокет, помнится, завершает соединения (порты, арендованные у ОС) при сборке мусора. Хотя может и не сообщать об этом другой стороне, и им придется отваливать клиента пол таймауту.
ShadoWalkeR
Наконец добрался потыкать снова redis-lua. В общем вызвать quit нельзя. Я по файлу полазил - вроде нигде неявно не вешается выполнение прототипа.
Snusmumriken
В смысле?
Сколько там вообще видно редисовых методов, а сколько генерируются в процессе исполнения? ))
Snusmumriken
Можно написать redis:['попа'](redis), и оно попробует послать редиске команду "попа", с quit - то же самое.
ShadoWalkeR
А не - наврал. Надо не
redis.quit
а
redis.quit()
писать 😃
ShadoWalkeR
Совсем с руби обленился))))
Snusmumriken
Двоеточие забыл.
Это метод.
Snusmumriken
client_prototype.quit = function(client)
request.multibulk(client, 'QUIT')
client.network.socket:shutdown()
return true
end
=>
redis:quit()
ShadoWalkeR
Я каждый раз забываю - в чем разница
ShadoWalkeR
Между вызовом через . и через :
Snusmumriken
Ну запомнил бы уж, за год-то другой : )
ShadoWalkeR
Да я не так часто на lua пишу на самом деле
Snusmumriken
Двоеточие - вызов функции как метода объекта.
Первым аргументом посылается таблица-объект (или класс).
class = {x = 10}
function class.method(client)
print(client.x)
end
object = setmetatable({}, {__index = class})
object.x = 20
print(class:method()) --> 10
print(object:method()) --> 20
print(class.method()) --> error: attempt to index global 'client' (a nil value)
print(object.method()) --> error: attempt to index global 'client' (a nil value)
Snusmumriken
И в твоём случае, если дёргать redis.quit(), оно вызовется с ошибками:
1. request.multibulk(client, 'QUIT') - client не найден, т.е. отсутствует
2. client.network.socket:shutdown() - опять client не найден, то есть не передан в функцию quit в принципе.
Snusmumriken
А вот redis.quit(redis) - уже можно, ибо мы руками сделали работу двоеточия. Указали конкретного клиента, который передаётся в функцию.
Snusmumriken
Карочи.
tbl = {}
function tbl.foo(t) return t.foo end
-- идентично
function tbl:foo() return self.foo end
print( tbl.foo(tbl) == tbl:foo() ) --> true
ShadoWalkeR
Как то так https://paste.fedoraproject.org/paste/MtDAlRf3JV6gcduNy-QWdA
Snusmumriken
Ну правильная ошибка.
rds:hmset(s_call_id, t_call_data)
Это тоже, как ни странно, метод. Угадаешь почему?
ShadoWalkeR
Пробовал уже - он проругался на такую запись
Snusmumriken
А ещё, у тебя плохой hmset: он хочет сразу много вещей:
1. hash - имя хеша;
2. key - ключ хеша;
3. value - значение;
[4. key2 - ещё один ключ;]
[5. value2 - ещё одно значение...]
Минимум - три аргумента в функции. Дока для кого? : )
ShadoWalkeR
Ттут забавней вещь - я в глобале объявляю
REDIS = require('redis_wrapper').init("192.168.1.1", nil, nil, nil)
и получаю чуть ниже attempt to index field 'REDIS' (a nil value)
ShadoWalkeR
Так точней)
ShadoWalkeR
Не - он в качестве списка k-v ждет таблицу
Snusmumriken
Подозреваю что это плохой вызов.
а) redis_wrapper наверняка уже содержит соединение (или сейчас будет содержать) и его типа не нужно инициализировать.
б) не факт что ты правильные методы дёргаешь.
Откуда ты брал redis_wrapper?
ShadoWalkeR
Проверял уже
ShadoWalkeR
Это код выше который кидал
Snusmumriken
Да, страшный вопрос. Ты - сишник, в основном? : )
ShadoWalkeR
Все подряд)
ShadoWalkeR
В основном C-like синтаксис приходилось писать. Сейчас больше руби)
Snusmumriken
Просто манера кошмарных конфигов типа этих:
local M = {
M_RDS_H = nil; -- Main redis host
M_RDS_P = nil; -- Main redis port
L_RDS_H = nil; -- Local redis host
L_RDS_P = nil; -- Local redis port
};
Они и так описаны внутри модуля "redis" (или redis_wrapper) и не светятся наружу почти никак (разве что через сам редис). Не имеет смысла их так кошмарно обзывать.
Вполне сойдёт:
M = {
host = 'localhost',
port = 6379
}
function M.init() .. bla-bla end
ShadoWalkeR
Я не только для себя пишу - вдруг ктото полезет в эту либо дописывать - это для этих людей
Snusmumriken
И так понятно : )
Они же лезут в редис-враппер? Интересно, что ещё это могло бы значить?
ShadoWalkeR
Плюс мне 2 редиса надо использовать - локальный кэш и удаленное хранилище все равно выльется в
remote_host = '192.168.1.1',
remote_port = 6379
local_host = 'localhost',
local_port = 6379
ShadoWalkeR
Ну дописать функционал - я в него логику работы с редисами пихаю, чтобы не засорять кодом основную программу. А культурно вызывать REDIS.storeCallInfo()
Snusmumriken
Мм. Открываешь два клиента.
local rcache = redis.connect(self.cache.host, self.cache.port)
local rstorage = redis.connect(self.storage.host, self.storage.port)
Правда, тогда init придётся тоже вызывать как метод, и делать уже пару табличек в конфиге : )
ShadoWalkeR
А зачем?) Идея - открыл коннект, отправил/забрал данные, закрыл коннект, обработал результат и вернул обратно если нужно чтото вернуть
Snusmumriken
Тебе в любом случае понадобится конфиг.
Например, ту же самую задачу, я бы сделал примерно так:
https://pastebin.com/kS8eLGdC
Snusmumriken
Просто потому что так резко растёт читаемость, хотя уровень "сишности" и "корпоративности" явно упал. И используется пара "продвинутых" штук, типа setmetatable.
Snusmumriken
И много методов, что безусловно добавляет непонятности ))
ShadoWalkeR
Вай нот?
ShadoWalkeR
call_info_root - это для каждго звонка генерируется свое, поэтому нгет смысла в конфиге держать
Snusmumriken
Это ID для каждого звонка генерируется своё.
Snusmumriken
А хеш, содержащий ключи-значения "call_id":"data" - имеет одно имя, как раз call_info_root.
Snusmumriken
Ты потом будешь получать инфу обо всех звонках, дёргая
redis:hgetall(self.call_info_root)
ShadoWalkeR
Нет. hmset так не работает. Что в редисе что в redis-lua.
Snusmumriken
Мм.
ShadoWalkeR
hset кладет за раз ключ поле значение записи
ShadoWalkeR
У hmset 'поле значение' может быть несколько
ShadoWalkeR
Кроме того у меня ключ - id звонка, а поля и значения данные об этом звонке. Поэтому 1 общий callid не получится в принципе
Snusmumriken
ShadoWalkeR
hgetall возвращает все поля/значения одного ключа)
Snusmumriken
Хе, что в твоём понимании "ключ", а что - "значение"? : )
Редис слегка отличается от луа.
Тут есть тип hash.
Он имеет свой "адрес", в качестве которого тут выступает "call_info".
Он сам содержит ключи-значения в виде айди звонка и данных, которые ты к этому звонку прицепил.
А твой способ redis:hmset(id, data) - не прокнет, потому что ты не указал сам хеш, к которому аттачить ключ-значение.
hgetall - возвращает все ключи-значения хеша.
ShadoWalkeR
HMSET myhash field1 "Hello" field2 "World"
myhash - ключ в редисе. Он же у меня Call-id
Snusmumriken
Ты собрался по хешу Call-id хранить кучу ключей-значений?
ShadoWalkeR
redis:hmset переданную таблицу разворачивает поля и их значения этого хэша
ShadoWalkeR
Это кэш - там они потом переливаются скриптом в мускуль
Snusmumriken
Хе, кажись и правда разворачивает.
А снус по старинке генерит такую лабуду:
local req = {}
for k, v in pairs(t) do
req[#res + 1] = k
req[#res + 1] = v
end
redis:hmset(hash, unpack(req))
ShadoWalkeR
Я просто пока звонок генерирует события обновляю хэш и потом по окончании проставляю специальную метку. Скрипт ищет хэши с этой меткой и залив в базу дропает хэши
ShadoWalkeR
У модуля убогая документация на самом деле)
Snusmumriken
Тогда ладно, ты правильно понял суть хешей, просто несколько странно объясняешься.
У модуля в принципе отсутствует документация, потому что вся документация вот тут:
https://redis.io/commands/
Так что нам реально важно только уметь приконнектиться.
ShadoWalkeR
Ну не помешало бы хотя бы кратко описать что ждет та или иная команда - чтобы не опытным путем это изучать)
Snusmumriken
А вот что мне остро не нравится в redis-lua - это плохой subscribe.
Он блокирует. Вообще всё. И не умеет иначе.
ShadoWalkeR
Snusmumriken
У снуса есть его собственная версия луа-редиса.
https://pastebin.com/VjnhPJUA
Она работает значительно быстрее, но пока в виде тестового варианта.
И да, тут всякие паблишеры-сабскрайберы ничего не блокируют, открывая отдельное соединение для таких махинаций.
ShadoWalkeR
Мне не особо нужны пока что, поэтому и redis-lua сойдет)
Snusmumriken
И тут есть всякие УЖАСНЫЕ штуки типа:
local hash = redis:newHash(call_id)
hash.key1 = 'value1'
hash.key2 = 'value2'
И оно сразу отправляется в базу.
А потом, ты такой:
local hash = redis:newHash(call_id)
print(hash.key1) --> 'value1'
print(hash.key1) --> 'value2'
Р-р-раз! И выдралось из базы. Сразу всё относящееся к хешу call_id. И закешировалось.
Но это - только для супер-профессионалов, которые не будут делать миллиард таких операций. Или запайплайнят такое месиво.
ShadoWalkeR
А надо будет - думаю форкну и перепишу)
ShadoWalkeR
Или воспользуюся pairs на hgetall 😃
Snusmumriken
Или воспользуюся pairs на hgetall 😃
Ммм.
Нет, это как раз можно. Тут специальные итераторы, одна операция чтобы выгрузить весь хеш и проитерировать. Если хеш ОЧЕНЬ большой - есть scan, которое то же самое, но по частям.
А ещё тут есть универсальный метод Inspect, который визуализирует тебе чьё угодно содержимое и сообщит тип.
Snusmumriken