Я использую ниже класс для отправки данных в нашу очередь сообщений, используя сокет либо синхронно, либо асинхронно, как показано ниже. Это зависит от требования, хочу ли я называть синхронный или асинхронный метод для отправки данных в сокет. В большинстве случаев мы будем отправлять данные асинхронно, но иногда мне может понадобиться синхронно отправлять данные.
-
sendAsync- Он отправляет данные асинхронно, и мы не блокируем поток, который отправляет данные. Если подтверждение не получено, оно будет снова повторяться из фонового потока, который запускается только в конструктореSendToQueue. -
send- Он отправляет данные синхронно в сокете. Он внутренне вызывает методdoSendAsync, а затем выполняет спящий режим для определенного периода ожидания и, если подтверждение не получено, оно удаляет из ведраcache, чтобы мы не повторили попытку.
Таким образом, единственное различие между этими двумя вышеописанными методами: - Для случая async мне нужно выполнить любую попытку, если подтверждение не получено, но для синхронизации мне вообще не нужно повторять попытку, и поэтому я сохраняю больше состояний в классе PendingMessage.
ResponsePoller - это класс, который получает подтверждение для данных, которые были отправлены в нашу очередь сообщений в конкретном сокете, а затем вызывает метод handleAckReceived ниже, чтобы удалить адрес, чтобы мы не повторили попытку после получения подтверждения, Если подтверждение получено, то сокет является живым, иначе он мертв.
public class SendToQueue {
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
private final Cache<Long, PendingMessage> cache = CacheBuilder.newBuilder()
.maximumSize(1000000)
.concurrencyLevel(100)
.build();
private static class PendingMessage {
private final long _address;
private final byte[] _encodedRecords;
private final boolean _retryEnabled;
private final Object _monitor = new Object();
private long _sendTimeMillis;
private volatile boolean _acknowledged;
public PendingMessage(long address, byte[] encodedRecords, boolean retryEnabled) {
_address = address;
_sendTimeMillis = System.currentTimeMillis();
_encodedRecords = encodedRecords;
_retryEnabled = retryEnabled;
}
public synchronized boolean hasExpired() {
return System.currentTimeMillis() - _sendTimeMillis > 500L;
}
public synchronized void markResent() {
_sendTimeMillis = System.currentTimeMillis();
}
public boolean shouldRetry() {
return _retryEnabled && !_acknowledged;
}
public boolean waitForAck() {
try {
synchronized (_monitor) {
_monitor.wait(500L);
}
return _acknowledged;
} catch (InterruptedException ie) {
return false;
}
}
public void ackReceived() {
_acknowledged = true;
synchronized (_monitor) {
_monitor.notifyAll();
}
}
public long getAddress() {
return _address;
}
public byte[] getEncodedRecords() {
return _encodedRecords;
}
}
private static class Holder {
private static final SendToQueue INSTANCE = new SendToQueue();
}
public static SendToQueue getInstance() {
return Holder.INSTANCE;
}
private void handleRetries() {
List<PendingMessage> messages = new ArrayList<>(cache.asMap().values());
for (PendingMessage m : messages) {
if (m.hasExpired()) {
if (m.shouldRetry()) {
m.markResent();
doSendAsync(m, Optional.<Socket>absent());
} else {
cache.invalidate(m.getAddress());
}
}
}
}
private SendToQueue() {
executorService.submit(new ResponsePoller()); // another thread which receives acknowledgment
// and then delete entry from the cache
// accordingly.
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
handleRetries();
}
}, 0, 1, TimeUnit.SECONDS);
}
public boolean sendAsync(final long address, final byte[] encodedRecords) {
PendingMessage m = new PendingMessage(address, encodedRecords, true);
cache.put(address, m);
return doSendAsync(m, Optional.<Socket>absent());
}
private boolean doSendAsync(final PendingMessage pendingMessage, final Optional<Socket> socket) {
Optional<Socket> actualSocket = socket;
if (!actualSocket.isPresent()) {
SocketHolder liveSocket = SocketManager.getInstance().getSocket();
actualSocket = Optional.of(liveSocket.getSocket());
}
ZMsg msg = new ZMsg();
msg.add(pendingMessage.getEncodedRecords());
try {
return msg.send(actualSocket.get());
} finally {
msg.destroy();
}
}
public boolean send(final long address, final byte[] encodedRecords) {
return send(address, encodedRecords, Optional.<Socket>absent());
}
public boolean send(final long address, final byte[] encodedRecords,
final Optional<Socket> socket) {
PendingMessage m = new PendingMessage(address, encodedRecords, false);
cache.put(address, m);
try {
if (doSendAsync(m, socket)) {
return m.waitForAck();
}
return false;
} finally {
cache.invalidate(address);
}
}
// called by acknowledgment thread which is in "ResponsePoller" class
public void handleAckReceived(final long address) {
PendingMessage m = cache.getIfPresent(address);
if (m != null) {
m.ackReceived();
cache.invalidate(address);
}
}
}
Поскольку я отправляю данные по сокету и , если я получаю подтверждение подтверждения для тех же данных, значит, Socket жив, но , если данные не подтверждаются, тогда это означает, что сокет мертв (но я буду продолжать повторную попытку отправки данных).
Итак, с моим дизайном (или если есть лучший способ), как я могу выяснить, мертв или жив какой-либо сокет, потому что либо подтверждение не было получено, либо получено от этого сокета и основы, на котором мне нужно отпустите сокет обратно в свой пул (независимо от того, жив он или нет), вызывая метод ниже в зависимости от того, получено подтверждение или нет для синхронизации или асинхронного случая.
Мне также нужно настроить счетчик, если подтверждение не получено в конкретном сокете для x (где x - число > 0, значение по умолчанию должно быть 2) раза, а затем только отметьте сокет мертвым. Каков наилучший и эффективный способ сделать это?
SocketManager.getInstance().releaseSocket(socket, SocketState.LIVE);
SocketManager.getInstance().releaseSocket(socket, SocketState.DEAD);