Сейчас наша база данных настроена и установлен драйвер для работы с MySQL из Golang, теперь можно попробовать подключиться к базе данных из веб-приложения.
Содержание статьи
Для создания пула соединений, нам потребуется функция sql.Open()
, которая используется следующим образом:
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
1 2 3 4 5 6 |
// Функция sql.Open() инициализирует новый объект sql.DB, который по сути // является пулом подключения к базе данных. db, err := sql.Open("mysql", "web:pass@/snippetbox?parseTime=true") if err != nil { ... } |
В этом коде есть несколько моментов, которые следует разобрать:
- Первый параметр из
sql.Open()
представляет собой название драйвера (в нашем случае это mysql), а второй параметр — название источника данных (иногда он еще называется строкой подключения или DSN), которое описывает, как подключиться к базе данных; - Формат названия источника данных будет зависеть от того, какую базу данных и драйвер вы используете. Как правило, эту информацию можно найти в документации к вашему конкретному драйверу. Документацию по нашему MySQL-драйверу можно найти здесь;
- Часть с
parseTime=true
в приведенном выше DSN представляет собой специфичный для драйвера параметр, который указывает драйверу, что нужно преобразовывать SQL поляTIME
иDATE
в Go объекты time.Time; - Функция
sql.Open()
возвращает объект sql.DB. Это не соединение с базой данных — это пул множества соединений. Это важное различие нужно запомнить. Go управляет этими подключениями по мере необходимости, автоматически открывая и закрывая подключения к базе данных через драйвер; - Пул соединений безопасен для конкурентного доступа, поэтому его можно спокойно использовать из обработчиков маршрутов веб-приложений;
- Пул соединений рассчитан на долгий срок службы. В веб-приложении, пул соединений обычно инициализируется в функции
main()
, а затем этот пул передается обработчикам. Не нужно вызыватьsql.Open()
в каждом обработчике — это будет пустой тратой памяти и сетевых ресурсов.
Использование пула подключений в веб-приложении на Go
Разберем использование sql.Open()
на практике. Откройте файл main.go
и добавьте в него следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
package main import ( "database/sql" // Новый импорт "flag" "log" "net/http" "os" _ "github.com/go-sql-driver/mysql" // Новый импорт ) type application struct { errorLog *log.Logger infoLog *log.Logger } func main() { addr := flag.String("addr", ":4000", "Сетевой адрес веб-сервера") // Определение нового флага из командной строки для настройки MySQL подключения. dsn := flag.String("dsn", "web:pass@/snippetbox?parseTime=true", "Название MySQL источника данных") flag.Parse() infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime) errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile) // Чтобы функция main() была более компактной, мы поместили код для создания // пула соединений в отдельную функцию openDB(). Мы передаем в нее полученный // источник данных (DSN) из флага командной строки. db, err := openDB(*dsn) if err != nil { errorLog.Fatal(err) } // Мы также откладываем вызов db.Close(), чтобы пул соединений был закрыт // до выхода из функции main(). // Подробнее про defer: https://golangs.org/errors#defer defer db.Close() app := &application{ errorLog: errorLog, infoLog: infoLog, } srv := &http.Server{ Addr: *addr, ErrorLog: errorLog, Handler: app.routes(), } infoLog.Printf("Запуск сервера на %s", *addr) // Поскольку переменная `err` уже объявлена в приведенном выше коде, нужно // использовать оператор присваивания = // вместо оператора := (объявить и присвоить) err = srv.ListenAndServe() errorLog.Fatal(err) } // Функция openDB() обертывает sql.Open() и возвращает пул соединений sql.DB // для заданной строки подключения (DSN). func openDB(dsn string) (*sql.DB, error) { db, err := sql.Open("mysql", dsn) if err != nil { return nil, err } if err = db.Ping(); err != nil { return nil, err } return db, nil } |
В этом коде есть несколько интересных моментов:
- Обратите внимание, что в пути импорта mysql-драйвера есть префикс подчеркивания
_
. Причина в том, что файлmain.go
ничего не использует из пакетаmysql
. Если мы попытаемся импортировать его в обычном режиме без подчеркивания, то компилятор выдаст ошибку. Во время импорта mysql-драйвера, он в фоновом режиме выполнит функциюinit()
, которая необходима для дальнейшей работы с пакетомdatabase/sql
. Смысл такого трюка состоит в том, чтобы связать название пакета с пустым идентификатором. Это стандартная практика для большинства SQL драйверов в Go; - Функция
sql.Open()
не создает никаких подключений к базе данных. Все, что она делает, это инициализирует пул подключений для использования в будущем. Фактические подключения к базе данных устанавливаются лениво, если это впервые. Чтобы убедиться, что все настроено правильно, нужно использовать метод db.Ping() для создания соединения с MySQL и проверки на наличие ошибок; - На данный момент вызов
defer db.Close()
является излишним. Наше приложение завершается только от ручной комбинации на клавиатуре (то естьCtrl+C
) или фатальной ошибки которая автоматически вызоветerrorLog.Fatal()
. В обоих случаях программа немедленно завершает работу, а отложенные функции никогда не будут запущены. Но использованиеdb.Close()
по завершению работы с базой данных, будет считаться хорошим тоном, и такой подход будет полезен в будущем, если вы добавите в свое приложение аккуратное завершение работы при возникновении фатальных ошибок.
Тестирование подключения к MySQL из веб-приложения
Убедитесь, что все отредактированные файлы сохранены. Затем попробуйте запустить наше веб-приложение из терминала. Если всё прошло по плану, пул подключений должен быть установлен, а метод db.Ping()
попробует подключится к базе данных без каких-либо ошибок. Если все в порядке, вы должны увидеть обычное сообщение из логов: Запуск сервера на…
1 2 |
$ go run ./cmd/web INFO 2021/01/27 18:40:01 Запуск сервера на :4000 |
Если приложение не запускается или появляется сообщение об ошибке "Access denied..."
, как показано ниже, то проблема скорее всего связана с вашей строкой подключения (DSN). Проверьте правильность имени пользователя и пароля, разрешения пользователей и что ваш MySQL сервер использует стандартные настройки.
Но, скорее всего вы неправильно указали логин или пароль от MySQL пользователя, пройдите еще раз прошлый урок по настройки базы данных.
1 2 |
$ go run ./cmd/web ERROR 2021/01/27 18:46:22 main.go:32: Error 1045: Access denied for user 'web'@'localhost' (using password: YES) |
Скачать исходный код
В конце каждой статьи мы предоставляем вам готовый архив с кодом для запуска веб-приложения на текущем этапе курса.
Скачать: snippetbox-17.zip
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»