Лучший способ предоставить клиент Redis для расширяемых приложений Go

Я использую redigo в приложении, и мне интересно, как мои службы должны взаимодействовать с Redis.

Вот что говорит Википедия о потокобезопасности:

Потокобезопасность — это концепция компьютерного программирования, применимая к многопоточному коду. Поточно-ориентированный код только манипулирует общими структурами данных таким образом, чтобы все потоки вели себя правильно и выполняли свои проектные спецификации без непреднамеренного взаимодействия.

Я интерпретирую это так, что если к структуре данных необходимо получить доступ несколькими клиентами (сотнями, тысячами, если не миллионами в современном мире микросервисов), потокобезопасность — это способ, с помощью которого мы обеспечиваем правильное сохранение состояния в системе независимо от того, какой клиент получает доступ к данным и когда. Это означает определение приоритета доступа (какой клиент получил доступ первым), обеспечение блокировки при изменении (только один клиент может писать одновременно) и одновременное продвижение параллелизма (многие клиенты могут читать данные, если нет изменений).

Из того, что я понял, клиент redigo может использоваться несколькими «горутинами» (или потоками) одновременно. Это наводит меня на мысль, что одноэлементной реализации, с которой я знаком в Java, должно быть достаточно.

Я вижу примеры, например, здесь и здесь, где соединения Redis (pools) просто создаются методом main и передаются различным функциям redigo. Это не кажется самым надежным способом добиться цели, хотя они, похоже, следуют одноэлементному шаблону. (Понятно, что второй пост на самом деле просто быстрый и грязный API.)

Я бы сделал это так:

  1. В функции main вызовите init, которая возвращает redigo pool.

  2. Создайте функции-обработчики (контроллеры), которые принимают pool в качестве параметра (своего рода "грязная" инъекция зависимостей).

Это (я думаю) гарантирует, что когда-либо будет создан только один pool.

В качестве альтернативы, есть ли причина, по которой я не могу создавать pool (клиент) каждый раз, когда хочу получить доступ к хранилищу данных? Если клиент убит после завершения транзакции, есть ли проблема с запуском нового pool каждый раз, когда обработчик получает запрос?


person franklin    schedule 12.11.2018    source источник
comment
Это действительно неэффективно. Весь смысл пула соединений заключается в повторном использовании соединений и избегании накладных расходов, связанных с установлением нового соединения. en.wikipedia.org/wiki/Connection_pool   -  person Adrian    schedule 12.11.2018
comment
Прохладно. Таким образом, одноэлементный доступ - это путь.   -  person franklin    schedule 12.11.2018
comment
документация по пулу от Redigo описывает, как использовать пул в веб-приложении. Вы должны создать единый пул и поделиться им.   -  person Cerise Limón    schedule 12.11.2018
comment
См. также документацию Redigo по разрешенному параллелизму. Redigo не имеет типа клиента, как упоминалось в вопросе. В Redigo есть потокобезопасный пул и частично небезопасное соединение.   -  person Cerise Limón    schedule 12.11.2018


Ответы (1)


Правильный ответ уже есть в комментариях, но все же хочу добавить свои 5 копеек.

В вашем вопросе смешиваются две концепции: параллелизм и пул ресурсов.

Параллельный код обеспечивает безопасную работу нескольких потоков выполнения внутри одного приложения с использованием общей памяти. В нашем примере у нас есть несколько http-запросов от наших пользователей и код обработчиков для этих запросов, который выполняется одновременно. Соединение с БД является частью этого потока и требуется для выполнения запроса.

Параллельный код должен иметь возможность использовать соединение с БД и при необходимости открывать/закрывать соединение.

Хотя параллелизм не имеет ничего общего с правилами, определяющими, сколько соединений должно быть открыто в любой момент времени и как эти соединения должны быть повторно использованы/разделены:

  • Кто-то может создать соединение с БД для каждого запроса, и этот код является параллельным.
  • Кто-то может использовать некоторые общие соединения с БД, и этот код является параллельным, пока несколько запросов не мешают друг другу. Что обычно достигается с помощью блокировок памяти.

Пул соединений, с другой стороны, представляет собой шаблон, обеспечивающий эффективный способ обработки соединений с БД. Почему важно управлять временем жизни соединений:

  1. Соединение дорого открывать
  2. Соединение дорого поддерживать без использования в качестве базы данных, может поддерживать только ограниченное количество открытых соединений.

С помощью пула соединений вы можете контролировать:

  1. Сколько подключений всегда открыто - прогреть медленный ресурс
  2. Максимальное количество открытых подключений — предотвращение открытия слишком большого количества подключений
  3. Время ожидания соединения — балансировка между повторным использованием соединений и высвобождением неиспользуемых ресурсов

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

Обычно приложение имеет один пул соединений, совместно используемый всеми запросами.

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

person Dmitry Harnitski    schedule 12.11.2018