Memory cache implemented

This commit is contained in:
kenshin 2013-07-24 18:29:38 +08:00
parent 2d49200a53
commit 57304bf910
4 changed files with 212 additions and 21 deletions

View File

@ -15,7 +15,13 @@ Similar as [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) ,but support
* Cache records save in memory or redis configurable * Cache records save in memory or redis configurable
### Configuration ## Install & Running
## Configuration
All the configuration on `godns.conf` a TOML formating config file. All the configuration on `godns.conf` a TOML formating config file.
More about Toml :[https://github.com/mojombo/toml](https://github.com/mojombo/toml) More about Toml :[https://github.com/mojombo/toml](https://github.com/mojombo/toml)
@ -33,5 +39,14 @@ If multi `namerserver` set at resolv.conf, the upsteam server will try in order
#### hosts
#### cache

139
cache.go
View File

@ -1,13 +1,144 @@
package main package main
type Cache struct { import (
config CacheSettings "crypto/md5"
"encoding/json"
"fmt"
"github.com/miekg/dns"
"time"
)
type KeyNotFound struct {
key string
} }
func (c *Cache) Get() { func (e KeyNotFound) Error() string {
return e.key + " " + "not found"
}
type KeyExpired struct {
Key string
}
func (e KeyExpired) Error() string {
return e.Key + " " + "expired"
}
type CacheIsFull struct {
}
func (e CacheIsFull) Error() string {
return "Cache is Full"
}
type SerializerError struct {
}
func (e SerializerError) Error() string {
return "Serializer error"
}
type Cache interface {
Get(string) ([]byte, error)
Set(string, *dns.Msg) error
Exists(string) bool
Remove()
Length() int
}
type MemoryCache struct {
backend map[string]string
serializer *JsonSerializer
expire time.Duration
maxcount int
}
func (c *MemoryCache) Get(key string) ([]byte, error) {
fmt.Println(c.backend)
data, ok := c.backend[key]
if !ok {
return nil, KeyNotFound{key}
}
return []byte(data), nil
// mesg := new(dns.Msg)
// if err := c.serializer.Loads([]byte(data), &mesg); err != nil {
// fmt.Println(err)
// return nil, SerializerError{}
// }
// return mesg, nil
} }
func (c *Cache) Set() { func (c *MemoryCache) Set(key string, mesg *dns.Msg) error {
if c.Full() && !c.Exists(key) {
return CacheIsFull{}
}
// data, err := c.serializer.Dumps(mesg)
// if err != nil {
// return SerializerError{}
// }
c.backend[key] = mesg.String()
return nil
}
func (c *MemoryCache) Remove() {
}
func (c *MemoryCache) Exists(key string) bool {
_, ok := c.backend[key]
return ok
}
func (c *MemoryCache) Length() int {
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
}
// type RedisCache struct{
// backend redis.client
// }
// func (c *RedisCache) Get(key string) {
// }
// func (c *RedisCache) Set() {
// }
// func (c &RedisCache) Remove(){
// }
func KeyGen(q Question) string {
h := md5.New()
h.Write([]byte(q.String()))
x := h.Sum(nil)
key := fmt.Sprintf("%x", x)
return key
}
type JsonSerializer struct {
}
func (*JsonSerializer) Dumps(mesg *dns.Msg) (encoded []byte, err error) {
encoded, err = json.Marshal(mesg)
return
}
func (*JsonSerializer) Loads(data []byte, mesg **dns.Msg) error {
err := json.Unmarshal(data, mesg)
return err
} }

View File

@ -3,12 +3,22 @@ package main
import ( import (
"github.com/miekg/dns" "github.com/miekg/dns"
// "log" // "log"
// "fmt" "fmt"
) )
type Question struct {
qname string
qtype string
qclass string
}
func (q *Question) String() string {
return q.qname + " " + q.qclass + " " + q.qtype
}
type GODNSHandler struct { type GODNSHandler struct {
resolver *Resolver resolver *Resolver
cache *Cache cache Cache
} }
func NewHandler() *GODNSHandler { func NewHandler() *GODNSHandler {
@ -16,6 +26,8 @@ func NewHandler() *GODNSHandler {
var ( var (
clientConfig *dns.ClientConfig clientConfig *dns.ClientConfig
cacheConfig CacheSettings cacheConfig CacheSettings
resolver *Resolver
cache Cache
) )
resolvConfig := settings.ResolvConfig resolvConfig := settings.ResolvConfig
@ -26,11 +38,28 @@ func NewHandler() *GODNSHandler {
panic(err) panic(err)
} }
clientConfig.Timeout = resolvConfig.Timeout clientConfig.Timeout = resolvConfig.Timeout
resolver := &Resolver{clientConfig} resolver = &Resolver{clientConfig}
cacheConfig = settings.Cache cacheConfig = settings.Cache
cache := &Cache{cacheConfig} switch cacheConfig.Backend {
case "memory":
cache = &MemoryCache{
backend: make(map[string]string),
serializer: new(JsonSerializer),
expire: cacheConfig.Expire,
maxcount: cacheConfig.Maxcount,
}
case "redis":
cache = &MemoryCache{
backend: make(map[string]string),
serializer: new(JsonSerializer),
expire: cacheConfig.Expire,
maxcount: cacheConfig.Maxcount,
}
default:
logger.Printf("Invalid cache backend %s", cacheConfig.Backend)
panic("Invalid cache backend")
}
return &GODNSHandler{resolver, cache} return &GODNSHandler{resolver, cache}
} }
@ -41,6 +70,22 @@ func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) {
Debug("Question: %s", Q.String()) Debug("Question: %s", Q.String())
key := KeyGen(Q)
fmt.Println(key)
// Only query cache when qtype == 'A' , qclass == 'IN'
if q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET {
mesg, err := h.cache.Get(key)
if err != nil {
Debug("%s didn't hit cache: %s", Q.String(), err)
} else {
Debug("%s hit cache", Q.String())
fmt.Println(string(mesg))
w.Write(mesg)
return
}
}
mesg, err := h.resolver.Lookup(net, req) mesg, err := h.resolver.Lookup(net, req)
if err != nil { if err != nil {
@ -51,6 +96,16 @@ func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) {
w.WriteMsg(mesg) w.WriteMsg(mesg)
if q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET {
err = h.cache.Set(key, mesg)
if err != nil {
Debug("Set %s cache failed: %s", Q.String(), err.Error())
}
Debug("Insert %s into cache", Q.String())
}
} }
func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) { func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
@ -60,13 +115,3 @@ func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) { func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
h.do("udp", w, req) h.do("udp", w, req)
} }
type Question struct {
qname string
qtype string
qclass string
}
func (q *Question) String() string {
return q.qname + " " + q.qclass + " " + q.qtype
}

View File

@ -46,7 +46,7 @@ type LogSettings struct {
type CacheSettings struct { type CacheSettings struct {
Backend string Backend string
Expire time.Duration Expire time.Duration
Maxcount uint32 Maxcount int
} }
func init() { func init() {