国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【go項目-geecache】動手寫分布式緩存 day2 - 單機并發(fā)緩存

這篇具有很好參考價值的文章主要介紹了【go項目-geecache】動手寫分布式緩存 day2 - 單機并發(fā)緩存。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

[ Github- geecache ](luckly-0/geecache (github.com))

收獲總結:

  • 了解接口的使用場景,它和函數之間的差別和優(yōu)略勢
  • 測試文件要以_test結尾
  • 系統(tǒng)設計要嚴謹,要考慮后期的拓展性和維護 ,比如load函數考慮到了分布式場景
  • 數據結構之間的封裝

sync.Mutex 互斥鎖

如果我們要是實現并發(fā)緩存,那么我們要引入sync.Mutex 互斥鎖來保證多個協(xié)程不沖突,確保同一時間只有一個協(xié)程運行,我們在使用的時候使用Lock() 和unLock()來實現阻塞

實現并發(fā)讀寫

實現ByteView表示緩存值 1.go

package geecache
type ByteView struct {
    b []byte //緩存值,byte是為了通用性
}
func (v ByteView) Len() int {
    return len(v.b)
}
func (v ByteView) ByteSlice() []byte {
    return cloneBytes(v.b)
}
func (v ByteView) String() string { // 返回一個字符串的拷貝
    return string(v.b)
}

func cloneBytes(b []byte) []byte { // 返回一個byte的拷貝
    c := make([]byte, len(b))
    copy(c, b)
    return c
}
  • ByteView,b表示實際的緩存值
  • Len()實現接口,表示所占的內存
  • ByteSlice() 返回一個拷貝,因為ByteView不能修改

封裝lru.Cache ,添加并發(fā)屬性 2.go

實現cache數據結構
package geecache
import (
    "geecache/lru"
    "sync"
)
type cache struct {
    mu         sync.Mutex
    lru        *lru.Cache
    cacheBytes int64
}

cache : 里面封裝了lru的Cache,控制并發(fā)的mutex鎖,和緩存大小

實現增加和get函數
func (c *cache) add(key string, value ByteView) {
    c.mu.Lock()
    defer c.mu.Unlock()
    if c.lru == nil {
        c.lru = lru.New(c.cacheBytes, nil)
    }
    c.lru.Add(key, value)
}
func (c *cache) get(key string) (value ByteView, ok bool) {
    c.mu.Lock()
    defer c.mu.Unlock()
    if c.lru == nil {
        return
    }
    if v, ok := c.lru.Get(key); ok {
        return v.(ByteView), ok
    }
    return
}

add和get函數邏輯類似,首先使用mutex鎖保證不沖突,然后查看cache已經存在,如果不存在則初始化,然后添加/獲得數據

defer 的 使用

這里使用了defer,defer的作用就是令函數最后執(zhí)行,所以雖然 c.mu.Lock()
defer c.mu.Unlock()寫在一起,但是Unlock()是最后運行的,即保證協(xié)程不沖突,又提高代碼可讀性,不會忘記解鎖

實現Group ,負責控制緩存值的存取 group.go

實現回調函數,在緩存不存在時獲取數據

當我們在緩存中找不到數據時,此時我們需要從外界獲取數據,由于數據來源的多種,我們不應該考慮這么多,為了拓展性和可讀性,我們實現一個回調函數來通知用戶我們需要數據,用戶通過這個接口把數據傳入

package geecache
import (
    "fmt"
    "log"
    "sync"
)
type Getter interface {
    Get(key string) ([]byte, error)
}
type GetterFunc func(key string) ([]byte, error)
func (f GetterFunc) Get(key string) ([]byte, error) {
    return f(key)
}

這里定義了Getter接口,GetterFunc函數類型,并且為這個類型實現了Getter接口Get方法

在這里為什么使用接口而不用直接函數實現呢?
  • 接口比函數更好的地方在于接口并不關心傳入的數據類型,所以接口可以實現多態(tài),更靈活也更節(jié)省代碼,面對其他情況也能處理
  • 而函數需要形參,對傳入參數有要求,面對復雜場景無法處理

實現Group數據結構

