Задача написать приложение, работающее с redis, умеющее как генерировать сообщения, так и обрабатывать. Параллельно может быть запущено сколько угодно приложений.
Обмен любой информацией между приложениями осуществлять только через redis.
Все запущенные копии приложения кроме генератора, являются обработчиками сообщений и в любой момент готовы получить сообщение из redis.
Все сообщения должны быть обработаны, причём только один раз, только одним из обработчиков.
Генератором должно быть только одно из запущенных приложений. Т.е. каждое приложение может стать генератором. Но в любой момент времени может работать только один генератор.
Если текущий генератор завершить принудительно (обработчик завершения запрещен, например, выключили из розетки), то одно из приложений должно заменить завершённое (упавшее) и стать генератором. Для определения кто генератор нельзя использовать средства ОС, считается что все приложения запущенны на разных серверах и общаются только через redis. Сообщения генерируются раз в 500 мс.
Для генерации сообщения использовать любую функцию с рандомным текстовым ответом.
Приложение, получая сообщение, с вероятностью 5% определяет, что сообщение содержит ошибку.
Если сообщение содержит ошибку, то сообщение следует поместить в redis для дальнейшего изучения.
Если приложение запустить с параметром 'getErrors', то оно заберет из redis все сообщения с ошибкой, выведет их на экран и завершится, при этом из базы сообщения удаляются.
Проверить что приложение может обработать 1000000 сообщений (интервал 500мс можно убрать для теста)
go run main.go # запуск приложения (генератор/читатель)
go run main.go --getErrors # получение ошибок
На каждом запуске приложение пытается выставить лок на взятие позиции мастера (SETNX masterKey
): если получилось, то играем в мастера, если нет то в читателя.
Мастер запускает 2 интервальных канала в которых генерирет сообщения и продлевает свой лок на expireTime / 2.
Читатель так же запускает 2 интервальных канала в которых читает сообщения и пытается получить свой лок на позицию мастера с итнтервалом expireTime.
Для подключения к редису используется пул соеденений, который позволят обрабатывает ситуация потери соеденений и его переподключения, при удержении 1 соединения к редису и его разрыве переподключения не происходит.
Если произошла задержка сети, или соедение к базе отвалилось, то возможна ситуация когда текущий мастер потерял свою позицию (произошел EXPIRE + какой-то читатель успел занять позицию) и тогда в системе будет > 1 мастера. Для этого случая переде отправкой сообщения происходит проверка того, что именно текущий мастер дрежит лок на позицию.
Замечания:
1 сообщение в 500мс * кол-во хостов слушателей, то всё, приехали. Очередь в редисе будет расти.
высокую нагрузку не выдержит такая схема.
что является большим оверхедом.
в идеоматичном go это то бы выглядило так: func newRedisPool(addr string) *redis.Pool {
то будет увеличинна нагрузка на redis в cpu_N раз.