Udvikling af webservere pÄ Golang - fra simpel til kompleks

Udvikling af webservere pÄ Golang - fra simpel til kompleks

For fem Är siden begyndte jeg at udvikle Gophish, det gav mig mulighed for at lÊre Golang. Jeg indsÄ, at Go er et kraftfuldt sprog, hvis muligheder supplerer et vÊld af biblioteker. Go er alsidigt: specielt kan det bruges til let at udvikle serverapplikationer.

Denne artikel handler om at skrive en server i Go. Vi starter med enkle elementer, sÄsom 'Hello world!', og afslutter med en applikation med fÞlgende funktioner:

— Anvendelse af Let’s Encrypt til HTTPS.
— Funktionere som API-router.
— Arbejde med middleware.
— HĂ„ndtering af statiske filer.
— Korrekt nedlukning.

Skillbox anbefaler: Praktisk kursus «Python-udvikler fra bunden».

Vi minder om: til alle lĂŠsere af 'Habr' — rabat pĂ„ 10.000 rubler ved tilmelding til et hvilket som helst Skillbox-kursus med rabatkoden 'Habr'.

Hello, world!

Det er meget hurtigt at oprette en webserver i Go. Her er et eksempel pÄ en handler, der returnerer den lovede 'Hello, world!'.

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

Efter dette, hvis du kÞrer applikationen og Äbner siden localhost, vil du straks se teksten 'Hello, world!' (naturligvis, hvis alt fungerer korrekt).

Herefter vil vi flere gange bruge handleren, men fÞrst lad os forstÄ, hvordan det hele fungerer.

net/http

I eksemplet blev pakken net/http, som er det primÊre vÊrktÞj i Go til udvikling af bÄde servere og HTTP-klienter. For at forstÄ koden, lad os se pÄ betydningen af tre vigtige elementer: http.Handler, http.ServeMux og http.Server.

HTTP-handlere

NÄr vi modtager en anmodning, analyserer handleren den og formidler et svar. Handlere i Go implementeres sÄledes:

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

I det fĂžrste eksempel bruges hjĂŠlpefunktionen http.HandleFunc. Den indkapsler en anden funktion, som igen modtager http.ResponseWriter og http.Request i ServeHTTP.

Med andre ord er handlere i Golang reprÊsenteret af et fÊlles interface, hvilket giver programmÞren mange muligheder. SÄledes er middleware implementeret ved hjÊlp af en handler, hvor ServeHTTP fÞrst gÞr noget, og derefter kalder metoden ServeHTTP for en anden handler.

Som nÊvnt tidligere, danner handlere blot svar pÄ anmodninger. Men hvilken specifik handler skal bruges pÄ et givet tidspunkt?

Routing af anmodninger

For making the right choice, use the HTTP multiplexer. In several libraries, it is called a muxer or router, but they are all the same. The function of the multiplexer is to analyze the request path and select the appropriate handler.

If support for complex routing is needed, it is better to use third-party libraries. Some of the most advanced are gorilla/mux og go-chi/chi, these libraries allow for middleware processing without any problems. With them, you can set up wildcard routing and perform a number of other tasks. Their advantage is compatibility with standard HTTP handlers. As a result, you can write simple code with the ability to modify it in the future.

Working with complex frameworks usually requires somewhat non-standard solutions, which significantly complicates the use of default handlers. For creating the overwhelming majority of applications, a combination of the default library and a simple router is sufficient.

Request processing

In addition, we need a component that will 'listen' for incoming connections and redirect all requests to the correct handler. This task can easily be handled by http.Server.

Below is shown that the server is responsible for all tasks related to connection processing. This includes working with the TLS protocol. The standard HTTP server is used to implement http.ListenAndServer.

Now let's look at more complex examples.

Adding Let’s Encrypt

By default, our application runs over the HTTP protocol, but it is recommended to use the HTTPS protocol. In Go, this can be done without problems. If you have obtained a certificate and a private key, it is sufficient to write ListenAndServeTLS with the correct certificate and key files specified.

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

There is always room for improvement.

Let’s Encrypt provides free certificates with the possibility of automatic renewal. To use the service, the package is needed autocert.

