Razvoj spletnih strežnikov v Golangu - od enostavnega do kompleksnega

Razvoj spletnih strežnikov v Golangu - od enostavnega do kompleksnega

Pred petimi leti sem začel razviti Gophish, je to ponudilo priložnost za učenje golanga. Spoznal sem, da je Go močan jezik, ki ga dopolnjujejo številne knjižnice. Go je vsestranski: zlasti ga je mogoče brez težav uporabljati za razvoj aplikacij na strani strežnika.

Ta članek govori o pisanju strežnika v Go. Začnimo s preprostimi stvarmi, kot je "Hello world!" in končajmo z aplikacijo z naslednjimi zmogljivostmi:

- Uporaba Let's Encrypt za HTTPS.
— Delo kot usmerjevalnik API.
— Delo z vmesno programsko opremo.
— Obdelava statičnih datotek.
— Pravilna zaustavitev.

Skillbox priporoča: Praktični tečaj "Python razvijalec iz nič".

Spomnimo: za vse bralce "Habr" - popust v višini 10 rubljev ob vpisu v kateri koli tečaj Skillbox s promocijsko kodo "Habr".

Pozdravljen, svet!

V Go lahko zelo hitro ustvarite spletni strežnik. Tukaj je primer uporabe upravljalnika, ki vrne "Hello, world!", obljubljeno zgoraj.

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

Po tem, če zaženete aplikacijo in odprete stran localhost, potem boste takoj videli besedilo "Pozdravljen, svet!" (če seveda vse deluje pravilno).

Pozneje bomo upravljalnik večkrat uporabili, a najprej razumemo, kako vse deluje.

net/http

Primer je uporabil paket net/http, je primarno orodje v Go za razvoj strežnikov in odjemalcev HTTP. Da bi razumeli kodo, poglejmo pomen treh pomembnih elementov: http.Handler, http.ServeMux in http.Server.

HTTP obdelovalci

Ko prejmemo zahtevo, jo upravljavec analizira in ustvari odgovor. Obdelovalci v Go so implementirani na naslednji način:

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

Prvi primer uporablja pomočno funkcijo http.HandleFunc. Ovije drugo funkcijo, ki prevzame http.ResponseWriter in http.Request v ServeHTTP.

Z drugimi besedami, obdelovalci v Golangu so predstavljeni v enem vmesniku, ki daje programerju veliko možnosti. Tako je na primer vmesna programska oprema implementirana z uporabo obdelovalca, kjer ServeHTTP najprej naredi nekaj, nato pa pokliče metodo ServeHTTP drugega obdelovalca.

Kot je navedeno zgoraj, upravljavci preprosto ustvarijo odgovore na zahteve. Toda kateri določen vodnik naj se uporabi v določenem trenutku?

Usmerjanje zahtev

Za pravo izbiro uporabite HTTP multiplekser. V številnih knjižnicah se imenuje muxer ali usmerjevalnik, vendar so vsi enaki. Funkcija multiplekserja je analizirati pot zahteve in izbrati ustrezen upravljavec.

Če potrebujete podporo za zapleteno usmerjanje, je bolje uporabiti knjižnice tretjih oseb. Nekaj ​​najnaprednejših - gorila/mux и go-chi/chi, te knjižnice omogočajo brez težav izvajanje vmesne obdelave. Z njihovo pomočjo lahko konfigurirate usmerjanje z nadomestnimi znaki in izvajate številne druge naloge. Njihova prednost je združljivost s standardnimi obdelovalniki HTTP. Posledično lahko napišete preprosto kodo, ki jo lahko v prihodnosti spremenite.

Delo s kompleksnimi okviri v normalnih razmerah bo zahtevalo nestandardne rešitve, kar znatno oteži uporabo privzetih obdelovalcev. Za ustvarjanje velike večine aplikacij bo zadostovala kombinacija privzete knjižnice in preprostega usmerjevalnika.

Obdelava poizvedbe

Poleg tega potrebujemo komponento, ki bo "poslušala" dohodne povezave in preusmerila vse zahteve k pravilnemu upravljavcu. http.Server se zlahka spopade s to nalogo.

