Развој веб сервера у Голангу - од једноставног до сложеног

Развој веб сервера у Голангу - од једноставног до сложеног

Пре пет година сам почео развити Гопхисх, ово је пружило прилику да научите Голанг. Схватио сам да је Го моћан језик, допуњен многим библиотекама. Го је свестран: посебно, може се користити за развој апликација на страни сервера без икаквих проблема.

Овај чланак је о писању сервера у Го. Почнимо са једноставним стварима попут „Здраво свет!“ и завршимо са апликацијом са следећим могућностима:

- Коришћење Лет'с Енцрипт за ХТТПС.
— Ради као АПИ рутер.
— Рад са средњим софтвером.
— Обрада статичких датотека.
— Исправно гашење.

Скиллбок препоручује: Практични курс „Питхон програмер од нуле“.

Подсећамо: за све читаоце „Хабра“ – попуст од 10 рубаља при упису на било који курс Скиллбок користећи промотивни код „Хабр“.

Здраво, Свете!

Можете да креирате веб сервер у Го веома брзо. Ево примера коришћења руковаоца који враћа „Здраво, свет!“ обећано изнад.

package main
 
import (
"fmt"
"net/http"
)
 
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
})
http.ListenAndServe(":80", nil)
}

Након овога, ако покренете апликацију и отворите страницу лоцалхост, тада ћете одмах видети текст „Здраво, свет!“ (ако све ради како треба, наравно).

Касније ћемо више пута користити руковалац, али прво да разумемо како све функционише.

нет/хттп

Пример је користио пакет net/http, то је примарни алат у Го-у за развој сервера и ХТТП клијената. Да бисмо разумели код, хајде да разумемо значење три важна елемента: хттп.Хандлер, хттп.СервеМук и хттп.Сервер.

ХТТП обрађивачи

Када примимо захтев, руковалац га анализира и генерише одговор. Руковаоци у Го-у су имплементирани на следећи начин:

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Први пример користи помоћну функцију хттп.ХандлеФунц. Обмотава другу функцију, која заузврат преузима хттп.РеспонсеВритер и хттп.Рекуест у СервеХТТП.

Другим речима, руковаоци у Голангу су представљени у једном интерфејсу, који програмеру даје много опција. Тако, на пример, средњи софтвер се имплементира помоћу руковаоца, где СервеХТТП прво уради нешто, а затим позива СервеХТТП метод другог руковаоца.

Као што је горе поменуто, руковаоци једноставно генеришу одговоре на захтеве. Али који одређени руковалац треба да се користи у одређеном тренутку?

Рекуест Роутинг

Да бисте направили прави избор, користите ХТТП мултиплексер. У великом броју библиотека назива се муксер или рутер, али су све исте ствари. Функција мултиплексера је да анализира путању захтева и изабере одговарајући руковалац.

Ако вам је потребна подршка за сложено рутирање, онда је боље користити библиотеке трећих страна. Неки од најнапреднијих - горила/мук и го-цхи/цхи, ове библиотеке омогућавају имплементацију посредне обраде без икаквих проблема. Уз њихову помоћ, можете да конфигуришете џокер рутирање и извршите низ других задатака. Њихова предност је компатибилност са стандардним ХТТП руковаоцима. Као резултат, можете написати једноставан код који се може мењати у будућности.

Рад са сложеним оквирима у нормалној ситуацији захтеваће нестандардна решења, а то значајно компликује коришћење подразумеваних руковаоца. За креирање велике већине апликација биће довољна комбинација подразумеване библиотеке и једноставног рутера.

Обрада упита

Поред тога, потребна нам је компонента која ће „слушати“ долазне везе и преусмерити све захтеве на исправан руковалац. хттп.Сервер може лако да се носи са овим задатком.

Следеће показује да је сервер одговоран за све задатке који се односе на обраду везе. Ово, на пример, функционише помоћу ТЛС протокола. За имплементацију позива хттп.ЛистенАндСервер користи се стандардни ХТТП сервер.

Погледајмо сада сложеније примере.

Додавање Лет'с Енцрипт

Наша апликација подразумевано ради преко ХТТП протокола, али се препоручује коришћење ХТТПС протокола. Ово се може урадити без проблема у Го. Ако сте добили сертификат и приватни кључ, довољно је да региструјете ЛистенАндСервеТЛС са исправним сертификатом и датотекама кључева.

http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)

Увек можете боље.

Хајде да шифрирамо пружа бесплатне сертификате са аутоматским обнављањем. Да бисте користили услугу, потребан вам је пакет autocert.

