C#
November 26, 2020

RESTful MySql

C# for ZennoPoster

0. What the fuck your RESTful hm.. ?

Представим себе любое веб приложение. Как взаимодействовать с ним все мы знаем и умеем? Конечно же с помощью основных HTTP методов GET/POST.

Наверняка многие уже знают или слышали про методы такие как PUT/DELETE/PATCH и так далее.

Принцип не отличается от известных всеми GET/POST, где

GET - получить "контент"
POST - отправить "контент"
DELETE - удал.....
PUT - обнов....

Если подсознательно дополнил определения, поздравляю, ты уже разобрались в принципе RESTful. Это тот же HTTP, где позиция метода запроса определяет поведение приложения.

Иначе говоря RESTful представление позволяет нам к примеру обновлять записи путем простого указания PUT в запросе, удалять посредством DELETE.

1. Introduce

Общие черты основного принципа RESTful вспомнили. На примере точно вспомните, наверняка взаимодействовали уже ранее с данным представлением.


Все то что многие так долго искали, а кто-то просто хотел, но не мог сформулировать.

Пришло время научится делать из любой mysql базы полноценную RESTFful прослойку и да она будет работать по HTTP протоколу. Даже не придётся в очередной раз гуглить, лезть на форума при составлении SQL запроса, а вспомните сколько ужаса связанного с экранированием спец символов и тому подобное.


2. Getting Started

Решение является кроссплатформенным, заведётся как на OS Windows, так и на unix-подобных системах с установленной node (requires node >= 7.6.0).


1) Убедимся что у нас стоит пакет node (https://nodejs.org/en/download/), и работает npm менеджер пакетов (ставиться вместе с node).

После установки Открываем новое окно терминала

C:\> node -v
v12.18.4

C:\> npm -v
6.14.8

2) Установим пакет xmysql который будет делать магию. (офф репозиторий)

Запустим терминал/консоль (с правами Администратора)

C:\> npm install -g xmysql

Если не увидели ошибок, можем приступать.


У кого разбор полетов, следуйте на страничку репозитория проекта, изучите решения или задайте вопрос разработчику (https://github.com/o1lab/xmysql)


3) Ну и конечно же нам нужна рабочая mysql бд.

Я буду использовать локальную версию MariaDB 10.3.26. Вы можете любую какая вам по душе.

C:\> xmysql
run xmysql [3]

4) Пропишем в параметрах наши значения для подключения к базе данных и укажем имя базы данных с которой будем работать

C:\> xmysql -h localhost -u root p "" -d open_disposable_base

Убедитесь что у вас не занят порт 3000, и добавьте в исключение при первом запуске сработает брандмауэр windows.

start xmysql on local db [4]

5) Проверим локальный адрес по порту 3000, на нем должен подняться RESTful HTTP нашей базы (по умолчанию http://localhost:3000)

Попробуем отправить запрос по этому адресу

p.s. можно также просто отрыть в браузере http://localhost:3000
curl query get routes [5]

На корневой странички увидим все доступные пути routing и методы взаимодействия RESTful (какой метод использовать в HTTP запросах)


На данном этапе мы настроили и запустили все необходимое.


!!!!Однако будьте внимательны!!!!!

Если запустите на выделенном сервере и не позаботитесь о доступе по порту 3000, скорее всего любой желающий сможет получить данные с базы, или куда похуже через методы upload/download.

Решения легко найти в google. Вариантов довольно много. От парольного доступа до исключения на сетевом уровне.


3. Query processing

Разберем пару примеров, попробуем на что способен этот зверь =)

Модель построения запросов 

host:port/api/[table_name]/?[method]

Модель построения запросов с перечислением параметров

host:port/api/[table_name]/?[method]=([param1],[param2]..)


Полный список https://github.com/o1lab/xmysql#api-overview

List of method api


  • Просмотр содержимого таблицы (аналогия запроса ниже)

sql select * from `open_disposable_base` limit 0,20

GET /api/disposable_emails/
[
  {
    "id": 1482,
    "domain": "ce.mintemail.com",
    "created_at": "2018-05-31T01:25:56.000Z",
    "updated_at": "2018-05-31T01:25:56.000Z",
    "deleted_at": null
  }
]

  • Получить кол-во в таблице.
GET /api/disposable_emails/count
[
  {
    "no_of_rows": 663
  }
]

Пример передачи параметров:

/findOne?_where=([Column],[Operator],[SearchValue])

[SearchValue] = ce.mintemail.com -искомое значение

[Column] = domain - имя столбца

[Operator] = eq - эквивалентно (полный список)


  • Поиск записи по указанному столбцу
GET /api/disposable_emails/findOne?_where=(domain,eq,ce.mintemail.com)
[
  {
    "id": 1482,
    "domain": "ce.mintemail.com",
    "created_at": "2018-05-31T01:25:56.000Z",
    "updated_at": "2018-05-31T01:25:56.000Z",
    "deleted_at": null
  }
]
  • Фильтрация по столбцам, перечисляем через запятую имена столбцов. Также инверсионный вариант с добавлением “-” перед именем.
GET /api/disposableemails/?fields=id,domain
[
  {
    "id": 1481,
    "domain": "cbair.com"
  },
  {
    "id": 1482,
    "domain": "ce.mintemail.com"
  }
GET /api/disposableemails/?fields=-domain
[
  {
    "id": 1481,
    "created_at": "2018-05-31T01:25:56.000Z",
    "updated_at": "2018-05-31T01:25:56.000Z",
    "deleted_at": null
  },
  {
    "id": 1482,
    "created_at": "2018-05-31T01:25:56.000Z",
    "updated_at": "2018-05-31T01:25:56.000Z",
    "deleted_at": null
  }

  • Создаем пагинацию (полезно при очень больших таблицах)
GET /api/disposableemails/?fields=domain&_pp=1&ze=3
[
  {
    "domain": "cheaphorde.com"
  },
  {
    "domain": "chef.asana.biz"
  },
  {
    "domain": "chielo.com"
  }
]

  • Также стоит отметить возможность работы с файлами. Upload, Download, в параметрах xmysql есть флаг указания каталога для файлов.

  • В документации описан также метод Dynamic queries, эта возможность присутствует в случае локального mysql сервера.
POST /dynamic
    {
        "query": "select * from ?? limit 1,20",
        "params": ["customers"]
    }

4. Work in ZP, try multitread inserts

На коленке написал минимальный код, для быстрого теста и попробовать многопоточную вставку. Общий принцип как начать внедрять в свои проекты xmysql.

string xsqlHost = "http://localhost";   // xmysql hostname or IP
string xsqlPort = "3000";
string xsqlTable = "test_multithread";  // Table name

string xsqlBase = string.Concat(xsqlHost, ":", xsqlPort);  // base path for query
string xsqlGetTable = string.Concat(xsqlBase, "/api/", xsqlTable);   // path to query for table

var xsqlHelth = new HttpRequest().Get(xsqlBase+"/_health").ToString();  // check avalible and status mysql
project.SendInfoToLog(xsqlHelth);

var xsqlRoutes = new HttpRequest().Get(xsqlBase).ToString();  // parse all avalible routes 
project.Json.FromString(xsqlRoutes);  // show routes with methods in zp varibles (json)
Project Maker sample

Плавненько подходим к концовке. Читать данные хорошо, но что же с записью/изменению/обновлению?

Произвели пару тестов, в окружении локально поднятой базой, для общей оценки и применения xmysql при работе в многопоточном режиме.

Схема заполняемой таблицы выглядело примерно след. образом:

CREATE TABLE `test_multithread` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `domain` varchar(255) NOT NULL,
  `add_date` timestamp DEFAULT CURRENT_TIMESTAMP
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5958 DEFAULT CHARSET=utf8;

Из списка загружались домены, причем умышленно по одному, и аналогично происходила вставка по одному INSERT.

Так делать не желательно, в xmysql помимо add row есть и пакетные обработки CREATE, LIST, DELETE. Прошу заметить, средствами слоя xmysql параметры переданные в теле POST запроса преобразуются в стандартный пакетный INSERT.

Первый тест обычными циклами средствами zp, 2 кубика, один из них шлет POST с параметром "domain=test.com"

Multithread add (25 threads)

Второй тест с использованием Threading.Tasks.Parallel.For

Для понимания кусок кода который отрабатывал указанное кол-во циклов параллельно

var listparams = project.Lists["domains_becnh"];

System.Threading.Tasks.Parallel.For(0, icount, (i, parallelLoopState) => {
  string domain = listparams[0];
  listparams.RemoveAt(0);
  using (var request = new HttpRequest())
{
    var reqParams = new RequestParams();
    reqParams["domain"] = domain;

    string addRow = request.Post(xsqlGetTable, reqParams).ToString();
}
return;
});

Parallel Task rows add

В конечном итоге протестировать удалось далеко еще не все.

Однако как видите, даже по одиночной вставке работает более чем.

Скорость вставки в моем случае следующие:

  1. 500 строк 25 потоками = 500 rows added in 7110,4820ms

Используя параллельные потоки (parallels threads):

  1. 100 rows added in 205,8456ms ( 2,1 second )
  2. 500 rows added in 4619,1725ms ( 4,6 second )
  3. 1000 rows added in 7503,2164ms ( 7,5 second )

p.s. судя по минимальному просмотру исходного кода xmysql, я не углядел работу с транзакциями и т.д. Данный вопрос оставил на потом. Убедитесь в отсутствие необходимости локов на уровне бд если вам будет необходимо много удалять строк и тд. Надеюсь там все хорошо в этом плане, в любом случае попробуйте перед тем как бежать переносить все на xmysql.

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



Следите за новостями, прокачивайте умы - C# for ZennoPoster