HTTP-серверы на Fiber в Golang

от автора

Привет, Хабр!

Когда речь заходит о создании 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 возвращает результат сразу, не ожидая завершения асинхронной задачи, которая работает в фоновом режиме.

Высокой производительность достигается благодаря нескольким факторам:

  1. Zero allocation routing: маршрутизация запросов в Fiber выполняется без дополнительных аллокаций, что минимизирует накладные расходы на обработку запросов.

  2. Многопоточность через 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, который включает в себя:

  1. Логирование запросов

  2. Сжатие ответов для повышения производительности

  3. Ограничение запросов для предотвращения атак

  4. Подключение к базе данных PostgreSQL

  5. Полноценную маршрутизацию для работы с продуктами (кормом для котиков)

  6. CRUD-операции


Подробнее с Fiber можно ознакомиться здесь.

А по ссылке вы можете зарегистрироваться на бесплатный вебинар курса «Golang Developer. Professional».


ссылка на оригинал статьи https://habr.com/ru/articles/841194/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *