Работа с образом Redis в Vscale
Введение
Redis - это размещаемое в оперативной памяти сервера хранилище, используемое в качестве базы данных, кэша или посредника сообщений.
С помощью Vscale можно развернуть образ для начала работы с Redis менее чем за 1 минуту.
В этой статье мы проведём небольшой теоретический обзор хранилища и его возможностей, а также создадим приложение, использующее Redis в качестве основной базы данных.
Структура образа
Сервер с Redis работает под управлением ОС Ubuntu 16.04.
В состав образа входит Redis версии 4.0.1, сконфигурированный для автоматического запуска.
Создание сервера
Выберите образ с Redis в панели управления:
На финальном этапе подготовки образа сервера, рекомендуем сразу создать или добавить SSH-ключ:
После завершения установки виртуального сервера можно подключиться к нему по SSH и начать работу с Redis.
Знакомство с Redis
Redis (расшифровывается как Remote Dictionary Server) - высокопроизводительное хранилище типа “ключ-значение”. Отличительной особенностью Redis является полное размещение хранилища в оперативной памяти сервера. Такое решение позволяет достичь очень высоких показателей скорости чтения и записи, однако имеет ряд недостатков: зависимость максимального размера базы данных от размера оперативной памяти, риск потери данных. Для решения этих проблем есть механизм регулярной репликации хранилища на диск, либо логирование каждой команды. Для преодоления ограничения по размеру базы данных есть возможность разделения данных на несколько управляемых объектов.
Кроме того, распространенный вариант проектирования системы хранения информации с Redis в качестве базы данных предполагает его использование только для небольшой области данных, подверженных частому обновлению и помещение крупных массивов информации в базу данных, размещенную на диске. Это похоже на кэширование, но на деле является более продвинутым использованием Redis, поскольку данные в нем обновляются одновременно с дисковой базой данных.
По доступным командам и настройкам Redis написано большое количество русскоязычных материалов, поэтому не погружаясь в детали, рассмотрим ключевые особенности хранилища, знания которых достаточно для начала работы с ним. Сразу можно отметить, что Redis достаточно прост в освоении и имеет удобный консольный режим, через который мы и будем работать в этой части руководства:
$ redis-cli
Запишем строковое значение и затем считаем его:
> set mykey myvalue
OK
> get mykey
“myvalue”
Отдельно стоит упомянуть атомарную команду INCR, позволяющей проводить операцию увеличения выбранного значения. Это означает, что при одновременном запросе увеличить значение от разных клиентов по одному ключу никогда не наступит состояние гонки. В частности, никогда не произойдет такого, что клиент 1 считал “10”, и клиент 2 считал “10” в одно и тоже время, оба увеличили до 11, и значение стало равно 11. При использование команды INCR финальное значение всегда будет равно 12.
>set a 10
OK
>incr a
(integer) 11
Поскольку ключи являются центральным понятием при изучении Redis, рассмотрим правила работы с ними.
Ключи в Redis
В качестве ключей в Redis можно использовать не только текст, но и любые бинарные последовательности от строки типа “foo” до содержимого JPEG-файла. Пустая строка тоже является валидным ключом.
Существует несколько правил по работе с ключами:
- рекомендуемый формат ключа выражается в виде “object-type:id”, например, “user:1000”. Для обозначения полей состоящих из нескольких слов часто используют точки или дефис, как в “comment:1234:reply.to” или “comment:1234:reply-to”;
- длина ключа должна быть сбалансированной для поставленной задачи: слишком длинные приведут к увеличению времени поиска и количества занимаемой памяти, слишком короткие гораздо менее понятны - для сравнения, “user:1000:followers” против “u1000flw”;
- максимальный размер ключа 512 МБ.
Структуры данных
Все данные хранятся в виде словаря, в котором ключи связаны со своими значениями, при этом последние не ограничиваются только строками.
Redis поддерживает 7 типов структур данных:
- бинарно-безопасные строки;
- списки;
- множества;
- упорядоченные множества;
- хеш-таблицы;
- битовые массивы;
- HyperLogLogs.
Мы не будем подробно рассматривать каждый из типов: в сети есть множество материалов на эту тему.
Примечание: подробная статья на тему выбора подходящей структуры данных для решения различных задач представлена на официальном сайте Redis.
Для демонстрации возможностей Redis создадим простое веб-приложение с поиском по базе данных по принципу автозаполнения.
Разработка приложения с использованием Redis в качестве базы данных в связке с Flask и Nginx
Чтобы испытать Redis в качестве базы данных, создадим простое веб-приложение, в котором будет реализовано две функции: добавление рецептов в базу данных и поиск среди имеющихся рецептов. Для создания веб-приложения используем фреймворк Flask и сервер Nginx. С клиентской стороны запросы к сервису будем осуществлять через jQuery.
Настройка среды и установка компонентов
Для работы с новым проектом создадим пользователя с sudo-правами:
$ useradd -m redis-user
$ passwd redis-user
$ usermod -aG sudo redis-user
$ su redis-user
Создадим новую директорию для приложения и перейдем в неё:
$ mkdir /home/redis-user/redis-app
$ cd /home/redis-user/redis-app
Установим все необходимые зависимости, такие как систему управления пакетами pip (Python 3 версии для поддержки кириллицы), виртуальную среду для Python и Nginx:
$ sudo apt-get update
$ sudo apt-get install python3-pip nginx python3-venv
Создадим новую виртуальную среду appenv и активируем её:
$ python3 -m venv appenv
$ source appenv/bin/activate
Внутри среди установим следующие пакеты:
$ pip install gunicorn flask redis Faker
Gunicorn будет использоваться в качестве HTTP сервера на Python. Для создания веб-приложения и управления хранилищем Redis установим flask и redis. Faker необходим для генерации случайных слов в приложении.
Создание Flask приложения
Создадим исполняемый файл, в котором реализуем логику нашего приложения:
$ nano main.py
И запишем следующий код:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, request
from flask import render_template
from flask import jsonify
from faker import Faker
import redis
fake = Faker('ru_RU')
r = redis.StrictRedis(host='localhost', decode_responses=True, port=6379, db=0)
page_limit = 50
app = Flask(__name__)
def initialize():
executed = r.get('executed')
if executed is None:
r.set('receipts:size', 0)
for i in range(1000):
r.hmset('receipt:' + str(i),
{'name': fake.word(), 'ingredients': fake.sentence(), 'receipt': fake.paragraph()})
r.incr('receipts:size')
r.set('executed', 'yes')
@app.route('/')
def main():
initialize()
return render_template('main.html')
@app.route('/search', methods=['POST'])
def search():
list = []
for i in range(int(r.get('receipts:size'))):
if any(request.form['text'].lower() in s.lower() for s in r.hvals('receipt:' + str(i))):
list.append(r.hgetall('receipt:' + str(i)))
if len(list) == page_limit:
break
return jsonify(list)
@app.route('/add_receipt', methods=['POST'])
def add_receipt():
new_receipt = {'name': request.form['name'], 'ingredients': request.form['ingredients'],
'receipt': request.form['receipt']}
r.hmset('receipt:' + str(r.get('receipts:size')), new_receipt)
r.incr('receipts:size')
return render_template('main.html')
if __name__ == '__main__':
app.run()
Для сохранения файла и выхода из редактора нажмите сочетание ‘Ctrl’ + ‘X’.
Рассмотрим подробнее, что происходит в данном исходном коде. Сначала мы импортировали класс Flask, объект которого и будет WSGI-приложением. Для создания соответствия между URL-запросом и функциями используются декораторы route() с указанием HTTP-методов.
Подключение к Redis происходит после вызова redis.StrictRedis() и дальнейшее взаимодействие с хранилищем происходит через полученный от указанной функции объект. Выполнение команд с Redis в Python происходит в формате [клиент Redis].[команда](arg1, arg2,..,argn). Например, для сохранения строковой переменной нужно выполнить:
r.set('my_string', ‘Hello World’)
Список команд доступен на официальном сайте Redis. Для использования их в python-среде необходимо лишь изменить их под указанный выше формат команд, например, “SET A 10” станет “r.set(‘a’, 10)”.
В функции initialize() происходит заполнение базы данных случайными данными при первом обращении к серверу. Функция search() вызывается при post-запросе по адресу [ip сервера]/search и выполняет поиск полученной строки по хранилищу и возвращает первые 50 найденных совпадений. В функции add_receipt() обрабатывается post-запрос на добавление нового рецепта в хранилище.
Создание шаблона HTML-страницы для приложения
Для создания интерфейса приложения создадим HTML-страничку с jQuery-скриптом, обрабатывающим запросы к серверу приложения. Для начала создадим папку templates, в которой будет html-файл:
$ mkdir templates
$ nano templates/main.html
В main.html запишем следующий код:
<!DOCTYPE html>
<html>
<title>Receipt book</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<style>
body,h1,h2,h3,h4,h5 {font-family: "Poppins", sans-serif}
body {font-size:16px;margin-left:300px;margin-right: 300px;margin-top:50px;}
button { background-color: #af221e;border: none;color: white;padding: 15px 32px;cursor: pointer;
text-align: center;text-decoration: none;display: inline-block;font-size: 16px;
}
input[type=text], select {width: 100%; padding: 12px 20px;margin: 8px 0;display: inline-block;
border: 1px solid #ccc;border-radius: 4px;box-sizing: border-box;
}
textarea {width:100%; padding: 12px 20px; margin: 8px 0; display:inline-block;
border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;}
input[type=submit] {width: 100%;background-color: #af221e;color: white;padding: 14px 20px;
margin: 8px 0;border: none;border-radius: 4px;cursor: pointer;
}
div {border-radius: 5px;background-color: #f2f2f2;padding: 20px;
}
.card{background-color:#FFFFFF; margin-bottom: 20px;}
</style>
<body>
<h1>Receipt book</h1>
<br/>
<button id="btn-add" class="button">Добавить новый рецепт</button>
<button id="btn-search" class="button">Поиск рецептов</button>
<br/>
<br/>
<div id="receipts_holder">
<h2>Список рецептов</h2>
<br/>
<input type="text" id="search" name="search" placeholder="Рецепт содержащий слово...">
<div id="search_holder">
</div>
</div>
<div id="form_holder">
<form action="/add_receipt" method="post">
<label for="name">Наименование</label>
<input required type="text" id="name" name="name" placeholder="Название рецепта..">
<br/>
<label for="ingredients">Состав</label>
<textarea name="ingredients" id="ingredients" rows="3" placeholder="Мука 500г Кабачок 300г"></textarea>
<label for="receipt">Описание</label>
<textarea name="receipt" id="receipt" rows="6"></textarea>
<input id="btn-save" type="submit" value="Сохранить">
</form>
</div>
</body>
<script>
$("#form_holder").hide();
$("#receipts_holder").show();
$('#btn-add').click(function(){
$("#form_holder").show();
$("#receipts_holder").hide();
});
$('#btn-search').click(function(){
$("#form_holder").hide();
$("#receipts_holder").show();
});
$('#search').on('input', function() {
$.post('/search', {
text: this.value
}).done(function(list) {
$("#search_holder").empty();
$.each( list, function( index, value ){
$("#search_holder").append("<div class=\"card\"> " +
"<h3><label>" + value['name']+"</label></h3>"+
"<h4>Состав</h4>"+
"<label>"+value['ingredients']+"</label>"+
"<h4>Рецепт</h4>"+
"<label>"+value['receipt']+"</label>"+
" </div>");
});
}).fail(function() {
});
});
</script>
</html>
Сохраним изменения в файл, нажав сочетание клавиш ‘Ctrl’ + ‘X’.
На этом этапе приложение уже готово, теперь необходимо настроить сервер Gunicorn и Nginx для работы с ним.
Создание WSGI точки входа
Вернёмся в корень нашего приложения и создадим файл, который будет содержать вызываемый объект app и являться входной точкой нашего сервиса. В нем будет содержаться информация, которая укажет Gunicorn как взаимодействовать с приложением.
$ nano wsgi.py
И укажем имя импортируемого объекта приложения:
from main import app
if __name__ == "__main__":
app.run()
Сохраним и закроем файл после завершения работы с ним.
Тестовый запуск приложения с помощью Gunicorn
Для проверки работоспособности Gunicorn достаточно передать ему имя точки входа в формате [названия модуля]:[имя вызываемого приложения], в примере wsgi:app.
Для запуска на публично доступном интерфейсе укажем адрес и порт при вызове команды:
$ gunicorn --bind 0.0.0.0:8000 wsgi:app
Пройдите по IP-адресу скалета с указанием порта:
http://scalet_IP:8000
Должна загрузиться веб-страница нашего приложения:
Теперь попробуем протестировать работу приложения с хранилищем Redis. Начните вводить в поле для ввода русскоязычные символы:
Протестируем возможность добавления нового рецепта. Для этого нажмём кнопку “Добавить новый рецепт”, заполним все поля и нажмем кнопку “Сохранить”:
Проверим, что рецепт успешно добавлен и доступен для поиска:
Если приложение работает без ошибок, остановите процесс сочетанием клавиш ‘Ctrl’+’C’. Теперь выйдем из виртуальной среды и приступим к настройке сервиса приложения:
$ deactivate
Создание сервиса на основе приложения
Для автоматического запуска со стартом системы нашего приложения создадим systemd файл, в котором пропишем настройки запуска:
$ sudo nano /etc/systemd/system/redisapp.service
И запишем следующий текст:
[Unit]
Description=Gunicorn instance to serve redis demo app
After=network.target
[Service]
User=redis-user
Group=www-data
WorkingDirectory=/home/redis-user/redis-app
Environment="PATH=/home/redis-user/redis-app/appenv/bin"
ExecStart=/home/redis-user/redis-app/appenv/bin/gunicorn --workers 3 --bind unix:redisapp.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target
Файл сервиса состоит из 3 частей:
- в первой части указываются метаданные и зависимости;
- во второй записывается пользователь и группа процесса, в нашем случае www-data, которая позволит без затруднений общаться Nginx и сервису Gunicorn. Затем указывается рабочая директория приложения и среда выполнения приложения через инициализацию переменной окружения PATH. Также указывается полный путь к исполняемому файлу Gunicorn, который установлен внутри нашей виртуальной среды;
- третья часть определяет к чему привязывается запуск процесса при старте системы. Мы хотим, чтобы сервис запускался при обычной работе многопользовательской системы.
Запустим созданный сервис и активируем автозапуск при старте системы:
$ sudo systemctl start redisapp.service
$ sudo systemctl enable redisapp.service
Настройка Nginx
Откроем стандартный конфигурационный файл Nginx:
$ sudo nano /etc/nginx/sites-available/default
Внутри заменим полностью блок location / {} следующим кодом:
location / {
include proxy_params;
proxy_pass http://unix:/home/redis-user/redis-app/redisapp.sock;
}
Для проверки конфигурации Nginx выполним команду:
$ sudo nginx -t
Если всё прошло без ошибок, то осталось лишь перезагрузить сервис Nginx:
$ sudo service nginx restart
Перейдем по IP адресу сервера для проверки работоспособности приложения, как мы делали выше, только без указания порта, поскольку теперь настроен прокси через Nginx.
Заключение
В этом руководстве мы рассмотрели Redis в качестве основной базы данных приложения.
Существует множество вариантов построения системы хранения данных с использованием Redis и большое количество административных встраиваемых инструментов, позволяющих добиться надежности и производительности разрабатываемой системы с использованием Redis.
Для непосредственного изучения команд в Redis можно воспользоваться сервисом Try Redis, который представляет собой совмещенный учебник и интерактивную оболочку. Кроме того, существует большое количество клиентов Redis для разных языков - рекомендуемые помечены звездочкой.
Redis определенно заслуживает внимания за свою невероятную производительность, но при его использовании необходимо помнить об ограничениях в виде размера оперативной памяти и о вопросах надежности данных.
Войдите в службу, чтобы оставить комментарий.
Комментарии
0 комментариев