如果Http連接失敗(例如服務器關閉),我需要遞減計數器。
我打算說「地獄是的」,但我在這句話後有點不太確定。我想你想要做這樣的事情:
def sendRequest(url)
request = new request to url
request.header["X-count"] = next serial
if request.send() != SUCCESS
rewind serial
在這種情況下,我猜想,兩個線程不應該被允許同時發送請求,然後你想要的東西,序列化的要求,而不是一個AtomicInteger
,這實際上只是讓你執行一些原子操作。如果兩個線程都同時調用sendRequest
,而第一個會失敗,會發生這種情況:
Thread | What happens?
--------+-------------------------
A | Creates new request
B | Creates new request
A | Set request["X-count"] = 0
A | Increment counter to 1
A | Send request
B | Set request["X-count"] = 1
B | Increment counter to 2
B | Send request
A | Request fails
B | Request succeeds
A | Rewind counter down to 1
C | Creates new request
C | Set request["X-count"] = 1
C | Increment counter to 2
而現在,你已經派了兩個請求,與X-數= 1。如果你想避免這種情況,你應該使用類似(假設Request
和Response
是用於處理請求的URL類別):
class SerialRequester {
private volatile int currentSerial = 0;
public synchronized Response sendRequest(URL url) throws SomeException {
Request request = new Request(url);
request.setHeader("X-count", currentSerial);
Response response = request.send();
if (response.isSuccess()) ++currentSerial;
return response;
}
}
此類保證沒有兩個成功的請求(通過相同SerialRequester
製造)具有相同的X-計數值。
編輯很多人似乎都擔心上述解決方案不能同時運行。它沒有。這是正確的。但它需要以這種方式來解決OP的問題。現在,如果在請求失敗時不需要減少計數器,那麼AtomicInteger
就是完美的,但在這種情況下它不正確。
編輯2我在我這裏寫了一個不容易凍結的串行請求者(比如上面的那個),這樣如果它們掛起太久,它會中止請求(例如,在工作線程中排隊但沒有開始)。因此,如果管道阻塞並且一個請求掛起很長時間,其他請求將最多等待一段固定時間,所以隊列不會無限增長,直到阻塞消失。
class SerialRequester {
private enum State { PENDING, STARTED, ABORTED }
private final ExecutorService executor =
Executors.newSingleThreadExecutor();
private int currentSerial = 0; // not volatile, used from executor thread only
public Response sendRequest(final URL url)
throws SomeException, InterruptedException {
final AtomicReference<State> state =
new AtomicReference<State>(State.PENDING);
Future<Response> result = executor.submit(new Callable<Response>(){
@Override
public Result call() throws SomeException {
if (!state.compareAndSet(State.PENDING, State.STARTED))
return null; // Aborted by calling thread
Request request = new Request(url);
request.setHeader("X-count", currentSerial);
Response response = request.send();
if (response.isSuccess()) ++currentSerial;
return response;
}
});
try {
try {
// Wait at most 30 secs for request to start
return result.get(30, TimeUnit.SECONDS);
} catch (TimeoutException e){
// 30 secs passed; abort task if not started
if (state.compareAndSet(State.PENDING, State.ABORTED))
throw new SomeException("Request queued too long", e);
return result.get(); // Task started; wait for completion
}
} catch (ExecutionException e) { // Network timeout, misc I/O errors etc
throw new SomeException("Request error", e);
}
}
}
但是我的2克拉,你應該注意,其中具有較高計數的HTTP請求是一個與之前的較低值發送的情況下(如情況下,其中的低線計數HTTP請求結束睡眠) – notnoop 2009-11-04 07:29:42
notnoop是正確的。如果你想在服務器端進行預定義排序(例如,發生 - 之前),你必須實現某種Lamport時鐘。 – 2009-11-04 15:40:12