Ž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.
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!“.
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.
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:
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:
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
}
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.
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.