Vývoj webových serverov v Golangu — od jednoduchého k zložitým

Vývoj webových serverov v Golangu — od jednoduchého k zložitým

Pred piatimi rokmi som začal vyvíjať Gophish, to mi umožnilo študovať Golang. Pochopil som, že Go je mocný jazyk, ktorého možnosti sú doplnené mnohými knižnicami. Go je univerzálny: konkrétne, s jeho pomocou je možné bez problémov vyvíjať serverové aplikácie.

Tento článok sa venuje písaniu servera v Go. Začneme od jednoduchých vecí, ako je „Hello world!“, a skončíme aplikáciou s takýmito možnosťami:

— Používanie Let’s Encrypt pre HTTPS.
— Práca ako API-router.
— Práca s middleware.
— Spracovanie statických súborov.
— Správne ukončenie.

Skillbox odporúča: Praktický kurz „Python-developer od nuly“.

Pripomíname: pre všetkých čitateľov „Habr“ — zľava 10 000 rubľov pri zapísaní na akýkoľvek kurz Skillbox s promo kódom „Habr“.

Ahoj, svete!

Vytvorenie webového servera na Go je veľmi rýchle. Tu je príklad použitia obsluhy, ktorá vracia sľúbené „Ahoj, svete!“.

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, okamžite uvidíte text „Ahoj, svete!“ (samozrejme, ak to všetko funguje správne).

Potom budeme opakovane používať obsluhu, ale najprv pochopíme, ako to všetko funguje.

net/http

Vo vzore bol použitý balík net/http, je to základný prostriedok v Go na vývoj serverov a HTTP klientov. Aby sme porozumeli kódu, poďme sa pozrieť na význam troch dôležitých prvkov: http.Handler, http.ServeMux a http.Server.

HTTP spracovatelia

Keď dostaneme požiadavku, spracovateľ ju analyzuje a vytvára odpoveď. Spracovatelia v Go sú implementovaní nasledovne:

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

V prvom príklade sa používa pomocná funkcia http.HandleFunc. Obaluje inú funkciu, ktorá na druhej strane prijíma http.ResponseWriter a http.Request v ServeHTTP.

Inými slovami, spracovatelia v Golangu sú reprezentovaní jedným rozhraním, čo ponúka množstvo možností pre programátora. Napríklad, middleware je implementované pomocou spracovateľa, kde ServeHTTP najprv niečo robí a potom volá metódu ServeHTTP iného spracovateľa.

Ako už bolo spomenuté, spracovatelia jednoducho vytvárajú odpovede na požiadavky. Ale ktorý konkrétny spracovateľ treba použiť v konkrétnom čase?

Smerovanie požiadaviek

Aby ste si správne vybrali, využite HTTP multiplexer. V niektorých knižniciach sa nazýva muxer alebo router, ale všetko je to rovnaké. Funkcia multiplexeru spočíva v analýze cesty požiadavky a vo výbere vhodného spracovateľa.

Ak je potrebná podpora zložitého smerovania, je lepšie využiť externé knižnice. Niektoré z najpokročilejších sú gorilla/mux a go-chi/chi, tieto knižnice umožňujú realizovať medzi spracovanie bez väčších problémov. S ich pomocou môžete nastaviť wildcard smerovanie a vykonať množstvo iných úloh. Ich výhodou je kompatibilita so štandardnými HTTP spracovateľmi. Výsledkom je, že môžete napísať jednoduchý kód s možnosťou jeho modifikácie v budúcnosti.

Práca so zložitými frameworkami v bežných situáciách si vyžaduje nie úplne štandardné riešenia, čo značne sťažuje používanie predvolených spracovateľov. Na vytvorenie drvivej väčšiny aplikácií stačí kombinácia knižnice predvolenej a jednoduchého smerovača.

Spracovanie požiadaviek

Okrem toho potrebujeme komponent, ktorý bude "počúvať" prichádzajúce pripojenia a presmeruje všetky požiadavky správnemu spracovateľovi. S touto úlohou si bez problémov poradí http.Server.

