Disvolviĝo de retservilo en Golang - de simpla ĝis kompleksa

Disvolviĝo de retservilo en Golang - de simpla ĝis kompleksa

Antaŭ kvin jaroj mi komencis disvolvi Gophish, tio donis ŝancon lerni Golangon. Mi konstatis, ke Go estas potenca lingvo, kompletigita de multaj bibliotekoj. Go estas multflanka: precipe ĝi povas esti uzata por disvolvi servilflankajn aplikaĵojn sen problemoj.

Ĉi tiu artikolo temas pri skribi servilon en Go. Ni komencu per simplaj aferoj kiel "Saluton mondo!" kaj fini per aplikaĵo kun la jenaj kapabloj:

- Uzante Ni Ĉifri por HTTPS.
— Laborante kiel API-enkursigilo.
— Laborante kun mezvaro.
— Prilaborado de senmovaj dosieroj.
— Ĝusta haltigo.

Skillbox rekomendas: Praktika kurso "Python-programisto de nulo".

Ni memorigas vin: por ĉiuj legantoj de "Habr" - rabato de 10 000 rubloj kiam oni enskribas en iu ajn Skillbox-kurso per la reklamkodo "Habr".

Saluton mondo!

Vi povas krei retservilon en Go tre rapide. Jen ekzemplo de uzado de prizorganto, kiu resendas la supre promesitan "Saluton, mondo!".

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

Post ĉi tio, se vi rulas la aplikon kaj malfermas la paĝon localhost, tiam vi tuj vidos la tekston "Saluton, mondo!" (se ĉio funkcias ĝuste, kompreneble).

Ni uzos la pritraktilon plurfoje poste, sed unue ni komprenu kiel ĉio funkcias.

net/http

La ekzemplo uzis la pakaĵon net/http, ĝi estas la ĉefa ilo en Go por disvolvi ambaŭ servilojn kaj HTTP-klientojn. Por kompreni la kodon, ni komprenu la signifon de tri gravaj elementoj: http.Handler, http.ServeMux kaj http.Server.

HTTP-traktiloj

Kiam ni ricevas peton, la prizorganto analizas ĝin kaj generas respondon. Pritraktiloj en Go estas efektivigitaj jene:

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

La unua ekzemplo uzas la helpan funkcion http.HandleFunc. Ĝi envolvas alian funkcion, kiu siavice prenas http.ResponseWriter kaj http.Request en ServeHTTP.

Alivorte, pritraktiloj en Golang estas prezentitaj en ununura interfaco, kiu donas multajn eblojn al la programisto. Do, ekzemple, mezvaro estas efektivigita uzante prizorganton, kie ServeHTTP unue faras ion kaj poste vokas la ServeHTTP-metodon de alia prizorganto.

Kiel menciite supre, pritraktantoj simple generas respondojn al petoj. Sed kiu aparta prizorganto devus esti uzata en aparta momento?

Peto Vojigon

Por fari la ĝustan elekton, uzu HTTP-multiplexilon. En kelkaj bibliotekoj ĝi nomiĝas muxer aŭ enkursigilo, sed ili ĉiuj estas la sama afero. La funkcio de la multipleksilo estas analizi la petan vojon kaj elekti la taŭgan prizorganton.

Se vi bezonas subtenon por kompleksa vojigo, tiam estas pli bone uzi triajn bibliotekojn. Kelkaj el la plej altnivelaj - gorilo/mux и go-chi/chi, ĉi tiuj bibliotekoj ebligas senprobleme efektivigi mezan prilaboradon. Kun ilia helpo, vi povas agordi ĵokeran vojigon kaj plenumi kelkajn aliajn taskojn. Ilia avantaĝo estas kongruo kun normaj HTTP-traktiloj. Kiel rezulto, vi povas skribi simplan kodon, kiu povas esti modifita estonte.

Labori kun kompleksaj kadroj en normala situacio postulos ne-normajn solvojn, kaj tio signife malfaciligas la uzon de defaŭltaj pritraktiloj. Por krei la vastan plimulton de aplikoj, sufiĉos kombinaĵo de la defaŭlta biblioteko kaj simpla enkursigilo.

Demandotraktado

Krome, ni bezonas komponanton, kiu "aŭskultos" por envenantaj konektoj kaj redirektos ĉiujn petojn al la ĝusta prizorganto. http.Server povas facile trakti ĉi tiun taskon.

La sekvaj montras, ke la servilo respondecas pri ĉiuj taskoj, kiuj rilatas al koneksa prilaborado. Ĉi tio, ekzemple, funkcias uzante la TLS-protokolon. Por efektivigi la http.ListenAndServer-vokon, estas uzata norma HTTP-servilo.

