2016-05-15 11 views
0

Ich arbeite an einer Bibliothek, in der ich Http-Anruf zu meinem Service mache und wenn meine Service-Maschine nicht antwortet (es gibt ein Sockettimeout oder ein Verbindungstimeout), I fügen Sie sie zu meinem lokalen blockList hinzu und wenn Maschine 5mal blockiert wird, dann mache ich keinen Anruf zu ihnen.Eine bestimmte Maschine für eine bestimmte Zeitspanne des Zeitintervalls blockieren

Lassen Sie sich also sagen, wenn machineA nicht (throwing RestClientException) reagiert, werde ich onFailure Methode jedes Mal anrufen und halte die Zähler erhöht wird und dann während Anruf wieder zu machineA machen, überprüfe ich isBlocked Methode von machineA als Hostname und 5 als Schwelle vorbei, so wenn machineA wurde 5 mal blockiert, dann mache ich keinen Anruf bei ihnen. Meine Bibliothek ist Multithread, deshalb verwende ich flüchtig hier, da ich möchte, dass alle Threads denselben Wert sehen.

Unten ist das, was ich in DataMapping Klasse:

public static volatile ConcurrentHashMap<String, AtomicInteger> blockedHosts = 
     new ConcurrentHashMap<String, AtomicInteger>(); 

boolean isBlocked(String hostname, int threshold) { 
    AtomicInteger count = blockedHosts.get(hostname); 
    return count != null && count.get() >= threshold; 
} 

void onFailure(String hostname) { 
    AtomicInteger newValue = new AtomicInteger(); 
    AtomicInteger val = blockedHosts.putIfAbsent(hostname, newValue); 
    // no need to care about over-reaching 5 here 
    (val == null ? newValue : val).incrementAndGet(); 
} 

void onSuccess(String hostname) { 
    blockedHosts.remove(hostname); 
} 

Problemstellung: -

Jetzt möchte ich eine weitere Funktion hinzuzufügen, die - Wenn machineA (da seine Sperr Zählung blockiert ist> = 5) dann möchte ich es für x-Intervall blockieren. Ich werde einen anderen Parameter (key.getInterval()) haben, der uns sagen wird, für wie lange ich diesen Computer blockiert haben will und nachdem dieses Intervall verstrichen ist, dann werde ich anfangen, Anruf zu ihnen zu machen. Ich kann nicht verstehen, wie Sie diese Funktion hinzufügen können.

Unten ist mein Haupt-Thread-Code, wo ich DataMapping Methoden verwenden, um zu überprüfen, ob Hostname blockiert ist oder nicht, und Hostnamen zu blockieren.

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

    List<String> hostnames = some_code_here; 

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

      // some code here to return the response if successful 
     } catch (RestClientException ex) { 
      // adding to block list 
      DataMapping.onFailure(hostname); 
     } 
    } 

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

Wie kann ich eine bestimmte Maschine für einen bestimmten Zeitraum blockieren und sobald das Intervall dann beginnen erst abgelaufen ist, Anrufe zu ihnen machen?

+0

Sie brauchen, um zu verfolgen, wenn es wurde blockiert, um zu sagen, ob das Intervall abgelaufen ist, nicht nur, ob es blockiert . –

Antwort

1

Sie können eine ScheduledExecutorService und schedule das Zurücksetzen des Zählers nach einer bestimmten Zeitüberschreitung verwenden.

Sie können dies in Ihrer DataMapping Klasse deklarieren:

private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // or perhaps the thread pool version ? 

Und in Ihrer onFailure() Methode können Sie entscheiden, ob Sie wollen oder nur Abnahme zurückgesetzt den Zähler nach einer gewissen Timeout:

void onFailure(String hostname) { 
    // you can use `computeIfAbsent` in java8 
    AtomicInteger val = blockedHosts.computeIfAbsent(hostname, key -> new AtomicInteger()); 
    int count = val.incrementAndGet(); 
    // the test here is `==` to make sure the task is scheduled only once 
    if (count == threshold) { 
     scheduler.schedule(() -> blockedHosts.remove(hostname), 5L, TimeUnit.MINUTES); // or you may choose to just decrement the counter 
    } 
} 

Als eine Randnotiz gibt es keinen Grund, blockedHostsvolatile zu machen. Diese Referenz ändert sich nie; es sollte stattdessen final sein; und wahrscheinlich private.


In Java7 würde der obige Code wie folgt aussehen:

void onFailure(String hostname) { 
    AtomicInteger newValue = new AtomicInteger(); 
    AtomicInteger val = blockedHosts.putIfAbsent(hostname, newValue); 
    int count = (val == null ? newValue : val).incrementAndGet(); 
    // the test here is `==` to make sure the task is scheduled only once 
    if (count == threshold) { 
     scheduler.schedule(new Runnable() { 
      @Override public void run() { 
       blockedHosts.remove(hostname); // or you may choose to just decrement the counter 
      } 
     }, 5L, TimeUnit.MINUTES); 
    } 
} 
+0

Leider bin ich noch auf Java 7 und kann nicht nach Java 8 wechseln. Wie sieht das mit Java 7 aus? – john

+0

Ziemlich genau dasselbe, außer für "computeIfAbsent" und die ordentliche Lambda-Syntax.Sie müssen ein "Callable" (oder "Runnable") instanziieren und es an den Executor-Service senden. –

+0

Können Sie dies auch mit Java 7 Vorschlag aktualisieren. Ich versuche immer noch zu verstehen, was der obige Code tut. Ich werde einige Fragen haben, sobald ich in Java 7 sehe, nur um sicherzustellen, dass ich verstehe. – john