Как работает простой сервер на Go?

просто сервер на go

Стандартная библиотека Go позволяет запустить веб-сервер буквально в несколько строк. Это одна из главных причин, почему разработчики так любят Go. Но даже в таком простом коде есть нюансы, которые я хотел бы разобрать в этой статье.

Давайте разберем конкретный работающий пример веб-сервера и увидим, как он устроен.

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        n, err := fmt.Fprintf(w, "Hello, world!")
        if err != nil {
            fmt.Println(err)
        }

        fmt.Printf("Number of bytes written: %d\n", n))
    })

    if err := http.ListenAndServe(":8070", nil); err != nil {
        fmt.Println("Server error:", err)
    }
}

Что делает этот код?

При запуске этого кода программа делает две вещи:

  1. Регистрирует обработчик для корневого маршрута /
  2. Запускает HTTP-сервер на порту 8070

Когда вы открываете в браузере http://localhost:8070, происходит следующее:

  • В окне браузера появляется текст: Hello, world!
  • В консоли, где запущена программа, появляется сообщение: Number of bytes written: 13

Детальный разбор

Регистрация обработчика

 
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    // тело функции
})

Что это значит:

http.HandleFunc — это команда для Go: «Когда кто-то зайдет на адрес /, выполни эту функцию».

Она принимает два параметра:

  • "/" — путь, на который реагирует обработчик (корень сайта)
  • функция — код, который будет выполнен при запросе

Почему / ловит все адреса?

В Go паттерн / соответствует корню и всем вложенным путям. То есть обработчик сработает и на http://localhost:8070/, и на http://localhost:8070/anything.

Что внутри обработчика?

Функция-обработчик получает два параметра:

  • w http.ResponseWriter — куда записывается ответ для пользователя. Все, что отправляется в w, улетает в браузер.
  • r *http.Request — информация о запросе (метод, заголовки, параметры). В этом примере не используется.
 
n, err := fmt.Fprintf(w, "Hello, world!")

fmt.Fprintf — это функция форматированного вывода. Ее особенность: она пишет результат не в консоль, а туда, куда укажет первый аргумент. Передавая w, мы говорим: «Напиши Hello, world! прям в HTTP-ответ».

Функция возвращает 2 значения:

  • n — количество байт, которые были записаны. Строка "Hello, world!" занимает 13 байт (каждый символ — 1 байт).
  • err — ошибка, если что-то пошло не так (например, пользователь закрыл браузер до получения ответа).
 
if err != nil {
    fmt.Println(err)
}

Если при отправке ответа произошла ошибка — печатаем ее в консоль. В большинстве случаев ошибки не будет, но хороший код всегда должен проверять это.

 
fmt.Printf("Number of bytes written: %d\n", n)

Это строка выводит в консоль сервера сообщение о том, сколько байт было отправлено.

Важно понять: этот вывод виден только в консоли сервера. Пользователь в браузере его не увидит.

Запуск сервера

if err := http.ListenAndServe(":8070", nil); err != nil {
    fmt.Println("Server error:", err)
}

http.ListenAndServe — главная команда, которая:

  • Запускает HTTP-сервер
  • Начинает слушать порт 8070
  • Блокирует выполнение программы (то есть программа не завершится, пока сервер работает)

Второй параметр — nil — означает «используй стандартный механизм маршрутизации», который мы уже настроили через http.HandleFunc.

С помощью err мы обрабатываем случай, когда по какой-то причине сервер не смог запустится (например, порт 8070 уже занят другой программой). В таком случае мы увидим понятное сообщение об ошибке.

Почему проверка ошибки так важна?

Представьте ситуацию: вы запускаете сервер на рабочем сервере, но порт 8070 случайно занят другим процессом. Без проверки ошибки программа просто завершится без единого сообщения. Вы будете думать, что сервер работает, а на самом деле он даже не запустился.

С проверкой ошибки вы сразу увидите что-то вроде:

Server error: listen tcp :8070: bind: address already in use

И сразу поймете, в чем проблема.

Ключевые элементы кода

  1. http.HandleFunc — регистрирует функцию, которая будет обрабатывать запросы к определенному пути.
  2. http.ResponseWriter — интерфейс для отправки ответа клиенту. Работает как приемник в fmt.Fprintf.
  3. http.Request — содержит данные о запросе (методы, заголовки, тело).
  4. fmt.Fprintf — записывает форматированную строку в любой io.Writer, включая http.ResponseWriter.
  5. http.ListenAndServe — запускает сервер и блокирует выполнение программы.

Ключевой вывод

Всего несколько строк кода — и у вас есть полноценный веб-сервер. Go сделал создание HTTP-сервисов настолько простым, насколько это возможно. При этом важно помнить о деталях: проверять ошибки, понимать, куда пишет fmt.Fprintf, и знать разницу между выводом для пользователя и выводом в лог сервера.

Если вы запустите этот код, то сообщение Number of bytes written: 13 выведется 2 раза. Как вы думаете, почему? Напишите в комментариях ваши предположения.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: