Java InetAddress.getHostName() taking a very long time to execute - 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.

Related

Datastax Java Driver does not connect if one host is missing

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.

Check reachability of servers in java programmatically

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.

Why would java give different DNS results with IPv4Stack

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?

What is the quickest way to detect an unreachable host in Java?

I would like the fastest and most accurate function boolean isReachable(String host, int port) that passes the following JUnit tests under the conditions below. Timeout values are specified by the JUnit test itself, and may be considered "unreachable."
Please note: All answers must be platform-independent. This means that InetAddress.isReachable(int timeout) is not going to work, since it relies on port 7 to do a ping on Windows (ICMP ping being an undocumented function on Windows), and this port is blocked in this setup.
LAN Setup:
thisMachine (192.168.0.100)
otherMachine (192.168.0.200)
no machine is called noMachine or has the IP 192.168.0.222 (always unreachable)
both machines are running Apache Tomcat on port 8080; all other ports are unreachable (including port 7)
example.com (208.77.188.166) is running a webserver on port 80 and is only reachable when the LAN is connected to the Internet
Occasionally, the LAN is disconnected from the Internet in which case only local machines called by IP address are reachable (all others are unreachable; there's no DNS).
All tests are run on thisMachine.
#Test(timeout=1600) // ~320ms per call (should be possible to do better)
public void testLocalhost() {
// We can always reach ourselves.
assertTrue(isReachable("localhost", 8080));
assertTrue(isReachable("127.0.0.1", 8080));
assertTrue(isReachable("thisMachine", 8080)); // Even if there's no DNS!
assertTrue(isReachable("192.168.0.100", 8080));
assertFalse(isReachable("localhost", 80)); // Nothing on that port.
}
#Test(timeout=5500) // ~1867ms per call (should be able to do better)
public void testLAN() {
assertTrue(isReachable("192.168.0.200", 8080)); // Always connected to the LAN.
assertFalse(isReachable("192.168.0.222", 8080)); // No such a machine.
assertFalse(isReachable("noMachine", 8080)); // No such machine.
}
The following test is only run when the LAN is disconnected from the Internet.
#Test(timeout=5600) // ~1867ms per call (reasonable?)
public void testNoDNS() {
assertFalse(isReachable("otherMachine", 8080)); // No DNS.
assertFalse(isReachable("example.com", 80)); // No DNS & no Internet.
assertFalse(isReachable("208.77.188.166", 80)); // No Internet.
}
The following test is only run when the LAN is connected to the Internet.
#Test(timeout=5600) // ~1867ms per call (reasonable?)
public void testHaveDNS() {
assertTrue(isReachable("otherMachine", 8080)); // DNS resolves local names.
assertTrue(isReachable("example.com", 80)); // DNS available.
assertTrue(isReachable("208.77.188.166", 80)); // Internet available.
}
Firstly you need to recognise that you have potentially conflicting requirements; IP sockets are not time deterministic. The quickest you can ever detect unreachability is after your elapsed timeout. You can only detect reachability quicker.
Assuming reachability/isReachable is your real objective, you should just use a straightforward non-blocking socket IO as shown in the Java Ping simulator, the example connects to the time service but would work equally well on 8080.
If you want to test whether you can connect to a web server you could also create a URL based on the host name and the port number and use that to create a URLConnection checking the result (including exceptions) of the connect method should tell you whether the webserver is reachable.
Not sure how practical this is.
How about doing the equivalent of traceroute(tracert on windows) and once you get a success, you can proceed.
In corporate networks, I've seen ICMP(ping) blocked by admins BUT usually, tracert still works. If you can figure out a quick way to do what tracert does, that should do the trick ?
Good luck!
My most recent solution depends using a TimedSocket (source code) with 3000ms timeout while performing a connect.
Timings:
1406ms : testLocalHost()
5280ms : testLAN()
Can't even get these to work properly:
testNoDNS()
testHaveDNS()
If you need to do this with a seriously large number of hosts in a very brief period of time, I'd consider using a tool like fping instead- shell out to exec it and parse the output when it comes back. fping runs a large number of parallel queries at once, so you could theoretically check a few thousand hosts in a minute (I think the limit is 4096?)
The rate determining step for host availability is not within your own code, but in the netlag. You must wait for the host to respond, and this can take time. If your program blocks while waiting for a response it could be a problem. I got around this by creating each host as an object, each with its own threaded method for checking availability. In my own situation I have 40 hosts I keep track of. My main program loops through an array of 40 machine objects once every 20 seconds, calling the appropriate method on each to check availability. Since each machine object spawns its own thread to do this, all 40 machines are interrogated concurrently and the (up to 500ms) response time for each isn't a problem.

How do you specify a port range for Java sockets?

In Java you can give the number zero as a single parameter for the Socket or DatagramSocket constructor. Java binds that Socket to a free port then. Is it possible to limit the port lookup to a specific range?
Hrm, after reading the docs, I don't think you can. You can either bind to any port, then rebind if it is not acceptable, or repeatedly bind to a port in your range until you succeed. The second method is going to be most "efficient".
I am uneasy about this answer, because it is... inelegant, yet I really can't find anything else either :/
Binding the socket to any free port is (usually) a feature of the operating system's socket support; it's not specific to java. Solaris, for example, supports adjusting the ephemeral port range through the ndd command. But only root can adjust the range, and it affects the entire system, not just your program.
If the regular ephemeral binding behavior doesn't suit your needs, you'll probably have to write your own using Socket.bind().
Here's the code you need:
public static Socket getListeningSocket() {
for ( int port = MIN_PORT ; port <= MAX_PORT ; port++ )
{
try {
ServerSocket s = new ServerSocket( port );
return s; // no exception means port was available
} catch (IOException e) {
// try the next port
}
}
return null; // port not found, perhaps throw exception?
}
You might glance at the java code that implements the function you are using. Most of the java libraries are written in Java, so you might just see what you need in there.
Assuming #Kenster was right and it's a system operation, you may have to simply iterate over ports trying to bind to each one or test it. Although it's a little painful, it shouldn't be more than a few lines of code.

Categories

Resources