...

Пример использования NoSQL-СУБД Redis в микросервисе поиска такси

Redis примеры простыми словами для аналитика, Redis Kafka пример, NoSQL key-value примеры для аналитика, Python Kafka Redis consumer serverless free, архитектура информационных систем простыми словами для начинающих примеры курсы обучение, NoSQl Redis примеры для аналитика как и где использовать, Redis Python Google Colab example, интеграция и архитектура ИС примеры курсы обучение, основы архитектуры и интеграции информационных систем для бизнес-аналитика, микросервисная архитектура информационных систем основы введение, NoSQL для аналитика краткий ликбез, обучение системных и бизнес-аналитиков, курсы системного и бизнес-анализа, Школа прикладного бизнес-анализа Учебный Центр Коммерсант

Прототип сервиса записи оперативных данных о местонахождении автомобилей такси по районам Москвы и поиска машин по запросу клиента. Пишем часть системы с микросервисной архитектурой на примере пары Python-скриптов для работы с резидентной key-value NoSQL-СУБД Redis.

Постановка задачи и проектирование системы

Обычно, рассказывая про микросервисную архитектуру на своем курсе «Основы архитектуры и интеграции информационных систем», я привожу в пример интернет-банк как информационную систему, за фасадом клиентском GUI которой находятся части нескольких микросервисов – автономных приложений, взаимодействующих между собой. В частности, сервис кредитов работает независимо от сервиса депозитов и каждый из них имеют свои модели и хранилище данных. Впрочем, банковский домен далеко не единственная наглядная демонстрация микросервисного подхода. Сегодня в качестве другого примера рассмотрим систему заказа такси, которая реализует следующие требования:

  • FT-1. Найти все машины такси в радиусе до 100 километров от текущей геолокации клиента;
  • FT-2. Рассчитать время и длительность пути от геолокации такси до местонахождения клиента с учетом особенностей дорожной инфраструктуры, такой как наличие автодороги, ее тип (одностороннее или двустороннее движение), возможность делать развороты и т.д.
  • NFT-1. Максимальное время поиска машины такси не должно превышать 5 секунд.

Требование FT-2, расчет времени и длительности пути, представляет собой классическую задачу на графах. Поэтому решать ее лучше всего именно специализированными для этого инструментами, такими как графовые алгоритмы и графовые базы данных, например, Neo4j. А вот с хранением оперативной информации о геолокации машин отлично справится резидентная key-value NoSQL-СУБД Redis (REmote DIctionary Server), которая работает очень быстро за счет хранения данных в оперативной памяти сервера. Подробнее о возможностях и принципах работы Redis я рассказывала  здесь.

Таким образом, рассматриваемая система заказа такси реализует шаблон проектирования микросервисной архитектуры под названием Database per Service, когда каждый микросервис имеет собственное независимое хранилище данных, оптимизированное под его задачи. Сделаем аллокацию, т.е. распределение требований по компонентам решения в табличном виде:

Требование к системе Микросервис СУБД
FT-1. Найти все машины такси в радиусе до 100 километров от текущей геолокации клиента Сервис записи и чтения данных о геолокации машин такси и клиента Redis
NFT-1. Максимальное время поиска машины такси не должно превышать 5 секунд
FT-2. Рассчитать время и длительность пути от геолокации такси до местонахождения клиента с учетом дорожной инфраструктуры Сервис поиска путей Neo4j

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

Прежде всего, следует ответить на вопрос, почему в качестве хранилища данных для этого сервиса я предложила Redis. Ответ: потому, что умею с ним работать (шутка)). На самом деле, исходя из формулировки задачи, информация о текущей геолокации машин такси и клиента не будет объемной. Кроме того, она не будет иметь сложную структуру данных, которую имеет смысл раскладывать по множеству таблиц реляционной базы. Однако, эта информация будет специфического типа и представляет собой геолокацию, т.е. координаты. Поэтому нужна база, которая поддерживает работу с геоданными и делает это очень быстро, чтобы реализовать нефункциональное требование к скорости поиска машины (NFT-1).

Всем этим требованиям удовлетворяет резидентная key-value база Redis. За счет хранения данных в оперативной памяти она работает очень быстро. Дополнительную скорость дает ее примитивный, но очень эффективный принцип хранения данных по типу ключ/значение. Причем, в отличие от многих других key-value баз данных, в Redis значения могут быть различных типов. Для работы с геоданными используется тип Geospatial, который поддерживает команды добавления координат, а также поиска местоположений в пределах заданного географического радиуса, площади или координат. Это как раз реализует наши требования к микросервису записи и чтения данных о геолокации машин такси и клиента. Чтобы проверить эту гипотезу, я написала небольшое подтверждение концепции в виде пары Python-скриптов, что и рассмотрим далее.

