
pro.jk.ejoker.eventing.impl.DefaultEventCommittingService Maven / Gradle / Ivy
package pro.jk.ejoker.eventing.impl;
import static pro.jk.ejoker.common.system.extension.LangUtil.await;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.jk.ejoker.EJokerEnvironment;
import pro.jk.ejoker.commanding.CommandResult;
import pro.jk.ejoker.commanding.CommandStatus;
import pro.jk.ejoker.commanding.ICommand;
import pro.jk.ejoker.commanding.ProcessingCommand;
import pro.jk.ejoker.commanding.ProcessingCommandMailbox;
import pro.jk.ejoker.common.context.annotation.context.Dependence;
import pro.jk.ejoker.common.context.annotation.context.EInitialize;
import pro.jk.ejoker.common.context.annotation.context.EService;
import pro.jk.ejoker.common.service.IJSONConverter;
import pro.jk.ejoker.common.system.enhance.EachUtilx;
import pro.jk.ejoker.common.system.enhance.MapUtilx;
import pro.jk.ejoker.common.system.enhance.StringUtilx;
import pro.jk.ejoker.common.system.extension.acrossSupport.EJokerFutureUtil;
import pro.jk.ejoker.common.system.task.context.SystemAsyncHelper;
import pro.jk.ejoker.common.system.task.io.IOHelper;
import pro.jk.ejoker.domain.IAggregateRootFactory;
import pro.jk.ejoker.domain.IAggregateStorage;
import pro.jk.ejoker.domain.IMemoryCache;
import pro.jk.ejoker.eventing.DomainEventStream;
import pro.jk.ejoker.eventing.DomainEventStreamMessage;
import pro.jk.ejoker.eventing.EventCommittingContext;
import pro.jk.ejoker.eventing.EventCommittingContextMailBox;
import pro.jk.ejoker.eventing.IEventCommittingService;
import pro.jk.ejoker.eventing.IEventStore;
import pro.jk.ejoker.messaging.IMessagePublisher;
@EService
public class DefaultEventCommittingService implements IEventCommittingService {
private final static Logger logger = LoggerFactory.getLogger(DefaultEventCommittingService.class);
private final int eventMailboxCount = EJokerEnvironment.EVENT_MAILBOX_ACTOR_COUNT;
@Dependence
private IJSONConverter jsonSerializer;
@Dependence
private IMemoryCache memoryCache;
@Dependence
private IAggregateRootFactory aggregateRootFactory;
@Dependence
private IAggregateStorage aggregateStorage;
@Dependence
private IEventStore eventStore;
@Dependence
private IMessagePublisher domainEventPublisher;
@Dependence
private IOHelper ioHelper;
@Dependence
private SystemAsyncHelper systemAsyncHelper;
private final List eventCommittingContextMailBoxList = new ArrayList<>();
@EInitialize
private void init() {
for(int i=0; i committingContexts) {
if (null == committingContexts || committingContexts.isEmpty())
return;
LinkedList domainEventStreams = new LinkedList<>();
EachUtilx.forEach(committingContexts, item -> domainEventStreams.add(item.getEventStream()));
ioHelper.tryAsyncAction2(
"BatchPersistEventAsync",
() -> eventStore.batchAppendAsync(domainEventStreams),
appendResult -> {
EventCommittingContextMailBox eventMailBox = committingContexts.get(0).getMailBox();
if(null == appendResult) {
logger.error("BatchPersistAggregateEvents result is null, the current event committing mailbox should be pending. [mailboxNumber: {}]",
eventMailBox.getNumber());
return;
}
Map> groupCommittedContextDict = new HashMap<>();
for(EventCommittingContext ecc : committingContexts) {
MapUtilx
.getOrAdd(groupCommittedContextDict, ecc.getEventStream().getAggregateRootId(), () -> new ArrayList<>())
.add(ecc);
}
Set successIds = appendResult.getSuccessAggregateRootIdList();
if(null != successIds && !successIds.isEmpty()) {
// 针对持久化成功的聚合根,发布这些聚合根的事件到Q端
EachUtilx.forEach(successIds, aggregateRootId -> {
List committingContextList = groupCommittedContextDict.get(aggregateRootId);
if(null != committingContextList && !committingContextList.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug(
"BatchPersistAggregateEvents succeed. [mailboxNumber: {}, aggregateRootId: {}, events: {}]",
eventMailBox.getNumber(),
aggregateRootId,
committingContextList
.stream()
.map(EventCommittingContext::getEventStream)
.map(DomainEventStream::toString)
.reduce((x, y) -> x + ", " + y)
.get()
);
}
for(EventCommittingContext ecc : committingContextList) {
publishDomainEventAsync(ecc.getProcessingCommand(), ecc.getEventStream());
}
}
});
}
Map> duplicateCommandAggregateRootIdList = appendResult.getDuplicateCommandAggregateRootIdList();
//针对持久化出现重复的命令ID,则重新发布这些命令对应的领域事件到Q端
EachUtilx.forEach(duplicateCommandAggregateRootIdList, (aggregateRootId, duplicateCommandIdList) -> {
List contextList = groupCommittedContextDict.get(aggregateRootId);
if(null == contextList || contextList.isEmpty()) return;
EventCommittingContext committingContext = contextList.get(0);
logger.warn("BatchPersistAggregateEvents has duplicate commandIds. [mailboxNumber: {}, aggregateRootId: {}, commandIds: {}]",
eventMailBox.getNumber(), aggregateRootId, duplicateCommandIdList);
if(1l == committingContext.getEventStream().getVersion()) {
await(handleFirstEventDuplicationAsync(committingContext));
} else {
await(resetCommandMailBoxConsumingSequence(committingContext, committingContext.getProcessingCommand().getSequence(), duplicateCommandIdList));
}
});
Set duplicateAggrIdList = appendResult.getDuplicateEventAggregateRootIdList();
if(null != duplicateAggrIdList && !duplicateAggrIdList.isEmpty()) {
//针对持久化出现版本冲突的聚合根,则自动处理每个聚合根的冲突
for(String aggregateRootId : duplicateAggrIdList) {
Optional committingContextOp = committingContexts
.stream()
.filter(x -> aggregateRootId.equals(x.getEventStream().getAggregateRootId()))
.findFirst();
if(committingContextOp.isPresent()) {
EventCommittingContext eventCommittingContext = committingContextOp.get();
logger.warn(
"BatchPersistAggregateEvents has version confliction. [mailboxNumber: {}, aggregateRootId: {}, conflictVersion: {}]",
eventMailBox.getNumber(),
aggregateRootId,
eventCommittingContext.getEventStream().getVersion()
);
if(1l == eventCommittingContext.getEventStream().getVersion()) {
await(handleFirstEventDuplicationAsync(eventCommittingContext));
} else {
await(resetCommandMailBoxConsumingSequence(eventCommittingContext, eventCommittingContext.getProcessingCommand().getSequence()));
}
}
}
}
eventMailBox.finishRun();
},
() -> StringUtilx.fmt("[contextListCount: {}]", committingContexts.size()),
true
);
}
/**
* * 这里是由CompletableFuture提供的任务链功能
* @param context
* @param consumingSequence
* @return
*/
private CompletableFuture resetCommandMailBoxConsumingSequence(EventCommittingContext context, long consumingSequence, List duplicateCommandIdList) {
// final EventMailBox eventMailBox = context.eventMailBox;
// IMailBox eventMailBox = context.getMailBox();
final ProcessingCommand processingCommand = context.getProcessingCommand();
final ICommand command = processingCommand.getMessage();
final ProcessingCommandMailbox commandMailBox = processingCommand.getMailBox();
final EventCommittingContextMailBox eventMailBox = context.getMailBox();
final String aggregateRootId = context.getEventStream().getAggregateRootId();
// 设置暂停标识,不排斥当前运行的任务,但是拒绝新的任务进入
commandMailBox.pauseOnly();
// commandMailBox.pause() 与 commandMailBox.resume() 并不在成对的try-finally过程中
// 会不会出问题?
// #fix 把pause过程拆解为pauseOnly 和 waitAcquireOnProcessing两个过程 更符合java
return CompletableFuture.supplyAsync(() -> {
// 等待完全的pause状态
commandMailBox.acquireOnRunning();
try {
eventMailBox.removeAggregateAllEventCommittingContexts(aggregateRootId);
// TODO @await
await(memoryCache.refreshAggregateFromEventStoreAsync(context.getEventStream().getAggregateRootTypeName(), aggregateRootId));
if(null != duplicateCommandIdList) {
for(String commandIdDuplicated : duplicateCommandIdList) {
commandMailBox.addDuplicateCommandId(commandIdDuplicated);
}
}
commandMailBox.resetConsumingSequence(consumingSequence);
} catch (RuntimeException ex) {
logger.error("ResetCommandMailBoxConsumingOffset has unknown exception!!! [aggregateRootId: {}]",
command.getAggregateRootId(), ex);
} finally {
commandMailBox.resume();
commandMailBox.tryRun();
}
return null;
});
}
private CompletableFuture resetCommandMailBoxConsumingSequence(EventCommittingContext context, long consumingSequence) {
return resetCommandMailBoxConsumingSequence(context, consumingSequence, null);
}
private void tryToRepublishEventAsync(EventCommittingContext context) {
ICommand command = context.getProcessingCommand().getMessage();
ioHelper.tryAsyncAction2(
"FindEventByCommandIdAsync",
() -> eventStore.findAsync(command.getAggregateRootId(), command.getId()),
existingEventStream -> {
if (null != existingEventStream) {
//这里,我们需要再重新做一遍发布事件这个操作;
//之所以要这样做是因为虽然该command产生的事件已经持久化成功,但并不表示事件已经发布出去了;
//因为有可能事件持久化成功了,但那时正好机器断电了,则发布事件都没有做;
publishDomainEventAsync(context.getProcessingCommand(), existingEventStream);
} else {
//到这里,说明当前command想添加到eventStore中时,提示command重复,但是尝试从eventStore中取出该command时却找不到该command。
//出现这种情况,我们就无法再做后续处理了,这种错误理论上不会出现,除非eventStore的Add接口和Get接口出现读写不一致的情况;
//框架会记录错误日志,让开发者排查具体是什么问题。
String errorMessage = StringUtilx.fmt(
"Command should be exist in the event store, but we cannot find it from the event store, this should not be happen, and we just complete the command!!! [commandType: {}, commandId: {}, aggregateRootId: {}]",
command.getClass().getName(),
command.getId(),
command.getAggregateRootId());
logger.error(errorMessage);
CommandResult commandResult = new CommandResult(CommandStatus.Failed,
command.getId(),
command.getAggregateRootId(),
"Command should be exist in the event store, but we cannot find it from the event store.",
String.class.getName());
finishCommandAsync(context.getProcessingCommand(), commandResult);
}
},
() -> StringUtilx.fmt("[aggregateRootId: {}, commandId: {}]", command.getAggregateRootId(), command.getId()),
true
);
}
/**
* 遇到Version为1的事件的重复的时候,做特殊处理。
* @param context
*/
private Future handleFirstEventDuplicationAsync(final EventCommittingContext context) {
DomainEventStream eventStream = context.getEventStream();
ioHelper.tryAsyncAction2(
"FindFirstEventByVersion",
() -> eventStore.findAsync(eventStream.getAggregateRootId(), 1),
firstEventStream -> {
String commandId = context.getProcessingCommand().getMessage().getId();
if(null != firstEventStream) {
//判断是否是同一个command,如果是,则再重新做一遍发布事件;
//之所以要这样做,是因为虽然该command产生的事件已经持久化成功,但并不表示事件也已经发布出去了;
//有可能事件持久化成功了,但那时正好机器断电了,则发布事件都没有做;
if(commandId.equals(firstEventStream.getCommandId())) {
resetCommandMailBoxConsumingSequence(context,
context.getProcessingCommand().getSequence() + 1).thenAcceptAsync(t -> {
publishDomainEventAsync(context.getProcessingCommand(), firstEventStream);
});
} else {
//如果不是同一个command,则认为是两个不同的command重复创建ID相同的聚合根,我们需要记录错误日志,然后通知当前command的处理完成;
logger.error(
"Duplicate aggregate creation. [currentCommandId: {}, existingCommandId: {}, aggregateRootId: {}, aggregateRootTypeName: {}]",
commandId,
firstEventStream.getCommandId(),
firstEventStream.getAggregateRootId(),
firstEventStream.getAggregateRootTypeName());
resetCommandMailBoxConsumingSequence(context, context.getProcessingCommand().getSequence() + 1)
.thenAcceptAsync(t -> {
CommandResult commandResult = new CommandResult(CommandStatus.Failed, commandId,
eventStream.getAggregateRootId(), "Duplicate aggregate creation.",
String.class.getName());
finishCommandAsync(context.getProcessingCommand(), commandResult);
});
}
} else {
logger.error("Duplicate aggregate creation, but we cannot find the existing eventstream from eventstore, this should not be happen, and we just complete the command!!! [commandId: {}, aggregateRootId: {}, aggregateRootTypeName: {}]",
eventStream.getCommandId(),
eventStream.getAggregateRootId(),
eventStream.getAggregateRootTypeName());
resetCommandMailBoxConsumingSequence(context, context.getProcessingCommand().getSequence() + 1)
.thenApplyAsync(t -> {
CommandResult commandResult = new CommandResult(CommandStatus.Failed, commandId,
eventStream.getAggregateRootId(),
"Duplicate aggregate creation, but we cannot find the existing eventstream from eventstore.",
String.class.getName());
finishCommandAsync(context.getProcessingCommand(), commandResult);
return null;
});
}
},
() -> StringUtilx.fmt("[eventStream: {}]", jsonSerializer.convert(eventStream)),
true
);
return EJokerFutureUtil.completeFuture();
}
private void publishDomainEventAsync(ProcessingCommand processingCommand, DomainEventStreamMessage eventStream) {
ioHelper.tryAsyncAction2(
"PublishEventAsync",
() -> domainEventPublisher.publishAsync(eventStream),
r -> {
logger.debug("Publish event success. [eventStream: {}]", eventStream.toString());
String commandHandleResult = processingCommand.getCommandExecuteContext().getResult();
CommandResult commandResult = new CommandResult(
CommandStatus.Success,
processingCommand.getMessage().getId(),
eventStream.getAggregateRootId(),
commandHandleResult,
String.class.getName());
finishCommandAsync(processingCommand, commandResult);
},
() -> StringUtilx.fmt("[eventStream: {}]", eventStream.toString()),
true
);
}
private Future finishCommandAsync(ProcessingCommand processingCommand, CommandResult commandResult) {
return processingCommand.getMailBox().finishMessage(processingCommand, commandResult);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy