DNS Lookup Caching

Fixes #29.
The purpose of singleflight is to coalesce multiple concurrent requests into one. Only one goroutine per group/key executes the closure (which includes the write lock and saving to the cache) and everyone shares the results.