type Group struct {
    name      string
    getter    Getter
    mainCache cache
}
var (
    mu     sync.RWMutex
    groups = make(map[string]*Group)
)
  • name 表示緩存的名字
  • getter 回調函數,用于不命中緩存時從用戶獲取數據
  • mainCache 緩存,實現lru算法,add/get等操作
    除此以外還有兩個全局變量
  • mu RW鎖 允許多個同時讀,禁止讀寫和寫寫同時操作
  • groups
實例化函數
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
    if getter == nil {
        panic("nil Getter")
    }
    mu.Lock()
    defer mu.Unlock()
    g := &Group{
        name:      name,
        getter:    getter,
        mainCache: cache{cacheBytes: cacheBytes},
    }
    groups[name] = g
    return g
}

首先還是判斷回調函數是否正常(是否為空),正常則開啟mutex鎖,保證協(xié)程正常運行,然后實例化文章來源地址http://www.zghlxwxcb.cn/news/detail-416948.html

實現Get函數和GetGroup函數

func GetGroup(name string) *Group {  
	mu.RLock()  
	g := groups[name]  
	mu.RUnlock()  
	return g  
}
  • GetGroup函數通過緩存名字得到group
func (g *Group) Get(key string) (ByteView, error) {
    if key == "" {
        return ByteView{}, fmt.Errorf("key is required")
    }
    if v, ok := g.mainCache.get(key); ok {
        log.Println("[GeeCache] hit")
        return v, nil
    }
    return g.load(key)
}
func (g *Group) load(key string) (value ByteView, err error) {
    return g.getLocally(key)
}
func (g *Group) getLocally(key string) (ByteView, error) {
    bytes, err := g.getter.Get(key)
    if err != nil {
        return ByteView{}, err
    }
    value := ByteView{b: cloneBytes(bytes)}
    g.populateCache(key, value)
    return value, nil
}
func (g *Group) populateCache(key string, value ByteView) {
    g.mainCache.add(key, value)
}
  • 實現Get函數,首先判斷key是否存在實現過濾,然后查看緩存,找的到就返回,否則使用load函數去進一步查找
  • load函數調用getLocally,在分布式場景會使用其他函數獲取
  • getLocally 首先使用回調函數,如果成功得到數據則將源數據添加到緩存 mainCache 中(通過 populateCache 方法)
  • populateCache 將數據添加main_Cache緩存

實現代碼和測試代碼

1.go

package geecache

  

type ByteView struct {

    b []byte //緩存值,byte是為了通用性

}

  

func (v ByteView) Len() int {

    return len(v.b)

}

  

func (v ByteView) ByteSlice() []byte {

    return cloneBytes(v.b)

}

  

func (v ByteView) String() string { // 返回一個字符串的拷貝

    return string(v.b)

}

  

func cloneBytes(b []byte) []byte { // 返回一個byte的拷貝

    c := make([]byte, len(b))

    copy(c, b)

    return c

}

2.go

package geecache

  

import (

    "geecache/lru"

    "sync"

)

  

type cache struct {

    mu         sync.Mutex

    lru        *lru.Cache

    cacheBytes int64

}

  

func (c *cache) add(key string, value ByteView) {

    c.mu.Lock()

    defer c.mu.Unlock()

    if c.lru == nil {

        c.lru = lru.New(c.cacheBytes, nil)

    }

    c.lru.Add(key, value)

}

  

func (c *cache) get(key string) (value ByteView, ok bool) {

    c.mu.Lock()

    defer c.mu.Unlock()

    if c.lru == nil {

        return

    }

  

    if v, ok := c.lru.Get(key); ok {

        return v.(ByteView), ok

    }

  

    return

}

group.go

package geecache

  

import (

    "fmt"

    "log"

    "sync"

)

  

type Group struct {

    name      string

    getter    Getter

    mainCache cache

}

  

type Getter interface {

    Get(key string) ([]byte, error)

}

  

type GetterFunc func(key string) ([]byte, error)

  

func (f GetterFunc) Get(key string) ([]byte, error) {

    return f(key)

}

  

var (

    mu     sync.RWMutex

    groups = make(map[string]*Group)

)

  

func NewGroup(name string, cacheBytes int64, getter Getter) *Group {

    if getter == nil {

        panic("nil Getter")

    }

    mu.Lock()

    defer mu.Unlock()

    g := &Group{

        name:      name,

        getter:    getter,

        mainCache: cache{cacheBytes: cacheBytes},

    }

    groups[name] = g

    return g

}

  