Naslednje prikazuje, da je strežnik odgovoren za vsa opravila, ki so povezana z obdelavo povezave. To na primer deluje s protokolom TLS. Za izvedbo klica http.ListenAndServer se uporablja standardni strežnik HTTP.

Zdaj pa poglejmo bolj zapletene primere.

Dodajanje Let's Encrypt

Naša aplikacija privzeto deluje po protokolu HTTP, vendar je priporočljiva uporaba protokola HTTPS. To lahko storite brez težav v Go. Če ste prejeli potrdilo in zasebni ključ, potem je dovolj, da registrirate ListenAndServeTLS s pravilnim potrdilom in datotekami ključev.

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

Vedno si lahko boljši.

Let's Encrypt ponuja brezplačne certifikate s samodejnim obnavljanjem. Za uporabo storitve potrebujete paket autocert.

Najlažji način za konfiguracijo je uporaba metode autocert.NewListener v kombinaciji s http.Serve. Metoda vam omogoča pridobivanje in posodabljanje potrdil TLS, medtem ko strežnik HTTP obdeluje zahteve:

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

Če odpremo v brskalniku example.com, bomo prejeli odgovor HTTPS »Pozdravljen, svet!«

Če potrebujete podrobnejšo konfiguracijo, uporabite upravitelja autocert.Manager. Nato ustvarimo svoj primerek http.Server (do zdaj smo ga uporabljali privzeto) in dodamo upravitelja strežniku 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 preprost način za implementacijo popolne podpore HTTPS s samodejnim obnavljanjem potrdila.

Dodajanje poti po meri

Privzeti usmerjevalnik, ki je vključen v standardno knjižnico, je dober, vendar je zelo osnovni. Večina aplikacij zahteva bolj zapleteno usmerjanje, vključno z ugnezdenimi in nadomestnimi potmi, ali postopek za nastavitev vzorcev poti in parametrov.

V tem primeru je vredno uporabiti pakete gorila/mux и go-chi/chi. S slednjim se bomo naučili delati – primer je prikazan spodaj.

Podana je datoteka api/v1/api.go, ki vsebuje poti za naš 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 glavni datoteki nastavimo predpono api/vq za poti.

Nato lahko to namestimo na naš glavni usmerjevalnik pod predpono api/v1/ nazaj v naši glavni aplikaciji:

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

Enostavnost Go-jevega dela s kompleksnimi potmi omogoča poenostavitev strukturiranja in vzdrževanja velikih, kompleksnih aplikacij.

Delo z vmesno programsko opremo

Staging vključuje ovijanje enega upravljalnika HTTP z drugim, kar omogoča hitro izvajanje preverjanja pristnosti, stiskanja, beleženja in več drugih funkcij.

Kot primer si poglejmo vmesnik http.Handler; uporabili ga bomo za pisanje upravljalnika, ki overja uporabnike storitev.

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

Obstajajo usmerjevalniki tretjih oseb, kot je chi, ki vam omogočajo razširitev funkcionalnosti vmesne programske opreme.

Delo s statičnimi datotekami

Standardna knjižnica Go vključuje zmožnosti za delo s statično vsebino, vključno s slikami, datotekami JavaScript in CSS. Do njih lahko dostopate prek funkcije http.FileServer. Vrne rokovalnik, ki služi datotekam iz določenega imenika.

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

Vsekakor si velja zapomniti, da http.Dir prikaže vsebino imenika, če ta ne vsebuje glavne datoteke index.html. V tem primeru uporabite paket, da preprečite ogrožanje imenika unindexed.

Pravilna zaustavitev

Go ima tudi funkcijo, imenovano elegantna zaustavitev strežnika HTTP. To lahko storite z metodo Shutdown(). Strežnik se zažene v goroutini, nato pa se kanal posluša, da prejme prekinitveni signal. Takoj ko je signal prejet, se strežnik izklopi, vendar ne takoj, ampak po nekaj sekundah.

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)

Kot zaključek

Go je zmogljiv jezik s skoraj univerzalno standardno knjižnico. Njegove privzete zmogljivosti so zelo široke in jih je mogoče izboljšati z uporabo vmesnikov - to vam omogoča razvoj resnično zanesljivih strežnikov HTTP.

Skillbox priporoča:

Vir: www.habr.com

Dodaj komentar