Статьи

Осколок в Граале


Важно создавать приложения, которые имеют систему горизонтального масштабирования с самого начала.
Sharding является хорошим решением для этого случая, поскольку он позволяет разработчикам определять несколько «сегментов» базы данных для масштабирования данных между несколькими схемами базы данных и серверами. Разработчики могут легко изменить базу данных, на которую нацелены классы доменов, с помощью простого вызова службы. Мы рассмотрим, как достигается разделение в Grails (на основе учетной записи пользователя) с помощью относительно
нового плагина .

Чтобы установить плагин, запустите скрипт install-plugin «sharding» (необходимо использовать Grails 1.2.1 или выше):


Grails установить плагин Sharding

Grails использует соглашение о конфигурации, но в некоторых случаях разработчикам плагинов для шардинга необходимо предоставить конфигурацию. Плагин использует базу данных индекса, которая содержит минимум две таблицы. Плагин создает одну таблицу, содержащую запись каждого шарда вместе с емкостью и полем использования. Эта таблица запрашивается при создании нового объекта (пользователя). Объект присваивается сегменту с самым низким коэффициентом использования. Другая таблица предоставляется приложением. Эта таблица отображает объект на осколок, и приложение запросит эту таблицу, чтобы получить правильный осколок для использования плагином.

Здесь описание DSL, используемого для настройки плагина sharding. Конфигурация содержится в grails-app / conf / Shards.groovy:

index— Этот раздел содержит данные о базе данных индекса

  • domainClass — имя класса домена, используемого для представления объекта, используемого для сегментирования данных.
  • shardNameFieldName — имя поля класса домена, в котором будет храниться фрагмент, которому назначен объект.
  • name — имя, используемое для представления SessionFactory и DataSource для этой базы данных индекса
  • user — имя пользователя, используемое для подключения к базе данных индекса
  • пароль — пароль, используемый для подключения к базе данных индекса
  • driverClass — имя класса драйвера, используемого для связи с базой данных индекса.
  • jdbcUrl — строка соединения jdbc, используемая для соединения с базой данных индекса
  • диалект — спящий диалект, используемый для общения с базой данных индекса

шарды — в этом разделе есть набор записей для каждого
шарда в системе
shard_XX — контейнер для шарда, XX должен быть двузначным числом, которое увеличивается и является уникальным

  • name — Строка, используемая для представления этого шарда.
  • user — имя пользователя, используемое для подключения к базе данных шарда
  • пароль — пароль, используемый для подключения к базе данных шарда
  • driverClass — имя класса драйвера, используемого для связи с базой данных шарда.
  • jdbcUrl — строка соединения jdbc, используемая для соединения с базой данных шарда
  • емкость — количество объектов, которые могут быть назначены базе данных осколков, прежде чем она будет считаться заполненной. Обратите внимание, что это используется только для расчета процента заполнения. Это не останавливает назначение объектов, если процент превышает 100%.

Это не останавливает назначение объектов, если процент превышает 100%.

Давайте посмотрим на пример Джеффа Рика (создателя плагина)
: во-

первых, создайте два класса домена:


grails create-domain-class UserIndex

grails create-domain-class Комментарий

Тогда некоторые свойства для доменных объектов:

grails-app / domain / UserIndex.groovy

class UserIndex {

String userName

String shard

static constraints = {
}
}

Grails-приложение / домен / Comment.groovy

class Comment {

Integer userIndexId

String comment

static constraints = {
}
}

UserIndex связывает имя пользователя с осколком, чтобы приложение могло определить, кому принадлежит какой осколок, и затем переключиться на этот осколок. Объект «Комментарий» представляет собой образец некоторых данных, которые вы можете хранить у пользователя.  

Далее мы создадим определение баз данных, содержащих данные приложения. Это базовый пример с двумя базами данных Shard и базой данных Index. Возможно, вам придется изменить параметры подключения в конфигурационном файле ниже, который определяет базы данных:

