Add a simple DNS caching system to BNetworkAddress

netresolv (and libbind) won't cache DNS requests, which can result in a
lot of DNS requests being made for the same host. Implement a simple
cache in RAM (local to each application) which will keep the most
recently requested addresses cached. This can speed up loading of an
HTTP page a lot, by saving a DNS request for each resource stored on the
same server as the main page.
This commit is contained in:
Adrien Destugues
2015-06-14 15:47:13 +02:00
parent 755f6d2f83
commit f972422c66
3 changed files with 149 additions and 15 deletions
+49 -1
View File
@@ -6,6 +6,9 @@
#define _NETWORK_ADDRESS_RESOLVER_H #define _NETWORK_ADDRESS_RESOLVER_H
#include <ObjectList.h>
#include <Referenceable.h>
#include <String.h>
#include <SupportDefs.h> #include <SupportDefs.h>
@@ -20,7 +23,7 @@ enum {
}; };
class BNetworkAddressResolver { class BNetworkAddressResolver: public BReferenceable {
public: public:
BNetworkAddressResolver(); BNetworkAddressResolver();
BNetworkAddressResolver(const char* address, BNetworkAddressResolver(const char* address,
@@ -53,9 +56,54 @@ public:
status_t GetNextAddress(int family, uint32* cookie, status_t GetNextAddress(int family, uint32* cookie,
BNetworkAddress& address) const; BNetworkAddress& address) const;
// TODO all the ::Resolve variants are needed. Maybe the SetTo and
// constructors could be removed as ::Resolve is the right way to get a
// resolver (using the cache).
static BReference<const BNetworkAddressResolver> Resolve(
const char* address, const char* service,
uint32 flags = 0);
static BReference<const BNetworkAddressResolver> Resolve(
const char* address, uint16 port = 0,
uint32 flags = 0);
static BReference<const BNetworkAddressResolver> Resolve(int family,
const char* address, const char* service,
uint32 flags = 0);
static BReference<const BNetworkAddressResolver> Resolve(int family,
const char* address, uint16 port = 0,
uint32 flags = 0);
private: private:
addrinfo* fInfo; addrinfo* fInfo;
status_t fStatus; status_t fStatus;
struct CacheEntry {
CacheEntry(int family, const char* address, const char* service,
uint32 flags, BNetworkAddressResolver* resolver)
:
fFamily(family),
fAddress(address),
fService(service),
fFlags(flags),
fResolver(resolver, false)
{
}
bool Matches(int family, BString address, BString service, uint32 flags)
{
return family == fFamily && flags == fFlags && address == fAddress
&& service == fService;
}
int fFamily;
BString fAddress;
BString fService;
uint32 fFlags;
BReference<const BNetworkAddressResolver> fResolver;
};
static BObjectList<CacheEntry> sCacheMap;
}; };
+26 -14
View File
@@ -158,40 +158,46 @@ BNetworkAddress::Unset()
status_t status_t
BNetworkAddress::SetTo(const char* host, uint16 port, uint32 flags) BNetworkAddress::SetTo(const char* host, uint16 port, uint32 flags)
{ {
BNetworkAddressResolver resolver; BReference<const BNetworkAddressResolver> resolver
status_t status = resolver.SetTo(host, port, flags); = BNetworkAddressResolver::Resolve(host, port, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK) if (status != B_OK)
return status; return status;
// Prefer IPv6 addresses // Prefer IPv6 addresses
uint32 cookie = 0; uint32 cookie = 0;
status = resolver.GetNextAddress(AF_INET6, &cookie, *this); status = resolver->GetNextAddress(AF_INET6, &cookie, *this);
if (status == B_OK) if (status == B_OK)
return B_OK; return B_OK;
cookie = 0; cookie = 0;
return resolver.GetNextAddress(&cookie, *this); return resolver->GetNextAddress(&cookie, *this);
} }
status_t status_t
BNetworkAddress::SetTo(const char* host, const char* service, uint32 flags) BNetworkAddress::SetTo(const char* host, const char* service, uint32 flags)
{ {
BNetworkAddressResolver resolver; BReference<const BNetworkAddressResolver> resolver
status_t status = resolver.SetTo(host, service, flags); = BNetworkAddressResolver::Resolve(host, service, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK) if (status != B_OK)
return status; return status;
// Prefer IPv6 addresses // Prefer IPv6 addresses
uint32 cookie = 0; uint32 cookie = 0;
status = resolver.GetNextAddress(AF_INET6, &cookie, *this); status = resolver->GetNextAddress(AF_INET6, &cookie, *this);
if (status == B_OK) if (status == B_OK)
return B_OK; return B_OK;
cookie = 0; cookie = 0;
return resolver.GetNextAddress(&cookie, *this); return resolver->GetNextAddress(&cookie, *this);
} }
@@ -204,13 +210,16 @@ BNetworkAddress::SetTo(int family, const char* host, uint16 port, uint32 flags)
return _ParseLinkAddress(host); return _ParseLinkAddress(host);
} }
BNetworkAddressResolver resolver; BReference<const BNetworkAddressResolver> resolver
status_t status = resolver.SetTo(family, host, port, flags); = BNetworkAddressResolver::Resolve(family, host, port, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK) if (status != B_OK)
return status; return status;
uint32 cookie = 0; uint32 cookie = 0;
return resolver.GetNextAddress(&cookie, *this); return resolver->GetNextAddress(&cookie, *this);
} }
@@ -224,13 +233,16 @@ BNetworkAddress::SetTo(int family, const char* host, const char* service,
return _ParseLinkAddress(host); return _ParseLinkAddress(host);
} }
BNetworkAddressResolver resolver; BReference<const BNetworkAddressResolver> resolver
status_t status = resolver.SetTo(family, host, service, flags); = BNetworkAddressResolver::Resolve(family, host, service, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK) if (status != B_OK)
return status; return status;
uint32 cookie = 0; uint32 cookie = 0;
return resolver.GetNextAddress(&cookie, *this); return resolver->GetNextAddress(&cookie, *this);
} }
@@ -39,6 +39,7 @@ strip_port(BString& host, BString& port)
BNetworkAddressResolver::BNetworkAddressResolver() BNetworkAddressResolver::BNetworkAddressResolver()
: :
BReferenceable(),
fInfo(NULL), fInfo(NULL),
fStatus(B_NO_INIT) fStatus(B_NO_INIT)
{ {
@@ -48,6 +49,7 @@ BNetworkAddressResolver::BNetworkAddressResolver()
BNetworkAddressResolver::BNetworkAddressResolver(const char* address, BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
uint16 port, uint32 flags) uint16 port, uint32 flags)
: :
BReferenceable(),
fInfo(NULL), fInfo(NULL),
fStatus(B_NO_INIT) fStatus(B_NO_INIT)
{ {
@@ -57,6 +59,7 @@ BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
BNetworkAddressResolver::BNetworkAddressResolver(const char* address, BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
const char* service, uint32 flags) const char* service, uint32 flags)
: :
BReferenceable(),
fInfo(NULL), fInfo(NULL),
fStatus(B_NO_INIT) fStatus(B_NO_INIT)
{ {
@@ -67,6 +70,7 @@ BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
BNetworkAddressResolver::BNetworkAddressResolver(int family, BNetworkAddressResolver::BNetworkAddressResolver(int family,
const char* address, uint16 port, uint32 flags) const char* address, uint16 port, uint32 flags)
: :
BReferenceable(),
fInfo(NULL), fInfo(NULL),
fStatus(B_NO_INIT) fStatus(B_NO_INIT)
{ {
@@ -77,6 +81,7 @@ BNetworkAddressResolver::BNetworkAddressResolver(int family,
BNetworkAddressResolver::BNetworkAddressResolver(int family, BNetworkAddressResolver::BNetworkAddressResolver(int family,
const char* address, const char* service, uint32 flags) const char* address, const char* service, uint32 flags)
: :
BReferenceable(),
fInfo(NULL), fInfo(NULL),
fStatus(B_NO_INIT) fStatus(B_NO_INIT)
{ {
@@ -258,3 +263,72 @@ BNetworkAddressResolver::GetNextAddress(int family, uint32* cookie,
return B_OK; return B_OK;
} }
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(const char* address, const char* service,
uint32 flags)
{
return Resolve(AF_UNSPEC, address, service, flags);
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(const char* address, uint16 port, uint32 flags)
{
return Resolve(AF_UNSPEC, address, port, flags);
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(int family, const char* address,
uint16 port, uint32 flags)
{
BString service;
service << port;
return Resolve(family, address, port == 0 ? NULL : service.String(), flags);
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(int family, const char* address,
const char* service, uint32 flags)
{
// TODO it may be faster to use an hash map to have faster lookup of the
// cache. However, we also need to access the cache by LRU, and for that
// a doubly-linked list is better. We should have these two share the same
// items, so it's easy to remove the LRU from the map, or insert a new
// item in both structures.
for (int i = 0; i < sCacheMap.CountItems(); i++) {
CacheEntry* entry = sCacheMap.ItemAt(i);
if (entry->Matches(family, address, service, flags)) {
// This entry is now the MRU, move to end of list.
// TODO if the item is old (more than 1 minute), it should be
// dropped and a new request made.
sCacheMap.MoveItem(i, sCacheMap.CountItems());
return entry->fResolver;
}
}
BNetworkAddressResolver* resolver = new(std::nothrow)
BNetworkAddressResolver(family, address, service, flags);
if (resolver != NULL && resolver->InitCheck() == B_OK) {
// TODO adjust capacity. Chrome uses 256 entries with a timeout of
// 1 minute, IE uses 1000 entries with a timeout of 30 seconds.
if (sCacheMap.CountItems() > 255)
delete sCacheMap.RemoveItemAt(0);
CacheEntry* entry = new(std::nothrow) CacheEntry(family, address,
service, flags, resolver);
if (entry)
sCacheMap.AddItem(entry, sCacheMap.CountItems());
}
return BReference<const BNetworkAddressResolver>(resolver, true);
}
BObjectList<BNetworkAddressResolver::CacheEntry>
BNetworkAddressResolver::sCacheMap;