Најлакши начин да га конфигуришете је да користите метод аутоцерт.НевЛистенер у комбинацији са хттп.Серве. Метод вам омогућава да добијете и ажурирате ТЛС сертификате док ХТТП сервер обрађује захтеве:

http.Serve(autocert.NewListener("example.com"), nil)

Ако отворимо у претраживачу екампле.цом, добићемо ХТТПС одговор „Здраво, свет!“

Ако вам је потребна детаљнија конфигурација, онда би требало да користите аутоцерт.Манагер менаџер. Затим креирамо сопствену инстанцу хттп.Сервер (до сада смо је подразумевано користили) и додамо менаџера на ТЛСЦонфиг сервер:

m := &autocert.Manager{
Cache:      autocert.DirCache("golang-autocert"),
Prompt:     autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
}
server := &http.Server{
    Addr:      ":443",
    TLSConfig: m.TLSConfig(),
}
server.ListenAndServeTLS("", "")

Ово је једноставан начин за имплементацију пуне ХТТПС подршке уз аутоматско обнављање сертификата.

Додавање прилагођених рута

Подразумевани рутер укључен у стандардну библиотеку је добар, али је врло једноставан. Већина апликација захтева сложеније рутирање, укључујући угнежђене и џокер руте, или процедуру за постављање образаца путање и параметара.

У овом случају вреди користити пакете горила/мук и го-цхи/цхи. Научићемо како да радимо са овим последњим - пример је приказан у наставку.

Наведена је датотека апи/в1/апи.го која садржи руте за наш АПИ:

/ HelloResponse is the JSON representation for a customized message
type HelloResponse struct {
Message string `json:"message"`
}
 
// HelloName returns a personalized JSON message
func HelloName(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
response := HelloResponse{
Message: fmt.Sprintf("Hello %s!", name),
}
jsonResponse(w, response, http.StatusOK)
}
 
// NewRouter returns an HTTP handler that implements the routes for the API
func NewRouter() http.Handler {
r := chi.NewRouter()
r.Get("/{name}", HelloName)
return r
}

Поставили смо апи/вк префикс за руте у главној датотеци.

Затим ово можемо монтирати на наш главни рутер под апи/в1/ префиксом назад у нашој главној апликацији:

// NewRouter returns a new HTTP handler that implements the main server routes
func NewRouter() http.Handler {
router := chi.NewRouter()
    router.Mount("/api/v1/", v1.NewRouter())
    return router
}
http.Serve(autocert.NewListener("example.com"), NewRouter())

Го-ова лакоћа рада са сложеним рутама омогућава да се поједностави структурирање и одржавање великих, сложених апликација.

Рад са средњим софтвером

Постављање укључује премотавање једног ХТТП руковаоца другим, што омогућава брзо извођење аутентификације, компресије, евидентирања и неколико других функција.

Као пример, погледајмо хттп.Хандлер интерфејс; користићемо га да напишемо руковалац који аутентификује кориснике услуге.

func RequireAuthentication(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !isAuthenticated(r) {
            http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
            return
        }
        // Assuming authentication passed, run the original handler
        next.ServeHTTP(w, r)
    })
}

Постоје рутери трећих страна, као што је цхи, који вам омогућавају да проширите функционалност међувера.

Рад са статичним датотекама

Го стандардна библиотека укључује могућности за рад са статичним садржајем, укључујући слике, ЈаваСцрипт и ЦСС датотеке. Њима се може приступити преко функције хттп.ФилеСервер. Враћа обрађивач који опслужује датотеке из одређеног директорија.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("/{name}", HelloName)
 
// Настройка раздачи статических файлов
staticPath, _ := filepath.Abs("../../static/")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("/*", fs)
    
    return r

Свакако вреди запамтити да хттп.Дир приказује садржај директоријума ако не садржи главну датотеку индек.хтмл. У овом случају, да бисте спречили да директоријум буде компромитован, требало би да користите пакет unindexed.

Исправно гашење

Го такође има функцију која се зове грациозно гашење ХТТП сервера. Ово се може урадити помоћу методе Схутдовн(). Сервер се покреће у горутини, а затим се канал слуша да би примио сигнал прекида. Чим се прими сигнал, сервер се искључује, али не одмах, већ након неколико секунди.

handler := server.NewRouter()
srv := &http.Server{
    Handler: handler,
}
 
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
 
// Wait for an interrupt
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
// Attempt a graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

Као закључак

Го је моћан језик са скоро универзалном стандардном библиотеком. Његове подразумеване могућности су веома широке и могу се побољшати коришћењем интерфејса - ово вам омогућава да развијете заиста поуздане ХТТП сервере.

Скиллбок препоручује:

Извор: ввв.хабр.цом

Додај коментар