Привет, Хабр!
Когда речь заходит о создании HTTP-серверов на Go, большинство сразу думают о привычных решениях, таких как net/http или Gin. Эти инструменты проверены временем, но что, если нужен сервер, который не просто стабилен, а работает очень быстро? Здесь помогает Fiber — лёгкий, но невероятно мощный HTTP-фреймворк, способный вывести производительность сервера на новый уровень.
С синтаксисом, знакомым всем юзерам Express.js, Fiber избавляет от лишней сложности и позволяет сосредоточиться на главном — максимальной скорости и эффективности.
Основные концепции Fiber
Асинхронность — это база Fiber. Он построен поверх Go‑рутин, что делает его нативно асинхронным, давая возможность обрабатывать множество запросов параллельно с минимальной нагрузкой на систему. Фича в том, что Go‑рутины крайне легковесны — они быстрее и требуют меньше ресурсов, чем традиционные потоки в других ЯП.
Fiber использует goroutine pooling — технику, при которой набор Go‑рутин заранее подготовлен для обработки запросов.
Пример простой асинхронной обработки запросов:
package main import ( "github.com/gofiber/fiber/v2" "time" ) func main() { app := fiber.New() app.Get("/async", func(c *fiber.Ctx) error { go func() { time.Sleep(2 * time.Second) println("Асинхронная задача завершена") }() return c.SendString("Запрос принят, задача выполняется в фоне!") }) app.Listen(":3000") }
Запрос на маршрут /async
возвращает результат сразу, не ожидая завершения асинхронной задачи, которая работает в фоновом режиме.
Высокой производительность достигается благодаря нескольким факторам:
-
Zero allocation routing: маршрутизация запросов в Fiber выполняется без дополнительных аллокаций, что минимизирует накладные расходы на обработку запросов.
-
Многопоточность через Go‑рутины: благодаря встроенным возможностям Go для работы с многопоточностью, Fiber масштабируется горизонтально и может обрабатывать тысячи запросов параллельно.
Пример конфигурации Fiber для макс. производительности:
package main import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" ) func main() { app := fiber.New(fiber.Config{ Prefork: true, // включаем предварительное форкование для увеличения производительности на многоядерных процессорах ServerHeader: "Fiber", // добавляем заголовок для идентификации сервера CaseSensitive: true, // включаем чувствительность к регистру в URL StrictRouting: true, // включаем строгую маршрутизацию }) app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, Fiber!") }) app.Listen(":3000") }
Здесь Prefork позволяет создавать несколько процессов.
Обзор основного синтаксиса
Маршрутизация в Fiber невероятно проста и быстра благодаря своей zero allocation архитектуре. Fiber поддерживает методы GET
, POST
, PUT
, DELETE
, а также динамическую маршрутизацию:
app.Get("/users/:id", func(c *fiber.Ctx) error { id := c.Params("id") return c.SendString("User ID: " + id) })
Маршрут с динамическим параметром :id
позволяет легко работать с URL и передавать данные в обработчик.
Middleware в Fiber подключается интуитивно и работает аналогично Express.js:
app.Use(func(c *fiber.Ctx) error { println("Запрос получен") return c.Next() // передаем управление дальше })
Можно легко добавить middleware для обработки авторизации, логирования или различной защиты.
Обработка запросов в Fiber поддерживает работу с телом запроса, заголовками и файлами. Пример работы с телом запроса:
app.Post("/submit", func(c *fiber.Ctx) error { data := new(struct { Name string `json:"name"` }) if err := c.BodyParser(data); err != nil { return err } return c.JSON(fiber.Map{"message": "Привет, " + data.Name}) })
Здесь Fiber позволяет парсить тело запроса и отправлять ответ в формате JSON.
Одним из больших плюсов Fiber является его совместимость с низкоуровневыми возможностями Go. Fiber предоставляет интерфейс для работы с нативными net/http хендлерами, что позволяет комбинировать его с уже существующими решениями:
app := fiber.New() httpHandler := func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Привет из net/http")) } app.Get("/legacy", func(c *fiber.Ctx) error { httpHandler(c.Context().Response().Writer, c.Context().Request()) return nil })
Продолжим разбор синтаксиса Fiber — фреймворка, который делает работу с HTTP-серверами на Go интуитивно понятной и невероятно производительной.
Извлечение динамических параметров из URL осуществляется очень просто. Например, при создании маршрута с динамическим сегментом можно легко получить значение параметра:
app.Get("/products/:id", func(c *fiber.Ctx) error { id := c.Params("id") // извлечение параметра "id" из URL return c.SendString("Product ID: " + id) })
Так можно передавать идентификатор продукта прямо в URL и обрабатывать его в функции-обработчике.
Query-параметры, которые передаются в строке запроса (например, ?sort=desc
), также легко извлекаются через Fiber:
app.Get("/search", func(c *fiber.Ctx) error { query := c.Query("q", "default") // получаем значение параметра "q", задаём "default" как значение по умолчанию return c.SendString("Searching for: " + query) })
Если нужно работать с заголовками запроса, Fiber имеет удобный API для их извлечения:
app.Get("/headers", func(c *fiber.Ctx) error { userAgent := c.Get("User-Agent") // извлекаем заголовок User-Agent return c.SendString("Your User-Agent is: " + userAgent) })
Пример сервера
Теперь создадим полноценный сервер на Fiber. Реализуем HTTP-сервер для онлайн-магазина корма для котиков.
Структура проекта
Определим структуру проекта, чтобы она была удобной для разработки и масштабирования:
cat-food-store/ │ ├── main.go // Главная точка входа ├── routes/ // Каталог с файлами маршрутов │ └── products.go // Маршруты для работы с продуктами (корм) ├── handlers/ // Обработчики для запросов │ └── product.go // Логика обработки продуктов ├── models/ // Модели для базы данных │ └── product.go // Модель данных для корма └── database.go // Подключение к базе данных
Теперь можно начинать с основного файла main.go
.
Реализация главного файла main.go
Этот файл будет отвечать за инициализацию приложения, подключение к БД, настройку middleware и запуск сервера:
package main import ( "log" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/limiter" "github.com/gofiber/fiber/v2/middleware/recover" "cat-food-store/database" "cat-food-store/routes" ) func main() { // инициализируем базу данных if err := database.Connect(); err != nil { log.Fatalf("Ошибка подключения к базе данных: %v", err) } // создаём новое приложение Fiber app := fiber.New(fiber.Config{ Prefork: true, // используем предварительное форкование для увеличения производительности }) // Подключаем middleware app.Use(logger.New()) // Логирование запросов app.Use(compress.New()) // Сжатие ответов app.Use(recover.New()) // Восстановление после паники app.Use(limiter.New()) // Лимит запросов для предотвращения DDOS атак // Регистрация маршрутов routes.RegisterProductRoutes(app) // Запускаем сервер log.Fatal(app.Listen(":3000")) }
Подключение к БД
Будем использовать PostgreSQL в качестве БД для хранения информации о продуктах (корме). Для подключения к БД создадим файл database.go
:
package database import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" ) var DB *sql.DB // функция подключения к базе данных func Connect() error { connStr := "user=username dbname=catfoodstore sslmode=disable password=yourpassword" db, err := sql.Open("postgres", connStr) if err != nil { return fmt.Errorf("ошибка подключения к БД: %v", err) } if err := db.Ping(); err != nil { return fmt.Errorf("не удалось подключиться к базе данных: %v", err) } DB = db log.Println("Успешно подключились к базе данных") return nil }
Модель данных для продуктов (models/product.go)
Теперь создадим модель данных для продукта, которая будет хранить информацию о кормах для котиков:
package models type Product struct { ID int `json:"id"` Name string `json:"name"` Description string `json:"description"` Price float64 `json:"price"` Stock int `json:"stock"` ImageURL string `json:"image_url"` }
Обработчик запросов для продуктов (handlers/product.go)
Обработчик будет содержать логику для взаимодействия с базой данных: создание, получение, обновление и удаление продуктов:
package handlers import ( "cat-food-store/database" "cat-food-store/models" "github.com/gofiber/fiber/v2" "strconv" ) // получение списка всех продуктов func GetProducts(c *fiber.Ctx) error { rows, err := database.DB.Query("SELECT id, name, description, price, stock, image_url FROM products") if err != nil { return c.Status(500).SendString("Ошибка выполнения запроса к базе данных") } defer rows.Close() var products []models.Product for rows.Next() { var product models.Product err := rows.Scan(&product.ID, &product.Name, &product.Description, &product.Price, &product.Stock, &product.ImageURL) if err != nil { return c.Status(500).SendString("Ошибка сканирования данных") } products = append(products, product) } return c.JSON(products) } // создание нового продукта func CreateProduct(c *fiber.Ctx) error { product := new(models.Product) if err := c.BodyParser(product); err != nil { return c.Status(400).SendString("Неверный формат запроса") } _, err := database.DB.Exec("INSERT INTO products (name, description, price, stock, image_url) VALUES ($1, $2, $3, $4, $5)", product.Name, product.Description, product.Price, product.Stock, product.ImageURL) if err != nil { return c.Status(500).SendString("Ошибка вставки данных в базу") } return c.Status(201).SendString("Продукт успешно создан") } // получение продукта по ID func GetProduct(c *fiber.Ctx) error { id := c.Params("id") row := database.DB.QueryRow("SELECT id, name, description, price, stock, image_url FROM products WHERE id = $1", id) var product models.Product err := row.Scan(&product.ID, &product.Name, &product.Description, &product.Price, &product.Stock, &product.ImageURL) if err != nil { return c.Status(404).SendString("Продукт не найден") } return c.JSON(product) } // обновление продукта func UpdateProduct(c *fiber.Ctx) error { id := c.Params("id") product := new(models.Product) if err := c.BodyParser(product); err != nil { return c.Status(400).SendString("Неверный формат запроса") } _, err := database.DB.Exec("UPDATE products SET name = $1, description = $2, price = $3, stock = $4, image_url = $5 WHERE id = $6", product.Name, product.Description, product.Price, product.Stock, product.ImageURL, id) if err != nil { return c.Status(500).SendString("Ошибка обновления данных") } return c.SendString("Продукт успешно обновлён") } // удаление продукта func DeleteProduct(c *fiber.Ctx) error { id := c.Params("id") _, err := database.DB.Exec("DELETE FROM products WHERE id = $1", id) if err != nil { return c.Status(500).SendString("Ошибка удаления продукта") } return c.SendString("Продукт успешно удалён") }
Маршруты для продуктов (routes/products.go)
Теперь зарегистрируем маршруты для работы с продуктами в отдельном файле:
package routes import ( "cat-food-store/handlers" "github.com/gofiber/fiber/v2" ) func RegisterProductRoutes(app *fiber.App) { api := app.Group("/api") api.Get("/products", handlers.GetProducts) // Получить все продукты api.Post("/products", handlers.CreateProduct) // Создать новый продукт api.Get("/products/:id", handlers.GetProduct) // Получить продукт по ID api.Put("/products/:id", handlers.UpdateProduct) // Обновить продукт api.Delete("/products/:id", handlers.DeleteProduct) // Удалить продукт }
Создали полноценный сервер для магазина корма для котиков на Fiber, который включает в себя:
-
Логирование запросов
-
Сжатие ответов для повышения производительности
-
Ограничение запросов для предотвращения атак
-
Подключение к базе данных PostgreSQL
-
Полноценную маршрутизацию для работы с продуктами (кормом для котиков)
-
CRUD-операции
Подробнее с Fiber можно ознакомиться здесь.
А по ссылке вы можете зарегистрироваться на бесплатный вебинар курса «Golang Developer. Professional».
ссылка на оригинал статьи https://habr.com/ru/articles/841194/
Добавить комментарий