func GetGroup(name string) *Group {

    mu.RLock()

    g := groups[name]

    mu.RUnlock()

    return g

}

  

func (g *Group) Get(key string) (ByteView, error) {

    if key == "" {

        return ByteView{}, fmt.Errorf("key is required")

    }

  

    if v, ok := g.mainCache.get(key); ok {

        log.Println("[GeeCache] hit")

        return v, nil

    }

  

    return g.load(key)

}

  

func (g *Group) load(key string) (value ByteView, err error) {

    return g.getLocally(key)

}

  

func (g *Group) getLocally(key string) (ByteView, error) {

    bytes, err := g.getter.Get(key)

    if err != nil {

        return ByteView{}, err

  

    }

    value := ByteView{b: cloneBytes(bytes)}

    g.populateCache(key, value)

    return value, nil

}

  

func (g *Group) populateCache(key string, value ByteView) {

    g.mainCache.add(key, value)

}

group_test.go

package geecache

  

import (

    "fmt"

    "log"

    "reflect"

    "testing"

)

  

var db = map[string]string{

    "Tom":  "630",

    "Jack": "589",

    "Sam":  "567",

}

  

func TestGetter(t *testing.T) {

    var f Getter = GetterFunc(func(key string) ([]byte, error) {

        return []byte(key), nil

    })

  

    expect := []byte("key")

    if v, _ := f.Get("key"); !reflect.DeepEqual(v, expect) {

        t.Fatal("callback failed")

    }

}

  

func TestGet(t *testing.T) {

    loadCounts := make(map[string]int, len(db))

    gee := NewGroup("scores", 2<<10, GetterFunc(

        func(key string) ([]byte, error) {

            log.Println("[SlowDB] search key", key)

            if v, ok := db[key]; ok {

                if _, ok := loadCounts[key]; !ok {

                    loadCounts[key] = 0

                }

                loadCounts[key]++

                return []byte(v), nil

            }

            return nil, fmt.Errorf("%s not exist", key)

        }))

  

    for k, v := range db {

        if view, err := gee.Get(k); err != nil || view.String() != v {

            t.Fatal("failed to get value of Tom")

        }

        if _, err := gee.Get(k); err != nil || loadCounts[k] > 1 {

            t.Fatalf("cache %s miss", k)

        }

    }

  

    if view, err := gee.Get("unknown"); err == nil {

        t.Fatalf("the value of unknow should be empty, but %s got", view)

    }

}

  

func TestGetGroup(t *testing.T) {

    groupName := "scores"

    NewGroup(groupName, 2<<10, GetterFunc(

        func(key string) (bytes []byte, err error) { return }))

    if group := GetGroup(groupName); group == nil || group.name != groupName {

        t.Fatalf("group %s not exist", groupName)

    }

  

    if group := GetGroup(groupName + "111"); group != nil {

        t.Fatalf("expect nil, but %s got", group.name)

    }

}

