Webszerverfejlesztés Golangban – az egyszerűtől a bonyolultig

Webszerverfejlesztés Golangban – az egyszerűtől a bonyolultig

Öt éve kezdtem fejleszteni a Gophish-t, ez lehetőséget adott a Golang megtanulására. Rájöttem, hogy a Go egy erőteljes nyelv, amelyet számos könyvtár egészít ki. A Go sokoldalú: különösen problémamentesen használható szerveroldali alkalmazások fejlesztésére.

Ez a cikk egy szerver létrehozásáról szól a Go-ban. Kezdjük az olyan egyszerű dolgokkal, mint a „Hello world!”, és fejezzük be a következő képességekkel rendelkező alkalmazást:

- A Let's Encrypt használata HTTPS-hez.
— API routerként dolgozik.
— Köztes szoftverrel való munka.
— Statikus fájlok feldolgozása.
— Korrekt leállítás.

A Skillbox a következőket ajánlja: Gyakorlati tanfolyam "Python fejlesztő a semmiből".

Emlékeztetünk: a "Habr" minden olvasója számára - 10 000 rubel kedvezmény, ha a "Habr" promóciós kóddal bármely Skillbox tanfolyamra jelentkezik.

Helló Világ!

Nagyon gyorsan létrehozhat webszervert a Go alkalmazásban. Íme egy példa egy olyan kezelő használatára, amely a fent megígért „Hello, world!”-t adja vissza.

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

Ezt követően, ha futtatja az alkalmazást és megnyitja az oldalt localhost, akkor azonnal megjelenik a „Hello, world!” szöveg. (természetesen ha minden jól működik).

Később többször fogjuk használni a kezelőt, de először értsük meg, hogyan működik minden.

net/http

A példa a csomagot használta net/http, ez a Go elsődleges eszköze szerverek és HTTP-kliensek fejlesztéséhez. A kód megértéséhez értsük meg három fontos elem jelentését: http.Handler, http.ServeMux és http.Server.

HTTP-kezelők

Amikor egy kérést kapunk, a kezelő elemzi azt, és választ generál. A Go kezelői a következőképpen vannak megvalósítva:

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

Az első példa a http.HandleFunc helper függvényt használja. Egy másik függvényt csomagol be, amely viszont a http.ResponseWriter és a http.Request szolgáltatást átveszi a ServeHTTP-be.

Más szóval, a Golang kezelői egyetlen felületen jelennek meg, ami sok lehetőséget ad a programozónak. Így például a köztes szoftver egy kezelő segítségével valósul meg, ahol a ServeHTTP először csinál valamit, majd meghívja egy másik kezelő ServeHTTP metódusát.

Ahogy fentebb említettük, a kezelők egyszerűen válaszokat generálnak a kérésekre. De melyik kezelőt kell használni egy adott időpontban?

Útválasztás kérése

A helyes választáshoz használjon HTTP multiplexert. Számos könyvtárban muxernek vagy routernek hívják, de mindegyik ugyanaz. A multiplexer feladata a kérés útvonalának elemzése és a megfelelő kezelő kiválasztása.

Ha támogatásra van szüksége az összetett útválasztáshoz, akkor jobb, ha harmadik féltől származó könyvtárakat használ. Néhány a legfejlettebb - gorilla/mux и go-chi/chi, ezek a könyvtárak lehetővé teszik a közbenső feldolgozás problémamentes megvalósítását. Segítségükkel konfigurálhatja a helyettesítő karakteres útválasztást, és számos egyéb feladatot is végrehajthat. Előnyük a szabványos HTTP-kezelőkkel való kompatibilitás. Ennek eredményeként egyszerű kódot írhat, amely a jövőben módosítható.

A bonyolult keretrendszerekkel való munka normál helyzetben nem szabványos megoldásokat igényel, és ez jelentősen megnehezíti az alapértelmezett kezelők használatát. Az alkalmazások túlnyomó többségének létrehozásához elegendő az alapértelmezett könyvtár és egy egyszerű útválasztó kombinációja.

Lekérdezés feldolgozása

Ezenkívül szükségünk van egy komponensre, amely „meghallgatja” a bejövő kapcsolatokat, és minden kérést átirányít a megfelelő kezelőhöz. A http.Server könnyen kezeli ezt a feladatot.

Az alábbiakból látható, hogy a szerver felelős minden olyan feladatért, amely a kapcsolat feldolgozásával kapcsolatos. Ez például a TLS protokoll használatával működik. A http.ListenAndServer hívás megvalósításához szabványos HTTP szervert használnak.

