Голангта веб-серверді әзірлеу – қарапайымнан күрделіге дейін

Голангта веб-серверді әзірлеу – қарапайымнан күрделіге дейін

Бес жыл бұрын мен бастадым гофишті дамыту, бұл Голанг тілін үйренуге мүмкіндік берді. Мен Go - көптеген кітапханалармен толықтырылған қуатты тіл екенін түсіндім. Go жан-жақты: атап айтқанда, оны серверлік қосымшаларды еш қиындықсыз әзірлеу үшін пайдалануға болады.

Бұл мақала Go бағдарламасында сервер жазу туралы. «Сәлем әлем!» сияқты қарапайым нәрселерден бастайық және келесі мүмкіндіктері бар қолданбамен аяқтаймыз:

- HTTPS үшін Let's Encrypt пайдалану.
— API маршрутизаторы ретінде жұмыс істеу.
— Орташа бағдарламалық құралмен жұмыс.
— Статикалық файлдарды өңдеу.
— Дұрыс өшіру.

Skillbox ұсынады: Практикалық курс «Нөлден Python әзірлеушісі».

Біз еске саламыз: «Хабрдың» барлық оқырмандары үшін - «Habr» жарнамалық кодын пайдаланып кез келген Skillbox курсына жазылу кезінде 10 000 рубль көлемінде жеңілдік.

Сәлем, әлем!

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

Осыдан кейін қолданбаны іске қосып, бетті ашсаңыз жергілікті, содан кейін сіз бірден «Сәлем, әлем!» мәтінін көресіз. (әрине, бәрі дұрыс жұмыс істесе).

Біз өңдегішті кейінірек бірнеше рет қолданамыз, бірақ алдымен бәрі қалай жұмыс істейтінін түсінейік.

net/http

Мысал буманы пайдаланды net/http, бұл серверлерді де, HTTP клиенттерін де әзірлеуге арналған Go бағдарламасындағы негізгі құрал. Кодты түсіну үшін үш маңызды элементтің мағынасын түсінейік: http.Handler, http.ServeMux және http.Server.

HTTP өңдеушілері

Сұрауды алған кезде өңдеуші оны талдап, жауап береді. Go бағдарламасындағы өңдеушілер келесідей жүзеге асырылады:

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

Бірінші мысал http.HandleFunc көмекші функциясын пайдаланады. Ол басқа функцияны орайды, ол өз кезегінде http.ResponseWriter және http.Request-ті ServeHTTP ішіне алады.

Басқаша айтқанда, Голангтағы өңдеушілер бағдарламашыға көптеген опцияларды беретін бір интерфейсте ұсынылған. Мәселен, мысалы, аралық бағдарлама өңдеуші арқылы жүзеге асырылады, мұнда ServeHTTP алдымен бірдеңе жасайды, содан кейін басқа өңдеушінің ServeHTTP әдісін шақырады.

Жоғарыда айтылғандай, өңдеушілер сұрауларға жауаптар жасайды. Бірақ қандай нақты өңдегішті белгілі бір уақытта пайдалану керек?

Маршруттауды сұрау

Дұрыс таңдау жасау үшін HTTP мультиплексорын пайдаланыңыз. Бірқатар кітапханаларда ол мухер немесе маршрутизатор деп аталады, бірақ олардың барлығы бірдей. Мультиплексордың қызметі сұраныс жолын талдау және сәйкес өңдеушіні таңдау болып табылады.

Егер сізге күрделі маршруттау үшін қолдау қажет болса, үшінші тарап кітапханаларын қолданған дұрыс. Кейбір ең озық - горилла/мукс и го-чи/чи, бұл кітапханалар аралық өңдеуді еш қиындықсыз жүзеге асыруға мүмкіндік береді. Олардың көмегімен қойылмалы таңбаны бағыттауды конфигурациялауға және басқа да бірқатар тапсырмаларды орындауға болады. Олардың артықшылығы стандартты HTTP өңдеушілерімен үйлесімділік болып табылады. Нәтижесінде сіз болашақта өзгертуге болатын қарапайым кодты жаза аласыз.

Қалыпты жағдайда күрделі құрылымдармен жұмыс істеу стандартты емес шешімдерді қажет етеді және бұл әдепкі өңдегіштерді пайдалануды айтарлықтай қиындатады. Қолданбалардың басым көпшілігін жасау үшін әдепкі кітапхана мен қарапайым маршрутизатордың тіркесімі жеткілікті болады.

Сұрауды өңдеу

Бұған қоса, бізге кіріс қосылымдарды «тыңдайтын» және барлық сұрауларды дұрыс өңдеушіге қайта бағыттайтын құрамдас қажет. http.Server бұл тапсырманы оңай орындай алады.

Төменде сервер қосылымды өңдеуге қатысты барлық тапсырмаларға жауапты екенін көрсетеді. Бұл, мысалы, TLS протоколы арқылы жұмыс істейді. http.ListenAndServer шақыруын жүзеге асыру үшін стандартты HTTP сервері пайдаланылады.

Енді күрделірек мысалдарды қарастырайық.

Let's Encrypt қосу

Әдепкі бойынша, біздің қолданба HTTP протоколы арқылы жұмыс істейді, бірақ HTTPS протоколын пайдалану ұсынылады. Мұны Go бағдарламасында қиындықсыз жасауға болады. Егер сіз сертификат пен жеке кілт алсаңыз, ListenAndServeTLS-ті дұрыс сертификатпен және негізгі файлдармен тіркеу жеткілікті.

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

Сіз әрқашан жақсырақ жасай аласыз.

Шифрлаймыз автоматты түрде жаңартылатын тегін сертификаттарды ұсынады. Қызметті пайдалану үшін сізге пакет қажет autocert.

Оны конфигурациялаудың ең оңай жолы - http.Serve бірге autocert.NewListener әдісін пайдалану. Бұл әдіс HTTP сервері сұрауларды өңдеген кезде TLS сертификаттарын алуға және жаңартуға мүмкіндік береді:

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 үшін маршруттарды қамтитын api/v1/api.go файлы берілген:

/ 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 ұсынады:

Ақпарат көзі: www.habr.com

пікір қалдыру