Go語言基于HTTP的內(nèi)存緩存服務(wù)的實現(xiàn)
所有的緩存數(shù)據(jù)都存儲在服務(wù)器的內(nèi)存中,因此重啟服務(wù)器會導(dǎo)致數(shù)據(jù)丟失,基于HTTP通信會將使開發(fā)變得簡單,但性能不會太好
緩存服務(wù)接口
本程序采用REST接口,支持設(shè)置(Set)、獲取(Get)和刪除(Del)這3個基本操作,同時還支持對緩存服務(wù)狀態(tài)進(jìn)行查詢。Set操作是將一對鍵值對設(shè)置到服務(wù)器中,通過HTTP的PUT方法進(jìn)行,Get操作用于查詢某個鍵并獲取其值,通過HTTP的GET方法進(jìn)行,Del操作用于從緩存中刪除某個鍵,通過HTTP的DELETE方法進(jìn)行,同時用戶可以查詢緩存服務(wù)器緩存了多少鍵值對,占據(jù)了多少字節(jié)
創(chuàng)建一個cache包,編寫緩存服務(wù)的主要邏輯
先定義了一個Cache接口類型,包含了要實現(xiàn)的4個方法(設(shè)置、獲取、刪除和狀態(tài)查詢)
package cache
type Cache interface {
Set(string, []byte) error
Get(string) ([]byte, error)
Del(string) error
GetStat() Stat
}緩存服務(wù)實現(xiàn)
綜上所述,這個緩存服務(wù)實現(xiàn)起來還是比較容易的,使用Go語言內(nèi)置的map存儲鍵值,使用http庫來處理HTTP請求,實現(xiàn)REST接口
定義狀態(tài)信息
定義了一個Stat結(jié)構(gòu)體,表示緩存服務(wù)狀態(tài):
type Stat struct {
Count int64
KeySize int64
ValueSize int64
}Count表示緩存目前保存的鍵值對數(shù)量,KeySize和ValueSize分別表示鍵和值所占的總字節(jié)數(shù)
實現(xiàn)兩個方法,用來更新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))
}緩存增加鍵值數(shù)據(jù)時,調(diào)用add函數(shù),更新緩存狀態(tài)信息,對應(yīng)地,刪除數(shù)據(jù)時就調(diào)用del,保持狀態(tài)信息的正確
實現(xiàn)Cache接口
下面定義一個New函數(shù),創(chuàng)建并返回一個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
}該函數(shù)會接收一個string類型的參數(shù),這個參數(shù)指定了要創(chuàng)建的Cache接口的具體結(jié)構(gòu)類型,這里考慮到以后可能不限于內(nèi)存緩存,有擴(kuò)展的可能。如果typ是"inmemory"代表是內(nèi)存緩存,就調(diào)用newInMemoryCache,并返回
如下定義了inMemoryCache結(jié)構(gòu)和對應(yīng)New函數(shù):
type inMemoryCache struct {
c map[string][]byte
mutex sync.RWMutex
Stat
}
func newInMemoryCache() *inMemoryCache {
return &inMemoryCache{
make(map[string][]byte),
sync.RWMutex{}, Stat{}}
}這個結(jié)構(gòu)中包含了存儲數(shù)據(jù)的map,和一個讀寫鎖用于并發(fā)控制,還有一個Stat匿名字段,用來記錄緩存狀態(tài)
下面一一實現(xiàn)所定義的接口方法:
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函數(shù)的作用是設(shè)置鍵值到map中,這要在上鎖的情況下進(jìn)行,首先判斷map中是否已有此鍵,之后用新值覆蓋,過程中要更新狀態(tài)信息
Get函數(shù)的作用是獲取指定鍵對應(yīng)的值,使用讀鎖即可
Del同樣須要互斥,先判斷map中是否有指定的鍵,如果有則刪除,并更新狀態(tài)信息
實現(xiàn)HTTP服務(wù)
接下來實現(xiàn)HTTP服務(wù),基于Go語言的標(biāo)準(zhǔn)HTTP包來實現(xiàn),在目錄下創(chuàng)建一個http包
先定義Server相關(guān)結(jié)構(gòu)、監(jiān)聽函數(shù)和New函數(shù):
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結(jié)構(gòu)體內(nèi)嵌了cache.Cache接口,這意味著http.Server也要實現(xiàn)對應(yīng)接口,為Server定義了一個Listen方法,其中會調(diào)用http.Handle函數(shù),會注冊兩個Handler分別用來處理/cache/和status這兩個http協(xié)議的端點
Server.cacheHandler和http.statusHandler返回一個http.Handler接口,用于處理HTTP請求,相關(guān)實現(xiàn)如下:
要實現(xiàn)http.Handler接口就要實現(xiàn)ServeHTTP方法,是真正處理HTTP請求的邏輯,該方法使用switch-case對請求方式進(jìn)行分支處理,處理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實現(xiàn)如下:
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請求,調(diào)用GetStat方法得到緩存狀態(tài)信息,將其序列化為JSON數(shù)據(jù)后寫回
測試運(yùn)行
編寫一個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)
}
}發(fā)起PUT請求,增加數(shù)據(jù):
$ 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
查看狀態(tài)信息:
$ curl localhost:9090/status
{"Count":1,"KeySize":3,"ValueSize":5}
查詢:
$ curl localhost:9090/cache/key value
到此這篇關(guān)于Go語言基于HTTP的內(nèi)存緩存服務(wù)的文章就介紹到這了,更多相關(guān)Go內(nèi)存緩存服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用go語言實現(xiàn)WebAssembly數(shù)據(jù)加密的示例講解
在Web開發(fā)中,有時候為了提升安全性需要對數(shù)據(jù)進(jìn)行加密,由于js代碼相對比較易讀,直接在js中做加密安全性較低,而WebAssembly代碼不如js易讀,本文提供一個用go語言實現(xiàn)的WebAssembly數(shù)據(jù)加密示例,需要的朋友可以參考下2024-03-03
Go語言在終端打開實現(xiàn)進(jìn)度條處理數(shù)據(jù)方法實例
這篇文章主要介紹了Go語言在終端打開實現(xiàn)進(jìn)度條處理數(shù)據(jù)方法實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Golang加權(quán)輪詢負(fù)載均衡的實現(xiàn)
負(fù)載均衡器在向后端服務(wù)分發(fā)流量負(fù)載時可以使用幾種策略。本文主要介紹了Golang加權(quán)輪詢負(fù)載均衡,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-06-06
golang常用庫之gorilla/mux-http路由庫使用詳解
這篇文章主要介紹了golang常用庫之gorilla/mux-http路由庫使用,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10