Most nézzünk bonyolultabb példákat.

A Let's Encrypt hozzáadása

Alkalmazásunk alapértelmezés szerint a HTTP protokollon fut, de javasolt a HTTPS protokoll használata. Ez Go-ban probléma nélkül megtehető. Ha kapott egy tanúsítványt és privát kulcsot, akkor elegendő regisztrálnia a ListenAndServeTLS-t a megfelelő tanúsítvány- és kulcsfájlokkal.

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

Mindig lehet jobbat csinálni.

Titkosítjuk ingyenes tanúsítványokat biztosít automatikus megújítással. A szolgáltatás igénybevételéhez csomagra van szükség autocert.

A konfigurálás legegyszerűbb módja az autocert.NewListener metódus használata a http.Serve-vel kombinálva. A módszer lehetővé teszi TLS-tanúsítványok beszerzését és frissítését, miközben a HTTP-kiszolgáló a következő kéréseket dolgozza fel:

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

Ha megnyitjuk a böngészőben example.com, kapunk egy HTTPS-választ „Hello, world!”

Ha részletesebb konfigurációra van szüksége, akkor használja az autocert.Manager managert. Ezután létrehozzuk a saját http.Server példányunkat (eddig alapértelmezés szerint ezt használtuk), és hozzáadjuk a kezelőt a TLSConfig szerverhez:

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

Ez egy egyszerű módja a teljes HTTPS-támogatás megvalósításának automatikus tanúsítványmegújítással.

Egyéni útvonalak hozzáadása

A szabványos könyvtárban található alapértelmezett útválasztó jó, de nagyon alap. A legtöbb alkalmazás bonyolultabb útválasztást igényel, beleértve a beágyazott és helyettesítő karakteres útvonalakat, vagy az útvonalminták és paraméterek beállítására szolgáló eljárást.

Ebben az esetben érdemes csomagokat használni gorilla/mux и go-chi/chi. Megtanuljuk, hogyan kell ez utóbbival dolgozni - egy példa az alábbiakban látható.

Adott az api/v1/api.go fájl, amely az API-nk útvonalait tartalmazza:

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

Beállítjuk az api/vq előtagot az útvonalakhoz a főfájlban.

Ezt követően csatlakoztathatjuk ezt a fő útválasztónkhoz az api/v1/ előtag alatt a fő alkalmazásunkban:

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

A Go egyszerű, összetett útvonalakkal való munkavégzése lehetővé teszi a nagy, összetett alkalmazások strukturálásának és karbantartásának egyszerűsítését.

Köztes szoftverrel való munka

A szakaszolás magában foglalja az egyik HTTP-kezelőt egy másikkal, ami lehetővé teszi a hitelesítés, a tömörítés, a naplózás és számos egyéb funkció gyors végrehajtását.

Példaként nézzük meg a http.Handler felületet, amely segítségével a szolgáltatás felhasználóit hitelesítő kezelőt írunk.

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

Vannak harmadik féltől származó útválasztók, például a chi, amelyek lehetővé teszik a köztes szoftver funkcióinak kiterjesztését.

Statikus fájlokkal végzett munka

A Go szabványos könyvtára képes statikus tartalommal dolgozni, beleértve a képeket, JavaScript- és CSS-fájlokat. Ezek a http.FileServer függvényen keresztül érhetők el. Egy kezelőt ad vissza, amely egy adott könyvtárból szolgál ki fájlokat.

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

Mindenképpen érdemes megjegyezni, hogy a http.Dir akkor jeleníti meg a könyvtár tartalmát, ha az nem tartalmazza a fő index.html fájlt. Ebben az esetben, hogy elkerülje a címtár feltörését, használja a csomagot unindexed.

Korrekt leállítás

A Go emellett rendelkezik a HTTP-kiszolgáló kecses leállításának nevezett funkciójával is. Ezt a Shutdown() metódussal lehet megtenni. A szerver gorutinban indul el, majd a csatorna meghallgatása megszakítási jelet kap. Amint a jel megérkezik, a szerver kikapcsol, de nem azonnal, hanem néhány másodperc múlva.

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)

Következtetésként

A Go egy erőteljes nyelv szinte univerzális szabványkönyvtárral. Alapértelmezett képességei igen szélesek, interfészekkel bővíthetők – így igazán megbízható HTTP szervereket fejleszthetünk ki.

A Skillbox a következőket ajánlja:

Forrás: will.com

Hozzászólás