到了這里,關于【go項目-geecache】動手寫分布式緩存 day2 - 單機并發(fā)緩存的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 一文拿捏分布式、分布式緩存及其問題解決

    一文拿捏分布式、分布式緩存及其問題解決

    1.集中式 傳統(tǒng)的計算模型通常是集中式的,所有的計算任務和數據處理都由 單一的計算機或服務器 完成。然而,隨著數據量和計算需求的增加,集中式系統(tǒng)可能會面臨性能瓶頸和可靠性問題。 故而引出了分布式↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    2024年02月07日
    瀏覽(20)
  • 分布式系統(tǒng)架構設計之分布式緩存技術選型

    分布式系統(tǒng)架構設計之分布式緩存技術選型

    隨著互聯(lián)網業(yè)務的快速發(fā)展,分布式系統(tǒng)已經成為了解決大規(guī)模并發(fā)請求、高可用性、可擴展性等問題的重要手段。在分布式系統(tǒng)中,緩存作為提高系統(tǒng)性能的關鍵技術,能夠顯著降低數據庫負載、減少網絡延遲、提高數據訪問速度。當面對大量并發(fā)請求時,如果每次都直接

    2024年02月03日
    瀏覽(519)
  • SpringBoot整合Redis、以及緩存穿透、緩存雪崩、緩存擊穿的理解分布式情況下如何添加分布式鎖 【續(xù)篇】

    SpringBoot整合Redis、以及緩存穿透、緩存雪崩、緩存擊穿的理解分布式情況下如何添加分布式鎖 【續(xù)篇】

    上一篇實現了單體應用下如何上鎖,這一篇主要說明如何在分布式場景下上鎖 上一篇地址:加鎖 需要注意的點是: 在上鎖和釋放鎖的過程中要保證 原子性操作 核心是上鎖和解鎖的過程 關于解鎖使用腳本參考:SET key value [EX seconds] [PX milliseconds] [NX|XX] 3.1 一個服務按照多個端口同時

    2023年04月10日
    瀏覽(29)
  • Redis 分布式緩存

    Redis 分布式緩存

    單點 Redis 的問題及解決 數據丟失:實現Redis數據持久化 并發(fā)能力:搭建主從集群,實現讀寫分離 存儲能力:搭建分片集群,利用插槽機制實現動態(tài)擴容 故障恢復能力:利用哨兵機制,實現健康檢測和自動恢復 RDB RDB全稱Redis Database Backup file (Redis數據備份文件),也被叫做

    2024年02月10日
    瀏覽(24)
  • Redis分布式緩存

    Redis分布式緩存

    -- 基于Redis集群解決單機Redis存在的問題 單機的Redis存在四大問題: Redis有兩種持久化方案: RDB持久化 AOF持久化 ? ? ? ?RDB全稱Redis Database Backup file(Redis數據備份文件),也被叫做 Redis數據快照 。簡單來說就是把 內存中的所有數據都記錄到磁盤 中。當Redis實例故障重啟后,

    2024年02月12日
    瀏覽(19)
  • 分布式緩存

    – 基于Redis集群解決單機Redis存在的問題 Redis有兩種持久化方案: RDB持久化 AOF持久化 RDB全稱Redis Database Backup file(Redis數據備份文件),也被叫做Redis數據快照。簡單來說就是 把內存中的所有數據 都記錄到磁盤中。當Redis實例故障重啟后,從磁盤讀取快照文件,恢復數據???/p>

    2023年04月25日
    瀏覽(19)
  • Redis(分布式緩存詳解)

    Redis(分布式緩存詳解)

    Redis:基于內存的鍵值存儲系統(tǒng),通常用作高性能的數據庫、緩存和消息隊列代理,是互聯(lián)網廣泛應用的存儲中間件 特點 :基于內存存儲,讀寫性能高 Redis與MySQL區(qū)別 Redis以鍵值對形式存儲,MySQL以表格形式存儲 Redis存儲在 內存 ,MySQL存儲在 磁盤 Redis存儲 高效 ,MySQL存儲 安

    2024年02月16日
    瀏覽(30)
  • Redis高級-分布式緩存

    – 基于Redis集群解決單機Redis存在的問題 單機的Redis存在四大問題: Redis有兩種持久化方案: RDB持久化 AOF持久化 RDB全稱Redis Database Backup file(Redis數據備份文件),也被叫做Redis數據快照。簡單來說就是把內存中的所有數據都記錄到磁盤中。當Redis實例故障重啟后,從磁盤讀取

    2024年04月16日
    瀏覽(26)
  • Redis分布式緩存方案

    Redis分布式緩存方案

    數據丟失:數據持久化 并發(fā)能力弱:搭建主從集群,實現讀寫分離 故障恢復問題:哨兵實現健康檢測,自動恢復 存儲能力:搭建分片集群,利用插槽機制實現動態(tài)擴容 RDB持久化 數據庫備份文件,也叫快照,把內存數據存到磁盤。使用save進行主動RDB,會阻塞所有命令。建議

    2023年04月25日
    瀏覽(17)
  • 緩存和分布式鎖筆記

    緩存 開發(fā)中,凡是放入緩存中的數據都應該指定過期時間,使其可以在系統(tǒng)即使沒有主動更新數據也能自動觸發(fā)數據加載進緩存的流程。避免業(yè)務崩潰導致的數據永久不一致 問題。 redis作為緩存使用redisTemplate操作redis 分布式鎖的原理和使用 分布式加鎖:本地鎖,只能鎖住當

    2024年02月09日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包