Datblygu gweinyddwyr gwe yn Golang - o'r syml i'r cymhleth

Datblygu gweinyddwyr gwe yn Golang - o'r syml i'r cymhleth

Bum mlynedd yn ôl dechreuais datblygu Gophish, gwnaeth hi yn bosibl i ddysgu Golang. Sylweddolais fod Go yn iaith bwerus, sy'n cael ei hategu gan lawer o lyfrgelloedd. Mae Go yn amlbwrpas: yn benodol, gallwch chi ddatblygu cymwysiadau ochr y gweinydd yn hawdd gydag ef.

Mae'r erthygl hon yn ymwneud ag ysgrifennu gweinydd yn Go. Gadewch i ni ddechrau gyda phethau syml fel "Helo fyd!" a gorffen gyda chymhwysiad gyda'r nodweddion hyn:

- Defnyddio Let's Encrypt ar gyfer HTTPS.
- Gweithio fel llwybrydd API.
- Gweithio gyda nwyddau canol.
- Trin ffeiliau statig.
- Cau i lawr yn gywir.

Mae Skillsbox yn argymell: Cwrs ymarferol "Datblygwr Python o'r dechrau".

Rydym yn atgoffa: i holl ddarllenwyr "Habr" - gostyngiad o 10 rubles wrth gofrestru ar unrhyw gwrs Skillbox gan ddefnyddio'r cod hyrwyddo "Habr".

Helo Byd!

Gall creu gweinydd gwe yn Go fod yn gyflym iawn. Dyma enghraifft o ddefnyddio triniwr sy'n dychwelyd y "Helo, byd!" a addawyd uchod.

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

Ar ôl hynny, os ydych chi'n rhedeg y cais ac yn agor y dudalen localhost, yna byddwch yn syth yn gweld y testun "Helo, byd!" (wrth gwrs, os yw popeth yn gweithio'n iawn).

Byddwn yn defnyddio'r triniwr dro ar ôl tro yn y canlynol, ond yn gyntaf gadewch i ni ddeall sut mae'r cyfan yn gweithio.

rhwyd/http

Defnyddiodd yr enghraifft y pecyn net/http, yw prif offeryn Go ar gyfer datblygu gweinyddwyr a chleientiaid HTTP. Er mwyn deall y cod, gadewch i ni ddeall ystyr tair elfen bwysig: http.Handler, http.ServeMux a http.Server.

Trinwyr HTTP

Pan fyddwn yn derbyn cais, mae'r triniwr yn ei ddosrannu ac yn cynhyrchu ymateb. Gweithredir Handlers in Go fel a ganlyn:

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

Mae'r enghraifft gyntaf yn defnyddio swyddogaeth helpwr http.HandleFunc. Mae'n lapio swyddogaeth arall sydd yn ei dro yn derbyn http.ResponseWriter a http.Request yn ServeHTTP.

Mewn geiriau eraill, mae trinwyr yn Golang yn cael eu cynrychioli gan un rhyngwyneb, sy'n rhoi llawer o gyfleoedd i'r rhaglennydd. Felly, er enghraifft, mae nwyddau canol yn cael eu gweithredu gan ddefnyddio triniwr, lle mae ServeHTTP yn gwneud rhywbeth yn gyntaf ac yna'n galw'r dull ServeHTTP o driniwr arall.

Fel y soniwyd uchod, y cyfan y mae'r trinwyr yn ei wneud yw ymateb i geisiadau. Ond pa driniwr y dylid ei ddefnyddio ar adeg benodol?

Cais Llwybro

Er mwyn gwneud y dewis cywir, defnyddiwch yr amlblecsydd HTTP. Mewn nifer o lyfrgelloedd fe'i gelwir yn muxer neu router, ond maent i gyd yr un peth. Swyddogaeth yr amlblecsydd yw dadansoddi'r llwybr cais a dewis y triniwr priodol.

Os oes angen cefnogaeth arnoch ar gyfer llwybro cymhleth, yna mae'n well defnyddio llyfrgelloedd trydydd parti. Un o'r rhai mwyaf datblygedig gorila/mux и go-chi/chi, mae'r llyfrgelloedd hyn yn ei gwneud hi'n bosibl gweithredu prosesu canolradd heb unrhyw broblemau. Gyda'u cymorth nhw, gallwch chi sefydlu llwybro cardiau gwyllt a chyflawni nifer o dasgau eraill. Eu mantais yw cydnawsedd â thrinwyr HTTP safonol. O ganlyniad, gallwch ysgrifennu cod syml y gellir ei addasu yn y dyfodol.

Ni fydd angen atebion cwbl safonol i weithio gyda fframweithiau cymhleth mewn sefyllfa arferol, ac mae hyn yn cymhlethu'r defnydd o drinwyr rhagosodedig yn fawr. Bydd y cyfuniad o'r llyfrgell ddiofyn a llwybrydd syml yn ddigon i greu mwyafrif helaeth y cymwysiadau.

Prosesu Ymholiad

Yn ogystal, mae arnom angen cydran a fydd yn "gwrando" ar gyfer cysylltiadau sy'n dod i mewn ac yn ailgyfeirio pob cais i'r triniwr cywir. Gall http.Server ymdopi â'r dasg hon yn hawdd.

Mae'r canlynol yn dangos bod y gweinydd yn gyfrifol am yr holl dasgau sy'n ymwneud â thrin cysylltiadau. Mae hyn, er enghraifft, yn waith ar y protocol TLS. Defnyddir gweinydd HTTP safonol i weithredu'r alwad http.ListenAndServer.

