Վեբ սերվերի մշակում Golang-ում՝ պարզից մինչև բարդ

Վեբ սերվերի մշակում Golang-ում՝ պարզից մինչև բարդ

Հինգ տարի առաջ ես սկսեցի զարգացնել Gophish, սա հնարավորություն տվեց սովորել Գոլանգը։ Ես հասկացա, որ Go-ն հզոր լեզու է, որը լրացվում է բազմաթիվ գրադարաններով: Go-ն բազմակողմանի է. մասնավորապես, այն կարող է օգտագործվել առանց որևէ խնդրի սերվերային հավելվածներ մշակելու համար:

Այս հոդվածը Go-ում սերվեր գրելու մասին է: Սկսենք պարզ բաներից, ինչպիսիք են «Բարև աշխարհ!» և ավարտենք հետևյալ հնարավորություններով հավելվածով.

- Օգտագործելով Let's Encrypt-ը HTTPS-ի համար:
— Աշխատում է որպես API երթուղիչ:
- Աշխատեք միջին ծրագրաշարի հետ:
- Ստատիկ ֆայլերի մշակում:
- Ճիշտ անջատում:

Skillbox-ը խորհուրդ է տալիս. Գործնական դասընթաց «Python-ի մշակողը զրոյից».

Հիշեցում. «Habr»-ի բոլոր ընթերցողների համար՝ 10 ռուբլի զեղչ «Habr» գովազդային կոդով Skillbox-ի ցանկացած դասընթացին գրանցվելիս:

Բարեւ աշխարհ!

Դուք կարող եք շատ արագ վեբ սերվեր ստեղծել Go-ում: Ահա կարգավորիչի օգտագործման օրինակ, որը վերադարձնում է վերևում խոստացված «Բարև, աշխարհ»:

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

Դրանից հետո, եթե գործարկեք հավելվածը և բացեք էջը localhost, ապա անմիջապես կտեսնեք «Բարև, աշխարհ» տեքստը: (եթե ամեն ինչ ճիշտ է աշխատում, իհարկե):

Ավելի ուշ մենք կօգտագործենք մշակիչը մի քանի անգամ, բայց նախ եկեք հասկանանք, թե ինչպես է ամեն ինչ աշխատում:

ցանց/http

Օրինակը օգտագործեց փաթեթը net/http, այն Go-ի առաջնային գործիքն է ինչպես սերվերների, այնպես էլ HTTP հաճախորդների մշակման համար: Կոդը հասկանալու համար եկեք հասկանանք երեք կարևոր տարրերի նշանակությունը՝ http.Handler, http.ServeMux և http.Server:

HTTP մշակիչներ

Երբ մենք հարցում ենք ստանում, մշակողը վերլուծում է այն և առաջացնում պատասխան: Handlers in Go-ն իրականացվում են հետևյալ կերպ.

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

Առաջին օրինակը օգտագործում է http.HandleFunc օգնական ֆունկցիան։ Այն փաթաթում է մեկ այլ գործառույթ, որն իր հերթին http.ResponseWriter-ը և http.Request-ը տեղափոխում է ServeHTTP:

Այսինքն, Golang-ում մշակողները ներկայացված են մեկ ինտերֆեյսով, որը ծրագրավորողին տալիս է բազմաթիվ տարբերակներ։ Այսպիսով, օրինակ, միջին ծրագիրը իրականացվում է մշակողի միջոցով, որտեղ ServeHTTP նախ ինչ-որ բան է անում, այնուհետև կանչում է մեկ այլ մշակողի ServeHTTP մեթոդը:

Ինչպես նշվեց վերևում, մշակողները պարզապես առաջացնում են հարցումների պատասխաններ: Բայց կոնկրետ ո՞ր կարգավորիչը պետք է օգտագործվի որոշակի ժամանակահատվածում:

Հարցրեք երթուղի

