Web serveru izstrāde Golangā - no vienkāršas līdz sarežģītai

Web serveru izstrāde Golangā - no vienkāršas līdz sarežģītai

Pirms pieciem gadiem es sāku izstrādāt Gophish, tas sniedza iespēju apgūt Golangu. Es sapratu, ka Go ir spēcīga valoda, ko papildina daudzas bibliotēkas. Go ir daudzpusīgs: jo īpaši to var izmantot, lai bez problēmām izstrādātu servera puses lietojumprogrammas.

Šis raksts ir par servera rakstīšanu programmā Go. Sāksim ar vienkāršām lietām, piemēram, "Sveika pasaule!", un beigsim ar lietojumprogrammu ar šādām iespējām:

- Izmantojot Let's Encrypt HTTPS.
— Darbs kā API maršrutētājs.
— Darbs ar starpprogrammatūru.
— Statisko failu apstrāde.
— Pareiza izslēgšana.

Skillbox iesaka: Praktiskais kurss "Python izstrādātājs no nulles".

Atgādinām: visiem "Habr" lasītājiem - atlaide 10 000 rubļu, reģistrējoties jebkurā Skillbox kursā, izmantojot "Habr" reklāmas kodu.

Sveika pasaule!

Programmā Go varat izveidot tīmekļa serveri ļoti ātri. Šeit ir piemērs, kā izmantot apdarinātāju, kas atgriež iepriekš apsolīto “Sveika, pasaule!”.

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

Pēc tam, ja palaižat lietojumprogrammu un atverat lapu localhost, tad uzreiz redzēsit tekstu “Sveika, pasaule!” (ja viss darbojas pareizi, protams).

Vēlāk mēs izmantosim apdarinātāju vairākas reizes, taču vispirms sapratīsim, kā viss darbojas.

net/http

Piemērā izmantota pakotne net/http, tas ir galvenais Go rīks gan serveru, gan HTTP klientu izstrādei. Lai saprastu kodu, sapratīsim trīs svarīgu elementu nozīmi: http.Handler, http.ServeMux un http.Server.

HTTP apstrādātāji

Kad mēs saņemam pieprasījumu, apstrādātājs to analizē un ģenerē atbildi. Apdarinātāji pakalpojumā Go tiek ieviesti šādi:

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

Pirmajā piemērā tiek izmantota http.HandleFunc palīga funkcija. Tas ietver citu funkciju, kas savukārt pārņem http.ResponseWriter un http.Request pakalpojumā ServeHTTP.

Citiem vārdiem sakot, Golang apstrādātāji tiek parādīti vienā saskarnē, kas programmētājam sniedz daudz iespēju. Tā, piemēram, starpprogrammatūra tiek ieviesta, izmantojot apdarinātāju, kur ServeHTTP vispirms kaut ko dara un pēc tam izsauc cita apdarinātāja ServeHTTP metodi.

Kā minēts iepriekš, apstrādātāji vienkārši ģenerē atbildes uz pieprasījumiem. Bet kurš konkrēts apdarinātājs būtu jāizmanto konkrētā brīdī?

Pieprasīt maršrutēšanu

Lai izdarītu pareizo izvēli, izmantojiet HTTP multipleksoru. Daudzās bibliotēkās to sauc par muxer vai router, taču tie visi ir viens un tas pats. Multipleksera funkcija ir analizēt pieprasījuma ceļu un izvēlēties atbilstošo apstrādātāju.

Ja jums ir nepieciešams atbalsts sarežģītai maršrutēšanai, labāk ir izmantot trešo pušu bibliotēkas. Daži no vismodernākajiem - gorilla/mux и go-chi/chi, šīs bibliotēkas ļauj bez problēmām īstenot starpapstrādi. Ar viņu palīdzību jūs varat konfigurēt aizstājējzīmju maršrutēšanu un veikt vairākus citus uzdevumus. To priekšrocība ir saderība ar standarta HTTP apstrādātājiem. Rezultātā jūs varat uzrakstīt vienkāršu kodu, ko nākotnē var mainīt.

Strādājot ar sarežģītiem ietvariem normālā situācijā, būs nepieciešami nestandarta risinājumi, un tas būtiski apgrūtina noklusējuma apstrādātāju izmantošanu. Lai izveidotu lielāko daļu lietojumprogrammu, pietiks ar noklusējuma bibliotēkas un vienkārša maršrutētāja kombināciju.

Vaicājumu apstrāde

Turklāt mums ir nepieciešams komponents, kas “noklausīsies” ienākošos savienojumus un novirzīs visus pieprasījumus uz pareizo apstrādātāju. http.Serveris var viegli tikt galā ar šo uzdevumu.

Tālāk redzams, ka serveris ir atbildīgs par visiem uzdevumiem, kas saistīti ar savienojuma apstrādi. Tas, piemēram, darbojas, izmantojot TLS protokolu. Lai ieviestu http.ListenAndServer izsaukumu, tiek izmantots standarta HTTP serveris.

