Verkkopalvelinkehitys Golangissa - yksinkertaisesta monimutkaiseen

Verkkopalvelinkehitys Golangissa - yksinkertaisesta monimutkaiseen

Viisi vuotta sitten aloitin kehittää Gophishia, tämä tarjosi mahdollisuuden oppia Golangia. Ymmärsin, että Go on voimakas kieli, jota täydentävät monet kirjastot. Go on monipuolinen: sillä voidaan erityisesti kehittää palvelinpuolen sovelluksia ilman ongelmia.

Tämä artikkeli käsittelee palvelimen kirjoittamista Gossa. Aloitetaan yksinkertaisista asioista, kuten "Hei maailma!", ja lopetetaan sovellukseen, jossa on seuraavat ominaisuudet:

- Let's Encryptin käyttö HTTPS:lle.
— Työskentely API-reitittimenä.
- Työskentely väliohjelmiston kanssa.
— Staattisten tiedostojen käsittely.
– Oikea sammutus.

Skillbox suosittelee: Käytännön kurssi "Python-kehittäjä tyhjästä".

Muistutamme sinua: kaikille "Habrin" lukijoille - 10 000 ruplan alennus ilmoittautuessaan mille tahansa Skillbox-kurssille "Habr" -tarjouskoodilla.

Hei, maailma!

Voit luoda verkkopalvelimen Gossa hyvin nopeasti. Tässä on esimerkki käsittelijän käytöstä, joka palauttaa yllä luvatun "Hei, maailma!".

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

Tämän jälkeen, jos suoritat sovelluksen ja avaat sivun localhost, näet heti tekstin "Hei, maailma!" (jos kaikki toimii oikein, tietysti).

Käytämme käsittelijää useita kertoja myöhemmin, mutta ensin ymmärretään, miten kaikki toimii.

net/http

Esimerkissä käytettiin pakettia net/http, se on Go:n ensisijainen työkalu sekä palvelimien että HTTP-asiakkaiden kehittämiseen. Ymmärtääksemme koodin, ymmärrämme kolmen tärkeän elementin merkityksen: http.Handler, http.ServeMux ja http.Server.

HTTP-käsittelijät

Kun saamme pyynnön, käsittelijä analysoi sen ja luo vastauksen. Käsittelijät Gossa toteutetaan seuraavasti:

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

Ensimmäinen esimerkki käyttää http.HandleFunc-aputoimintoa. Se kääri toisen toiminnon, joka puolestaan ​​vie http.ResponseWriter ja http.Request osaksi ServeHTTP.

Toisin sanoen Golangin käsittelijät esitetään yhdessä käyttöliittymässä, mikä antaa ohjelmoijalle paljon vaihtoehtoja. Joten esimerkiksi väliohjelmisto on toteutettu käsittelijän avulla, jossa ServeHTTP ensin tekee jotain ja sitten kutsuu toisen käsittelijän ServeHTTP-metodia.

Kuten edellä mainittiin, käsittelijät yksinkertaisesti luovat vastauksia pyyntöihin. Mutta mitä käsittelijää tulisi käyttää tietyllä hetkellä?

Pyydä reititystä

Käytä HTTP-multiplekseria tehdäksesi oikean valinnan. Useissa kirjastoissa sitä kutsutaan muxeriksi tai reitittimeksi, mutta ne ovat kaikki sama asia. Multiplekserin tehtävänä on analysoida pyyntöpolku ja valita sopiva käsittelijä.

Jos tarvitset tukea monimutkaiselle reititykselle, on parempi käyttää kolmannen osapuolen kirjastoja. Jotkut edistyneimmistä - gorilla/mux и go-chi/chi, nämä kirjastot mahdollistavat välikäsittelyn toteuttamisen ilman ongelmia. Heidän avullaan voit määrittää jokerimerkkireitityksen ja suorittaa useita muita tehtäviä. Niiden etuna on yhteensopivuus tavallisten HTTP-käsittelijöiden kanssa. Tämän seurauksena voit kirjoittaa yksinkertaista koodia, jota voidaan muokata tulevaisuudessa.

Monimutkaisten kehysten kanssa työskentely normaalitilanteessa vaatii epätyypillisiä ratkaisuja, mikä vaikeuttaa merkittävästi oletuskäsittelijöiden käyttöä. Suurimman osan sovelluksista luomiseen riittää oletuskirjaston ja yksinkertaisen reitittimen yhdistelmä.

Kyselyn käsittely

Lisäksi tarvitsemme komponentin, joka "kuuntelee" saapuvia yhteyksiä ja ohjaa kaikki pyynnöt oikealle käsittelijälle. http.Server voi helposti hoitaa tämän tehtävän.

