Таҳияи веб-сервер дар Голанг - аз оддӣ ба мураккаб

Таҳияи веб-сервер дар Голанг - аз оддӣ ба мураккаб

Панҷ сол пеш ман оғоз кардам Gophish инкишоф диҳед, ин барои омухтани Голанг имконият дод. Ман фаҳмидам, ки Go як забони пурқувватест, ки онро китобхонаҳои зиёде пурра мекунанд. Go бисёрҷониба аст: махсусан, он метавонад барои таҳияи замимаҳои сервер бе ягон мушкилот истифода шавад.

Ин мақола дар бораи навиштани сервер дар Go аст. Биёед бо чизҳои оддӣ ба монанди "Салом ҷаҳон!" оғоз кунем ва бо замимаи дорои қобилиятҳои зерин хотима диҳем:

- Истифодаи Let's Encrypt барои HTTPS.
— Ҳамчун роутери API кор мекунад.
- Кор бо миёнаравӣ.
— Коркарди файлҳои статикӣ.
— Хомӯшии дуруст.

Skillbox тавсия медиҳад: Курси амалӣ "Таҳиягари Python аз сифр".

Мо ба шумо хотиррасон мекунем: барои ҳамаи хонандагони "Habr" - тахфифи 10 000 рубл ҳангоми номнавис шудан ба курсҳои Skillbox бо истифода аз рамзи таблиғотии "Habr".

Салом Ҷаҳон!

Шумо метавонед сервери вебро дар 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, пас шумо фавран матни "Салом, ҷаҳон!" (агар ҳама чиз дуруст кор кунад, албатта).

Мо коркардкунандаро чанд маротиба баъдтар истифода хоҳем кард, аммо аввал биёед бифаҳмем, ки ҳама чиз чӣ гуна кор мекунад.

net/http

Мисоли бастаро истифода бурд net/http, он воситаи асосии Go барои таҳияи ҳам серверҳо ва ҳам мизоҷони HTTP мебошад. Барои фаҳмидани рамз, биёед маънои се унсури муҳимро дарк кунем: http.Handler, http.ServeMux ва http.Server.

коркардкунандагони HTTP

Вақте ки мо дархост мегирем, коркардкунанда онро таҳлил мекунад ва посух медиҳад. Дастгоҳҳо дар Go ба таври зерин амалӣ карда мешаванд:

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

Мисоли аввал функсияи ёрирасони http.HandleFunc -ро истифода мебарад. Он функсияи дигареро фаро мегирад, ки дар навбати худ http.ResponseWriter ва http.Request-ро ба ServeHTTP мегирад.

Ба ибораи дигар, коркардкунандагон дар Голанг дар интерфейси ягона пешниҳод карда мешаванд, ки ба барномасоз имконоти зиёде медиҳад. Ҳамин тавр, масалан, миёнаравӣ бо истифода аз коркардкунанда амалӣ карда мешавад, ки дар он ServeHTTP аввал коре мекунад ва сипас усули ServeHTTP-и коркардкунандаи дигарро даъват мекунад.

Тавре ки дар боло зикр гардид, коркардкунандагон танҳо ба дархостҳо ҷавоб медиҳанд. Аммо кадом коркардкунанда бояд дар як лаҳзаи муайян истифода шавад?

Дархости масир

Барои интихоби дуруст, мултиплекси HTTP-ро истифода баред. Дар як қатор китобхонаҳо онро muxer ё роутер меноманд, аммо ҳамаашон якхелаанд. Вазифаи мултиплексор таҳлили роҳи дархост ва интихоби коркарди мувофиқ мебошад.

Агар ба шумо барои масири мураккаб дастгирӣ лозим бошад, пас беҳтар аст, ки китобхонаҳои тарафи сеюмро истифода баред. Баъзе аз пешрафтатарин - горилла/мукс и го-чи/чи, ин китобхонахо имкон меди-ханд, ки коркарди мобайнй бе ягон мушкилй чорй карда шавад. Бо кӯмаки онҳо, шумо метавонед масирро танзим кунед ва як қатор вазифаҳои дигарро иҷро кунед. Бартарии онҳо мутобиқат бо коркардкунандагони стандартии 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 бо навсозии автоматии сертификат мебошад.

Илова кардани хатсайрҳои фармоишӣ

Роутери пешфарз, ки ба китобхонаи стандартӣ дохил карда шудааст, хуб аст, аммо он хеле асосӣ аст. Аксари барномаҳо масири мураккабтарро талаб мекунанд, аз ҷумла хатсайрҳои лона ва аломати ваҳшӣ ё тартиби муқаррар кардани намунаҳо ва параметрҳои роҳ.

Дар ин ҳолат истифодаи бастаҳо меарзад горилла/мукс и го-чи/чи. Мо чӣ гуна кор карданро бо охирин меомӯзем - мисол дар зер нишон дода шудааст.

Файли 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() анҷом дод. Сервер дар goroutine оғоз карда мешавад ва сипас канал гӯш карда мешавад, то сигнали қатъро қабул кунад. Ҳамин ки сигнал қабул шуд, сервер хомӯш мешавад, аммо на дарҳол, балки пас аз чанд сония.

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 тавсия медиҳад:

Манбаъ: will.com

Илова Эзоҳ