ホームページ  >  記事  >  バックエンド開発  >  Golangのmemcacheを簡単に実装する方法

Golangのmemcacheを簡単に実装する方法

藏色散人
藏色散人転載
2021-02-10 09:31:472792ブラウズ

golang の次のチュートリアル コラムでは、Golang での簡単な memcache 実装方法を紹介します。

Golangのmemcacheを簡単に実装する方法

#過去 2 日間のプロジェクト作業中に、グローバル変数へのアクセスに関する問題シナリオに遭遇しました。トークンを取得するメソッドを作成するid に対応する値を取得するには、トークンをキャッシュする必要があります (グローバル変数メモリ キャッシュ)。取得できない場合、またはトークンの有効期限が切れた場合は、http リクエストを相手側に送信して取得し、キャッシュしてから返します。コードは次のとおりです:

code.go:

package person

import (
	"time"
)

var gAccId2Token map[int]interface{} = make(map[int]interface{})

func GetTokenByAccountId(accountId uint, acl string) (map[string]interface{}, error) {
	//get token from cache
	if token, ok := gAccId2Token[accountId]; ok {
		if token != nil {
			now := time.Now().Unix()
			if int(now) < int(token.(map[string]interface{})["expireDate"].(float64)) {
				return token.(map[string]interface{}), nil
			}
		}
	}

	token, err := getTokenByHttpUrl(apiUrl)
	if err != nil {
		return map[string]interface{}{}, err
	}

	gAccId2Token[accountId] = token

	return token.(map[string]interface{}), nil
}

ここで問題が発生します:

1. gAccId2Token 変数はグローバル変数の場合、読み取りと書き込みが同時に発生する可能性があり、読み取りと書き込みの間で不整合が発生します。

2. この例では、id=2 のトークンが取得され、キャッシュされたトークンの有効期限が切れている場合、http リクエストを送信して取得し、キャッシュに書き込みます。キャッシュ書き込み時間が非常に長いため、この間にid=2のトークン取得リクエストが大量に発生します トークンの有効期限が切れているため、httpサーバーへのリクエストが大量に発生する問題が発生しますキャッシュを取得するという目的を達成できないだけでなく、キャッシュのサイズも大きくなります。最後に負荷がかかり、同時に複数の書き込みキャッシュ操作があり、Golang のマップはアトミックではないはずです、そのため、大量のメモリに書き込むとクラッシュの問題が発生する可能性もあります。

したがって、読み取りおよび書き込み操作をロックする必要があります:

memcache.go:

package person

import (
	"sync"
	"time"
)

type memoryCache struct {
	lock  *sync.RWMutex
	items map[interface{}]interface{}
}

func (mc *memoryCache) set(key interface{}, value interface{}) error {
	mc.lock.Lock()
	defer mc.lock.Unlock()
	mc.items[key] = value
	return nil
}

func (mc *memoryCache) get(key interface{}) interface{} {
	mc.lock.RLock()
	defer mc.lock.RUnlock()

	if val, ok := mc.items[key]; ok {
		return val
	}
	return nil
}

var gAccId2Token *memoryCache = &memoryCache{
		lock:  new(sync.RWMutex),
		items: make(map[interface{}]interface{}),
	}

func GetTokenByAccountId(accountId uint, acl string) (map[string]interface{}, error) {
	//get token from cache
	token := gAccId2Token.get(accountId)
	if token != nil {
		now := time.Now().Unix()
		if int(now) < int(token.(map[string]interface{})["expireDate"].(float64)) {
			return token.(map[string]interface{}), nil
		}
	}

	token, err := getTokenByHttpUrl(apiUrl)
	if err != nil {
		return map[string]interface{}{}, err
	}

	gAccId2Token.set(accountId, token)

	return token.(map[string]interface{}), nil
}

いくつかの注意事項:

1. 書き込み操作の場合グローバル ロックがロックされると、ロックが解放されるまで他のロックはロックできません (Unlock())。つまり、書き込み操作のアトミック性が保証されます。

2. 読み取り操作に読み取りロックが設定されている場合、複数のスレッドは領域のロックを Rlock() して、すべての読み取りロックが RUnlock() になるまで領域が読み取り可能であることを確認できます。

3. マップのキーと値のタイプをインターフェース タイプとして定義します。インターフェース{} は、Java の Object と同様に、任意のタイプを受け取ることができます。

4.Interface{} 型変換メソッド、value.(type)。つまり、値を型 type に変換します。例: value.(int)、value.(map[string]interface{}) 、など。

以上がGolangのmemcacheを簡単に実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。