The simplest way to set it up is to use the autocert.NewListener method in combination with http.Serve. The method allows obtaining and updating TLS certificates while the HTTP server processes requests:

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

If we open in the browser example.com, vi fÄr HTTPS-svaret «Hello, world!».

Hvis der er behov for mere prĂŠcis konfiguration, bĂžr man bruge autocert.Manager. Derefter opretter vi vores egen instans af http.Server (indtil nu har vi brugt standardvarianten) og tilfĂžjer manageren til serverens 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("", "")

Dette er en simpel metode til at implementere fuld stĂžtte til HTTPS med automatisk opdatering af certifikatet.

TilfĂžjelse af ikke-standardruter

Den standardrouter, der er inkluderet i standardbiblioteket, er god, men den er meget simpel. De fleste applikationer har brug for mere kompleks routing, herunder indlejrede og wildcard-ruter eller en procedure for at opsĂŠtte skabeloner og sti-parametre.

I dette tilfélde bþr man bruge pakker gorilla/mux og go-chi/chi. Med den sidstnévnte vil vi lére at arbejde — nedenfor er et eksempel.

Givet — filen api/v1/api.go, der indeholder ruter for vores 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
}

Vi sĂŠtter et prĂŠfiks api/vq for ruterne i hovedfilen.

Vi kan derefter montere dette til vores hovedrouter under api/v1/-prĂŠfikset tilbage i vores hovedapplikation:

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

Den enkle hÄndtering af komplekse ruter i Go gÞr det muligt at simplificere strukturen ved vedligeholdelse af store, komplekse applikationer.

Arbejde med middleware

I tilfĂŠlde af mellemliggende behandling bruges omsluttende en HTTP-handler med en anden, hvilket muliggĂžr hurtig autentificering, komprimering, logfĂžring og nogle andre funktioner.

Som eksempel ser vi pÄ http.Handler-grÊnsefladen, som vi bruger til at skrive en handler med autentificering af tjeneste brugere.

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
        }
        // Antager at autentificering er bestÄet, kÞr den oprindelige handler
        next.ServeHTTP(w, r)
    })
}

Der er tredjeparts-rutere, sÄsom chi, der tilbyder udvidet funktionalitet til mellemliggende behandling.

Arbejder med statiske filer

Standardbiblioteket i Go indeholder funktioner til at arbejde med statisk indhold, herunder billeder samt JavaScript- og CSS-filer. Adgang til dem kan fÄs gennem funktionen http.FileServer. Den returnerer en handler, der serverer filer fra et bestemt bibliotek.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("/{name}", HelloName)
 
// Konfigurering af statiske filfordelinger
staticPath, _ := filepath.Abs("..//..//static//")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("/*", fs)
    
    return r

Det er vigtigt at huske, at http.Dir viser indholdet af en mappe, hvis der ikke er nogen hovedfil index.html. I sÄ fald, for at undgÄ kompromittering af mappen, bÞr pakken anvendes uindekseret.

Korrekt afslutning af arbejdet

I Go er der ogsÄ en funktion til korrekt afslutning af en HTTP-server. Dette kan gÞres ved hjÊlp af metoden Shutdown(). Serveren kÞrer i en goroutine, og kanalen lytter derefter efter afbrydelsessignaler. SÄ snart signalet er modtaget, slukkes serveren, men ikke straks, men efter et par sekunder.

handler := server.NewRouter()
srv := &http.Server{
    Handler: handler,
}
 
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
 
// Vent pÄ et afbrudt signal
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
// ForsĂžg en effektiv nedlukning
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defers cancel()
srv.Shutdown(ctx)

Som konklusion

Go — et kraftfuldt sprog med en nĂŠsten universel standardbibliotek. Dens standardfunktioner er ret omfattende, og de kan styrkes ved hjĂŠlp af grĂŠnseflader — dette muliggĂžr udvikling af virkelig pĂ„lidelige HTTP-servere.

Skillbox anbefaler:

Kilde: habr.com

KĂžb pĂ„lidelig webhosting med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KĂžb pĂ„lidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster