網站首頁 編程語言 正文
所有的緩存數據都存儲在服務器的內存中,因此重啟服務器會導致數據丟失,基于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
原文鏈接:https://www.cnblogs.com/N3ptune/p/16623738.html
相關推薦
- 2022-05-11 垃圾收集器G1&ZGC詳解
- 2022-10-27 python使用pika庫調用rabbitmq交換機模式詳解_python
- 2022-09-03 C#中DataSet、DataTable、DataRow數據的復制方法_C#教程
- 2022-02-27 centos8安裝nginx時報錯Status:404
- 2022-05-23 ELK與Grafana聯合打造可視化監控來分析nginx日志_nginx
- 2022-08-05 C語言簡明介紹常見關鍵字的用法_C 語言
- 2022-06-29 docker環境搭建mgr集群的問題及原理_docker
- 2024-03-09 【Redis】Redis中的布隆過濾器
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支