Моя работа с хранилищами данных NoSQL за последние пару лет дала мне некоторое представление о направлении, в котором неизбежно пойдут приложения, поскольку NoSQL становится доминирующим методом хранения и извлечения данных — по крайней мере, для веб-приложений и облачных приложений (корпоративные приложения будут в конечном итоге достигнуты , но это займет намного больше времени). За эти годы я научился доверять своим инстинктам, и мои инстинкты кричат мне, что этот подход имеет ценность и должен быть кем-то исследован, даже если у меня лично нет времени на написание этой системы.
Асинхронный доступ
Я думаю, что весь подход основан на неблокирующем асинхронном доступе к данным. Вообще говоря, если мы хотим использовать асинхронный обмен сообщениями в наших приложениях, мы должны были бы заручиться помощью брокера сообщений, единственной целью которого является асинхронная маршрутизация сообщений. Мой любимый, как вы, без сомнения, знаете, это RabbitMQ . Но пытаясь расширить некоторые ключевые функциональные возможности RabbitMQ и понять, насколько это сложно на практике, я предполагаю, что можно добиться большего прогресса быстрее, используя гораздо более легкую асинхронную библиотеку, не имеющую соответствия конкретной протокол, как делает RabbitMQ для AMQP.
На мой взгляд, Node.js изменил форму разработки веб-приложений в лучшую сторону. Хотя его истинные асинхронные приложения сложнее создать (поэтому они пока еще не так популярны среди рядовых пользователей), они явно более масштабируемы и работают лучше в облачной среде, где пользователю может потребоваться множество относительно небольших экземпляров виртуальных машин. которые могут координировать друг с другом. Обмен сообщениями является логичным выбором для достижения этой цели.
Мы могли бы, конечно, заручиться услугами нашего удобного брокера сообщений, добавить некоторых потребителей, написать несколько производителей в нашем веб-приложении и назвать его хорошим.
Но я не доволен хорошим. Когда-либо.
Данные король
Если мы сократим наше приложение до его самой чистой формы, единственное, что нас волнует, это данные. Я мог бы выбрать что-то вроде моего любимого хранилища данных NoSQL, Riak, потому что оно может масштабировать мои данные, и я могу выполнять распределенное Map / Reduce для своих данных. Но данные являются основной причиной заявки в первую очередь. Все, что я делаю внутри приложения, делается в контексте данных. Если я использую брокер сообщений, сообщения являются данными. Если я размещу веб-форму, я принимаю данные. Если я создаю отчет, я сообщаю о данных.
Ничто не имеет значения, кроме данных.
Но брокеру сообщений нет дела до данных. Это просто проводник. И хранилище сообщений не заботится о проводнике. Но это действительно не должно быть так.
Так как я думаю в коде, может быть, пример для того, чтобы проиллюстрировать мою точку зрения.
Представьте, что мне нужно конвертировать и масштабировать загруженное изображение в миниатюру. Для этого я пишу простую программу, которая использует ImageMagick для масштабирования, обрезки и преобразования изображения в JPEG. Я также создаю веб-форму, которая позволяет пользователю загружать свое изображение. В этом мире асинхронного хранилища данных логика моего преобразователя изображений должна иметь возможность прослушивать события INSERT или UPDATE в хранилище данных и преобразовывать входящие данные, автоматически сохраняя миниатюру загруженного изображения.
Псевдокод конвертера изображений:
def db = asyncdb.connect("tcp://localhost:5555") def img = request.get_upload_data("image") def metadata = [ content_type: "image/jpg" ] db.push("imagebucket", img.name, img.data, metadata, { event, data -> if(event.type == SUCCESS) { db.push("profilebucket", "$user.profile", [ avatar: data.key + ".thumbnail" ]) } })
Что это делает:
- Подключается к узлу хранилища данных.
- Подписывается на события хранилища данных, передавая вызываемое Closure, которое возвращает true | false и передает Closure для вызова, когда фильтр Closure возвращает true.
- При вызове это автоматически обновляет эскиз и сохраняет версию исходного изображения под специальной клавишей.
В моем контроллере веб-приложения я вставляю загруженное изображение с помощью клиента асинхронного доступа к хранилищу данных.
FFF
def db = asyncdb.connect("tcp://localhost:5555", [ name: "image.converter", description: "Image thumbnailing listener" ])
Что это делает:
- Подключается к узлу хранилища данных.
- Создает новую запись в хранилище данных, включая метаданные, которые будут достаточны для запуска прослушивателя миниатюрного пользователя.
- Асинхронно «выталкивает» данные изображения в хранилище данных и регистрирует обработчик событий, так что, когда слушатель успешно делает миниатюру изображения, вызывается обратный вызов клиента.
- Когда миниатюра была успешно создана, профиль пользователя обновляется путем добавления в нее новых данных, которые ссылаются на недавно преобразованную миниатюру.
Обратите внимание, что все делается неблокирующим и асинхронным способом. Целостность данных поддерживается за счет того, что профиль не обновляется, пока не будет создано уменьшенное изображение. Эта система также не имеет статуса. Каждый узел знает друг о друге, поэтому балансировщик нагрузки может отправить первую часть запроса на один сервер, а вторую половину запроса — на другой сервер, но это не имеет значения, поскольку одна операция, зависящая от другой, ожидает конкретное событие, которое будет выпущено.
Сочетание обмена сообщениями и обработки данных
Эта форма доступа к данным имеет большой смысл для меня. Хотя он может смешивать части приложения, которые традиционно не объединяются (асинхронный обмен сообщениями и хранение данных), это может привести к очень кратким и простым для понимания приложениям.
Хранилище данных должно предоставлять веб-интерфейс, чтобы разработчики могли опросить внутреннюю часть системы и посмотреть, ожидают ли события доставки. Клиентские методы в идеале должны также принимать произвольные метаданные, которые веб-интерфейс мог бы показать разработчику, чтобы они могли легко видеть, что на самом деле делает прослушиватель, о котором сообщают. Что-то вроде этого:
def db = asyncdb.connect("tcp://localhost:5555", [ name: "image.converter", description: "Image thumbnailing listener" ])
Ни одно хранилище данных, о котором я знаю, в настоящее время не поддерживает подобные вещи. Может быть, я единственный, кто действительно хотел бы иметь такие вещи. Но по мере того, как доступ к данным переходит в более асинхронный мир NoSQL, наши шаблоны разработки приложений изменятся. Я могу гарантировать вам это. Единственный вопрос: «На что это изменится?»