Ճիշտ ընտրություն կատարելու համար օգտագործեք HTTP մուլտիպլեքսոր: Մի շարք գրադարաններում այն ​​կոչվում է muxer կամ router, բայց դրանք բոլորը նույնն են: Multiplexer-ի գործառույթն է վերլուծել հարցումների ուղին և ընտրել համապատասխան մշակողը:

Եթե ​​Ձեզ անհրաժեշտ է աջակցություն բարդ երթուղիների համար, ապա ավելի լավ է օգտագործել երրորդ կողմի գրադարանները: Որոշ առավել առաջադեմ - գորիլա/մաքս и go-chi/chi, այս գրադարանները հնարավորություն են տալիս առանց խնդիրների իրականացնել միջանկյալ մշակում։ Նրանց օգնությամբ դուք կարող եք կարգավորել wildcard routing-ը և կատարել մի շարք այլ առաջադրանքներ: Նրանց առավելությունը ստանդարտ HTTP մշակողների հետ համատեղելիությունն է: Արդյունքում կարող եք գրել պարզ կոդ, որը կարող է փոփոխվել ապագայում։

Նորմալ իրավիճակում բարդ շրջանակների հետ աշխատելը կպահանջի ոչ ստանդարտ լուծումներ, և դա զգալիորեն բարդացնում է լռելյայն մշակողների օգտագործումը: Հավելվածների ճնշող մեծամասնությունը ստեղծելու համար բավական կլինի լռելյայն գրադարանի և պարզ երթուղիչի համադրությունը:

Հարցման մշակում

Բացի այդ, մեզ անհրաժեշտ է բաղադրիչ, որը «կլսի» մուտքային կապերի համար և վերահղելու բոլոր հարցումները ճիշտ մշակողին: http.Server-ը կարող է հեշտությամբ կարգավորել այս խնդիրը:

Հետևյալը ցույց է տալիս, որ սերվերը պատասխանատու է բոլոր առաջադրանքների համար, որոնք կապված են կապի մշակման հետ: Սա, օրինակ, աշխատում է TLS արձանագրության միջոցով: http.ListenAndServer զանգն իրականացնելու համար օգտագործվում է ստանդարտ HTTP սերվեր:

Այժմ դիտարկենք ավելի բարդ օրինակներ։

Let's Encrypt-ի ավելացում

Լռելյայնորեն, մեր հավելվածը աշխատում է HTTP արձանագրությամբ, սակայն խորհուրդ է տրվում օգտագործել HTTPS արձանագրությունը: Դա կարելի է անել առանց խնդիրների Go-ում: Եթե ​​ստացել եք վկայական և անձնական բանալի, ապա բավական է գրանցել ListenAndServeTLS-ը ճիշտ վկայականով և բանալի ֆայլերով։

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

Դուք միշտ կարող եք ավելի լավ անել:

Եկեք շեղենք տրամադրում է անվճար վկայականներ՝ ավտոմատ թարմացմամբ: Ծառայությունից օգտվելու համար անհրաժեշտ է փաթեթ autocert.

Այն կարգավորելու ամենահեշտ ձևը autocert.NewListener մեթոդի օգտագործումն է՝ http.Serve-ի հետ համատեղ: Մեթոդը թույլ է տալիս ստանալ և թարմացնել TLS վկայագրերը, մինչ HTTP սերվերը մշակում է հարցումները.

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

Եթե ​​բրաուզերում բացենք example.com, մենք կստանանք HTTPS պատասխան «Բարև, աշխարհ»:

Եթե ​​Ձեզ անհրաժեշտ է ավելի մանրամասն կազմաձևում, ապա դուք պետք է օգտագործեք autocert.Manager մենեջերը: Այնուհետև մենք ստեղծում ենք մեր սեփական http.Server օրինակը (մինչ այժմ մենք օգտագործում էինք այն լռելյայն) և ավելացնում կառավարիչը 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("", "")

Սա HTTPS-ի ամբողջական աջակցությունն իրականացնելու հեշտ միջոց է՝ վկայագրի ավտոմատ նորացման միջոցով:

Հատուկ երթուղիների ավելացում

Ստանդարտ գրադարանում ներառված լռելյայն երթուղիչը լավն է, բայց այն շատ հիմնական է: Ծրագրերի մեծամասնությունը պահանջում է ավելի բարդ երթուղիներ, ներառյալ ներդիր և նիշերի երթուղիները, կամ ուղու օրինաչափությունների և պարամետրերի սահմանման ընթացակարգ:

Այս դեպքում արժե օգտագործել փաթեթներ գորիլա/մաքս и go-chi/chi. Մենք կսովորենք, թե ինչպես աշխատել վերջինիս հետ, օրինակ՝ ստորև:

Տրված է api/v1/api.go ֆայլը, որը պարունակում է երթուղիներ մեր 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
}

Հիմնական ֆայլի երթուղիների համար մենք սահմանել ենք api/vq նախածանցը:

Այնուհետև մենք կարող ենք սա տեղադրել մեր հիմնական երթուղիչի վրա api/v1/ նախածանցի տակ մեր հիմնական հավելվածում.

// 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-ի բարդ երթուղիների հետ աշխատելու հեշտությունը հնարավորություն է տալիս պարզեցնել խոշոր, բարդ հավելվածների կառուցվածքն ու սպասարկումը:

Աշխատում է միջին ծրագրաշարի հետ

Բեմականացումը ներառում է մեկ HTTP կարգավորիչի փաթաթում մյուսով, ինչը հնարավորություն է տալիս արագորեն կատարել նույնականացում, սեղմում, գրանցում և մի քանի այլ գործառույթներ:

Որպես օրինակ, եկեք նայենք http.Handler ինտերֆեյսին, մենք կօգտագործենք այն գրելու համար մշակող, որը վավերացնում է ծառայության օգտվողներին:

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

Կան երրորդ կողմի երթուղիչներ, ինչպիսիք են chi-ն, որոնք թույլ են տալիս ընդլայնել միջին ծրագրաշարի գործառույթը:

Աշխատեք ստատիկ ֆայլերի հետ

Go ստանդարտ գրադարանը ներառում է ստատիկ բովանդակության հետ աշխատելու հնարավորություններ, ներառյալ պատկերներ, JavaScript և CSS ֆայլեր: Դրանք հասանելի են http.FileServer ֆունկցիայի միջոցով: Այն վերադարձնում է մշակիչ, որը ֆայլեր է սպասարկում կոնկրետ գրացուցակից:

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

Միանշանակ արժե հիշել, որ http.Dir-ը ցուցադրում է գրացուցակի բովանդակությունը, եթե այն չի պարունակում հիմնական index.html ֆայլը։ Այս դեպքում, գրացուցակը վտանգված չլինելու համար, դուք պետք է օգտագործեք փաթեթը unindexed.

Ճիշտ անջատում

Go-ն ունի նաև հնարավորություն, որը կոչվում է HTTP սերվերի նրբագեղ անջատում: Դա կարելի է անել՝ օգտագործելով Shutdown() մեթոդը: Սերվերը գործարկվում է գորուտինով, այնուհետև լսվում է ալիքը՝ ընդհատման ազդանշան ստանալու համար: Ազդանշանն ստանալուն պես սերվերն անջատվում է, բայց ոչ անմիջապես, այլ մի քանի վայրկյան հետո։

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)

Որպես եզրակացություն

Go-ն հզոր լեզու է՝ գրեթե ունիվերսալ ստանդարտ գրադարանով: Նրա լռելյայն հնարավորությունները շատ լայն են, և դրանք կարող են ընդլայնվել ինտերֆեյսների միջոցով. սա թույլ է տալիս զարգացնել իսկապես հուսալի HTTP սերվերներ:

Skillbox-ը խորհուրդ է տալիս.

Source: www.habr.com

Добавить комментарий