Rumah  >  Artikel  >  pembangunan bahagian belakang  >  golang melaksanakan pintu masuk api

golang melaksanakan pintu masuk api

王林
王林asal
2023-05-10 09:34:36843semak imbas

Dengan populariti seni bina perkhidmatan mikro dan peningkatan dalam bilangan perkhidmatan, untuk meningkatkan keselamatan, kebolehpercayaan dan kebolehskalaan perkhidmatan, teknologi get laluan API telah muncul mengikut keperluan masa. Hari ini kita akan bercakap tentang cara menggunakan Golang untuk menulis get laluan API.

1. Mengapa memilih untuk menggunakan Golang

  1. Prestasi Concurrency: Golang secara semula jadi menyokong coroutine, yang boleh meningkatkan kecekapan dalam senario concurrency yang tinggi dan mempunyai daya pemprosesan yang lebih tinggi.
  2. Ciri lengkap: Golang mempunyai pakej rangkaian lengkap, seperti HTTP, WebSocket, dsb., dan ia juga menyokong berbilang protokol, seperti gRPC, MQTT, dsb.
  3. Kualiti kod: Golang ialah bahasa statik sistem jenisnya boleh memastikan kualiti kod Pada masa yang sama, ia juga boleh menggunakan sepenuhnya penyiapan editor, gesaan dan fungsi lain untuk meningkatkan kecekapan pembangunan.
  4. Merentas platform: Golang boleh dilaksanakan pada pelbagai platform berbeza, dan penggunaannya juga sangat mudah.

2. Idea reka bentuk get laluan API

Sebelum mereka bentuk get laluan API, kita perlu tahu perkara yang perlu dilakukan oleh get laluan API. Secara umumnya, get laluan API perlu melakukan perkara berikut:

  1. Penghalaan: Halakan permintaan ke perkhidmatan yang sepadan berdasarkan URI yang diminta.
  2. Penapisan: Sahkan dan sahkan permintaan.
  3. Pengimbangan beban: edarkan permintaan kepada contoh perkhidmatan yang berbeza pada bahagian belakang.
  4. Cache: keputusan respons cache untuk meningkatkan kelajuan tindak balas.
  5. Pemantauan: Statistik dan pemantauan keputusan respons permintaan.

Menurut idea di atas, kami mereka bentuk get laluan API yang ringkas, yang terutamanya merangkumi komponen berikut:

  1. Penghala: bertanggungjawab untuk penghalaan permintaan kepada perkhidmatan yang sepadan.
  2. Penapis: Bertanggungjawab untuk pengesahan, pengesahan dan operasi permintaan yang lain.
  3. Pengimbangan beban: Bertanggungjawab untuk mengedarkan permintaan kepada contoh perkhidmatan yang berbeza di bahagian belakang.
  4. Caching: Bertanggungjawab untuk menyimpan hasil respons dan meningkatkan kelajuan tindak balas.
  5. Pemantauan: Bertanggungjawab untuk statistik dan pemantauan keputusan respons permintaan.

3. Laksanakan API Gateway

Seterusnya kita akan melaksanakan komponen di atas satu persatu.

1. Penghala

Untuk melaksanakan penghala, kita perlu mentakrifkan item penghalaan terlebih dahulu:

type Route struct {
    Path      string
    Method    string
    Handler   http.Handler
}

Laluan mengandungi Laluan untuk menyimpan laluan penghalaan dan Kaedah untuk menyimpan Jenis kaedah permintaan, Pengendali digunakan untuk menyimpan kaedah untuk memproses permintaan.

Seterusnya kami mentakrifkan struktur Penghala untuk menyimpan senarai laluan dan menghalakan permintaan ke pemproses yang sepadan:

type Router struct {
    routes []*Route
}

func (r *Router) HandleFunc(path string, method string, handlerFunc http.HandlerFunc) {
    r.routes = append(r.routes, &Route{Path: path, Method: method, Handler: handlerFunc})
}

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    for _, route := range r.routes {
        if route.Path == req.URL.Path && route.Method == req.Method {
            route.Handler.ServeHTTP(w, req)
            return
        }
    }

    http.Error(w, "404 not found", http.StatusNotFound)
}

Dalam kod, kami menyediakan dua kaedah untuk pendaftaran Permintaan kaedah pemprosesan dan penghalaan harus serupa dengan http.HandleFunc Ia akan mengikat alamat penghalaan, kaedah permintaan dan kaedah pemprosesan permintaan kepada objek ServeHTTP akan mencari laluan yang sepadan selepas menerima permintaan tersebut kaedah, dan 404 dikembalikan jika tidak dijumpai.

Seterusnya, tulis pelaksanaan kaedah pemprosesan mudah:

func main() {
    router := &Router{}

    router.HandleFunc("/hello", "GET", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "hello")
    })

    http.ListenAndServe(":8080", router)
}

Kaedah pemprosesan ini akan membalas hello untuk sebarang permintaan GET dengan alamat /hello.

2. Penapis

WAF (Web Application Firewall) ialah penapis yang biasa digunakan untuk melindungi aplikasi web daripada pelbagai serangan. Kami boleh menapis permintaan berdasarkan kaedah permintaan, pengepala permintaan, parameter permintaan dan maklumat lain. Di sini, kami menggunakan pengepala permintaan sebagai penapis. Jika pengepala permintaan mengandungi teg tertentu, ia diluluskan, jika tidak, ia dianggap sebagai permintaan tidak sah.

Untuk melaksanakan penapis menggunakan Golang, kami perlu menulis perisian tengah yang akan menyemak sama ada teg tertentu disertakan dalam setiap permintaan. Jika ia disertakan, teruskan turunkan permintaan, jika tidak ralat akan dikembalikan. Kita boleh menggunakan gorila/mux untuk melaksanakan middleware.

type FilterMiddleware struct {
    next http.Handler
}

func (f *FilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.Header.Get("X-Auth-Token") != "magic" {
        http.Error(w, "Unauthorized request", http.StatusUnauthorized)
        return
    }

    f.next.ServeHTTP(w, r)
}

func (r *Router) RegisterMiddleware(middleware func(http.Handler) http.Handler) {
    handler := http.Handler(r)

    for _, m := range middlewareFunctions {
        handler = m(handler)
    }

    r.Handler = handler
}

Dalam kod, FilterMiddleware akan menyemak sama ada pengepala permintaan mengandungi teg "X-Auth-Token" Jika ya, permintaan itu akan diturunkan, jika tidak, ralat yang tidak dibenarkan akan dikembalikan. Kami juga mentakrifkan fungsi RegisterMiddleware untuk mendaftar middleware.

3. Pengimbangan beban

Pengimbangan beban ialah salah satu komponen terpenting dalam get laluan API Ia boleh mengedarkan permintaan kepada contoh perkhidmatan yang berbeza pada bahagian belakang. Kita boleh menggunakan algoritma pengundian, rawak dan lain-lain untuk mencapai ini.

Di sini kami menggunakan algoritma tinjauan pendapat mudah untuk pengimbangan beban. Kami boleh memilih alamat pelayan seterusnya dari kumpulan dan memajukan permintaan ke pelayan itu.

type LoadBalancer struct {
    Pool []string
    Next int
}

func (l *LoadBalancer) Pick() string {
    if l.Next >= len(l.Pool) {
        l.Next = 0
    }

    host := l.Pool[l.Next]

    l.Next++

    return host
}

func (r *Router) HandleFunc(path string, method string, handlerFunc http.HandlerFunc) {
    l := &LoadBalancer{
        Pool: []string{"http://127.0.0.1:8081", "http://127.0.0.1:8082"},
        Next: 0,
    }

    handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        url, err := url.Parse(l.Pick())
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        proxy := httputil.NewSingleHostReverseProxy(url)
        proxy.ServeHTTP(w, req)
    })

    r.routes = append(r.routes, &Route{Path: path, Method: method, Handler: handler})
}