Nun ni rigardu pli kompleksajn ekzemplojn.

Aldonante Ni Ĉifri

Defaŭlte, nia aplikaĵo funkcias per la HTTP-protokolo, sed rekomendas uzi la HTTPS-protokolon. Ĉi tio povas esti farita sen problemoj en Go. Se vi ricevis atestilon kaj privatan ŝlosilon, tiam sufiĉas registri ListenAndServeTLS kun la ĝustaj atestiloj kaj ŝlosilaj dosieroj.

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

Vi ĉiam povas fari pli bone.

Lasita-a Kryri provizas senpagajn atestilojn kun aŭtomata renovigo. Por uzi la servon, vi bezonas pakaĵon autocert.

La plej facila maniero agordi ĝin estas uzi la metodon autocert.NewListener kombine kun http.Serve. La metodo permesas vin akiri kaj ĝisdatigi TLS-atestilojn dum la HTTP-servilo procesas petojn:

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

Se ni malfermas en la retumilo example.com, ni ricevos HTTPS-respondon "Saluton, mondo!"

Se vi bezonas pli detalan agordon, tiam vi devus uzi la administranton autocert.Manager. Poste ni kreas nian propran http.Server-instancon (ĝis nun ni uzis ĝin defaŭlte) kaj aldonas la administranton al la servilo 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("", "")

Ĉi tio estas facila maniero efektivigi plenan HTTPS-subtenon kun aŭtomata atestila renovigo.

Aldonante kutimajn itinerojn

La defaŭlta enkursigilo inkluzivita en la norma biblioteko estas bona, sed ĝi estas tre baza. La plej multaj aplikoj postulas pli kompleksan vojigon, inkluzive de nestitaj kaj ĵokeraj itineroj, aŭ proceduron por fiksado de padpadronoj kaj parametroj.

En ĉi tiu kazo indas uzi pakaĵojn gorilo/mux и go-chi/chi. Ni lernos kiel labori kun ĉi-lasta - ekzemplo estas montrita sube.

Donita estas la dosiero api/v1/api.go enhavanta itinerojn por nia 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
}

Ni starigas la prefikson api/vq por itineroj en la ĉefa dosiero.

Ni tiam povas munti ĉi tion al nia ĉefa enkursigilo sub la prefikso api/v1/ reen en nia ĉefa aplikaĵo:

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

La facileco de Go labori kun kompleksaj itineroj ebligas simpligi la strukturadon kaj prizorgadon de grandaj, kompleksaj aplikoj.

Laborante kun mezvaro

Enscenigo implikas envolvi unu HTTP-traktilon kun alia, ebligante rapide plenumi aŭtentikigon, kunpremadon, registradon kaj plurajn aliajn funkciojn.

Ekzemple, ni rigardu la http.Handler-interfacon, ni uzos ĝin por skribi pritraktilon, kiu aŭtentikigas servouzantoj.

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

Estas triaj enkursigiloj, kiel chi, kiuj ebligas al vi etendi la mezvaran funkcion.

Laborante kun statikaj dosieroj

La norma biblioteko Go inkluzivas kapablojn por labori kun senmova enhavo, inkluzive de bildoj, JavaScript kaj CSS-dosieroj. Ili estas alireblaj per la funkcio http.FileServer. Ĝi resendas pritraktilon kiu servas dosierojn de specifa dosierujo.

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

Nepre indas memori, ke http.Dir montras la enhavon de la dosierujo se ĝi ne enhavas la ĉefan index.html dosieron. En ĉi tiu kazo, por malhelpi la dosierujon esti kompromitita, vi devus uzi la pakaĵon unindexed.

Ĝusta haltigo

Go ankaŭ havas funkcion nomatan gracia malŝalto de la HTTP-servilo. Ĉi tio povas esti farita per la metodo Shutdown(). La servilo estas komencita en goroutino, kaj tiam la kanalo estas aŭskultita por ricevi interrompan signalon. Tuj kiam la signalo estas ricevita, la servilo malŝaltas, sed ne tuj, sed post kelkaj sekundoj.

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)

Kiel konkludo

Go estas potenca lingvo kun preskaŭ universala norma biblioteko. Ĝiaj defaŭltaj kapabloj estas tre larĝaj, kaj ili povas esti plibonigitaj per interfacoj - tio ebligas al vi evoluigi vere fidindajn HTTP-servilojn.

Skillbox rekomendas:

fonto: www.habr.com

Aldoni komenton