Tagad apskatīsim sarežģītākus piemērus.

Pievienojot Let's Encrypt

Pēc noklusējuma mūsu lietojumprogramma darbojas, izmantojot HTTP protokolu, taču ieteicams izmantot HTTPS protokolu. To var izdarīt bez problēmām Go. Ja esat saņēmis sertifikātu un privāto atslēgu, tad pietiek reģistrēt ListenAndServeTLS ar pareizajiem sertifikāta un atslēgu failiem.

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

Jūs vienmēr varat darīt labāk.

Let's Encrypt nodrošina bezmaksas sertifikātus ar automātisku atjaunošanu. Lai izmantotu pakalpojumu, nepieciešama pakete autocert.

Vienkāršākais veids, kā to konfigurēt, ir izmantot autocert.NewListener metodi kombinācijā ar http.Serve. Šī metode ļauj iegūt un atjaunināt TLS sertifikātus, kamēr HTTP serveris apstrādā pieprasījumus:

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

Ja atveram pārlūkprogrammā example.com, mēs saņemsim HTTPS atbildi "Sveika, pasaule!"

Ja nepieciešama detalizētāka konfigurācija, tad jāizmanto autocert.Manager pārvaldnieks. Pēc tam mēs izveidojam savu http.Server instanci (līdz šim mēs to izmantojām pēc noklusējuma) un pievienojam pārvaldnieku TLSConfig serverim:

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

Tas ir vienkāršs veids, kā ieviest pilnu HTTPS atbalstu ar automātisku sertifikātu atjaunošanu.

Pielāgotu maršrutu pievienošana

Standarta bibliotēkā iekļautais noklusējuma maršrutētājs ir labs, taču tas ir ļoti vienkāršs. Lielākajai daļai lietojumprogrammu ir nepieciešama sarežģītāka maršrutēšana, tostarp ligzdoti un aizstājējzīmju maršruti, vai procedūra ceļu modeļu un parametru iestatīšanai.

Šajā gadījumā ir vērts izmantot paketes gorilla/mux и go-chi/chi. Mēs uzzināsim, kā strādāt ar pēdējo - piemērs ir parādīts zemāk.

Ir dots fails api/v1/api.go, kurā ir mūsu API maršruti:

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

Mēs iestatām api/vq prefiksu maršrutiem galvenajā failā.

Pēc tam mēs to varam pievienot mūsu galvenajam maršrutētājam ar prefiksu api/v1/ atpakaļ mūsu galvenajā lietojumprogrammā:

// 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 vienkāršais darbs ar sarežģītiem maršrutiem ļauj vienkāršot lielu, sarežģītu lietojumu strukturēšanu un uzturēšanu.

Darbs ar starpprogrammatūru

Uzstādīšana ietver viena HTTP apdarinātāja iesaiņošanu ar citu, ļaujot ātri veikt autentifikāciju, saspiešanu, reģistrēšanu un vairākas citas funkcijas.

Piemēram, aplūkosim http.Handler saskarni; mēs to izmantosim, lai uzrakstītu apdarinātāju, kas autentificē pakalpojuma lietotājus.

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

Ir trešo pušu maršrutētāji, piemēram, chi, kas ļauj paplašināt starpprogrammatūras funkcionalitāti.

Darbs ar statiskiem failiem

Go standarta bibliotēkā ir iekļautas iespējas darbam ar statisku saturu, tostarp attēliem, JavaScript un CSS failiem. Tiem var piekļūt, izmantojot funkciju http.FileServer. Tas atgriež apstrādātāju, kas apkalpo failus no noteikta direktorija.

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

Noteikti der atcerēties, ka http.Dir parāda direktorijas saturu, ja tajā nav galvenā index.html faila. Šajā gadījumā, lai novērstu direktorija uzlauzšanu, jums vajadzētu izmantot pakotni unindexed.

Pareiza izslēgšana

Go ir arī funkcija, ko sauc par HTTP servera graciozu izslēgšanu. To var izdarīt, izmantojot Shutdown() metodi. Serveris tiek palaists gorutīnā, un pēc tam tiek klausīts kanāls, lai saņemtu pārtraukuma signālu. Tiklīdz tiek saņemts signāls, serveris izslēdzas, bet ne uzreiz, bet pēc dažām sekundēm.

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ā secinājums

Go ir spēcīga valoda ar gandrīz universālu standarta bibliotēku. Tā noklusējuma iespējas ir ļoti plašas, un tās var uzlabot, izmantojot saskarnes – tas ļauj izstrādāt patiesi uzticamus HTTP serverus.

Skillbox iesaka:

Avots: www.habr.com

Iegādājieties uzticamu mitināšanu vietnēm ar DDoS aizsardzību, VPS VDS serveriem 🔥 Iegādājieties uzticamu tīmekļa vietņu mitināšanu ar DDoS aizsardzību, VPS VDS serveriem | ProHoster