Я работаю над своим приложением, которое отправляет данные в zeromq
. Ниже приведено мое приложение:
- У меня есть класс
SendToZeroMQ
, который отправляет данные в zeromq. - Добавьте те же данные в
retryQueue
в том же классе, чтобы впоследствии его можно было повторить, если подтверждение не получено. Он использует кеш guava с пределом максимальной суммы. - Имейте отдельный поток, который получает подтверждение от zeromq для данных, которые были отправлены ранее, и если подтверждение не получено, то
SendToZeroMQ
будет повторять отправку той же самой части данных. И если подтверждение получено, мы удалим его изretryQueue
, чтобы его нельзя было повторить повторно.
Идея очень проста, и я должен убедиться, что моя политика повторных попыток работает нормально, поэтому я не теряю свои данные. Это очень редко, но в случае, если мы не получим acknolwedgements.
Я думаю о создании двух типов RetryPolicies
, но я не могу понять, как построить это здесь, соответствующее моей программе:
-
RetryNTimes:
В этом случае он будет повторять N раз с определенным сном между каждой попыткой, и после этого он потеряет запись. -
ExponentialBackoffRetry:
В этом случае экспоненциально будет продолжаться повторная попытка. Мы можем установить предел max max, и после этого он не будет повторять попытку и отбросит запись.
Ниже представлен мой класс SendToZeroMQ
, который отправляет данные в zeromq, также каждые 30 секунд повторяет фоновый поток и запускает run ResponsePoller
, который работает постоянно:
public class SendToZeroMQ {
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
private final Cache<Long, byte[]> retryQueue =
CacheBuilder
.newBuilder()
.maximumSize(10000000)
.concurrencyLevel(200)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(), executorService)).build();
private static class Holder {
private static final SendToZeroMQ INSTANCE = new SendToZeroMQ();
}
public static SendToZeroMQ getInstance() {
return Holder.INSTANCE;
}
private SendToZeroMQ() {
executorService.submit(new ResponsePoller());
// retry every 30 seconds for now
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
for (Entry<Long, byte[]> entry : retryQueue.asMap().entrySet()) {
sendTo(entry.getKey(), entry.getValue());
}
}
}, 0, 30, TimeUnit.SECONDS);
}
public boolean sendTo(final long address, final byte[] encodedRecords) {
Optional<ZMQSocketInfo> liveSockets = PoolManager.getInstance().getNextSocket();
if (!liveSockets.isPresent()) {
return false;
}
return sendTo(address, encodedRecords, liveSockets.get().getSocket());
}
public boolean sendTo(final long address, final byte[] encodedByteArray, final Socket socket) {
ZMsg msg = new ZMsg();
msg.add(encodedByteArray);
boolean sent = msg.send(socket);
msg.destroy();
// adding to retry queue
retryQueue.put(address, encodedByteArray);
return sent;
}
public void removeFromRetryQueue(final long address) {
retryQueue.invalidate(address);
}
}
Ниже представлен мой класс ResponsePoller
, который проверяет все подтверждения из zeromq. И если мы получим подтверждение от zeromq, то мы удалим эту запись из очереди повторов, чтобы она не повторилась, иначе она будет повторена.
public class ResponsePoller implements Runnable {
private static final Random random = new Random();
@Override
public void run() {
ZContext ctx = new ZContext();
Socket client = ctx.createSocket(ZMQ.PULL);
String identity = String.format("%04X-%04X", random.nextInt(), random.nextInt());
client.setIdentity(identity.getBytes(ZMQ.CHARSET));
client.bind("tcp://" + TestUtils.getIpaddress() + ":8076");
PollItem[] items = new PollItem[] {new PollItem(client, Poller.POLLIN)};
while (!Thread.currentThread().isInterrupted()) {
// Tick once per second, pulling in arriving messages
for (int centitick = 0; centitick < 100; centitick++) {
ZMQ.poll(items, 10);
if (items[0].isReadable()) {
ZMsg msg = ZMsg.recvMsg(client);
Iterator<ZFrame> it = msg.iterator();
while (it.hasNext()) {
ZFrame frame = it.next();
try {
long address = TestUtils.getAddress(frame.getData());
// remove from retry queue since we got the acknowledgment for this record
SendToZeroMQ.getInstance().removeFromRetryQueue(address);
} catch (Exception ex) {
// log error
} finally {
frame.destroy();
}
}
msg.destroy();
}
}
}
ctx.destroy();
}
}
Вопрос:
Как вы можете видеть выше, я отправляю encodedRecords
в zeromq с помощью класса SendToZeroMQ
, а затем его повторно проверяют каждые 30 секунд, в зависимости от того, получили ли мы обратно acknolwedgement из класса ResponsePoller
или нет.
Для каждого encodedRecords
существует уникальный ключ с именем address
и тот, который мы вернем из zeromq в качестве подтверждения.
Как я могу продолжить и расширить этот пример, чтобы построить две политики повтора, о которых я упоминал выше, и затем я могу выбрать, какую политику повтора я хочу использовать при отправке данных. Я придумал интерфейс ниже, но тогда я не могу понять, как мне двигаться вперед, чтобы реализовать эти политики повтора и использовать его в моем вышеприведенном коде.
public interface RetryPolicy {
/**
* Called when an operation has failed for some reason. This method should return
* true to make another attempt.
*/
public boolean allowRetry(int retryCount, long elapsedTimeMs);
}
Могу ли я использовать guava-retrying или failsafe здесь, потому что в этих библиотеках уже есть много политик повтора, которые я могу использовать?