Golang'da web sunucusu geliştirme - basitten karmaşığa

Golang'da web sunucusu geliştirme - basitten karmaşığa

Beş yıl önce başladım Gophish'i geliştirBu Golang'ı öğrenme fırsatı sağladı. Go'nun birçok kütüphaneyle desteklenen güçlü bir dil olduğunu fark ettim. Go çok yönlüdür: özellikle sunucu tarafı uygulamaları sorunsuz bir şekilde geliştirmek için kullanılabilir.

Bu makale Go'da bir sunucu yazmakla ilgilidir. "Merhaba dünya!" gibi basit şeylerle başlayalım ve aşağıdaki yeteneklere sahip bir uygulamayla bitirelim:

- HTTPS için Let's Encrypt'i kullanma.
— API yönlendiricisi olarak çalışmak.
— Ara yazılımlarla çalışma.
— Statik dosyaların işlenmesi.
— Doğru kapatma.

Skillbox şunları önerir: pratik kurs "Sıfırdan Python geliştiricisi".

Hatırlatıyoruz: tüm "Habr" okuyucuları için - "Habr" promosyon kodunu kullanarak herhangi bir Skillbox kursuna kayıt olurken 10 ruble indirim.

Merhaba dünya!

Go'da çok hızlı bir şekilde bir web sunucusu oluşturabilirsiniz. Yukarıda vaat edilen “Merhaba dünya!” ifadesini döndüren bir işleyici kullanmanın bir örneğini burada bulabilirsiniz.

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

Bundan sonra uygulamayı çalıştırıp sayfayı açarsanız localhost, hemen “Merhaba dünya!” metnini göreceksiniz. (tabii ki her şey doğru çalışıyorsa).

İşleyiciyi daha sonra birçok kez kullanacağız, ancak önce her şeyin nasıl çalıştığını anlayalım.

ağ/http

Örnekte paket kullanıldı net/http, Go'da hem sunucuları hem de HTTP istemcilerini geliştirmek için kullanılan birincil araçtır. Kodu anlamak için üç önemli unsurun anlamını anlayalım: http.Handler, http.ServeMux ve http.Server.

HTTP işleyicileri

Bir istek aldığımızda işleyici bunu analiz eder ve bir yanıt oluşturur. Go'daki işleyiciler şu şekilde uygulanır:

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

İlk örnekte http.HandleFunc yardımcı işlevi kullanılmaktadır. Başka bir işlevi sarar ve bu da http.ResponseWriter ve http.Request'i ServeHTTP'ye alır.

Başka bir deyişle Golang'daki işleyiciler tek bir arayüzde sunulur ve bu da programcıya birçok seçenek sunar. Örneğin, ara katman yazılımı bir işleyici kullanılarak uygulanır; burada ServeHTTP önce bir şey yapar ve ardından başka bir işleyicinin ServeHTTP yöntemini çağırır.

Yukarıda belirtildiği gibi işleyiciler yalnızca isteklere yanıt üretir. Ancak belirli bir zamanda hangi işleyici kullanılmalıdır?

Yönlendirme İste

Doğru seçimi yapmak için bir HTTP çoklayıcı kullanın. Bazı kütüphanelerde buna muxer veya router denir, ancak hepsi aynı şeydir. Çoklayıcının işlevi istek yolunu analiz etmek ve uygun işleyiciyi seçmektir.

Karmaşık yönlendirme desteğine ihtiyacınız varsa üçüncü taraf kitaplıkları kullanmak daha iyidir. En gelişmişlerden bazıları - goril/mux и go-chi/chi, bu kütüphaneler ara işlemlerin sorunsuz bir şekilde uygulanmasını mümkün kılar. Onların yardımıyla joker karakter yönlendirmesini yapılandırabilir ve bir dizi başka görevi gerçekleştirebilirsiniz. Avantajları standart HTTP işleyicileriyle uyumluluktur. Sonuç olarak gelecekte değiştirilebilecek basit bir kod yazabilirsiniz.

Normal bir durumda karmaşık çerçevelerle çalışmak standart olmayan çözümler gerektirecektir ve bu, varsayılan işleyicilerin kullanımını önemli ölçüde karmaşıklaştırır. Uygulamaların büyük çoğunluğunu oluşturmak için varsayılan kitaplığın ve basit bir yönlendiricinin birleşimi yeterli olacaktır.

Sorgu İşleme

Ayrıca gelen bağlantıları "dinleyecek" ve tüm istekleri doğru işleyiciye yönlendirecek bir bileşene ihtiyacımız var. http.Server bu görevi kolaylıkla halledebilir.

Aşağıda, bağlantının işlenmesiyle ilgili tüm görevlerden sunucunun sorumlu olduğu gösterilmektedir. Bu, örneğin TLS protokolü kullanılarak çalışır. http.ListenAndServer çağrısını uygulamak için standart bir HTTP sunucusu kullanılır.

