简体   繁体   English

多线程应用程序连续失败后,如何将主机名添加到阻止列表?

[英]How to add hostname to block list after consecutive failures in multithreading application?

I am using Callable in my code which will be called by multiple threads as shown below. 我在代码中使用Callable,它将被多个线程调用,如下所示。 As of now, whenever any RestClientException is thrown then I am adding hostname to blockList. 到目前为止,无论何时抛出任何RestClientException我都将hostname添加到blockList中。

public class Task implements Callable<DataResponse> {

    private DataKey key;
    private RestTemplate restTemplate;

    public Task(DataKey key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() {
        ResponseEntity<String> response = null;

        // construct what are the hostnames I can call basis on user id
        List<String> hostnames = some_code_here;

        for (String hostname : hostnames) {
            // If host name is null or host name is in block list, skip sending request to this host
            if (DataUtils.isEmpty(hostname) || DataMapping.isBlocked(hostname)) {
                continue;
            }
            try {
                String url = createURL(hostname);
                response = restTemplate.exchange(url, HttpMethod.GET, key.getEntity(), String.class);

                // some code here to return the response if successful
            } catch (HttpClientErrorException ex) {
                // log exception
                return new DataResponse(errorMessage, error, DataStatusEnum.ERROR);
            } catch (HttpServerErrorException ex) {
                // log exception
                return new DataResponse(errorMessage, error, DataStatusEnum.ERROR);
            } catch (RestClientException ex) {
                // I don't want to add it to block list instantly.
                // If same hostname as failed five times consecutively, then only add it
                DataMapping.blockHost(hostname);
            }
        }

        return new DataResponse(DataErrorEnum.SERVER_UNAVAILABLE, DataStatusEnum.ERROR);        
    }
}

Below is what I have in DataMapping class: 下面是我在DataMapping类中的内容:

private static final AtomicReference<ConcurrentHashMap<String, String>> blockedHosts = 
        new AtomicReference<ConcurrentHashMap<String, String>>(new ConcurrentHashMap<String, String>());

public static boolean isBlocked(String hostName) {
    return blockedHosts.get().containsKey(hostName);
}

public static void blockHost(String hostName) {
    blockedHosts.get().put(hostName, hostName);
}

Problem Statement:- 问题陈述:-

Now as you can see in call method, I am blocking the hostname as soon as it throws RestClientException which may not be right. 现在,您可以在call方法中看到,一旦hostname抛出RestClientException异常,我将立即阻止它。 I need to see if a particular hostname has thrown RestClientException for five times consecutively, then only add this hostname to blockList by calling this line DataMapping.blockHost(hostname); 我需要查看某个特定的hostname是否已连续五次抛出RestClientException ,然后仅通过调用以下行DataMapping.blockHost(hostname);将此hostname添加到blockList中DataMapping.blockHost(hostname); otherwise don't add it to the blockList. 否则不要将其添加到blockList中。

What is the most efficient and best way to do this? 什么是最有效,最好的方法? Maximum, I will have 70-100 unique machines in total. 最多,我总共将拥有70-100台唯一的计算机。

In this case, my call method will be called from multiple threads so I need to make sure I am keeping the count properly for each hostname in case they throw RestClientException . 在这种情况下,将从多个线程调用我的call方法,因此我需要确保正确保存每个hostname的计数,以防它们抛出RestClientException

EDIT: 编辑:

I also have below method in DataMapping class as well: 我在DataMapping类中也有以下方法:

I have a background thread which runs every 2 minutes that replaces the entire set, since my service provides real data whether any hostname is really blocked or not. 我有一个后台线程,每2分钟运行一次,它将替换整个线程集,因为无论是否确实阻止了任何主机名,我的服务都会提供真实数据。 And I guess I do need the atomic reference for when I replace the entire set. 而且我猜我在替换整个集合时确实需要atomic reference

I am adding block feature locally in the code as well since I might be to know which machine is blocked after 2 minutes so it's better to know it before hand if possible. 我也在代码中本地添加了块功能,因为我可能会在2分钟后知道哪台机器被阻止,因此,如果可能的话,最好事先知道它。

// this is being updated from my background thread which runs every 2 minutes
public static void replaceBlockedHosts(List<String> hostNames) {
    ConcurrentHashMap<String, String> newBlockedHosts = new ConcurrentHashMap<>();
    for (String hostName : hostNames) {
        newBlockedHosts.put(hostName, hostName);
    }
    blockedHosts.set(newBlockedHosts);
}

I would associate each host with an AtomicInteger that is incremented on each RestClientException . 我会将每个主机与在每个RestClientException上递增的AtomicInteger RestClientException This integer would be set to zero on a succesful call to enforce the "five consecutive times" constraint. 在成功执行“五次连续 ”约束时,此整数将被设置为零。 The code would look something like this. 代码看起来像这样。

private final ConcurrentHashMap<String, AtomicInteger> failedCallCount = new ConcurrentHashMap<>();

void call() {
      try {
          String url = createURL(host);
          // make rest call
          resetFailedCallCount(host);
          // ...
      } catch (RestClientException ex) {
          registerFailedCall(host);
          if (shouldBeBlocked(host)) {
              DataMapping.blockHost(host);
          }
      }
}


private boolean shouldBeBlocked(String hostName) {
    AtomicInteger count = failedCallCount.getOrDefault(hostName, new AtomicInteger());
    return count.get() >= 5;
}

private void registerFailedCall(String hostName) {
    AtomicInteger newValue = new AtomicInteger();
    AtomicInteger val = failedCallCount.putIfAbsent(hostName, newValue);
    if (val == null) {
        val = newValue;
    }
    if (val.get() < 5) {
        val.incrementAndGet();
    }
}

private void resetFailedCallCount(String hostName) {
    AtomicInteger count = failedCallCount.get(hostName);
    if (count != null) {
        count.set(0);
    }
}

This is lock free (in our own code at least) and is very efficient. 这是无锁的(至少在我们自己的代码中),并且非常有效。 However it is vulnerable to some race conditions. 但是,它容易受到某些比赛条件的影响。 Most notable the count can become larger than 5. However, that should not be a problem since the host is blocked anyways and the count is not used for anything else. 最值得注意的是,计数可能会大于5。但是,这应该不是问题,因为主机仍然受到阻止,并且该计数未用于其他任何用途。

Maintain a static register like - public static ConcurrentHashMap<String, Integer> toBeBlockedHostName = new ConcurrentHashMap<String, Integer>(); 维护一个静态寄存器,例如public static ConcurrentHashMap<String, Integer> toBeBlockedHostName = new ConcurrentHashMap<String, Integer>(); in your DataMapping class. 在您的DataMapping类中。 And then use it like this your FOR loop: 然后像这样在FOR循环中使用它:

  for (String hostname : hostnames) {

        // .. some code here
        //After ensuring everything is success and no RestClientException, i.e. can be last line of your TRY block...
        DataMapping.toBeBlockedHostName.remove("stackoverflow6361");
        catch (RestClientException ex) {
            if(DataMapping.toBeBlockedHostName.get("stackoverflow6361") == null){
                DataMapping.toBeBlockedHostName.put("stackoverflow6361", new Integer(1));
            } else{
                if(DataMapping.toBeBlockedHostName.get("stackoverflow6361") == 5){ //Don't hard code 5, have it from some property file after defining as retryThreshold...
                    System.out.println("Blocking threshold reached, block the hostname...");
                    DataMapping.blockHost(hostname);
                } else{
                    DataMapping.toBeBlockedHostName.put("stackoverflow6361", (toBeBlockedHostName.get("stackoverflow6361") + 1));
                }
            }
        }

Please note: : For ConcurrentHashMap even though all operations are thread-safe, retrieval operations do not entail locking. 请注意:对于ConcurrentHashMap即使所有操作都是线程安全的,检索操作也不需要锁定。

Please pay attention, that after 5 consecutive retry fails, you will block the hostname, but if you are unblocking it again, then you should clear the register. 请注意,连续5次重试失败后,您将阻止主机名,但是如果再次取消阻止,则应清除该寄存器。

PS: Have appropriate getter and setter for HashMap. PS:具有适用于HashMap的getter和setter。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM