Update hosts records as a regular interval

1. /etc/hosts will be update cycle instead of update once at the progress run
2. Avoid access redis in each requests
This commit is contained in:
kenshinx 2015-02-12 14:09:49 +08:00
parent a5a8615b32
commit 81450a3983
2 changed files with 55 additions and 69 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"net"
"sync"
"time"
@ -86,7 +87,8 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
// Query hosts
if settings.Hosts.Enable && IPQuery > 0 {
if ip, ok := h.hosts.Get(Q.qname, IPQuery); ok {
if sip, ok := h.hosts.Get(Q.qname); ok {
var ip net.IP
m := new(dns.Msg)
m.SetReply(req)
@ -98,6 +100,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
Class: dns.ClassINET,
Ttl: settings.Hosts.TTL,
}
ip = net.ParseIP(sip).To4()
a := &dns.A{rr_header, ip}
m.Answer = append(m.Answer, a)
case _IP6Query:
@ -107,6 +110,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
Class: dns.ClassINET,
Ttl: settings.Hosts.TTL,
}
ip = net.ParseIP(sip).To16()
aaaa := &dns.AAAA{rr_header, ip}
m.Answer = append(m.Answer, aaaa)
}

118
hosts.go
View File

@ -2,116 +2,98 @@ package main
import (
"bufio"
"errors"
"net"
"os"
"regexp"
"strings"
"time"
"github.com/hoisie/redis"
)
type Hosts struct {
FileHosts *FileHosts
RedisHosts *RedisHosts
fileHosts *FileHosts
redisHosts *RedisHosts
}
func NewHosts(hs HostsSettings, rs RedisSettings) Hosts {
fileHosts := &FileHosts{hs.HostsFile}
fileHosts := &FileHosts{hs.HostsFile, make(map[string]string)}
var redisHosts *RedisHosts
if hs.RedisEnable {
rc := &redis.Client{Addr: rs.Addr(), Db: rs.DB, Password: rs.Password}
redisHosts = &RedisHosts{rc, hs.RedisKey}
} else {
redisHosts = new(RedisHosts)
redisHosts = &RedisHosts{rc, hs.RedisKey, make(map[string]string)}
}
hosts := Hosts{fileHosts, redisHosts}
hosts.refresh()
return hosts
}
/*
1. Resolve hosts file only one times
2. Request redis on every query called, not found performance lose serious yet.
3. Match local /etc/hosts file first, remote redis records second
1. Match local /etc/hosts file first, remote redis records second
2. Fetch hosts records from /etc/hosts file and redis per minute
*/
func (h *Hosts) Get(domain string, family int) (ip net.IP, ok bool) {
var sip string
func (h *Hosts) Get(domain string) (ip string, ok bool) {
if sip, ok = h.FileHosts.Get(domain); !ok {
if sip, ok = h.RedisHosts.Get(domain); !ok {
return nil, false
}
if ip, ok = h.fileHosts.Get(domain); ok {
return
}
switch family {
case _IP4Query:
ip = net.ParseIP(sip).To4()
return ip, (ip != nil)
case _IP6Query:
ip = net.ParseIP(sip).To16()
return ip, (ip != nil)
if h.redisHosts != nil {
ip, ok = h.redisHosts.Get(domain)
return
}
return nil, false
return ip, false
}
func (h *Hosts) GetAll() map[string]string {
m := make(map[string]string)
for domain, ip := range h.RedisHosts.GetAll() {
m[domain] = ip
}
for domain, ip := range h.FileHosts.GetAll() {
m[domain] = ip
}
return m
func (h *Hosts) refresh() {
ticker := time.NewTicker(time.Minute)
go func() {
for {
h.fileHosts.Refresh()
if h.redisHosts != nil {
h.redisHosts.Refresh()
}
<-ticker.C
}
}()
}
type RedisHosts struct {
redis *redis.Client
key string
}
func (r *RedisHosts) GetAll() map[string]string {
if r.redis == nil {
return map[string]string{}
}
var hosts = make(map[string]string)
r.redis.Hgetall(r.key, hosts)
return hosts
hosts map[string]string
}
func (r *RedisHosts) Get(domain string) (ip string, ok bool) {
if r.redis == nil {
return "", false
}
b, err := r.redis.Hget(r.key, domain)
return string(b), err == nil
}
func (r *RedisHosts) Set(domain, ip string) (bool, error) {
if r.redis == nil {
return false, errors.New("Redis not enabled")
}
return r.redis.Hset(r.key, domain, []byte(ip))
}
type FileHosts struct {
file string
}
func (f *FileHosts) Get(domain string) (ip string, ok bool) {
hosts := f.GetAll()
ip, ok = hosts[domain]
ip, ok = r.hosts[domain]
return
}
func (f *FileHosts) GetAll() map[string]string {
var hosts = make(map[string]string)
func (r *RedisHosts) Set(domain, ip string) (bool, error) {
return r.redis.Hset(r.key, domain, []byte(ip))
}
func (r *RedisHosts) Refresh() {
r.redis.Hgetall(r.key, r.hosts)
Debug("update hosts records from redis")
}
type FileHosts struct {
file string
hosts map[string]string
}
func (f *FileHosts) Get(domain string) (ip string, ok bool) {
ip, ok = f.hosts[domain]
return
}
func (f *FileHosts) Refresh() {
buf, err := os.Open(f.file)
if err != nil {
panic("Can't open " + f.file)
@ -142,9 +124,9 @@ func (f *FileHosts) GetAll() map[string]string {
continue
}
hosts[domain] = ip
f.hosts[domain] = ip
}
return hosts
Debug("update hosts records from %s", f.file)
}
func (f *FileHosts) isDomain(domain string) bool {