Şimdi daha karmaşık örneklere bakalım.

Let's Encrypt'i ekleme

Uygulamamız varsayılan olarak HTTP protokolü üzerinden çalışmaktadır ancak HTTPS protokolünün kullanılması tavsiye edilmektedir. Bu Go'da sorunsuz bir şekilde yapılabilir. Sertifika ve özel anahtar aldıysanız ListenAndServeTLS'yi doğru sertifika ve anahtar dosyalarıyla kaydetmeniz yeterlidir.

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

Her zaman daha iyisini yapabilirsiniz.

Şifreleyelim otomatik yenilemeli ücretsiz sertifikalar sağlar. Hizmeti kullanabilmek için bir pakete ihtiyacınız var autocert.

Bunu yapılandırmanın en kolay yolu, autocert.NewListener yöntemini http.Serve ile birlikte kullanmaktır. Bu yöntem, HTTP sunucusu istekleri işlerken TLS sertifikaları almanızı ve güncellemenizi sağlar:

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

Tarayıcıda açarsak example.com, bir HTTPS yanıtı alacağız: "Merhaba dünya!"

Daha ayrıntılı konfigürasyona ihtiyacınız varsa autocert.Manager yöneticisini kullanmalısınız. Daha sonra kendi http.Server örneğimizi oluşturuyoruz (şimdiye kadar bunu varsayılan olarak kullanıyorduk) ve yöneticiyi TLSConfig sunucusuna ekliyoruz:

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

Bu, otomatik sertifika yenilemeyle tam HTTPS desteğini uygulamanın kolay bir yoludur.

Özel rotalar ekleme

Standart kitaplıkta bulunan varsayılan yönlendirici iyidir, ancak çok basittir. Çoğu uygulama, iç içe geçmiş ve joker karakterli rotalar veya yol modellerini ve parametrelerini ayarlama prosedürünü içeren daha karmaşık yönlendirme gerektirir.

Bu durumda paketleri kullanmaya değer goril/mux и go-chi/chi. İkincisiyle nasıl çalışılacağını öğreneceğiz - aşağıda bir örnek gösterilmiştir.

API'miz için rotaları içeren api/v1/api.go dosyası verilmiştir:

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

Ana dosyadaki rotalar için api/vq önekini ayarladık.

Daha sonra bunu ana uygulamamızda api/v1/ öneki altında ana yönlendiricimize bağlayabiliriz:

// 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'nun karmaşık rotalarla çalışma kolaylığı, büyük, karmaşık uygulamaların yapılandırılmasını ve bakımını basitleştirmeyi mümkün kılar.

Ara yazılımla çalışma

Aşamalandırma, bir HTTP işleyicisini diğerine sarmalamayı içerir; böylece kimlik doğrulama, sıkıştırma, günlüğe kaydetme ve diğer birçok işlevin hızlı bir şekilde gerçekleştirilmesi mümkün olur.

Örnek olarak http.Handler arayüzüne bakalım; onu hizmet kullanıcılarının kimliğini doğrulayan bir işleyici yazmak için kullanacağız.

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

Ara yazılım işlevselliğini genişletmenize olanak tanıyan chi gibi üçüncü taraf yönlendiriciler vardır.

Statik dosyalarla çalışma

Go standart kitaplığı; resimler, JavaScript ve CSS dosyaları da dahil olmak üzere statik içerikle çalışmaya yönelik yetenekler içerir. Bunlara http.FileServer işlevi aracılığıyla erişilebilir. Belirli bir dizindeki dosyaları sunan bir işleyici döndürür.

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

Ana index.html dosyasını içermiyorsa, http.Dir'in dizinin içeriğini görüntülediğini kesinlikle hatırlamakta fayda var. Bu durumda dizinin tehlikeye atılmasını önlemek için paketi kullanmalısınız. unindexed.

Doğru kapatma

Go ayrıca HTTP sunucusunun zarif bir şekilde kapatılması adı verilen bir özelliğe de sahiptir. Bu, Shutdown() yöntemi kullanılarak yapılabilir. Sunucu bir goroutine ile başlatılır ve ardından bir kesme sinyali almak için kanal dinlenir. Sinyal alınır alınmaz sunucu kapanır, ancak hemen değil, birkaç saniye sonra.

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)

Sonuç olarak

Go, neredeyse evrensel standart kütüphaneye sahip güçlü bir dildir. Varsayılan yetenekleri çok geniştir ve arayüzler kullanılarak geliştirilebilirler; bu, gerçekten güvenilir HTTP sunucuları geliştirmenize olanak tanır.

Skillbox şunları önerir:

Kaynak: habr.com

Yorum ekle