Nižšie je uvedené, že server je zodpovedný za všetky úlohy, ktoré súvisia so spracovaním pripojení. Napríklad prevádzka cez protokol 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

Predvolene naša aplikácia funguje cez HTTP protokol, avšak odporúča sa používať protokol HTTPS. V Go to možno urobiť bez problémov. Ak ste získali certifikát a súkromný kľúč, stačí uviesť ListenAndServeTLS so správnymi súbormi certifikátu a kľúča.

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

Vždy je možné to urobiť lepšie.

Let’s Encrypt poskytuje bezplatné certifikáty s možnosťou automatickej aktualizácie. Na využitie služby je potrebný balík autocert.

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

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

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

Ak potrebujete podrobnejšie nastavenie, mali by ste využiť manažér autocert.Manager. Potom vytvoríme vlastný inštanciu http.Server (do tejto chvíle sme ho používali predvolene) a pridáme manažér do servera 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("", "")

To je jednoduchý spôsob, ako implementovať plnú podporu HTTPS s automatickou aktualizáciou certifikátu.

Pridanie vlastných trás

Predvolený router zahrnutý v štandardnej knižnici je dobrý, ale je veľmi jednoduchý. V väčšine aplikácií je potrebná zložitejšia smerovanie, vrátane vnorených a wildcard trás alebo procedúry nastavenia šablón a parametrov trás.

V tomto prípade by malo byť rozumné použiť balíky gorilla/mux a go-chi/chi. S posledným sa naučíme pracovať — nižšie je uvedený príklad.

Máme súbor api/v1/api.go, ktorý obsahuje 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
}

Nastavíme predponu pre trasy api/vq v hlavnom súbore.

Potom to môžeme pripojiť k nášmu hlavnému routeru pod prefixom api/v1 vo 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())

Jednoduchosť práce so zložitými trasami v Go umožňuje zjednodušiť štruktúrovanie pri správe veľkých komplexných aplikácií.

Práca s middleware

Pri medziobslužnom spracovaní sa používa obalenie jedného HTTP spracovateľa ďalším, čo umožňuje rýchlo vykonávať autentifikáciu, kompresiu, logovanie a niektoré ďalšie funkcie.

Ako príklad si vezmime rozhranie http.Handler, pomocou ktorého napíšeme spracovateľa s autentifikáciou 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
        }
        \/\/ Ak autentifikácia prešla, spusti pôvodného spracovateľa
        next.ServeHTTP(w, r)
    })
}

Existujú aj tretie strany routery, ako napríklad chi, ktoré umožňujú rozšíriť funkčnosť medziobsluhy.

Práca so statickými súbormi

Štandardná knižnica Go obsahuje možnosti pre prácu so statickým obsahom, vrátane obrázkov, ako aj súborov JavaScript a CSS. K nim sa dá pristupovať cez funkciu http.FileServer. Tá vracia spracovateľa, ktorý distribuuje súbory z určitej priečinky.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("\/ {name}\", HelloName)
 
\/\/ Nastavenie distribúcie statických súborov
staticPath, _ := filepath.Abs("..\/..\/static\/\")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("\/*\", fs)
    
    return r

Je dôležité pamätať na to, že http.Dir zobrazuje obsah priečinka, ak v ňom nie je hlavný súbor index.html. V takom prípade, aby sme predišli kompromitácii priečinka, je dobré použiť balík unindexed.

Korektné ukončenie práce

V Go existuje aj funkcia, ako korektné ukončenie HTTP servera. To sa dá dosiahnuť pomocou metódy Shutdown(). Server beží v gorutine, a následne je kanál počúvaný na prijatie signálu 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...))
}()
 
\/\/ Čakajte na prerušenie
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
\/\/ Pokus o korektné ukončenie
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

Na záver

Go — mocný jazyk s takmer univerzálnou štandardnou knižnicou. Jeho predvolené možnosti sú veľmi široké, a ich rozšírenie je možné pomocou rozhraní — to umožňuje vyvíjať skutočne spoľahlivé HTTP servery.

Skillbox odporúča:

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster