Žiniatinklio serverio kūrimas Golang mieste – nuo ​​paprasto iki sudėtingo

Žiniatinklio serverio kūrimas Golang mieste – nuo ​​paprasto iki sudėtingo

Prieš penkerius metus pradėjau sukurti Gophish, tai suteikė galimybę išmokti Golango. Supratau, kad „Go“ yra galinga kalba, kurią papildo daugybė bibliotekų. „Go“ yra universalus: ypač jis gali būti naudojamas kuriant serverio programas be jokių problemų.

Šis straipsnis yra apie serverio kūrimą naudojant Go. Pradėkime nuo paprastų dalykų, pvz., „Sveikas pasauli!“ ir baigkime programa su šiomis galimybėmis:

- Naudojant Let's Encrypt HTTPS.
- Darbas API maršrutizatoriumi.
- Darbas su tarpine programine įranga.
— Statinių failų apdorojimas.
— Teisingas išjungimas.

„Skillbox“ rekomenduoja: Praktinis kursas „Python kūrėjas nuo nulio“.

Primename: visiems „Habr“ skaitytojams – 10 000 rublių nuolaida užsiregistravus į bet kurį „Skillbox“ kursą naudojant „Habr“ reklamos kodą.

Labas pasauli!

Naudodami Go galite labai greitai sukurti žiniatinklio serverį. Štai pavyzdys, kaip naudoti tvarkyklę, kuri grąžina aukščiau pažadėtą ​​žodį „Sveikas, pasauli!“.

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 to, jei paleisite programą ir atidarysite puslapį localhost, tada iškart pamatysite tekstą „Sveikas, pasauli! (jei viskas veikia teisingai, žinoma).

Vėliau tvarkyklę naudosime kelis kartus, bet pirmiausia išsiaiškinkime, kaip viskas veikia.

net/http

Pavyzdyje buvo naudojamas paketas net/http, tai yra pagrindinis „Go“ įrankis, skirtas serveriams ir HTTP klientams kurti. Norėdami suprasti kodą, supraskime trijų svarbių elementų reikšmę: http.Handler, http.ServeMux ir http.Server.

HTTP tvarkyklės

Kai gauname užklausą, tvarkytojas ją analizuoja ir sugeneruoja atsakymą. „Go“ tvarkyklės įdiegtos taip:

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

Pirmajame pavyzdyje naudojama http.HandleFunc pagalbinė funkcija. Ji apima kitą funkciją, kuri savo ruožtu perkelia http.ResponseWriter ir http.Request į ServeHTTP.

Kitaip tariant, Golang tvarkyklės pateikiamos vienoje sąsajoje, kuri programuotojui suteikia daug galimybių. Taigi, pavyzdžiui, tarpinė programinė įranga yra įdiegta naudojant tvarkyklę, kur ServeHTTP pirmiausia ką nors daro ir tada iškviečia kitos tvarkyklės ServeHTTP metodą.

Kaip minėta pirmiau, tvarkytojai tiesiog generuoja atsakymus į užklausas. Bet kuris konkretus tvarkytuvas turėtų būti naudojamas tam tikru momentu?

Prašyti maršruto parinkimo

Norėdami teisingai pasirinkti, naudokite HTTP multiplekserį. Daugelyje bibliotekų jis vadinamas muxer arba maršrutizatoriumi, tačiau jie visi yra vienodi. Multiplekserio funkcija yra išanalizuoti užklausos kelią ir pasirinkti tinkamą tvarkyklę.

Jei jums reikia sudėtingo maršruto parinkimo, geriau naudoti trečiųjų šalių bibliotekas. Kai kurie iš pažangiausių - gorila/mux и go-chi/chi, šios bibliotekos leidžia be jokių problemų įgyvendinti tarpinį apdorojimą. Su jų pagalba galite sukonfigūruoti pakaitos simbolių maršrutą ir atlikti daugybę kitų užduočių. Jų pranašumas yra suderinamumas su standartinėmis HTTP tvarkyklėmis. Dėl to galite parašyti paprastą kodą, kurį ateityje bus galima modifikuoti.

Dirbant su sudėtingomis sistemomis įprastoje situacijoje reikės nestandartinių sprendimų, o tai labai apsunkina numatytųjų tvarkyklių naudojimą. Norint sukurti didžiąją dalį programų, pakaks numatytosios bibliotekos ir paprasto maršrutizatoriaus derinio.

Užklausų apdorojimas

Be to, mums reikia komponento, kuris „klausys“ gaunamų ryšių ir nukreips visas užklausas į tinkamą tvarkyklę. http.Serveris gali lengvai susidoroti su šia užduotimi.

Toliau parodyta, kad serveris yra atsakingas už visas užduotis, susijusias su ryšio apdorojimu. Tai, pavyzdžiui, veikia naudojant TLS protokolą. Norėdami įgyvendinti http.ListenAndServer iškvietimą, naudojamas standartinis HTTP serveris.

Dabar pažvelkime į sudėtingesnius pavyzdžius.

Pridedamas Let's Encrypt

Pagal numatytuosius nustatymus mūsų programa veikia per HTTP protokolą, tačiau rekomenduojama naudoti HTTPS protokolą. Tai galima padaryti be problemų naudojant „Go“. Jei gavote sertifikatą ir privatų raktą, pakanka užregistruoti ListenAndServeTLS su tinkamais sertifikatais ir raktų failais.

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

Jūs visada galite padaryti geriau.

Let's Encrypt suteikia nemokamus sertifikatus su automatiniu atnaujinimu. Norint naudotis paslauga, reikalingas paketas autocert.

Lengviausias būdas jį sukonfigūruoti yra naudoti autocert.NewListener metodą kartu su http.Serve. Šis metodas leidžia gauti ir atnaujinti TLS sertifikatus, kai HTTP serveris apdoroja užklausas:

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

Jei atidarysime naršyklėje example.com, gausime HTTPS atsakymą „Sveikas, pasauli!

Jei jums reikia išsamesnės konfigūracijos, turėtumėte naudoti autocert.Manager tvarkyklę. Tada sukuriame savo http.Server egzempliorių (iki šiol jį naudojome pagal numatytuosius nustatymus) ir pridedame tvarkyklę prie TLSConfig serverio:

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

Tai paprastas būdas įdiegti visą HTTPS palaikymą su automatiniu sertifikato atnaujinimu.

Pridedamas pasirinktiniai maršrutai

Numatytasis maršrutizatorius, įtrauktas į standartinę biblioteką, yra geras, tačiau jis yra labai paprastas. Daugeliui programų reikalingas sudėtingesnis maršrutas, įskaitant įdėtus ir pakaitos simbolius, arba kelių šablonų ir parametrų nustatymo procedūrą.

Šiuo atveju verta naudoti paketus gorila/mux и go-chi/chi. Išmoksime dirbti su pastaruoju – pavyzdys parodytas žemiau.

Pateikiamas failas api/v1/api.go, kuriame yra mūsų API maršrutai:

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

Pagrindiniame faile nustatome maršrutų api/vq priešdėlį.

Tada galime prijungti jį prie pagrindinio maršrutizatoriaus su api/v1/ priešdėliu pagrindinėje programoje:

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

„Go“ paprastas darbas su sudėtingais maršrutais leidžia supaprastinti didelių, sudėtingų programų struktūrizavimą ir priežiūrą.

Darbas su tarpine programine įranga

Suskirstymas apima vienos HTTP tvarkyklės suvyniojimą į kitą, todėl galima greitai atlikti autentifikavimą, suspaudimą, registravimą ir keletą kitų funkcijų.

Kaip pavyzdį pažvelkime į http.Handler sąsają; naudosime ją tvarkyklei, kuri autentifikuoja paslaugų vartotojus, parašyti.

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

Yra trečiųjų šalių maršrutizatoriai, tokie kaip chi, kurie leidžia išplėsti tarpinės programinės įrangos funkcionalumą.

Darbas su statiniais failais

Standartinėje Go bibliotekoje yra galimybių dirbti su statiniu turiniu, įskaitant vaizdus, ​​JavaScript ir CSS failus. Juos galima pasiekti naudojant http.FileServer funkciją. Jis grąžina tvarkyklę, kuri aptarnauja failus iš konkretaus katalogo.

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

Tikrai verta atsiminti, kad http.Dir rodo katalogo turinį, jei jame nėra pagrindinio index.html failo. Tokiu atveju, kad katalogas nebūtų pažeistas, turėtumėte naudoti paketą unindexed.

Teisingas išjungimas

„Go“ taip pat turi funkciją, vadinamą grakščiu HTTP serverio išjungimu. Tai galima padaryti naudojant Shutdown() metodą. Serveris paleidžiamas gorutinoje, o tada klausomasi kanalo, kad gautų pertraukimo signalą. Kai tik gaunamas signalas, serveris išsijungia, bet ne iš karto, o po kelių sekundžių.

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)

Kaip išvadą

Go yra galinga kalba su beveik universalia standartine biblioteka. Jo numatytosios galimybės yra labai plačios ir jas galima patobulinti naudojant sąsajas – tai leidžia sukurti tikrai patikimus HTTP serverius.

„Skillbox“ rekomenduoja:

Šaltinis: www.habr.com

Добавить комментарий