Nawr, gadewch i ni edrych ar enghreifftiau mwy cymhleth.

Ychwanegu Let's Encrypt

Yn ddiofyn, mae ein cais yn rhedeg dros y protocol HTTP, ond argymhellir defnyddio'r protocol HTTPS. Yn Go, gellir gwneud hyn heb broblemau. Os cawsoch dystysgrif ac allwedd breifat, yna mae'n ddigon ysgrifennu ListenAndServeTLS gyda'r dystysgrif gywir a'r ffeiliau allweddol.

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

Gallwch chi bob amser wneud yn well.

Gadewch i ni Amgryptio yn rhoi tystysgrifau am ddim gyda'r posibilrwydd o adnewyddu awtomatig. Er mwyn defnyddio'r gwasanaeth, mae angen pecyn arnoch chi autocert.

Y ffordd hawsaf i'w sefydlu yw defnyddio'r dull autocert.NewListener ar y cyd â http.Serve. Mae'r dull yn caniatáu ichi dderbyn ac adnewyddu tystysgrifau TLS tra bod y gweinydd HTTP yn prosesu ceisiadau:

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

Os byddwn yn agor yn y porwr example.com, rydym yn cael ymateb HTTPS "Helo, byd!".

Os oes angen cyfluniad manylach arnoch, yna dylech ddefnyddio'r autocert.Manager. Yna rydym yn creu ein enghraifft http.Server ein hunain (hyd yn hyn rydym wedi ei ddefnyddio yn ddiofyn) ac yn ychwanegu'r rheolwr at y gweinydd 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("", "")

Mae hon yn ffordd hawdd o weithredu cefnogaeth HTTPS lawn gydag adnewyddu tystysgrif yn awtomatig.

Ychwanegu Llwybrau Personol

Mae'r llwybrydd diofyn sydd wedi'i gynnwys yn y llyfrgell safonol yn braf, ond yn sylfaenol iawn. Mae angen llwybro mwy cymhleth ar y rhan fwyaf o geisiadau, gan gynnwys llwybrau nythu a chard gwyllt, neu'r weithdrefn ar gyfer pennu patrymau a pharamedrau llwybrau.

Yn yr achos hwn, dylech ddefnyddio pecynnau gorila/mux и go-chi/chi. Byddwn yn dysgu sut i weithio gyda'r olaf - dangosir enghraifft isod.

Rhoddir y ffeil api/v1/api.go yn cynnwys y llwybrau ar gyfer ein 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
}

Rydym yn gosod y rhagddodiad api/vq ar gyfer y llwybrau yn y brif ffeil.

Yna gallwn osod hwn ar ein prif lwybrydd o dan y rhagddodiad api/v1/ yn ôl yn ein prif gais:

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

Mae rhwyddineb gweithio gyda llwybrau cymhleth yn Go yn ei gwneud hi'n bosibl symleiddio strwythuro a chynnal a chadw cymwysiadau cymhleth mawr.

Gweithio gyda nwyddau canol

Yn achos prosesu canolraddol, defnyddir lapio un triniwr HTTP ag un arall, sy'n ei gwneud hi'n bosibl cyflawni dilysu, cywasgu, logio a rhai swyddogaethau eraill yn gyflym.

Fel enghraifft, gadewch i ni ystyried y rhyngwyneb http.Handler, gyda'i help byddwn yn ysgrifennu triniwr gyda dilysu defnyddwyr gwasanaeth.

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

Mae llwybryddion trydydd parti, fel chi, sy'n eich galluogi i ymestyn ymarferoldeb prosesu canolradd.

Gweithio gyda ffeiliau statig

Mae llyfrgell safonol Go yn cynnwys cyfleusterau ar gyfer gweithio gyda chynnwys statig, gan gynnwys delweddau, yn ogystal â ffeiliau JavaScript a CSS. Gellir eu cyrchu trwy'r swyddogaeth http.FileServer. Mae'n dychwelyd triniwr sy'n dosbarthu ffeiliau o gyfeiriadur penodol.

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

Cofiwch fod http.Dir yn dangos cynnwys y cyfeiriadur os nad yw'n cynnwys y brif ffeil index.html. Yn yr achos hwn, er mwyn atal y cyfeiriadur rhag cael ei beryglu, dylech ddefnyddio'r pecyn unindexed.

Cau i lawr yn gywir

Mae gan Go hefyd nodwedd o'r fath â chau'r gweinydd HTTP yn osgeiddig. Gellir gwneud hyn gan ddefnyddio'r dull Shutdown(). Mae'r gweinydd yn cael ei gychwyn mewn goroutine, ac yna gwrandewir ar y sianel i dderbyn signal ymyrraeth. Cyn gynted ag y derbynnir y signal, mae'r gweinydd yn diffodd, ond nid ar unwaith, ond ar ôl ychydig eiliadau.

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)

Fel casgliad

Mae Go yn iaith bwerus gyda llyfrgell safonol bron yn gyffredinol. Mae ei alluoedd diofyn yn eang iawn, a gallwch eu cryfhau gyda chymorth rhyngwynebau - mae hyn yn caniatáu ichi ddatblygu gweinyddwyr HTTP gwirioneddol ddibynadwy.

Mae Skillsbox yn argymell:

Ffynhonnell: hab.com

Ychwanegu sylw