Основы архитектуры и интеграции информационных систем

Код курса
OAIS
Ближайшая дата курса
5 ноября, 2024
Продолжительность
16 ак.часов
Стоимость обучения
36 000 руб.

Проверка гипотезы: Python-скрипты для работы с Redis

Воспользуемся облачным сервером Redis, развернув собственный экземпляр на платформе Upstash. Я уже упоминала про эту замечательную платформу в статье о Kafka. Сперва создадим свою базу данных. На бесплатном тарифе Upstash позволяет создать только 1 экземпляр. Ключом для проектируемого сервиса записи и чтения данных о геолокации машин такси и клиента в Redis будут названия районов Москвы, а значениями – геокоординаты машин такси и идентификаторы самих автомобилей. Поскольку машины такси почти всегда находятся в движении, в рамках одного района в разные моменты времени будут находиться разные автомобили: одни приезжают, а другие уезжают. Чтобы отразить этот динамичный характер данных, зададим ключам и их значениям время жизни (TTL, Time To Live) – период, по истечении которого они автоматически удалятся из базы.

Как обычно, я пишу и запускаю Python-код в блокнотах Google Colab – интерактивной легковесной облачной среде разработки. В первой ячейке установим необходимые библиотеки для аботы с Redis и импортируем нужные модули:

####################################ячейка в Google Colab №1 - установка и импорт библиотек###########################################
#установка библиотеки 
!pip install redis

#импорт модулей 
from datetime import datetime
import random
import redis
import time
from time import sleep

Учетные данные для подключения к своей базе сразу в виде кода на нужном языке программирования можно просто скопировать из интерфейса Upstash.

Redis serverless Upstash Python
Учетные данные для подключения к Redis

Эти учетные данные я вставила в следующую ячейку в Google Colab:

####################################ячейка в Google Colab №2 - запись данных###########################################
#подключение к redis
r = redis.Redis(
  host= '.........здесь название инстанса.upstash.io',
  port= '34670',
  password= 'здесь пароль пользователя Redis в upstash'
)
#создаем словарь районов и машин
districts = ['Arbat', 'Basmannyi', 'Zamoskvoreche', 'Khamovniki', 'Yakimanka', 'Tverskoy','Taganskyi', 'Sokol', 'Khovrino', 'Aeroport', 'Bibirevo', 
            'Lianozovo', 'Sviblovo', 'Rostokino', 'Ostankino','Perovo', 'Izmailovo', 'Sokolniki', 'Golyanovo','Strogino','Mitino', 'Kurkino',
            'Vnukovo', 'Ramenki', 'Butovo','Vikhino', 'Lefortovo', 'Capotnya', 'Brateevo','Donskoy','Orekhovo-Borisovo']
taxis = ['Honda', 'Ford', 'Renault', 'Mazda', 'Haval', 'Jeep', 'Toyota', 'Lexus', 'Opel', 'Niva', 'Volga', 'Nissan', 'Lada','Tesla']

#бесконечный цикл случайного выбора района, машины и записи этих данных в Redis
while True:
  now=datetime.now()
  
  #случайный выбор
  district = random.choice(districts)
  cars = random.randint(0, 10)
  if cars>0 :
    print('На ', now.strftime("%m/%d/%Y %H:%M:%S"), ' в районе ', district, ' находится ', cars, ' машин такси')
    for i in range(cars):
      taxi = random.choice(taxis)+'_'+str(random.randint(100, 999))
      coordinate_x = 55+(random.randint(1, 10)/10)+(random.randint(1, 100)/100)
      coordinate_y = 37+(random.randint(1, 10)/10)+(random.randint(1, 100)/100)
      #запись данных в Redis
      r.geoadd(district, [coordinate_x, coordinate_y, taxi], nx=False)
      r.expire(district, 600)
      print(i+1,'-ое такси ', taxi, ' с координатами ', coordinate_x, coordinate_y)
  
  time.sleep(3)

В этом скрипте случайным образом каждые 3 секунды генерируется от 0 до 10 автомобилей такси в случайном районе города и записывается в Redis вместе с координатами каждой машины. Время жизни каждого ключа ограничено 10-ю минутами, что задано в параметрах метода expire().

Python Redis example Google Colab
Генерация данных о такси и их запись в Redis

В GUI платформы Upstash можно посмотреть, как данные записываются в Redis.

Redis GUI Upstash
Просмотр данных в Redis

Чтобы считать данные из Redis программным образом, имитируя поиск машин по запросу клиента, я написала второй Python-скрипт. Разумеется, сперва следует установить библиотеки и импортировать нужные модули. Для этого в 1-ой ячейке Google Colab надо написать следующие команды:

####################################ячейка в Google Colab №1 - установка и импорт библиотек###########################################
#установка библиотеки 
!pip install redis

#импорт модулей 
from datetime import datetime
import random
import redis

Затем идет ячейка с кодом запроса к Redis. В этом примере я реализую POC (Prof-Of-Concept), формы прототипа, которая представляет собой модель для подтверждения дизайна системы без учета ее внешнего вида, материалов, бизнес-процессов и потоков работ. Поэтому никакого GUI я делать не стала, а решила сымитировать запрос клиента прямо в коде, генерируя случайным образом район и геокоординаты. Далее выполняется поиск машины рядом с клиентом. Если машина рядом не найдена, радиус поиска расширяется.

####################################ячейка в Google Colab №2 - запись данных###########################################
#подключение к redis
r = redis.Redis(
  host= '.........здесь название инстанса.upstash.io',
  port= '34670',
  password= 'здесь пароль пользователя Redis в upstash'
)

# Создаем словарь с названиями районов (для случайного выбора, имитации запроса от пользователя)
districts = ['Arbat', 'Basmannyi', 'Zamoskvoreche', 'Khamovniki', 'Yakimanka', 'Tverskoy','Taganskyi', 'Sokol', 'Khovrino', 'Aeroport', 'Bibirevo', 
            'Lianozovo', 'Sviblovo', 'Rostokino', 'Ostankino','Perovo', 'Izmailovo', 'Sokolniki', 'Golyanovo','Strogino','Mitino', 'Kurkino',
            'Vnukovo', 'Ramenki', 'Butovo','Vikhino', 'Lefortovo', 'Capotnya', 'Brateevo','Donskoy','Orekhovo-Borisovo']

#случайный выбор района и координат клиента - имитация клиентского запроса        
point = random.choice(districts)
coordinate_x = 55+(random.randint(1, 10)/10)+(random.randint(1, 100)/100)
coordinate_y = 37+(random.randint(1, 10)/10)+(random.randint(1, 100)/100)
dist=0
dist_unit='km'

now=datetime.now()

print('На ', now.strftime("%m/%d/%Y %H:%M:%S"),' запрос из района ', point, ', примерные координаты клиента ', coordinate_x, coordinate_y, '. Ищем машины в радиусе ', dist, ' км')

#ищем варианты такси в Redis для клиента со сгенерированным случайным образом районом и координатами 
result = []
for i in range(10): #максимум 10 попыток расширения радиуса поиска, начинаем с 0-вого радиуса
    dist = dist+random.randint(i, 10)
    result = r.georadius(point, coordinate_x, coordinate_y, dist, unit=dist_unit, withdist=True, withcoord=True)
    print('Радиус поиска: ', dist, dist_unit)
    if result: #если найден хоть 1 результат
        result_str = []
        for item in result:
            if isinstance(item, bytes):
                result_str.append(item.decode('utf-8'))
            else:
                result_str.append(item)
        result_str = str(result).replace("b",' ').replace('\'', '\"')
        print('Найдены варианты такси: ', result_str)
        break
if not result: #если результатов не найдено за 10 попыток расширения радиуса поиска
    print('Машин в радиусе ', dist, ' км не найдено')
Python Redis example Google Colab
Поиск данных о такси в Redis

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

Надеюсь, этот небольшой пример позволил понять некоторые принципы проектирования сложных систем и поближе познакомиться с NoSQL-СУБД Redis, чтобы использовать ее возможности для реализации требований к ПО.

Разработка ТЗ на информационную систему по ГОСТ и SRS

Код курса
TTIS
Ближайшая дата курса
2 декабря, 2024
Продолжительность
16 ак.часов
Стоимость обучения
36 000 руб.

Подробнее про все эти и другие аспекты архитектуры и интеграции информационных систем, я рассказываю на своих курсах Школы прикладного бизнес-анализа в нашем лицензированном учебном центре обучения и повышения квалификации системных и бизнес-аналитиков в Москве:

 

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.

Источники

  1. https://redis.io/docs/
  2. https://github.com/AnnaVichugova/DataBases/blob/main/Redis_write_data_taxi
  3. https://github.com/AnnaVichugova/DataBases/blob/main/read_data_taxi_from_Redis

Добавить комментарий