Seuraava osoittaa, että palvelin on vastuussa kaikista yhteydenkäsittelyyn liittyvistä tehtävistä. Tämä toimii esimerkiksi TLS-protokollalla. http.ListenAndServer-kutsun toteuttamiseen käytetään tavallista HTTP-palvelinta.

Katsotaanpa nyt monimutkaisempia esimerkkejä.

Lisätään Let's Encrypt

Sovelluksemme käyttää oletusarvoisesti HTTP-protokollaa, mutta on suositeltavaa käyttää HTTPS-protokollaa. Tämä voidaan tehdä ilman ongelmia Gossa. Jos olet saanut varmenteen ja yksityisen avaimen, riittää, että rekisteröit ListenAndServeTLS oikeilla varmenne- ja avaintiedostoilla.

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

Aina voi tehdä paremmin.

Let's Encrypt tarjoaa ilmaisia ​​varmenteita, jotka uusitaan automaattisesti. Palvelun käyttämiseen tarvitaan paketti autocert.

Helpoin tapa määrittää se on käyttää autocert.NewListener-menetelmää yhdessä http.Serven kanssa. Menetelmän avulla voit hankkia ja päivittää TLS-varmenteita, kun HTTP-palvelin käsittelee pyyntöjä:

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

Jos avataan selaimessa example.com, saamme HTTPS-vastauksen "Hei, maailma!"

Jos tarvitset tarkempia määrityksiä, käytä autocert.Manager manageria. Sitten luomme oman http.Server-esiintymän (tähän asti käytimme sitä oletuksena) ja lisäämme hallintaohjelman TLSConfig-palvelimeen:

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

Tämä on helppo tapa ottaa käyttöön täysi HTTPS-tuki automaattisella varmenteiden uusimisella.

Mukautettujen reittien lisääminen

Vakiokirjastoon sisältyvä oletusreititin on hyvä, mutta se on hyvin yksinkertainen. Useimmat sovellukset vaativat monimutkaisempaa reititystä, mukaan lukien sisäkkäiset ja jokerimerkkireitit tai polkukuvioiden ja parametrien asettamismenettelyn.

Tässä tapauksessa kannattaa käyttää paketteja gorilla/mux и go-chi/chi. Opimme työskentelemään jälkimmäisen kanssa - esimerkki on alla.

Annettu on tiedosto api/v1/api.go, joka sisältää API:mme reitit:

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

Asetamme päätiedoston reiteille api/vq-etuliitteen.

Voimme sitten asentaa tämän pääreitittimeemme api/v1/-etuliitteellä takaisin pääsovelluksessamme:

// 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:n helppous työskennellä monimutkaisten reittien kanssa mahdollistaa suurten, monimutkaisten sovellusten rakenteen ja ylläpidon yksinkertaistamisen.

Työskentely väliohjelmiston kanssa

Vaiheeseen kuuluu yhden HTTP-käsittelijän kääriminen toiseen, mikä mahdollistaa nopean todennuksen, pakkaamisen, kirjaamisen ja useiden muiden toimintojen suorittamisen.

Katsotaanpa esimerkkinä http.Handler-käyttöliittymää, jonka avulla kirjoitetaan käsittelijä, joka todentaa palvelun käyttäjät.

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

On olemassa kolmannen osapuolen reitittimiä, kuten chi, joiden avulla voit laajentaa väliohjelmiston toimivuutta.

Työskentely staattisten tiedostojen kanssa

Go-standardikirjasto sisältää ominaisuudet staattisen sisällön, kuten kuvien, JavaScript- ja CSS-tiedostojen, kanssa työskentelyyn. Niitä pääsee käsiksi http.FileServer-toiminnon kautta. Se palauttaa käsittelijän, joka palvelee tiedostoja tietystä hakemistosta.

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

Kannattaa ehdottomasti muistaa, että http.Dir näyttää hakemiston sisällön, jos se ei sisällä päätiedostoa index.html. Tässä tapauksessa sinun tulee käyttää pakettia estääksesi hakemiston vaarantumisen unindexed.

Oikea sammutus

Gossa on myös ominaisuus nimeltä HTTP-palvelimen siro sammutus. Tämä voidaan tehdä käyttämällä Shutdown()-menetelmää. Palvelin käynnistetään gorutiinissa, jonka jälkeen kanavaa kuunnellaan keskeytyssignaalin vastaanottamiseksi. Heti kun signaali vastaanotetaan, palvelin sammuu, mutta ei heti, vaan muutaman sekunnin kuluttua.

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)

Lopuksi

Go on tehokas kieli, jolla on lähes universaali standardikirjasto. Sen oletusominaisuudet ovat erittäin laajat, ja niitä voidaan parantaa käyttöliittymien avulla - näin voit kehittää todella luotettavia HTTP-palvelimia.

Skillbox suosittelee:

Lähde: will.com

Lisää kommentti