Vývoj webových serverov v Golang - od jednoduchých po komplexné

Vývoj webových serverov v Golang - od jednoduchých po komplexné

Pred piatimi rokmi som začal rozvíjať Gophish, to poskytlo príležitosť naučiť sa Golang. Uvedomil som si, že Go je silný jazyk, ktorý dopĺňa množstvo knižníc. Go je všestranný: najmä ho možno bez problémov použiť na vývoj aplikácií na strane servera.

Tento článok je o písaní servera v Go. Začnime jednoduchými vecami ako „Ahoj svet!“ a skončíme aplikáciou s nasledujúcimi možnosťami:

- Používanie Let's Encrypt pre HTTPS.
— Pracuje ako API router.
— Práca s middleware.
— Spracovanie statických súborov.
— Správne vypnutie.

Skillbox odporúča: Praktický kurz "Vývojár Pythonu od nuly".

Pripomíname vám: pre všetkých čitateľov „Habr“ - zľava 10 000 rubľov pri registrácii do akéhokoľvek kurzu Skillbox pomocou propagačného kódu „Habr“.

Ahoj, svet!

V Go môžete vytvoriť webový server veľmi rýchlo. Tu je príklad použitia handlera, ktorý vráti „Ahoj, svet!“ sľúbené vyššie.

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)
}

Potom, ak spustíte aplikáciu a otvoríte stránku localhost, potom sa vám okamžite zobrazí text „Ahoj, svet!“ (ak všetko funguje správne, samozrejme).

Obslužný program použijeme neskôr niekoľkokrát, ale najprv pochopme, ako všetko funguje.

net/http

V príklade bol použitý balík net/http, je to primárny nástroj v Go na vývoj serverov aj HTTP klientov. Aby sme pochopili kód, pochopme význam troch dôležitých prvkov: http.Handler, http.ServeMux a http.Server.

HTTP obslužné programy

Keď dostaneme požiadavku, handler ju analyzuje a vygeneruje odpoveď. Obslužné nástroje v Go sú implementované nasledovne:

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

Prvý príklad používa pomocnú funkciu http.HandleFunc. Zabalí ďalšiu funkciu, ktorá zas preberá http.ResponseWriter a http.Request do ServeHTTP.

Inými slovami, manipulátory v Golangu sú prezentované v jedinom rozhraní, ktoré dáva programátorovi veľa možností. Napríklad middleware je implementovaný pomocou handlera, kde ServeHTTP najprv niečo urobí a potom zavolá metódu ServeHTTP iného handlera.

Ako už bolo spomenuté vyššie, handleri jednoducho generujú odpovede na požiadavky. Ale ktorý konkrétny handler by sa mal použiť v konkrétnom čase?

Smerovanie žiadosti

Ak chcete urobiť správnu voľbu, použite multiplexer HTTP. V mnohých knižniciach sa nazýva muxer alebo router, ale všetky sú to isté. Funkciou multiplexora je analyzovať cestu požiadavky a vybrať vhodnú obsluhu.

Ak potrebujete podporu pre zložité smerovanie, potom je lepšie použiť knižnice tretích strán. Niektoré z najpokročilejších - gorila/mux и go-chi/chi, tieto knižnice umožňujú bez problémov implementovať medzispracovanie. S ich pomocou môžete nakonfigurovať smerovanie zástupných znakov a vykonávať množstvo ďalších úloh. Ich výhodou je kompatibilita so štandardnými HTTP handlermi. Vďaka tomu môžete napísať jednoduchý kód, ktorý je možné v budúcnosti upraviť.

Práca so zložitými frameworkami v bežnej situácii bude vyžadovať neštandardné riešenia, čo výrazne komplikuje používanie predvolených handlerov. Na vytvorenie drvivej väčšiny aplikácií bude stačiť kombinácia predvolenej knižnice a jednoduchého routera.

Spracovanie dopytu

Okrem toho potrebujeme komponent, ktorý bude „počúvať“ prichádzajúce pripojenia a presmeruje všetky požiadavky na správny handler. http.Server túto úlohu ľahko zvládne.

Nasledujúce ukazuje, že server je zodpovedný za všetky úlohy, ktoré súvisia so spracovaním pripojenia. Toto napríklad funguje pomocou protokolu TLS. Na implementáciu volania http.ListenAndServer sa používa štandardný HTTP server.

