<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
所有的快取資料都儲存在伺服器的記憶體中,因此重啟伺服器會導致資料丟失,基於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,保持狀態資訊的正確
下面定義一個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服務,基於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的記憶體快取服務的文章就介紹到這了,更多相關Go記憶體快取服務內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45