If I am not wrong, one can connect to a Cassandra cluster knowing at least one of the nodes that is in the cluster, and then the others can be discovered.
Lets say I have three nodes (1, 2 and 3) and I connect to those nodes like this:
Cluster.builder().addContactPoints("1,2,3".split(",")).build();
Then, if node 3 for example goes down, and the IP cannot be resolved, this line of code will throw an IllegalArgumentException as stated in the docs:
#throws IllegalArgumentException if no IP address for at least one of {#code addresses} could be found
Why would anyone want this behavior? I mean, if one of the nodes is down, I want the app to be able to run, as the Cassandra is still working fine.
I have checked this Cassandra Java driver: how many contact points is reasonable?
but that does not answer my question as it doesn't say anything about hosts than can't be reachable.
How should I handle this? Maybe this is changed in another version of the java driver? I am currently using cassandra-driver-core-3.0.3
This validation is only to make sure that all the provided hosts can be resolved, it doesn't even check if a Cassandra server is running on each host. So it is basically to ensure that you did not do any typos while providing the hosts as indeed it doesn't assume that it could be a normal use case to have a provided host that cannot be resolved.
As workaround in your case (host been removed from the DNS entries), you could simply call the method addContactPoint(String address) explicitly instead of using addContactPoints(String... addresses) (which behind the scene simply call addContactPoint(String address) for each provided address) and manage the exception by yourself.
The code could be something like this:
Cluster.Builder builder = Cluster.builder();
// Boolean used to check if at least one host could be resolved
boolean found = false;
for (String address : "1,2,3".split(",")) {
try {
builder.addContactPoint(address);
// One host could be resolved
found = true;
} catch (IllegalArgumentException e) {
// This host could not be resolved so we log a message and keep going
Log.log(
Level.WARNING,
String.format("The host '%s' is unknown so it will be ignored", address)
);
}
}
if (!found) {
// No host could be resolved so we throw an exception
throw new IllegalStateException("All provided hosts are unknown");
}
Cluster cluster = builder.build();
FYI: I've just created a ticket to propose an improvement in the Java driver https://datastax-oss.atlassian.net/browse/JAVA-1334.
As Nick mentioned, it's based on DNS resolution, not Cassandra server health.
If you remove hosts from your environment more often than you recompile your application, then you should consider not baking your contact points into the code, and instead, feed them in through some other means (environment variable, REST service, a single DNS name that always resolves to one live seed, etc).
The documentation there is just in regards to "resolving" the contact points that are passed in. So converting hostnames to ip addresses. If you are specifying ip addresses to begin with, they will not be resolved, simply checked for validity. If you are using hostnames then each contact point will need to be resolvable. This doesn't mean that the cassandra machine needs to be running, just that a DNS lookup on the hostname returns any ip address. So the case where things would break would be if you removed a DNS entry for one of your contact points and restarted your application.
Related
In my application architecture I am having two database servers primary db and secondary db server (Replica server).
In my java code I am making a connection with DB to fetch some data now what I want is I will give the IP addresses of both DB servers in my code and will check which DB server is reachable and will connect with that only. But I am not getting how to implement it, the one way is try to telnet but not a good option because I want to disable the telnet on application server for some reasons.
Is there any other and best way to do this?
Personally, I would just attempt the connection (using standard database classes) and handle the exceptions if the connection fails.
Even if you confirm connectivity initially, nothing prevents a network problem occurring between that test and your actual attempt to use the database.
You can try pinging both hosts and use the one which responds. Here is a sample program.
InetAddress address = InetAddress.getByName("172.16.2.0");
// Try to reach the specified address within the timeout
// periode. If during this periode the address cannot be
// reach then the method returns false.
boolean reachable = address.isReachable(10000);
System.out.println("Is host reachable? " + reachable);
For a more elaborate program, see this Ping program example in Java.
I have the following little code snippet:
InetAddress address = InetAddress.getByName(host);
if(address.isReachable(TIMEOUT_IN_MILLISECONDS)) {
System.out.println(host + " is reachable.");
String hostName = address.getHostName();
System.out.println(hostName);
}
The getHostName() method is taking quite some time to execute if a machine has been found. Could someone please explain why?
From the InetAddress#getHostName() javadocs, that method will perform a reverse hostname lookup. So the performance of that method call depends on the performance of the network/technology stack between the JVM and the domain name server for the target host.
In brief, that method will make a system call to perform the reverse lookup (e.g. getaddrinfo(3)) and that call will be implemented by the operating system to perform the network actions required to gather the host information via the Name Server configured for your machine.
Some of the addresses need longer time to be resolved. InetAddress has a cache to store successful and unsuccessful resolutions. Also, make a threadpool. You can improve the performance
toString() seems to be faster:
given an InetAddress ia or InterfaceAddress ia,
System.out.println ( ia.toString() ) will show a string containing your ipAddress faster than ia.getHostName()
You can then use ia.toString().substring to extract it.
I don't know why.
Here is some code to determine the local host name that is supposed to work on a multi-homed box:
/**
* Work out the first local host name by iterating the network interfaces
*
* #return
* #throws SocketException
*/
private String findFirstLocalHostName() throws SocketException {
Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
while (ifaces.hasMoreElements()) {
NetworkInterface iface = ifaces.nextElement();
Enumeration<InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress add = addresses.nextElement();
if (!add.isLoopbackAddress() && add.isSiteLocalAddress()) {
return add.getHostName();
}
}
}
throw new RuntimeException("Failed to determine local hostname");
}
Does the call to isSiteLocalAddress introduce a bug? I can't find any useful information about this method, but I have a feeling that it relates to IP v 6 only and is deprecated.
The method is definitely not deprecated and it's definitely not just used in IPv6.
In IPv4 there are 3 network address ranges that are defined for site-local addresses: 10/8, 172.16/12 and 192.168/16.
Reading Inet4Address.isSiteLocalAddress() shows that addresses from exactly those 3 networks will return true on those methods.
IPv6 has a similar concept, here these addresses are called unique local addresses.
Effectively this tells you if the address you have is definitely not a public one (note that even if this method returns false, the address might still not be public).
Looking at the implementation...
For an Inet4Address, it checks to see if it's one of the RFC1918 "unrouteable" addresses: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16.
For an Inet6Address, it checks the first two octets to see if it's a real "site local" address.
'Site local' is a deprecated name for private IP space. (Some nuances, but basically right.) See RFC 1918.
I just came across what I believe is a similar problem: trying to determine what IPv6 I should use for LAN comuncation:
IMHO, Inet6Address.isSiteLocalAddress() is useless. Given that the 0xFEC0 prefix has been depricated by RFC 3879 as #tigz mentioned. I have yet to see any device (android, win, osx) actually have a 0xFEC0 (with limited testing)
//from java.net.Inet6Address (1.8.0_45)
boolean isSiteLocalAddress() {
return ((ipaddress[0] & 0xff) == 0xfe
&& (ipaddress[1] & 0xc0) == 0xc0);
}
0xFE80 address although not supposed be used for traffic (from my understanding and reading (www.cisco.com)) did work for LAN communication with my single router (ping6, curl, http).
My Global Unicast (which is just another name for public IP) 2601::/20 from Comcast worked for my LAN communication. So I would say that this is the correct address to use.
Prefix table: www.iana.org
As far as I know the isSiteLocalAddress method is not deprecated.
isSiteLocalAddress - Explanation
indicating if the InetAddress is a site local address; or false if address is not a site local unicast address.
The InetAddress even have two direct subclasses;
Inet4Address and
Inet6Address
The best bet is to read the JavaDocs.
Which version of the JDK are you using?
I have the following code:
import java.net.InetAddress;
public class lookup {
public static void main(String[] args) throws Exception {
for(String host : args){
for(InetAddress addr : InetAddress.getAllByName(host)){
System.out.println(addr.getHostAddress());
}
}
}
}
We recently changed the CNAME for a host we'll call foo.example.com from pointing at bar.example.com to point at baz.example.com. If I run:
java -Djava.net.preferIPv4Stack=true lookup foo.example.com
I get baz.example.com's ip address, as expected. However if I run:
java lookup foo.example.com
I still get bar.example.com's ip address.
I've confirmed that neither bar.example.com nor baz.example.com have AAAA records. dig and ping both resolve baz.example.com as expected. How do I get java's ipv6 stack to properly resolve this?
The problem was nscd was running and had cached the records. Why it didn't respect the TTLs and why ping doesn't use the cache are still a mystery.
Hmm, there's something a bit odd going on here. I suspect it might have something to do with DNS caching the domain name lookups performed. Modify the following properties in:
java.home/lib/security/java.security
and see whether this resolves the issue:
networkaddress.cache.ttl: 10
networkaddress.cache.negative.ttl: 10
A value of -1 (the default for networkaddress.cache) indicates "cache forever" which might be causing what you see above.
Could it be that your operating system is aware of two different DNS servers, one reachable by IPv4, the other reachable by and preferred for IPv6 and that the IPv6 preferred DNS server is still caching the old configuration?
As of now, I'm using the below code to get DNS name of the given IPAddress. Instead of fetching it for each IPAddress in the network, I want to fetch all the DNS entries (IPAddress - HostName mapping) from the DNS Server in one go. Is it possible? If so, how to do it?
InetAddress addr = InetAddress.getByName(address);
dnsname = addr.getCanonicalHostName().trim();
From a public DNS server, there is no way to pull out all the data it holds. Enumerating all the IP addresses one by one is the only solution.
If you have a special relationship with the DNS server (for instance, it is managed by your employer), you may request from the DNS administrator a right to transfer the whole zone (the DNS request known as AXFR). They may authorize your IP address or gives you a TSIG key to authentify yourself.
Then, you will have to find a way to do a zone transfer (possibly with TSIG authentication) in Java. Using these keywords, I find some code and documentation. Use a code search engine like Google Code Search or Krugle to find examples of use.
[DNS experts will probably scream "Use zone walking on NSEC" but most DNS zones are not signed with NSEC.]