Dalam kod, kami mencipta struktur pengimbangan beban mudah yang mengandungi kumpulan pelayan dan lokasi pelayan seterusnya yang akan dipilih. Kaedah pick() akan memilih alamat pelayan berdasarkan panjang kumpulan pelayan. Dalam HandleFunc, kami menggunakan algoritma Round Robin untuk memajukan permintaan ke pelayan yang berbeza.

4. Caching

Caching boleh meningkatkan prestasi sistem dan juga mengurangkan bilangan permintaan ke bahagian belakang. Dalam get laluan API, kami boleh membenamkan cache dalam proses respons permintaan dan terus mengembalikan permintaan dalam cache yang boleh memberikan respons dengan cepat.

type Cache struct {
    data map[string] []byte
    mutex sync.Mutex
}

func (c *Cache) Get(key string) []byte {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    if value, ok := c.data[key]; ok {
        return value
    }

    return nil
}

func (c *Cache) Set(key string, value []byte) {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    c.data[key] = value
}

func (r *Router) HandleFunc(path string, method string, handlerFunc http.HandlerFunc) {
    cache := &Cache{
        data: make(map[string][]byte),
    }

    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        key := r.URL.String()
        if data := cache.Get(key); data != nil {
            w.Write(data)
            return
        }

        buffer := &bytes.Buffer{}
        proxy := httputil.NewSingleHostReverseProxy(r.URL)
        proxy.ServeHTTP(buffer, r)
        cache.Set(key, buffer.Bytes())
        w.Write(buffer.Bytes())
    })

    r.routes = append(r.routes, &Route{Path: path, Method: method, Handler: handler})
}

Dalam kod, kami mencipta struktur cache dan cache permintaan dalam HandleFunc. Jika permintaan yang sama wujud dalam cache, kami boleh mengembalikan hasil terus daripada cache, sekali gus mengurangkan bilangan permintaan ke bahagian belakang.

5. Pemantauan

Pemantauan boleh membantu kami memahami dengan lebih baik status pengendalian sistem dan memahami kelajuan tindak balas sistem semasa.

使用Prometheus来实现API网关的监控。我们只需记录下来每一个请求的响应时间、状态码及信息,然后将所有的数据发送到Prometheus。

var (
    apiRequestsDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "api_request_duration_seconds",
        Help:    "The API request duration distribution",
        Buckets: prometheus.DefBuckets,
    }, []string{"status"})
)

type MetricMiddleware struct {
    next http.Handler
}

func (m *MetricMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    start := time.Now()

    ww := httptest.NewRecorder()

    m.next.ServeHTTP(ww, r)

    duration := time.Since(start)

    apiRequestsDuration.WithLabelValues(strconv.Itoa(ww.Code)).Observe(duration.Seconds())

    for key, values := range ww.Header() {
        for _, value := range values {
            w.Header().Add(key, value)
        }
    }

    w.WriteHeader(ww.Code)

    body := ww.Body.Bytes()

    w.Write(body)
}

func main() {
    router := &Router{}

    router.HandleFunc("/hello", "GET", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "hello")
    })

    logger := NewLoggerMiddleware(router)

    http.Handle("/metrics", prometheus.Handler())
    http.Handle("/", logger)

    service := ":8080"

    log.Printf("Server starting on %v
", service)
    log.Fatal(http.ListenAndServe(service, nil))
}

在代码中,我们定义了一个MetricMiddleware并在请求结束后统计相关时间数据,最后通过Prometheus将数据输出到Metrics监控系统中。我们还通过http.Handle将Prometheus绑定在“/metrics”路径上,方便查询。

结束语

至此,我们用Golang实现了一个简单的API网关。在实际使用中,我们还可以添加更多的功能,如熔断、限流、容错等,来提高其安全性、稳定性和可用性。

Atas ialah kandungan terperinci golang melaksanakan pintu masuk api. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn