Module resty.dns.balancer.base
Base-balancer.
The base class for balancers. It implements DNS resolution and fanning out hostnames to addresses. It builds and maintains a tree structure:
balancer <1 --- many> hosts <1 --- many> addresses
Updating the DNS records is active, meaning that a background task is running to periodically update DNS records when they expire. This only applies to the hostnames added to the balancer, not to any nested DNS records.
Weights
Weights will be tracked as follows. Since a Balancer has multiple Hosts, and
a Host has multiple Addresses. The Host weight will be the sum of all its
addresses, and the Balancer weight will be the sum of all Hosts.
See addHost on how to set the weight for an address
.
The weight of each address
will be the weight provided as nodeWeight
when adding
a host
. So adding a host
with weight 20, that resolves to 2 IP addresses, will
insert 2 addresses each with a weight of 20, totalling the weight of the host
to
40.
Exception 1: If the host
resolves to an SRV record, in which case each
address
gets the weight as specified in the DNS record. In this case the
nodeWeight
property will be ignored.
Exception 2: If the DNS record for the host
has a ttl=0
then the record contents
will be ignored, and a single address with the original hostname will be
inserted. This address will get a weight assigned of nodeWeight
.
Whenever the balancer hits this address, it will be resolved on the spot, hence
honouring the ttl=0
value.
Adding and resolving hosts
When adding a host, it will be resolved and for each entry an address
will be
added. With the exception of a ttl=0
setting as noted above. When resolving the
names, any CNAME records will be dereferenced immediately, any other records
will not.
Example 1: add an IP address "127.0.0.1"
The host object will resolve the name, and since it's and IP address it returns a single A record with 1 entry, that same IP address.
host object 1 : hostname="127.0.0.1" --> since this is the name added address object 1: ip="127.0.0.1" --> single IP address
Example 2: complex DNS lookup chain
assuming the following lookup chain for a host
added by name "myhost"
:
myhost --> CNAME yourhost yourhost --> CNAME herhost herhost --> CNAME theirhost theirhost --> SRV with 2 entries: host1.com, host2.com host1.com --> A with 1 entry: 192.168.1.10 host2.com --> A with 1 entry: 192.168.1.11
Adding a host by name myhost
will first create a host
by name myhost
. It will then
resolve the name myhost
, the CNAME chain will be dereferenced immediately, so the
result will be an SRV record with 2 named entries. The names will be used for the
addresses:
host object 1 : hostname="myhost" address object 1: ip="host1.com" --> NOT an ip, but a name! address object 2: ip="host2.com" --> NOT an ip, but a name!
When the balancer hits these addresses (when calling getPeer), it will dereference them (so they will be resolved at balancer-runtime, not at balancer-buildtime).
There is another special case, in case a record has a ttl=0 setting. In that case a "fake" SRV record is inserted, to make sure we honour the ttl=0 and resolve on each getPeer invocation, without altering the balancer every time. Here's an example:
myhost --> A with 1 entry: 192.168.1.10, and TTL=0
Internally it is converted into a fake SRV record which results in the following balancer structure:
host object 1 : hostname="myhost" address object 1: ip="myhost" --> NOT an ip, but a name!
This in turn will result in DNS resolution on each call for an IP, and hence will make sure the ttl=0 setting is effectively being used.
Handle management
handles are used to retain state between consecutive invocations (calls to the objBalancer:getPeer method). The handles are re-used and tracked for garbage collection. There are two uses:
- tracking progress (eg. keeping a retry count)
- tracking resources (eg. with least connections a handle 'owns' 1 connection, to be released when the connection is finished)
The basic flow and related responsibilities:
- user code calls getPeer to get an ip/port/hostname according to the load balancing algorithm.
- getPeer both takes a handle (on a retry), and returns it (on success).
- handles are managed by the base balancer, and getPeer can call objBalancer:getHandle to get one.
- on a retry getPeer should call
objAddress:release(handle, ignore)
to release the previous (failed) try, and it should clear thehandle.address
field. - on success getPeer should set
handle.address
with the address object that returned the ip, port, and hostname, and return the handle with those. - at the end of the connection life cycle the user code should call handle:release to release the resources, and/or collect necessary statistics.
- as a safety check the handles will have a GC method attached. So in case
they are not explicitly released the (default) GC handler will call
handle.address:release(handle, true)
to make sure no resources leak.
Clustering
The base-balancer is deterministic in the way it adds/removes elements. So as long as the confguration is the same, and adding/removing hosts is done in the same order the exact same balancer will be created. This is important in case of consistent-hashing approaches, since each cluster member needs to behave the same.
NOTE: there is one caveat, DNS resolution is not deterministic, because timing differences might cause different orders of adding/removing. Hence the structures can potentially slowly diverge. If this is unacceptable, make sure you do not invlove DNS by adding hosts by their IP adresses instead of their hostname.
Housekeeping
The balancer does some house keeping and may insert
some extra fields in dns records. Those fields will have an __
prefix
(double underscores).
Info:
- Copyright: 2016-2020 Kong Inc. All rights reserved.
- License: Apache 2.0
- Author: Thijs Schreijer
Implementation properties
objBalancer.addresses | List of addresses. |
objBalancer.hosts | List of hosts. |
objBalancer:afterHostUpdate (host) | This method is called after changes have been made to the addresses. |
objBalancer:beforeHostDelete (host) | This method is called after a host is being removed from the balancer. |
objBalancer:getHandle (gc_handler, release_handler) | Gets a handle to be returned by getPeer. |
objBalancer:newAddress (addr) | Creates a new address object. |
objBalancer:newHost (host) | Creates a new host object. |
objBalancer:onAddAddress (address) | This method is called after an address is being added to the balancer. |
objBalancer:onRemoveAddress (address) | This method is called after an address has been deleted from the balancer. |
User properties
new (opts) | Creates a new base balancer. |
objBalancer:addHost (hostname, port, nodeWeight) | Adds a host to the balancer. |
objBalancer:getPeer (cacheOnly, handle, hashValue) | Gets the next ip address and port according to the loadbalancing scheme. |
objBalancer:getStatus () | Gets the status of the balancer. |
objBalancer:removeHost (hostname, port) | Removes a host from the balancer. |
objBalancer:setAddressStatus (available, ip_address_handle, port, hostname) | Sets the current status of an address. |
objBalancer:setCallback (callback) | Sets an event callback for user code. |
objBalancer:setHostStatus (available, hostname, port) | Sets the status of a host. |
Implementation properties
- objBalancer.addresses
- List of addresses. This is a list of addresses, ordered based on when they were added.
- objBalancer.hosts
- List of hosts. This is a list of addresses, ordered based on when they were added.
- objBalancer:afterHostUpdate (host)
-
This method is called after changes have been made to the addresses.
When implementing a new balancer algorithm, you might want to override this method.
The call is after the addition of new, and disabling old, but before deleting old addresses. The
address.disabled
field will betrue
for addresses that are about to be deleted.Parameters:
- host
the
host
object that had its addresses updated
- host
the
- objBalancer:beforeHostDelete (host)
-
This method is called after a host is being removed from the balancer.
When implementing a new balancer algorithm, you might want to override this method.
The call is after disabling, but before deleting the associated addresses. The address.disabled field will be true for addresses that are about to be deleted.
Parameters:
- host
the
host
object about to be deleted
- host
the
- objBalancer:getHandle (gc_handler, release_handler)
-
Gets a handle to be returned by getPeer. A handle will have two functions attached to it:
the release handler. This can be called from user code as
handle:release(...)
. The default handler will behandle:release(ignore)
and will callhandle.address:release(handle, ignore)
, and then release the handle itself, so it can be reused.the GC handler, which is a fallback in case the handle wasn't released by user code. It will be called as
gc_handler(handle)
and the default implementation will callhandle.address:release(handle, true)
.
Parameters:
- gc_handler (optional) a custom GC method for when the handle is not explicitly released.
- release_handler (optional) a custom release method to release the resources when the request cycle is complete.
Returns:
-
handle
- objBalancer:newAddress (addr)
-
Creates a new address object. There is no need to call this from user code. When implementing a new balancer algorithm, you might want to override this method. The
addr
table should contain:ip
: the upstream ip address or target nameport
: the upstream port numberweight
: the relative weight for the balancer algorithmhost
: the host object the new address belongs to
Parameters:
- addr table to be transformed to Address object
Returns:
-
new address object, or error on bad input
- objBalancer:newHost (host)
-
Creates a new host object. There is no need to call this from user code. When implementing a new balancer algorithm, you might want to override this method. The
host
table should have fields:hostname
: the upstream hostname (as used in dns queries)port
: the upstream port number for A and AAAA dns records. For SRV records the reported port by the DNS server will be used.nodeWeight
: the relative weight for the balancer algorithm to assign to each A or AAAA dns record. For SRV records the reported weight by the DNS server will be used.balancer
: the balancer object the host belongs to
Parameters:
- host table to create the host object from.
Returns:
-
new host object, or error on bad input.
- objBalancer:onAddAddress (address)
-
This method is called after an address is being added to the balancer.
When implementing a new balancer algorithm, you might want to override this method.
Parameters:
- address
- objBalancer:onRemoveAddress (address)
-
This method is called after an address has been deleted from the balancer.
When implementing a new balancer algorithm, you might want to override this method.
Parameters:
- address
User properties
- new (opts)
-
Creates a new base balancer.
A single balancer can hold multiple hosts. A host can be an ip address or a name. As such each host can have multiple addresses (or actual ip+port combinations).
The options table has the following fields;
dns
(required) a configured dns.client object for querying the dns server.requery
(optional) interval of requerying the dns server for previously failed queries. Defaults to 30 if omitted (in seconds)ttl0
(optional) Maximum lifetime for records inserted withttl=0
, to verify the ttl is still 0. Defaults to 60 if omitted (in seconds)callback
(optional) a function called when an address is added/changed. See setCallback for details.log_prefix
(optional) a name used in the prefix for log messages. Defaults to"balancer"
which results in log prefix"[balancer 1]"
(the number is a sequential id number)healthThreshold
(optional) minimum percentage of the balancer weight that must be healthy/available for the whole balancer to be considered healthy. Defaults to 0% if omitted.useSRVname
(optional) if truthy, then in case of the hostname resolving to an SRV record with another level of names, the returned hostname by getPeer will not be the name of the host as added, but the name of the entry in the SRV record.
Parameters:
- opts table with options
Returns:
-
new balancer object or nil+error
- objBalancer:addHost (hostname, port, nodeWeight)
-
Adds a host to the balancer.
The name will be resolved and for each DNS entry an
address
will be added.Within a balancer the combination of
hostname
andport
must be unique, so multiple calls with the same target will only update theweight
of the existing entry.Parameters:
- hostname the hostname/ip to add. It will be resolved and based on that 1 or more addresses will be added to the balancer.
- port the port to use for the addresses. If the hostname resolves to an SRV record, this will be ignored, and the port will be taken from the SRV record.
- nodeWeight the weight to use for the addresses. If the hostname resolves to an SRV record, this will be ignored, and the weight will be taken from the SRV record.
Returns:
-
balancer object, or throw an error on bad input
- objBalancer:getPeer (cacheOnly, handle, hashValue)
-
Gets the next ip address and port according to the loadbalancing scheme.
If the dns record attached to the requested address is expired, then it will
be renewed and as a consequence the balancer algorithm might be updated.
Parameters:
- cacheOnly If truthy, no dns lookups will be done, only cache.
- handle the handle returned by a previous call to getPeer. This will retain some state over retries. See also setAddressStatus.
- hashValue
(optional) number for consistent hashing, if supported by
the algorithm. The hashValue must be an (evenly distributed)
integer >= 0
.
Returns:
ip + port + hostheader
+ handle, ornil+error
Usage:
-- get an IP address local ip, port, hostheader, handle = b:getPeer() -- go do the connection stuff here... -- on a retry do: ip, port, hostheader, handle = b:getPeer(true, handle) -- pass in previous 'handle' -- go try again -- when it finally fails handle:release(true) -- release resources, but ignore stats -- on a successful connection handle:release() -- release resources, and collect stats
- objBalancer:getStatus ()
-
Gets the status of the balancer.
This reports the full structure of the balancer state, including hosts,
addresses, weights, and availability.
Returns:
-
table with balancer status
- objBalancer:removeHost (hostname, port)
-
Removes a host from the balancer. All associated addresses will be
deleted, causing updates to the balancer algorithm.
Will not throw an error if the hostname is not in the current list.
Parameters:
- hostname hostname to remove
- port port to remove (optional, defaults to 80 if omitted)
Returns:
-
balancer object, or throws an error on bad input
- objBalancer:setAddressStatus (available, ip_address_handle, port, hostname)
-
Sets the current status of an address.
This allows to temporarily suspend peers when they are offline/unhealthy,
it will not modify the address held by the record. The parameters passed in should
be previous results from getPeer.
Call this either as:
setAddressStatus(available, address)
,setAddressStatus(available, handle)
, or assetAddressStatus(available, ip, port, hostname)
Using the
address
or handle is preferred since it is guaranteed to match. By ip/port/name might fail if there are too many DNS levels.Parameters:
- available
true
for enabled/healthy,false
for disabled/unhealthy - ip_address_handle
ip address of the peer, the
address
object, or the handle. - port the port of the peer (in address object, not as recorded with the Host!)
- hostname
(optional, defaults to the value of
ip
) the hostname
Returns:
true
on success, ornil+err
if not found - objBalancer:setCallback (callback)
-
Sets an event callback for user code. The callback is invoked for
every address added to/removed from the balancer, and on health changes.
Signature of the callback is for address adding/removing:
function(balancer, "added"/"removed", address, ip, port, hostname, hostheader)
address
is the address object addedip
is the IP address for this object, but might also be a hostname if the DNS resolution returns another name (usually in SRV records)port
is the port to usehostname
is the hostname for which the address was added to the balancer with addHost (resolving that name caused the creation of this address)hostheader
is the hostheader to be used. This can have 3 values; 1)nil
if thehostname
added was an ip-address to begin with, 2) it will be equal to the name inip
if there is a named SRV entry, anduseSRVname == true
, 3) otherwise it will be equal tohostname
For health updates the signature is:
function(balancer, "health", isHealthy)
NOTE: the callback will be executed async (on a timer) so maybe executed only after the methods (addHost and removeHost for example) have returned.
Parameters:
- callback a function called when an address is added/removed
Returns:
true
, or throws an error on bad input - objBalancer:setHostStatus (available, hostname, port)
-
Sets the status of a host.
This will switch all underlying
address
objects to the specified state.Parameters:
- available
true
for enabled/healthy,false
for disabled/unhealthy - hostname name by which it was added to the balancer.
- port the port of the host by which it was added to the balancer (optional, defaults to 80 if omitted).
Returns:
true
on success, ornil+err
if not found - available