Павло Щербуха

Logo

Персональна освітня сорінка

Розробка на Node.js, VUE.js, Python, IBM Integration Bus (App Connect Ent) , ORACLE PL/SQL
2 September 2022

Python - flask інтеграція з Redis

by Pavlo Shcherbukha

Зміст

  1. Про що цей блог
  2. Розгортання Redis локально в Docker контейнері
  3. Короткий довідник по командах Redis які можна виконати з допомогою redis-клієнта
  4. Використання Redis сумісно з Python-flask application
  5. Запуск та налаштування контейнерів
  6. Запуск контейнерів docker compose

1. Про що цей блог

Це продовження записок початківця про python flask. В попереніх серіях було:

В цьому блозі:

В попередній серії Python - flask запуск в контейнері від RadHat UBI8 було показано, що в Linux контейнері під Gunicorn додаток запускається в кілька потоків. Тепер виникла інша проблема, як управляти всіма цими потоками. Ну, уявимо собі, що на сервіс приходить якась команда і всі потоки повинні виконати її. Що це може бути за команда:

Для організації всього цього було прийняте рішення використати Publish/Subscribe схему на базі in-memory DB Redis. Якщо можна так сказати, то для Cloud Native applicatoin Redis виконує функцію пам’яті для public та shared змінних. Тому в цьому блозі:

Демонстраційний приклад знаходиться в репозиторії за лінком Flask app Rest API та взаємодія з Redis

2. Розгортання Redis локально в Docker контейнері

Перш, ніж інтегрувати Python application з Redis потрібно навчитися впевнено розгортати сам redis. А, якщо зважити на те, що потрібно запустити як мінімум 2 сервіси: python service і БД redis - то краще запускати все це в контейнерах за допомогою docker-compolse, якщо запускати на вланому laptop.

Для прикладів вибрана redis-5 з офіційного образу redis на Docker hub з тегом версії redis:5.0.14-alpine . Для старту достатньо підготувати docker-composer.yaml


version: '3.8'
services:
  redisserver:
    image: redis:5.0.14-alpine
    restart: always
    ports:
      - '6379:6379'
    command: redis-server --save 20 1 --loglevel warning --requirepass 22

В цьому фрагмені Redis стартує з авторизацією по паролю [–requirepass 22]. Для запуску, потрібно перейти в каталог з файлом docker-composer.yaml та і запустити старт командою

docker compose up

На екарні отримаєте щось схоже на таке.

pic-01

Якщо подивитися на контейнери, що стартонули, то можна побачити redis:

В іншій cmd сесії запускаємо:

  docker ps

На екарні отримаєте щось схоже на таке.

pic-02

Тепер спробуємо підключитися до контейнера з допомогою redis-cli. Тут ми нічого не інсталюємо на робочу станцію, а заходиво в середину контейнера, шляхом запука redis-cli:

    docker exec  -u root -it 1c78b4161df0 redis-cli -a 22

де 1c78b4161df0 - ідентифікатор контейнера.

Тобто, тут ми заходимо по ssh як root в контейнер з id 1c78b4161df, запускаючи redis-cli з авторизацією, вказуючи пароль до БД з ключем -a

На екрані отримаєте щось схоже на таке.

pic-03

Ну і для перевірки з’єднання задаємо комнаду “PING”, а у відповідь отримуємо PONG. Таким чином в мінімальному вигляді контейнре запущено.

3. Короткий довідник по командах Redis які можна виконати з допомогою redis-клієнта

Особисто я Redis-cli не користуюся. Але в цьому розділі хочу паказати деякі можливості redis, які в майбутньому буду викистовувати через спеціальні мовні бібіліотеки

3.1 [SET, GET, INCR] Встановлення - читання ключа та його значення

Найпершою і найпростішою операцією є запам’ятати пари: ключ-знаення. Цікавим варіаном є можливість зберігання ключа заданий період часу (секунд, мілісекунд).

set myKey myvaluse
get myKey

pic-04

Встановити ключ, що “живе” протягом визначеного часу. На прикладі, що показано, видно, як встановлено значення ключа на 4 секунди. А потім, через якийсь час - ключ “пропадає”


#
# set myKey value [expiration EX seconds|PX milliseconds] [NX|XX]

 set myKey XCODE EX 4

pic-05

Тут показано простий варіант установки лічильника і його постійного збільшення

127.0.0.1:6379> set xcntr 0
OK
127.0.0.1:6379> get xcntr
"0"
127.0.0.1:6379> incr xcntr
(integer) 1
127.0.0.1:6379> incr xcntr
(integer) 2
127.0.0.1:6379> incr xcntr
(integer) 3
127.0.0.1:6379> incr xcntr
(integer) 4
127.0.0.1:6379> incr xcntr
(integer) 5
127.0.0.1:6379> get xcntr

pic-06

[KEYS] Отримати список всіх ключів в БД

Отримати всі ключі в БД можа командою

  # keys pattern

На приклад:

127.0.0.1:6379> keys *
1) "jsondata"
2) "myhash"
3) "xcntr"
4) "counter"
5) "shkey1"

3.3. [HSET, HGET, HDEL, HEXISTS] Встановлення - читання hash-ключа

Ця команда під одним ключем може зберігати кілька значень FIELD: VALUE. Обмеження - прочитатит можна тілько по одному полю. Збережемо під ключем sh-book опис книжки: назву, автора, рік публікації. В принципі, це аналогічно збереженню JSON

{

“sh-book”: {“title”: “”, “author”: “”, published: 2009} }


   hset sh-book  title  "The Museum of Abandoned Secrets"
   hset sh-book  author "Oksana Zabuzhko"
   hset sh-book  published "2009" 

або ж можна ввести групову команду

hset sh-book  title  "Second Attempt" author "Oksana Zabuzhko" published "2005" 

  hexists sh-book title

  hdel sh-book title

По суті ми зберегли та к би мовити плоский json

3.4. [DEL, EXISTS] Видалити ключ, перевірити навність ключа

  del shkey
127.0.0.1:6379> keys *
 1) "myhash"
 2) "xcntr"
 3) "counter"
 4) "shhkey2"
 5) "gey"
 6) "shkey1"
 7) "jsondata"
 8) "get"
 9) "sh-book"
10) "shhkey"
127.0.0.1:6379> del shhkey
(integer) 1
127.0.0.1:6379> keys *
1) "myhash"
2) "xcntr"
3) "counter"
4) "shhkey2"
5) "gey"
6) "shkey1"
7) "jsondata"
8) "get"
9) "sh-book"
127.0.0.1:6379>

Так можна перевірити наявнісмть ключа

127.0.0.1:6379> keys *
1) "myhash"
2) "xcntr"
3) "counter"
4) "shhkey2"
5) "gey"
6) "shkey1"
7) "jsondata"
8) "get"
9) "sh-book"
127.0.0.1:6379> exists sh-book
(integer) 1
127.0.0.1:6379>

3.5. Як зберегти JSON в Redis

Допустимо у нас є JSON:

{ “title”: “Second Attempt” , “author”: “Oksana Zabuzhko”, “published”: 2005 }

то зберегти його можна звичайною командою set Зберегти його можна звичайною командою set

set book1 '{ "title": "Second Attempt" ,  "author": "Oksana Zabuzhko", "published": 2005 }'

Ось результат роботи комнади:

127.0.0.1:6379> set book1 '{ "title": "Second Attempt" ,  "author": "Oksana Zabuzhko", "published": 2005 }'
OK
127.0.0.1:6379> get book1
"{ \"title\": \"Second Attempt\" ,  \"author\": \"Oksana Zabuzhko\", \"published\": 2005 }"
127.0.0.1:6379>

3.6. Корисні посилання по redis

4. Використання Redis сумісно з python flask application

В цьому розділі описана проста демка, для демонстрації того, як використати redis сумісно з python flask applicatoin. Програмний код демки можна знайти за лінком Flask app Rest API та взаємодія з Redis. Опис бібліотеки Python для роботи з redis знаходиться за лінком: redis 4.3.4 або ж прямо на github redis-py. Цікаві і потрібні, як на мій погляд, приклади наедені в ndexing / querying JSON documents

В демці розглядається, як запустити redis та Python web service, що зверта ться до redis на совєму ноутбуці в контейнерах, викорситовуючи docker-composer. Це, так би мовити, створення та запуск середовища розробки на своєму laptop.

Опис програмного коду демки

Redis використовується в модулі views.py. Підключення до redis описано у наведеному фрагменті:

log("Підключення до Redis")

irds_host = os.getenv('RDS_HOST');
irds_port = os.getenv('RDS_PORT');
irds_psw = os.getenv('RDS_PSW');

log('Підключеня до redis: ' + 'host=' + irds_host  )
log('Підключеня до redis: ' + 'Порт=' + str(irds_port)  )
log('Підключеня до redis: ' +  'Пароль: ' + irds_psw )

red = redis.StrictRedis(irds_host, irds_port, charset="utf-8", password=irds_psw, decode_responses=True)
log(" Trying PING")
log("1=======================")
rping=red.ping()
log( str(rping) )
if rping:
    log("redis Connected")
    #sub = red.pubsub()    
    #sub.subscribe( ichannel )    
else:
    log("redis NOT CONNECTED!!!")    

log("2=======================")

Підключення до redis

Як видно, парамери підключення параметризуються в ENV-variables сервісу: RDS_HOST, RDS_PORT, RDS_PSW, які вичитуються при старті сервісу і повинні бути задані при старті контейнера (якщо сервіс стартує в контейнері). Підклюення (чи то створення об’єкту підклченя) описано в команді: red = redis.StrictRedis ….. А ось перевырити можливість взаємодії сервіса та redis можна з допопогою команди PING:

rping=red.ping()

Тобто, якщо rping=True, значить до redis сервіс підключився.

pic-07

Проста функція лічилька кількості викликів АПІ

