I am working on a project in which I construct a URL with a valid hostname (but not a blocked hostname) and then execute that URL using RestTemplate
from my main thread. I also have a single background thread in my application which parses the data from the URL and extracts the block list of hostnames from it.
If any block list of hostnames is present, then I won't make a call to that hostname from the main thread and I will try making a call to another hostname. By block list, I mean whenever any server is down, its hostname is on the block list.
Here is my background thread code. It will get the data from my service URL and keep on running every 10 minutes once my application has started up. It will then parse the data coming from the URL and store it in a ClientData
class variable.
public class TempScheduler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startScheduler() {
final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
callServiceURL();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}, 0, 10, TimeUnit.MINUTES);
}
}
// call the service and get the data and then parse
// the response.
private void callServiceURL() {
String url = "url";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
parseResponse(response);
}
// parse the response and store it in a variable
private void parseResponse(String response) {
//...
Map<String, Map<Integer, String>> primaryTables = null;
Map<String, Map<Integer, String>> secondaryTables = null;
Map<String, Map<Integer, String>> tertiaryTables = null;
//...
// store the data in ClientData class variables if anything has changed
// which can be used by other threads
if(changed) {
ClientData.setMappings(primaryTables, secondaryTables, tertiaryTables);
}
// get the block list of hostnames
Map<String, List<String>> coloExceptionList = gson.fromJson(response.split("blocklist=")[1], Map.class);
List<String> blockList = new ArrayList<String>();
for(Map.Entry<String, List<String>> entry : coloExceptionList.entrySet()) {
for(String hosts : entry.getValue()) {
blockList.add(hosts);
}
}
// store the block list of hostnames which I am not supposed to make a call
// from my main application
ClientData.setBlockListOfHostname(blockList);
}
}
Below is my ClientData
class in which I am using CountDownLatch
-
public class ClientData {
public static class Mappings {
public final Map<String, Map<Integer, String>> primary;
public final Map<String, Map<Integer, String>> secondary;
public final Map<String, Map<Integer, String>> tertiary;
public Mappings(
Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary
) {
this.primary = primary;
this.secondary = secondary;
this.tertiary = tertiary;
}
}
private static final AtomicReference<Mappings> mappings = new AtomicReference<>();
private static final CountDownLatch hasBeenInitialized = new CountDownLatch(1);
// do I need this extra AtomicReference?
private static final AtomicReference<List<String>> blockListOfHosts = new AtomicReference<List<String>>();
// do I need this extra latch here?
private static final CountDownLatch hasBeenInitializedBlockHostnames = new CountDownLatch(1);
public static Mappings getMappings() {
try {
hasBeenInitialized.await();
return mappings.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
public static void setMappings(
Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary
) {
setMappings(new Mappings(primary, secondary, tertiary));
}
public static void setMappings(Mappings newMappings) {
mappings.set(newMappings);
hasBeenInitialized.countDown();
}
public static void setBlockListOfHostname(List<String> listsOfHostnames) {
blockListOfHosts.set(listsOfHostnames);
hasBeenInitializedBlockHostnames.countDown();
}
public static boolean isExceptionHost(String hostName) {
List<String> blockHostList = blockListOfHosts.get();
if (blockHostList != null) {
return blockHostList.contains(hostName);
} else {
return false;
}
}
}
Here is my main application thread code in which I find all the hostnames on which I can make a call and then iterate the hostnames
list to make a call.
If that hostname
is null
or in the block list, then I won't make a call to that particular hostname and will try the next hostname in the list.
@Override
public DataResponse call() throws Exception {
List<String> hostnames = new LinkedList<String>();
Mappings mappings = ClientData.getMappings();
// use mappings.primary
// use mappings.secondary
// use mappings.tertiary
// .. some code here
for (String hostname : hostnames) {
// If host name is null or host name is in block list category, skip sending request to this host
if (hostname == null || ClientData.isExceptionHost(hostname)) {
continue;
}
try {
String url = generateURL(hostname);
response = restTemplate.getForObject(url, String.class);
break;
} catch (RestClientException ex) {
// log exception
// how to add this hostname in the block list as well in `ClientData` class?
}
}
}
I don't need to make a call to the hostname whenever it is down from the main thread. And my background thread gets these detail from one of my services, whenever any server is down. It will have the list of hostnames and whenever they are up, that list will get updated.
- Do I need extra
CountDownLatch
for block list of hostname inClientData
class or not? - Do I need extra
AtomicReference
for block list of hostname as well or not?
This code will be called at a rate of 1000 requests per second so it has to be fast. For the first time, whenever my blockListOfHosts
is being updated from the background thread, I can return false instead of blocking the call using CountDownLatch
but it has to be atomic, all the threads should see correct value of the block list of hostnames.
And also, whenever any RestClientException
is being thrown, I will add that hostname in the blockListOfHosts
as well since my background thread is running every 10 minutes so that list won't have this hostname until 10 minutes is done. And whenever this server came back up, my background will update this list automatically.