All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.chanhohang.akka.actor.ClusterMessengerReceiver Maven / Gradle / Ivy
package com.github.chanhohang.akka.actor;
import akka.Done;
import akka.NotUsed;
import akka.persistence.RecoveryCompleted;
import akka.persistence.UntypedPersistentActor;
import akka.persistence.query.EventEnvelope;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.stream.javadsl.Source;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.chanhohang.akka.AkkaClusterMessengerConstants;
import com.github.chanhohang.akka.cluster.ClusterMessageRouter;
import com.github.chanhohang.akka.cluster.MessagePublishRouter;
import com.github.chanhohang.akka.journal.ClusterJdbcReadJournal;
import com.github.chanhohang.akka.journal.ClusterReadJournal;
import com.github.chanhohang.akka.message.Acknowledgement;
import com.github.chanhohang.akka.message.MessageEnvelopeImpl;
import com.github.chanhohang.akka.message.persist.AcknowledgementPersist;
import com.github.chanhohang.akka.message.resend.ResendComplete;
import com.github.chanhohang.akka.message.resend.ResendRequest;
import com.github.chanhohang.akka.message.reset.ResetSequenceNumberRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.PostConstruct;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Component(AkkaClusterMessengerConstants.PersistentId.Cluster.Receiver)
@Lazy
public class ClusterMessengerReceiver extends UntypedPersistentActor {
private Logger log = LoggerFactory.getLogger(this.getClass());
private ObjectMapper mapper = new ObjectMapper();
private Map sequenceNumberMap = new HashMap<>();
@Value("${actp.akka.role}")
private String role;
@Autowired(required = false)
private ClusterMessageRouter router;
@Autowired(required = false)
private ClusterReadJournal readJournal;
private boolean resendTrigger = false;
@PostConstruct
private void postConstruct() {
if (readJournal == null) {
readJournal = new ClusterJdbcReadJournal();
}
if (router == null) {
router = new MessagePublishRouter(context().system());
}
}
@Override
public String persistenceId() {
return role + "." + AkkaClusterMessengerConstants.PersistentId.Cluster.Receiver;
}
@Override
public void onReceiveCommand(Object message) throws Exception {
try {
log.info("onReceiveCommand {} from sender {}", message, getSender());
if (message instanceof ResendComplete) {
handleResendComplete((ResendComplete) message);
} else if (message instanceof ResetSequenceNumberRequest) {
handleResetSequenceNumber((ResetSequenceNumberRequest) message);
} else if (message instanceof MessageEnvelopeImpl) {
handleMessage((MessageEnvelopeImpl) message);
} else {
unhandled(message);
}
} catch (Exception exception) {
log.error("Unexpected Exception.", exception);
try {
getSender().tell(exception, self());
} catch (Exception replyException) {
log.error("Unexpected replyException.", replyException);
}
}
}
private void handleMessage(MessageEnvelopeImpl msg)
throws JsonParseException, JsonMappingException, IOException {
sendAcknowledgement(msg, role);
String role = msg.getRole();
AtomicLong sequenceNumber = getSequenceNumber(role);
if (msg.getSequenceNumber() <= sequenceNumber.get()) {
// Message Already Processed.
log.info("Sequence Number {} from {} is already processed.", sequenceNumber, role);
} else if (msg.getSequenceNumber() > sequenceNumber.get() + 1) {
// Gap Detected.
if (resendTrigger == false) {
resendTrigger = true;
ResendRequest resendRequest = new ResendRequest(sequenceNumber.get() + 1,
msg.getSequenceNumber());
resendRequest.setTargetRole(getRole());
log.info("start resend. {}", resendRequest);
getSender().tell(resendRequest, getSelf());
} else {
log.info("resend from {} is in progressed.", role);
}
} else {
// Normal Message.
sequenceNumber.set(msg.getSequenceNumber());
Object event = null;
if (msg.getPayload() != null && msg.getPayloadClass() != null) {
event = mapper.readValue(msg.getPayload(), msg.getPayloadClass());
} else {
log.info("Skip parsing unrecognized payloadClass {} and payload {}", msg.getPayloadClass(),
msg.getPayload());
}
AcknowledgementPersist ackPersist = new AcknowledgementPersist();
ackPersist.setRole(role);
ackPersist.setSequenceNumber(msg.getSequenceNumber());
ackPersist.setPayloadClass(msg.getPayloadClass());
ackPersist.setPayload(event);
ackPersist.setTargetRole(msg.getRole());
persistAsync(ackPersist, evt -> {
if (evt.getPayload() != null) {
router.route(evt);
}
});
}
}
private void sendAcknowledgement(MessageEnvelopeImpl msg, String role) {
Acknowledgement ack = new Acknowledgement();
ack.setDeliveryId(msg.getDeliveryId());
ack.setTargetRole(role);
ack.setRole(getRole());
log.info("Sending Acknowledgement {} to sender {}", ack, getSender());
getSender().tell(ack, getSelf());
}
private void handleResetSequenceNumber(ResetSequenceNumberRequest message) {
long resetSequenceNumber = message.getSequenceNumber();
AtomicLong currentSequenceNumber = getSequenceNumber(message.getRole());
String role = message.getRole();
List deleteList = new ArrayList<>();
if (currentSequenceNumber.get() == resetSequenceNumber) {
log.info("handleResetSequenceNumber already reset {}, role {}", resetSequenceNumber, role);
} else {
log.info("handleResetSequenceNumber for reset sequence number {}, role {}, "
+ "current sequence number {}", resetSequenceNumber, role, currentSequenceNumber);
currentSequenceNumber.set(resetSequenceNumber);
Source events = readJournal
.currentEventsByPersistenceId(getContext().system(), persistenceId(), 0, Long.MAX_VALUE);
Materializer materializer = ActorMaterializer.create(getContext().system());
CompletionStage stage = events.runForeach(eventEnvelope -> {
if (eventEnvelope.event() instanceof AcknowledgementPersist) {
AcknowledgementPersist msgPersist = (AcknowledgementPersist) eventEnvelope.event();
if (message.getRole() == null || message.getRole().equals(msgPersist.getTargetRole())) {
long seqNo = eventEnvelope.sequenceNr();
if (msgPersist.getSequenceNumber() >= resetSequenceNumber) {
deleteList.add(seqNo);
}
}
}
}, materializer);
stage.thenRun(() -> {
log.info("Delete Event Store Sequence Number {}", deleteList);
for (long i : deleteList) {
deleteMessages(i);
}
});
}
sendAcknowledgement(message, message.getRole());
}
private void handleResendComplete(ResendComplete message) {
long endSequenceNumber = message.getEndSequenceNumber();
AtomicLong sequenceNumber = getSequenceNumber(message.getRole());
if (sequenceNumber.get() == endSequenceNumber) {
log.info("handleResendComplete Complete for end sequence number {}, role {}",
endSequenceNumber, message.getRole());
} else {
log.info("handleResendComplete incomplete and reset sequence number to {}, role {}, ",
endSequenceNumber, message.getRole());
sequenceNumber.set(endSequenceNumber);
}
resendTrigger = false;
sendAcknowledgement(message, message.getRole());
}
private AtomicLong getSequenceNumber(String role) {
AtomicLong sequenceNumber = sequenceNumberMap.get(role);
if (sequenceNumber == null) {
sequenceNumber = new AtomicLong();
sequenceNumberMap.put(role, sequenceNumber);
}
return sequenceNumber;
}
@Override
public void onReceiveRecover(Object event) {
log.info("onReceiveRecover {}", event);
if (event instanceof AcknowledgementPersist) {
AcknowledgementPersist msg = (AcknowledgementPersist) event;
String role = msg.getRole();
AtomicLong sequenceNumber = getSequenceNumber(role);
if (sequenceNumber.get() < msg.getSequenceNumber()) {
sequenceNumber.set(msg.getSequenceNumber());
}
} else if (event instanceof RecoveryCompleted) {
log.info("sequenceNumberMap {}", sequenceNumberMap);
}
}
}