Використаемо просут команду бібліотеки: incrby(name, amount=1). Також, при старті сервісу ключ потрібно створити, а потім бажано ще і прочитати. Для цього використаємо комнади:

Цю задачу вирішують два простих фагменти кода.

Тут при старті створюється ключ в БД

i_apicntr_key="APICALLS"
if rping:
    log("redis Connected")

    log("set predefined key by 0 value: " +  i_apicntr_key ) 
    red.set(i_apicntr_key, 0)
    log("Check the valuse of key: " + i_apicntr_key )
    log( "Read value: " + str( red.get(i_apicntr_key) ) )

А тут є функція, що виконє інкремент значення ключа

#==================================================
# Функція підраховування викликів API
#
#=================================================
def apicallscntr():
    l_label="apicallscntr"
    log("Старт", l_label)
    return red.incrby( i_apicntr_key, 1)

Ну і далі виклик цієї функції вмонтовуємо в оброники викликів API

Розробка API методів, що дозволяюь створити ключ, прочитати занчення ключа, отримати список всіх ключів в Redis

Для читання ключів використовується функціія: keys(pattern=’*’, **kwargs).

У відповіді повинні отримати масив всіх існуючих ключів, типу такого:

{
  "list": ["shhkey2", "shkey1", "APICALLS", "book1", "myhash", "jsondata", "get", "counter", "xcntr", "sh-book", "gey"]
}

Ну а для створення ключа використовуєму функцію: - set(name, value, ex=None, px=None, nx=False, xx=False, keepttl=False, get=False, exat=None, pxat=None)

Тіло POST запиту:

{
"keyname": "test1",
"keyvalue": "test1 value"
}

Для демонстрації взаємодії з redis цього досить. Тепер проблема, як це запустити у себе на машині.

Запуск та налаштування контейнерів

Коли програмний код уже існує, потрібно налаштувати узгожений запуск двох контейнерів:

Для цього використано docker-composer. Хоч, выдверто кажучи, я його ы не дуже люблю. Мені більш звичним є такі платформи як Openshift та kubernetes. Але для цього випадку прийшлося використати docker-composer. Основним конфігураційним файлом, що зв’язує різні контейнери між собою є yaml-файл: docker-compose.yaml:

version: '3.8'
services:
  redisserver:
    image: redis:5.0.14-alpine
    restart: always
    ports:
      - '6379:6379'
    command: redis-server --save 20 1 --loglevel warning --requirepass 22
  smplapp-srvc-redis:
    build:
      context: ./
      dockerfile: Dockerfile_local 
    ports:
      - "8081:8080" 
    links:
      - "redisserver:redis"     
    environment:
      GUNICORN_CMD_ARGS: "--workers=1 --bind=0.0.0.0:8080 --access-logfile=-"
      APP_MODULE: "hello_app.webapp"
      RDS_HOST: "redisserver"
      RDS_PORT: 6379
      RDS_PSW: "22"

На pic-08 показано з яких частин цей файл склажається:

pic-08

  1. Описуэ запуск контейнера redis
  2. Описує запуск контейнера python application

На pic-09 показані основні конфігураційні елементи, пов’язані з конфігуруванням запуску redis

pic-09

  1. Кореневий елемен окрем взятого сервісу, що параметризуємо. В подальшому на нього можна буде посилатися.

  2. Вказує на образ, з якого буде створюватися та стартувати контейнер.

  3. Вказує на визначення портів в форматі [локальний порт вашого laptop]:[порт контейнера].

  4. Вказує на команду, що перекриває комнаду Dockerfile CMD при старті контейнера. В даному випадку ми додаємо додаткові параметри при старті сервісу БД.

На pic-10 показані основні конфігураційні елементи, пов’язані з конфігуруванням запуску контейнера додатку на Python

pic-10

  1. Кореневий елемен окрем взятого сервісу, що параметризуємо. В подальшому на нього можна буде посилатися.

  2. Вказує на Dockerfile, з якого буде створюватися образ та стартувати контейнер. При цьому, context вказує на path до Dockerfile . - значить поточний каталог, а dockerfile вказує на найменування Dockerfile в якому описані правила побудови образу.

  3. Вказує на визначення портів в форматі [локальний порт вашого laptop]:[порт контейнера].

  4. Вказує на блок links, що так би мовити зв’язує два контейнера між собою так, що вони можуть між собою взаємодіяти по tcp/ip. В даному випадку вказано, що сервіс smplapp-srvc-redis може звертатися до сервісу з назвою хоста “redisserver”. :redis це альтернативна назва.

  5. Вказує на блок environments змінних які потрібно задати для старту сервісу. Цифрою 6 показано, як сконфігуровано підключення до redis.

Запуск контейнерів docker compose

Запукаються командою

docker compose up

Але мені потрібно, щоб при запуску сервіс smplapp-srvc-redis перебудовував образ, бо я в процесі розробки можу міняти програмний код. Для цього використовується команда: –build smplapp-srvc-redis, що запускає перебудову образа

docker compose up --build smplapp-srvc-redis

Зупинити сервыс можна по CTRL + C або просто:


docker compose stop
tags: