中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言基于HTTP的內存緩存服務怎么實現

發布時間:2022-08-25 13:57:39 來源:億速云 閱讀:120 作者:iii 欄目:開發技術

本篇內容介紹了“Go語言基于HTTP的內存緩存服務怎么實現”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    所有的緩存數據都存儲在服務器的內存中,因此重啟服務器會導致數據丟失,基于HTTP通信會將使開發變得簡單,但性能不會太好

    緩存服務接口

    本程序采用REST接口,支持設置(Set)、獲取(Get)和刪除(Del)這3個基本操作,同時還支持對緩存服務狀態進行查詢。Set操作是將一對鍵值對設置到服務器中,通過HTTP的PUT方法進行,Get操作用于查詢某個鍵并獲取其值,通過HTTP的GET方法進行,Del操作用于從緩存中刪除某個鍵,通過HTTP的DELETE方法進行,同時用戶可以查詢緩存服務器緩存了多少鍵值對,占據了多少字節

    創建一個cache包,編寫緩存服務的主要邏輯

    先定義了一個Cache接口類型,包含了要實現的4個方法(設置、獲取、刪除和狀態查詢)

    package cache
    type Cache interface {
    	Set(string, []byte) error
    	Get(string) ([]byte, error)
    	Del(string) error
    	GetStat() Stat
    }

    緩存服務實現

    綜上所述,這個緩存服務實現起來還是比較容易的,使用Go語言內置的map存儲鍵值,使用http庫來處理HTTP請求,實現REST接口

    定義狀態信息

    定義了一個Stat結構體,表示緩存服務狀態:

    type Stat struct {
    	Count     int64
    	KeySize   int64
    	ValueSize int64
    }

    Count表示緩存目前保存的鍵值對數量,KeySize和ValueSize分別表示鍵和值所占的總字節數

    實現兩個方法,用來更新Stat信息:

    func (s *Stat) add(k string, v []byte) {
    	s.Count += 1
    	s.KeySize += int64(len(k))
    	s.ValueSize += int64(len(v))
    }
    func (s *Stat) del(k string, v []byte) {
    	s.Count -= 1
    	s.KeySize -= int64(len(k))
    	s.ValueSize -= int64(len(v))
    }

    緩存增加鍵值數據時,調用add函數,更新緩存狀態信息,對應地,刪除數據時就調用del,保持狀態信息的正確

    實現Cache接口

    下面定義一個New函數,創建并返回一個Cache接口:

    func New(typ string) Cache {
    	var c Cache
    	if typ == "inmemory" {
    		c = newInMemoryCache()
    	}
    	if c == nil {
    		panic("unknown cache type " + typ)
    	}
    	log.Println(typ, "ready to serve")
    	return c
    }

    該函數會接收一個string類型的參數,這個參數指定了要創建的Cache接口的具體結構類型,這里考慮到以后可能不限于內存緩存,有擴展的可能。如果typ是"inmemory"代表是內存緩存,就調用newInMemoryCache,并返回

    如下定義了inMemoryCache結構和對應New函數:

    type inMemoryCache struct {
    	c     map[string][]byte
    	mutex sync.RWMutex
    	Stat
    }
     
    func newInMemoryCache() *inMemoryCache {
    	return &inMemoryCache{
    		make(map[string][]byte),
    		sync.RWMutex{}, Stat{}}
    }

    這個結構中包含了存儲數據的map,和一個讀寫鎖用于并發控制,還有一個Stat匿名字段,用來記錄緩存狀態

    下面一一實現所定義的接口方法:

    func (c *inMemoryCache) Set(k string, v []byte) error {
    	c.mutex.Lock()
    	defer c.mutex.Unlock()
    	tmp, exist := c.c[k]
    	if exist {
    		c.del(k, tmp)
    	}
    	c.c[k] = v
    	c.add(k, v)
    	return nil
    }
     
    func (c *inMemoryCache) Get(k string) ([]byte, error) {
    	c.mutex.RLock()
    	defer c.mutex.RLock()
    	return c.c[k], nil
    }
     
    func (c *inMemoryCache) Del(k string) error {
    	c.mutex.Lock()
    	defer c.mutex.Unlock()
    	v, exist := c.c[k]
    	if exist {
    		delete(c.c, k)
    		c.del(k, v)
    	}
    	return nil
    }
     
    func (c *inMemoryCache) GetStat() Stat {
    	return c.Stat
    }

    Set函數的作用是設置鍵值到map中,這要在上鎖的情況下進行,首先判斷map中是否已有此鍵,之后用新值覆蓋,過程中要更新狀態信息

    Get函數的作用是獲取指定鍵對應的值,使用讀鎖即可

    Del同樣須要互斥,先判斷map中是否有指定的鍵,如果有則刪除,并更新狀態信息

    實現HTTP服務

    接下來實現HTTP服務,基于Go語言的標準HTTP包來實現,在目錄下創建一個http包

    先定義Server相關結構、監聽函數和New函數:

    type Server struct {
    	cache.Cache
    }
     
    func (s *Server) Listen() error {
    	http.Handle("/cache/", s.cacheHandler())
    	http.Handle("/status", s.statusHandler())
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		log.Println(err)
    		return err
    	}
    	return nil
    }
     
    func New(c cache.Cache) *Server {
    	return &Server{c}
    }

    Server結構體內嵌了cache.Cache接口,這意味著http.Server也要實現對應接口,為Server定義了一個Listen方法,其中會調用http.Handle函數,會注冊兩個Handler分別用來處理/cache/和status這兩個http協議的端點

    Server.cacheHandler和http.statusHandler返回一個http.Handler接口,用于處理HTTP請求,相關實現如下:

    要實現http.Handler接口就要實現ServeHTTP方法,是真正處理HTTP請求的邏輯,該方法使用switch-case對請求方式進行分支處理,處理PUT、GET、DELETE請求,其他都丟棄

    package http
     
    import (
    	"io/ioutil"
    	"log"
    	"net/http"
    	"strings"
    )
     
    type cacheHandler struct {
    	*Server
    }
     
    func (h *cacheHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	key := strings.Split(r.URL.EscapedPath(), "/")[2]
    	if len(key) == 0 {
    		w.WriteHeader(http.StatusBadRequest)
    		return
    	}
    	switch r.Method {
    	case http.MethodPut:
    		b, _ := ioutil.ReadAll(r.Body)
    		if len(b) != 0 {
    			e := h.Set(key, b)
    			if e != nil {
    				log.Println(e)
    				w.WriteHeader(http.StatusInternalServerError)
    			}
    		}
    		return
    	case http.MethodGet:
    		b, e := h.Get(key)
    		if e != nil {
    			log.Println(e)
    			w.WriteHeader(http.StatusInternalServerError)
    			return
    		}
    		if len(b) == 0 {
    			w.WriteHeader(http.StatusNotFound)
    			return
    		}
    		w.Write(b)
    		return
    	case http.MethodDelete:
    		e := h.Del(key)
    		if e != nil {
    			log.Println(e)
    			w.WriteHeader(http.StatusInternalServerError)
    		}
    		return
    	default:
    		w.WriteHeader(http.StatusMethodNotAllowed)
    	}
    }
     
    func (s *Server) cacheHandler() http.Handler {
    	return &cacheHandler{s}
    }

    同理,statusHandler實現如下:

    package http
     
    import (
    	"encoding/json"
    	"log"
    	"net/http"
    )
     
    type statusHandler struct {
    	*Server
    }
     
    func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	if r.Method != http.MethodGet {
    		w.WriteHeader(http.StatusMethodNotAllowed)
    		return
    	}
    	b, e := json.Marshal(h.GetStat())
    	if e != nil {
    		log.Println(e)
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    	w.Write(b)
    }
     
    func (s *Server) statusHandler() http.Handler {
    	return &statusHandler{s}
    }

    該方法只處理GET請求,調用GetStat方法得到緩存狀態信息,將其序列化為JSON數據后寫回

    測試運行

    編寫一個main.main,作為程序的入口:

    package main
    import (
    	"cache/cache"
    	"cache/http"
    	"log"
    )
     
    func main() {
    	c := cache.New("inmemory")
    	s := http.New(c)
    	err := s.Listen()
    	if err != nil {
    		log.Fatalln(err)
    	}
    }

    發起PUT請求,增加數據:

    $ curl -v localhost:9090/cache/key -XPUT -d value
    *   Trying 127.0.0.1:9090...
    * TCP_NODELAY set
    * Connected to localhost (127.0.0.1) port 9090 (#0)
    > PUT /cache/key HTTP/1.1
    > Host: localhost:9090
    > User-Agent: curl/7.68.0
    > Accept: */*
    > Content-Length: 5
    > Content-Type: application/x-www-form-urlencoded
    > 
    * upload completely sent off: 5 out of 5 bytes
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < Date: Thu, 25 Aug 2022 03:19:47 GMT
    < Content-Length: 0
    < 
    * Connection #0 to host localhost left intact

    查看狀態信息:

    $ curl localhost:9090/status
    {"Count":1,"KeySize":3,"ValueSize":5}

    查詢:

    $ curl localhost:9090/cache/key
    value

    “Go語言基于HTTP的內存緩存服務怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    岳阳市| 临沂市| 亚东县| 成安县| 苏州市| 青海省| 麻城市| 农安县| 威信县| 南京市| 酒泉市| 建昌县| 渭南市| 香河县| 东乡县| 陵水| 西青区| 九寨沟县| 广宗县| 柳林县| 青冈县| 五寨县| 当涂县| 金寨县| 永年县| 青海省| 江津市| 永福县| 进贤县| 永康市| 大宁县| 德昌县| 双鸭山市| 吉木萨尔县| 府谷县| 邓州市| 兴安盟| 左贡县| 绥中县| 会理县| 庆安县|