From 57304bf91096593ecf199c008dde3e164951eece Mon Sep 17 00:00:00 2001 From: kenshin Date: Wed, 24 Jul 2013 18:29:38 +0800 Subject: [PATCH] Memory cache implemented --- README.MD | 17 ++++++- cache.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++-- handler.go | 75 ++++++++++++++++++++++------ settings.go | 2 +- 4 files changed, 212 insertions(+), 21 deletions(-) diff --git a/README.MD b/README.MD index 5a856e3..d631d14 100644 --- a/README.MD +++ b/README.MD @@ -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 -### Configuration +## Install & Running + + + + + +## Configuration All the configuration on `godns.conf` a TOML formating config file. 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 + + + diff --git a/cache.go b/cache.go index ec8e64c..1f8dbb6 100644 --- a/cache.go +++ b/cache.go @@ -1,13 +1,144 @@ package main -type Cache struct { - config CacheSettings +import ( + "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 } diff --git a/handler.go b/handler.go index b20e036..a140339 100644 --- a/handler.go +++ b/handler.go @@ -3,12 +3,22 @@ package main import ( "github.com/miekg/dns" // "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 { resolver *Resolver - cache *Cache + cache Cache } func NewHandler() *GODNSHandler { @@ -16,6 +26,8 @@ func NewHandler() *GODNSHandler { var ( clientConfig *dns.ClientConfig cacheConfig CacheSettings + resolver *Resolver + cache Cache ) resolvConfig := settings.ResolvConfig @@ -26,11 +38,28 @@ func NewHandler() *GODNSHandler { panic(err) } clientConfig.Timeout = resolvConfig.Timeout - resolver := &Resolver{clientConfig} + resolver = &Resolver{clientConfig} 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} } @@ -41,6 +70,22 @@ func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) { 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) if err != nil { @@ -51,6 +96,16 @@ func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) { 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) { @@ -60,13 +115,3 @@ func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) { func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) { 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 -} diff --git a/settings.go b/settings.go index 0afd815..43a365b 100644 --- a/settings.go +++ b/settings.go @@ -46,7 +46,7 @@ type LogSettings struct { type CacheSettings struct { Backend string Expire time.Duration - Maxcount uint32 + Maxcount int } func init() {