Zhvillimi i serverëve web në Golang — nga e thjeshta në të komplikuar

Zhvillimi i serverëve web në Golang — nga e thjeshta në të komplikuar

Pesë vjet më parë, fillova të zhvilloja Gophish, kjo më dha mundësinë të mësoj Golang. Kuptova se Go është një gjuhë e fuqishme, pavarësisht mundësive të saj që plotësohen nga shumë biblioteka. Go është universal: në veçanti, me të mund të zhvillosh pa probleme aplikacione serveri.

Ky artikull i kushtohet zhvillimit të një serveri në Go. Le të fillojmë me gjërat e thjeshta, si "Përshëndetje, botë!", dhe të përfundojmë me një aplikacion që ka këto mundësi:

— Përdorimi i Let’s Encrypt për HTTPS.
— Funksionimi si një API router.
— Puna me middleware.
— Trajtimi i skedarëve statikë.
— Mbyllja e duhur.

Skillbox rekomandon: Kurs praktik "Zhvillues Python nga zero".

Kujtojmë: për të gjithë lexuesit e «Habra» — zbritje prej 10,000 rublesh për regjistrimin në çdo kurs Skillbox me kodin promovues «Habr».

Përshëndetje, botë!

Të krijosh një server web në Go është shumë e shpejtë. Ja një shembull i përdorimit të një trajtuesi që kthen "Përshëndetje, botë!" siç u premtua më parë.

package main

import (
"fmt"
"net/http"
)

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Përshëndetje, botë!")
})
http.ListenAndServe(":80", nil)
}

Pas kësaj, nëse e nisni aplikacionin dhe hapni faqen localhost, do të shihni menjëherë tekstin "Përshëndetje, botë!" (sigurisht, nëse gjithçka funksionon siç duhet).

Më pas, do të përdorim shpesh trajtuesin, por së pari le të kuptojmë se si funksionon gjithçka.

net/http

Në shembull u përdor paketa net/http, kjo është mjeti kryesor në Go për zhvillimin e serverëve dhe HTTP klientëve. Për të kuptuar kodin, le të shqyrtojmë tri elementë të rëndësishëm: http.Handler, http.ServeMux dhe http.Server.

Trajtuesit HTTP

Kur marrim një kërkesë, trajtuesi e analizon atë dhe formon një përgjigje. Trajtuesit në Go janë realizuar në këtë mënyrë:

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

Në shembullin e parë përdoret funksioni ndihmës http.HandleFunc. Ai mbështjell një funksion tjetër, i cili, nga ana e tij, pranon http.ResponseWriter dhe http.Request në ServeHTTP.

Me fjalë të tjera, trajtuesit në Golang janë të përfaqësuar nga një interface, që ofron shumë mundësi për programuesin. Kështu, për shembull, middleware është realizuar nëpërmjet trajtuesit, ku ServeHTTP së pari bën diçka, dhe më pas thërret metodën ServeHTTP të një trajtuesi tjetër.

Siç u tha më lart, trajtuesit thjesht formojnë përgjigje për kërkesat. Por cili trajtues duhet të përdorim në një moment të caktuar?

Routimi i kërkesave

Për të bërë një zgjedhje të saktë, përdorni shumëkaluarësin HTTP. Në disa biblioteka ai quhet muxer ose router, por të gjitha janë e njëjta gjë. Funksioni i shumëkaluarësit është të analizojë rrugën e kërkesës dhe të zgjedhë trajtuesin e duhur.

Nëse nevojitet mbështetje për routing të komplikuar, atëherë është më mirë të përdoren biblioteka të jashtme. Disa nga më të avancuarat janë gorilla/mux dhe go-chi/chi, këto biblioteka ofrojnë mundësinë për të realizuar përpunimin ndërmjetës pa shumë probleme. Me ndihmën e tyre, mund të konfigurohet routing me wildcard dhe të realizohen një sërë detyrash të tjera. Avantazhi i tyre është se janë të përputhshme me trajtuesit standardë HTTP. Si rezultat, mund të shkruani kod të thjeshtë me mundësi të modifikimit në të ardhmen.

Puna me framework të komplikuara në situata të zakonshme do të kërkojë zgjidhje jo standarde, gjë që e komplikon ndjeshëm përdorimin e trajtuesve default. Për krijimin e shumicës dërrmuese të aplikacioneve mjafton një kombinim i bibliotekës më të zakonshme dhe një router të thjeshtë.

Përpunimi i kërkesave

Përveç kësaj, na nevojitet një komponent që do të "dëgjojë" lidhjet që vijnë dhe të redirektojë të gjitha kërkesat te trajtuesi i duhur. Me këtë detyrë do të përballet pa problemet http.Server.

Më poshtë tregohet se serveri është përgjegjës për të gjitha detyrat që kanë të bëjnë me përpunimin e lidhjeve. Kjo, për shembull, përfshin punën sipas protokollit TLS. Për realizimin e thirrjes http.ListenAndServer përdoret serveri standard HTTP.

Tani le të shqyrtojmë shembuj më kompleks.

