Redis caching

This commit is contained in:
Tyler 2018-07-01 11:49:24 -04:00
parent 53e24ccdbd
commit adf6a32039
6 changed files with 219 additions and 181 deletions

176
cache.go
View File

@ -1,15 +1,11 @@
package main package main
import ( import (
"crypto/md5"
"encoding/json"
"fmt" "fmt"
"sync"
"time" "time"
"github.com/bradfitz/gomemcache/memcache"
"github.com/hoisie/redis"
"github.com/miekg/dns" "github.com/miekg/dns"
"crypto/md5"
) )
type KeyNotFound struct { type KeyNotFound struct {
@ -56,178 +52,10 @@ type Cache interface {
Full() bool Full() bool
} }
type MemoryCache struct {
Backend map[string]Mesg
Expire time.Duration
Maxcount int
mu sync.RWMutex
}
func (c *MemoryCache) Get(key string) (*dns.Msg, error) {
c.mu.RLock()
mesg, ok := c.Backend[key]
c.mu.RUnlock()
if !ok {
return nil, KeyNotFound{key}
}
if mesg.Expire.Before(time.Now()) {
c.Remove(key)
return nil, KeyExpired{key}
}
return mesg.Msg, nil
}
func (c *MemoryCache) Set(key string, msg *dns.Msg) error {
if c.Full() && !c.Exists(key) {
return CacheIsFull{}
}
expire := time.Now().Add(c.Expire)
mesg := Mesg{msg, expire}
c.mu.Lock()
c.Backend[key] = mesg
c.mu.Unlock()
return nil
}
func (c *MemoryCache) Remove(key string) error {
c.mu.Lock()
delete(c.Backend, key)
c.mu.Unlock()
return nil
}
func (c *MemoryCache) Exists(key string) bool {
c.mu.RLock()
_, ok := c.Backend[key]
c.mu.RUnlock()
return ok
}
func (c *MemoryCache) Length() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.Backend)
}
func (c *MemoryCache) Full() bool {
// if Maxcount is zero. the cache will never be full.
if c.Maxcount == 0 {
return false
}
return c.Length() >= c.Maxcount
}
/*
Memcached backend
*/
func NewMemcachedCache(servers []string, expire int32) *MemcachedCache {
c := memcache.New(servers...)
return &MemcachedCache{
backend: c,
expire: expire,
}
}
type MemcachedCache struct {
backend *memcache.Client
expire int32
}
func (m *MemcachedCache) Set(key string, msg *dns.Msg) error {
var val []byte
var err error
// handle cases for negacache where it sets nil values
if msg == nil {
val = []byte("nil")
} else {
val, err = msg.Pack()
}
if err != nil {
err = SerializerError{err}
}
return m.backend.Set(&memcache.Item{Key: key, Value: val, Expiration: m.expire})
}
func (m *MemcachedCache) Get(key string) (*dns.Msg, error) {
var msg dns.Msg
item, err := m.backend.Get(key)
if err != nil {
err = KeyNotFound{key}
return &msg, err
}
err = msg.Unpack(item.Value)
if err != nil {
err = SerializerError{err}
}
return &msg, err
}
func (m *MemcachedCache) Exists(key string) bool {
_, err := m.backend.Get(key)
if err != nil {
return true
}
return false
}
func (m *MemcachedCache) Remove(key string) error {
return m.backend.Delete(key)
}
func (m *MemcachedCache) Full() bool {
// memcache is never full (LRU)
return false
}
/*
TODO: Redis cache Backend
*/
type RedisCache struct {
Backend *redis.Client
Serializer JsonSerializer
Expire time.Duration
Maxcount int
}
func (c *RedisCache) Get() {
}
func (c *RedisCache) Set() {
}
func (c *RedisCache) Remove() {
}
func KeyGen(q Question) string { func KeyGen(q Question) string {
h := md5.New() h := md5.New()
h.Write([]byte(q.String())) h.Write([]byte(q.String()))
x := h.Sum(nil) x := h.Sum(nil)
key := fmt.Sprintf("%x", x) key := fmt.Sprintf("%x", x)
return key return key
} }
/* we need to define marsheling to encode and decode
*/
type JsonSerializer struct {
}
func (*JsonSerializer) Dumps(mesg *dns.Msg) (encoded []byte, err error) {
encoded, err = json.Marshal(*mesg)
return
}
func (*JsonSerializer) Loads(data []byte) (*dns.Msg, error) {
var mesg dns.Msg
err := json.Unmarshal(data, &mesg)
return &mesg, err
}

66
cache_memcached.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"github.com/bradfitz/gomemcache/memcache"
"github.com/miekg/dns"
)
func NewMemcachedCache(servers []string, expire int32) *MemcachedCache {
c := memcache.New(servers...)
return &MemcachedCache{
backend: c,
expire: expire,
}
}
type MemcachedCache struct {
backend *memcache.Client
expire int32
}
func (m *MemcachedCache) Set(key string, msg *dns.Msg) error {
var val []byte
var err error
// handle cases for negacache where it sets nil values
if msg == nil {
val = []byte("nil")
} else {
val, err = msg.Pack()
}
if err != nil {
err = SerializerError{err}
}
return m.backend.Set(&memcache.Item{Key: key, Value: val, Expiration: m.expire})
}
func (m *MemcachedCache) Get(key string) (*dns.Msg, error) {
var msg dns.Msg
item, err := m.backend.Get(key)
if err != nil {
err = KeyNotFound{key}
return &msg, err
}
err = msg.Unpack(item.Value)
if err != nil {
err = SerializerError{err}
}
return &msg, err
}
func (m *MemcachedCache) Exists(key string) bool {
_, err := m.backend.Get(key)
if err != nil {
return true
}
return false
}
func (m *MemcachedCache) Remove(key string) error {
return m.backend.Delete(key)
}
func (m *MemcachedCache) Full() bool {
// memcache is never full (LRU)
return false
}

74
cache_memory.go Normal file
View File

@ -0,0 +1,74 @@
package main
import (
"time"
"sync"
"github.com/miekg/dns"
)
type MemoryCache struct {
Cache
Backend map[string]Mesg
Expire time.Duration
Maxcount int
mu sync.RWMutex
}
func (c *MemoryCache) Get(key string) (*dns.Msg, error) {
c.mu.RLock()
mesg, ok := c.Backend[key]
c.mu.RUnlock()
if !ok {
return nil, KeyNotFound{key}
}
if mesg.Expire.Before(time.Now()) {
c.Remove(key)
return nil, KeyExpired{key}
}
return mesg.Msg, nil
}
func (c *MemoryCache) Set(key string, msg *dns.Msg) error {
if c.Full() && !c.Exists(key) {
return CacheIsFull{}
}
expire := time.Now().Add(c.Expire)
mesg := Mesg{msg, expire}
c.mu.Lock()
c.Backend[key] = mesg
c.mu.Unlock()
return nil
}
func (c *MemoryCache) Remove(key string) error {
c.mu.Lock()
delete(c.Backend, key)
c.mu.Unlock()
return nil
}
func (c *MemoryCache) Exists(key string) bool {
c.mu.RLock()
_, ok := c.Backend[key]
c.mu.RUnlock()
return ok
}
func (c *MemoryCache) Length() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.Backend)
}
func (c *MemoryCache) Full() bool {
// if Maxcount is zero. the cache will never be full.
if c.Maxcount == 0 {
return false
}
return c.Length() >= c.Maxcount
}

70
cache_redis.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"github.com/hoisie/redis"
"github.com/miekg/dns"
)
func NewRedisCache(c RedisSettings, expire int32) *RedisCache {
rc := &redis.Client{Addr: c.Addr(), Db: c.DB, Password: c.Password}
return &RedisCache{
backend: rc,
expire: expire,
}
}
type RedisCache struct {
Cache
backend *redis.Client
expire int32
}
func (m *RedisCache) Set(key string, msg *dns.Msg) error {
var val []byte
var err error
// handle cases for negacache where it sets nil values
if msg == nil {
val = []byte("nil")
} else {
val, err = msg.Pack()
}
if err != nil {
err = SerializerError{err}
}
return m.backend.Setex(key, int64(m.expire), val)
}
func (m *RedisCache) Get(key string) (*dns.Msg, error) {
var msg dns.Msg
item, err := m.backend.Get(key)
if err != nil {
err = KeyNotFound{key}
return &msg, err
}
err = msg.Unpack(item)
if err != nil {
err = SerializerError{err}
}
return &msg, err
}
func (m *RedisCache) Exists(key string) bool {
exists, err := m.backend.Exists(key)
if err != nil {
return true
}
return exists
}
func (m *RedisCache) Remove(key string) error {
_, err := m.backend.Del(key)
return err
}
func (m *RedisCache) Full() bool {
// memcache is never full (LRU)
return false
}

View File

@ -16,6 +16,7 @@ port = 53
# Semicolon separate multiple files. # Semicolon separate multiple files.
server-list-file = "./etc/apple.china.conf;./etc/google.china.conf" server-list-file = "./etc/apple.china.conf;./etc/google.china.conf"
resolv-file = "/etc/resolv.conf" resolv-file = "/etc/resolv.conf"
dns-over-https = "https://cloudflare-dns.com/dns-query"
timeout = 5 # 5 seconds timeout = 5 # 5 seconds
# The concurrency interval request upstream recursive server # The concurrency interval request upstream recursive server
# Match the PR15, https://github.com/kenshinx/godns/pull/15 # Match the PR15, https://github.com/kenshinx/godns/pull/15

View File

@ -60,13 +60,12 @@ func NewHandler() *GODNSHandler {
settings.Memcache.Servers, settings.Memcache.Servers,
int32(cacheConfig.Expire/2)) int32(cacheConfig.Expire/2))
case "redis": case "redis":
// cache = &MemoryCache{ cache = NewRedisCache(
// Backend: make(map[string]*dns.Msg), settings.Redis,
// Expire: time.Duration(cacheConfig.Expire) * time.Second, int32(cacheConfig.Expire))
// Serializer: new(JsonSerializer), negCache = NewRedisCache(
// Maxcount: cacheConfig.Maxcount, settings.Redis,
// } int32(cacheConfig.Expire/2))
panic("Redis cache backend not implement yet")
default: default:
logger.Error("Invalid cache backend %s", cacheConfig.Backend) logger.Error("Invalid cache backend %s", cacheConfig.Backend)
panic("Invalid cache backend") panic("Invalid cache backend")