grails-app/conf/Shards.groovy:
index = {
domainClass('UserIndex')
shardNameFieldName('shard')

name('shardINDEX')
user('root')
password('PASSWORD')
driverClass('com.mysql.jdbc.Driver')
jdbcUrl('jdbc:mysql://localhost:3306/shardINDEX')
dialect(org.hibernate.dialect.MySQL5InnoDBDialect)
}
shards = {
shard_01 {
name('shard1001')
user('root')
password('PASSWORD')
driverClass('com.mysql.jdbc.Driver')
capacity(1000)
jdbcUrl('jdbc:mysql://localhost:3306/shard1001')
}
shard_02 {
name('shard1002')
user('root')
password('PASSWORD')
driverClass('com.mysql.jdbc.Driver')
capacity(1000)
jdbcUrl('jdbc:mysql://localhost:3306/shard1002')
}
}

Создайте три схемы в базе данных (пример использует MySQL):


создать схему shardINDEX;

создать схему shard1001;

создать схему shard1002;

Добавьте зависимость для вашего драйвера БД (в данном случае MySQL) в раздел зависимостей grails-app / conf / BuildConfig.groovy.

dependencies {
runtime 'mysql:mysql-connector-java:5.1.5'
}

И создайте шаблоны по умолчанию для UserIndex и Comment.


grails generate-all UserIndex

grails generate-all Комментарий

Добавьте эти замыкания в grails-app / controllers / UserIndexController.groovy:

def login = {
session.userName = params.userName
render "User logged in."
}

def logout = {
session.userName = null
render "User logged out"
}

И измените grails-app.controllers / controllers / CommentController.groovy (который будет использовать имя вошедшего в систему пользователя и переключиться на соответствующий шард):

import UserIndex

class CommentController {
def shardService

static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

def index = {
redirect(action: "list", params: params)
}

def list = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

params.max = Math.min(params.max ? params.int('max') : 10, 100)
[commentInstanceList: Comment.list(params), commentInstanceTotal: Comment.count()]
}

def create = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

def commentInstance = new Comment()
commentInstance.properties = params
return [commentInstance: commentInstance]
}

def save = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

def commentInstance = new Comment(params)
if (commentInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'comment.label', default: 'Comment'), commentInstance.id])}"
redirect(action: "show", id: commentInstance.id)
}
else {
render(view: "create", model: [commentInstance: commentInstance])
}
}

def show = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

def commentInstance = Comment.get(params.id)
if (!commentInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'comment.label', default: 'Comment'), params.id])}"
redirect(action: "list")
}
else {
[commentInstance: commentInstance]
}
}

def edit = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

def commentInstance = Comment.get(params.id)
if (!commentInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'comment.label', default: 'Comment'), params.id])}"
redirect(action: "list")
}
else {
return [commentInstance: commentInstance]
}
}

def update = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

def commentInstance = Comment.get(params.id)
if (commentInstance) {
if (params.version) {
def version = params.version.toLong()
if (commentInstance.version > version) {

commentInstance.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: 'comment.label', default: 'Comment')] as Object[], "Another user has updated this Comment while you were editing")
render(view: "edit", model: [commentInstance: commentInstance])
return
}
}
commentInstance.properties = params
if (!commentInstance.hasErrors() && commentInstance.save(flush: true)) {
flash.message = "${message(code: 'default.updated.message', args: [message(code: 'comment.label', default: 'Comment'), commentInstance.id])}"
redirect(action: "show", id: commentInstance.id)
}
else {
render(view: "edit", model: [commentInstance: commentInstance])
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'comment.label', default: 'Comment'), params.id])}"
redirect(action: "list")
}
}

def delete = {
def user = UserIndex.findByUserName(session.userName)
shardService.changeByObject(user)

def commentInstance = Comment.get(params.id)
if (commentInstance) {
try {
commentInstance.delete(flush: true)
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'comment.label', default: 'Comment'), params.id])}"
redirect(action: "list")
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'comment.label', default: 'Comment'), params.id])}"
redirect(action: "show", id: params.id)
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'comment.label', default: 'Comment'), params.id])}"
redirect(action: "list")
}
}
}

Теперь вы запускаете приложение! (grails run-app)

Наконец, вы перейдете на http: // localhost: 8080 / ShardingExample / userIndex и создадите несколько пользователей, войдите в систему, создадите комментарии, а затем сделаете то же самое для второго пользователя, чтобы увидеть, что сегменты разделены ,

Пример приложения, описанного выше, можно получить здесь .