Shtimi i Let’s Encrypt

Për default, aplikacioni ynë punon sipas protokollit HTTP, megjithatë rekomandohet përdorimi i protokollit HTTPS. Në Go kjo mund të bëhet pa probleme. Nëse keni marrë një certifikatë dhe çelës privat, atëherë mjafton të shkruani ListenAndServeTLS me tregimin e skedarëve të duhur të certifikatës dhe çelësit.

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

Gjithmonë mund të bëhet më mirë.

Let’s Encrypt ofron certifikata falas me mundësinë e rinovimit automatik. Për të shfrytëzuar këtë shërbim, nevojitet paketa autocert.

Mënyra më e thjeshtë për ta konfiguruar atë — është të përdorni metodën autocert.NewListener në kombinim me http.Serve. Metoda lejon marrjen dhe rinovimin e certifikatave TLS, ndërkohë që serveri HTTP përpunon kërkesat:

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

Nëse e hapim në shfletues example.com, do të marrësh përgjigje HTTPS "Hello, world!".

Nëse nevojitet një konfigurim më i detajuar, ka kuptim të përdorësh menaxherin autocert.Manager. Më pas krijojmë instancën tonë http.Server (deri më tani e kemi përdorur atë si parazgjedhje) dhe e shtojmë menaxherin në serverin 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("", "")

Ky është një mënyrë e thjeshtë për të implementuar mbështetje të plotë HTTPS me azhurnimin automatik të certifikatës.

Shtimi i rrugëve jo standarde

Rruga e parazgjedhur, e cila është e përfshirë në bibliotekën standarde, është e mirë, por është shumë e thjeshtë. Në shumicën e aplikacioneve nevojitet një rrugëzim më kompleks, duke përfshirë rrugë të brendshme dhe wildcard ose procedurën e vendosjes së pamjeve dhe parametrave të rrugëve.

Në këtë rast ka kuptim të përdoren paketat gorilla/mux dhe go-chi/chi. Me të fundit do të mësojmë të punojmë - më poshtë është një shembull.

I dhënë - file api/v1/api.go, që përmban rrugët për API-në tonë:

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

Caktimi i prefiksit api/vq për rrugët në skedarin kryesor.

Mund ta ngjisim këtë në routerin tonë kryesor nën prefiksin api/v1 në aplikacionin tonë kryesor:

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

Thjeshtësia e punës me rrugë të komplikuara në Go e bën të mundur thjeshtimin e strukturimit me mbështetje për aplikacione të mëdha dhe komplekse.

Puna me middleware

Në rastin e përpunimit të ndërmjetëm, një HTTP-handler i mbështjellë nga një tjetër bëhet e mundur për të kryer shpejt autentifikimin, kompresimin, regjistrimin dhe disa funksione të tjera.

Si shembull, le të shqyrtojmë ndërfaqen http.Handler, me ndihmën e së cilës do të shkruajmë një handler me autentifikimin e përdoruesve të shërbimit.

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
        }
        \/\/ Supozoni se autentifikimi kaloi, ekzekutoni handler-in origjinal
        next.ServeHTTP(w, r)
    })
}

Ekzistojnë routerë të tjerë, si chi, të cilët lejojnë zgjerimin e funksionalitetit të përpunimit të ndërmjetëm.

Puna me skedarët statikë

Biblioteka standarde Go përmban mundësi për të punuar me përmbajtje statistikë, duke përfshirë imazhe, si dhe skedarë JavaScript dhe CSS. Praksesat mund të arrihen përmes funksionit http.FileServer. Ajo kthen një handler që shpërndan skedarë nga një direktori e caktuar.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("/{name}", HelloName)
 
// Konfigurimi i shërbimit të skedarëve statikë
staticPath, _ := filepath.Abs("../../static/")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("/*", fs)
    
    return r

Është e rëndësishme të kujtojmë se http.Dir tregon përmbajtjen e katalogut vetëm në rast se nuk ka skedarin kryesor index.html. Në këtë rast, për të mos e lejuar kompromisimin e katalogut, është e nevojshme të përdorim paketën pa indeksuar.

Mbyllje e saktë

Në Go ekziston dhe një funksion për mbyllje të saktë të serverit HTTP. Kjo mund të bëhet përmes metodës Shutdown(). Serveri niset në një gorutinë, dhe pastaj kanali dëgjon për marrjen e një sinjali ndërprerjeje. Sa herë që merret sinjali, serveri mbyllet, por jo menjëherë, por pas disa sekondash.

handler := server.NewRouter()
srv := &http.Server{
    Handler: handler,
}
 
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
 
// Prit për një ndërprerje
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
// Përpiqu për një mbyllje të saktë
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

Në përfundim

Go — një gjuhë e fuqishme me pothuajse një bibliotekë standarde universale. Mundësitë e saj të paracaktuara janë shumë të gjera, dhe ato mund të përmirësohen përmes ndërfaqeve — kjo lejon zhvillimin e serverëve HTTP vërtet të besueshëm.

Skillbox rekomandon:

Burimi: habr.com

Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS 🔥 Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS - ProHoster