Teraz sa pozrime na zložitejšie príklady.

Pridanie Let's Encrypt

Štandardne beží naša aplikácia cez protokol HTTP, ale odporúča sa použiť protokol HTTPS. V Go to možno urobiť bez problémov. Ak ste dostali certifikát a súkromný kľúč, stačí zaregistrovať ListenAndServeTLS so správnymi súbormi certifikátov a kľúčov.

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

Vždy to môžete urobiť lepšie.

Zašifrovať poskytuje bezplatné certifikáty s automatickou obnovou. Aby ste mohli využívať službu, potrebujete balík autocert.

Najjednoduchší spôsob, ako ho nakonfigurovať, je použiť metódu autocert.NewListener v kombinácii s http.Serve. Táto metóda vám umožňuje získať a aktualizovať certifikáty TLS, zatiaľ čo server HTTP spracováva požiadavky:

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

Ak otvoríme v prehliadači example.com, dostaneme odpoveď HTTPS „Ahoj, svet!“

Ak potrebujete podrobnejšiu konfiguráciu, mali by ste použiť správcu autocert.Manager. Potom vytvoríme vlastnú inštanciu http.Server (doteraz sme ju štandardne používali) a pridáme správcu na server TLSConfig:

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("", "")

Ide o jednoduchý spôsob implementácie plnej podpory HTTPS s automatickou obnovou certifikátu.

Pridanie vlastných trás

Predvolený smerovač zahrnutý v štandardnej knižnici je dobrý, ale je veľmi jednoduchý. Väčšina aplikácií vyžaduje zložitejšie smerovanie vrátane vnorených trás a trás so zástupnými znakmi alebo postup na nastavenie vzorov ciest a parametrov.

V tomto prípade sa oplatí použiť balíčky gorila/mux и go-chi/chi. Naučíme sa pracovať s posledným - príklad je uvedený nižšie.

Daný je súbor api/v1/api.go obsahujúci trasy pre naše API:

/ 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
}

V hlavnom súbore sme nastavili predponu api/vq pre trasy.

Potom to môžeme pripojiť k nášmu hlavnému smerovaču pod predponou api/v1/ späť v našej hlavnej aplikácii:

// 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())

Jednoduchá práca so zložitými cestami od Go umožňuje zjednodušiť štruktúrovanie a údržbu veľkých a zložitých aplikácií.

Práca s middleware

Staging zahŕňa zabalenie jedného HTTP handlera do druhého, čo umožňuje rýchle vykonávanie autentifikácie, kompresie, protokolovania a niekoľkých ďalších funkcií.

Ako príklad sa pozrime na rozhranie http.Handler, použijeme ho na vytvorenie handlera, ktorý overí používateľov služby.

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)
    })
}

Existujú smerovače tretích strán, ako napríklad chi, ktoré vám umožňujú rozšíriť funkčnosť midlvéru.

Práca so statickými súbormi

Štandardná knižnica Go obsahuje funkcie na prácu so statickým obsahom vrátane obrázkov, súborov JavaScript a CSS. Môžete k nim pristupovať prostredníctvom funkcie http.FileServer. Vracia obsluhu, ktorá obsluhuje súbory z konkrétneho adresára.

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

Určite stojí za to pripomenúť, že http.Dir zobrazuje obsah adresára, ak neobsahuje hlavný súbor index.html. V tomto prípade by ste mali použiť balík, aby ste zabránili kompromitácii adresára unindexed.

Správne vypnutie

Go má tiež funkciu nazývanú elegantné vypnutie servera HTTP. Dá sa to urobiť pomocou metódy Shutdown(). Server sa spustí v rutine a potom sa kanál počúva, aby prijal signál prerušenia. Akonáhle je signál prijatý, server sa vypne, ale nie okamžite, ale po niekoľkých sekundách.

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)

Ako záver

Go je výkonný jazyk s takmer univerzálnou štandardnou knižnicou. Jeho predvolené možnosti sú veľmi široké a možno ich vylepšiť pomocou rozhraní – to vám umožní vyvinúť skutočne spoľahlivé servery HTTP.

Skillbox odporúča:

Zdroj: hab.com

Pridať komentár