feat(REQ-2324): 调整 MQ 类路径, 新增事务回滚的回调钩子, 同时在发送成功的回调钩子中,新增返回 MQ 的 MessageID
This commit is contained in:
parent
a1fed917e7
commit
d0511ed4ed
@ -38,6 +38,11 @@ public abstract class AbstractEventProducer<Meta> implements EventProducer<Meta>
|
|||||||
this.defaultContext = defaultContext;
|
this.defaultContext = defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BiConsumer<Event, Context<RocketMQEventProducer.RocketMQMessageMeta>> getRollbackHandler() {
|
||||||
|
return ((event, context) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(@NonNull Event event, @NonNull Context context) {
|
public void send(@NonNull Event event, @NonNull Context context) {
|
||||||
// XXX:不要在send的时候修改event的值,有副作用。
|
// XXX:不要在send的时候修改event的值,有副作用。
|
||||||
@ -69,12 +74,20 @@ public abstract class AbstractEventProducer<Meta> implements EventProducer<Meta>
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Runnable rollbackRunnable = () -> {
|
||||||
|
try{
|
||||||
|
getRollbackHandler().accept(copiedEvent, copiedContext);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
};
|
||||||
if (copiedContext.isTransactional()) {
|
if (copiedContext.isTransactional()) {
|
||||||
// https://www.jianshu.com/p/59891ede5f90
|
// https://www.jianshu.com/p/59891ede5f90
|
||||||
runnable.run();
|
runnable.run();
|
||||||
} else {
|
} else {
|
||||||
// 并发会导致事件时序出现问题. 所以串行执行
|
// 并发会导致事件时序出现问题. 所以串行执行
|
||||||
afterCommitExecutor.execute(() -> runnable.run());
|
afterCommitExecutor.executeAndRollback(() -> runnable.run(), ()-> rollbackRunnable.run());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,58 +96,7 @@ public abstract class AbstractEventProducer<Meta> implements EventProducer<Meta>
|
|||||||
send(event, defaultContext);
|
send(event, defaultContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public AfterCommitExecutorImpl getAfterCommitExecutor() {
|
||||||
* stolen from http://azagorneanu.blogspot.jp/2013/06/transaction-synchronization-callbacks.html
|
return afterCommitExecutor;
|
||||||
* 保证在交易结束后被调用.
|
|
||||||
*/
|
|
||||||
private static class AfterCommitExecutorImpl extends TransactionSynchronizationAdapter {
|
|
||||||
private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal<List<Runnable>>();
|
|
||||||
|
|
||||||
public void execute(Runnable runnable) {
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("Submitting new runnable {} to run after commit", runnable);
|
|
||||||
}
|
|
||||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("Transaction synchronization is NOT ACTIVE. Executing right now runnable {}", runnable);
|
|
||||||
}
|
|
||||||
runnable.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<Runnable> threadRunnables = RUNNABLES.get();
|
|
||||||
if (threadRunnables == null) {
|
|
||||||
threadRunnables = new ArrayList<>();
|
|
||||||
RUNNABLES.set(threadRunnables);
|
|
||||||
TransactionSynchronizationManager.registerSynchronization(this);
|
|
||||||
}
|
|
||||||
threadRunnables.add(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
List<Runnable> threadRunnables = RUNNABLES.get();
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.info("Transaction successfully committed, executing {} runnables", threadRunnables.size());
|
|
||||||
}
|
|
||||||
for (int i = 0; i < threadRunnables.size(); i++) {
|
|
||||||
Runnable runnable = threadRunnables.get(i);
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("Executing runnable {}", runnable);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
runnable.run();
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
log.error("Failed to execute runnable " + runnable, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterCompletion(int status) {
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug("Transaction completed with status {}", status == TransactionSynchronization.STATUS_COMMITTED ? "COMMITTED" : "ROLLED_BACK");
|
|
||||||
}
|
|
||||||
RUNNABLES.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,90 @@
|
|||||||
|
package cn.axzo.framework.rocketmq;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronization;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List; /**
|
||||||
|
* stolen from http://azagorneanu.blogspot.jp/2013/06/transaction-synchronization-callbacks.html
|
||||||
|
* 保证在交易结束后被调用.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class AfterCommitExecutorImpl extends TransactionSynchronizationAdapter {
|
||||||
|
private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal<List<Runnable>>();
|
||||||
|
private static final ThreadLocal<List<Runnable>> ROLLBACK_RUNNABLES = new ThreadLocal<List<Runnable>>();
|
||||||
|
|
||||||
|
public void execute(Runnable runnable) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Submitting new runnable {} to run after commit", runnable);
|
||||||
|
}
|
||||||
|
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Transaction synchronization is NOT ACTIVE. Executing right now runnable {}", runnable);
|
||||||
|
}
|
||||||
|
runnable.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Runnable> threadRunnables = RUNNABLES.get();
|
||||||
|
if (threadRunnables == null) {
|
||||||
|
threadRunnables = new ArrayList<>();
|
||||||
|
RUNNABLES.set(threadRunnables);
|
||||||
|
TransactionSynchronizationManager.registerSynchronization(this);
|
||||||
|
}
|
||||||
|
threadRunnables.add(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeAndRollback(Runnable runnable, Runnable rollbackRunnable) {
|
||||||
|
execute(runnable);
|
||||||
|
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||||
|
List<Runnable> runnables = ROLLBACK_RUNNABLES.get();
|
||||||
|
if (runnables == null) {
|
||||||
|
runnables = new ArrayList<>();
|
||||||
|
ROLLBACK_RUNNABLES.set(runnables);
|
||||||
|
}
|
||||||
|
runnables.add(rollbackRunnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCommit() {
|
||||||
|
List<Runnable> threadRunnables = RUNNABLES.get();
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.info("Transaction successfully committed, executing {} runnables", threadRunnables.size());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < threadRunnables.size(); i++) {
|
||||||
|
Runnable runnable = threadRunnables.get(i);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Executing runnable {}", runnable);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("Failed to execute runnable " + runnable, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(int status) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Transaction completed with status {}", status == TransactionSynchronization.STATUS_COMMITTED ? "COMMITTED" : "ROLLED_BACK");
|
||||||
|
}
|
||||||
|
if(status != TransactionSynchronization.STATUS_COMMITTED) {
|
||||||
|
for (int i = 0; i < ROLLBACK_RUNNABLES.get().size(); i++) {
|
||||||
|
Runnable runnable = ROLLBACK_RUNNABLES.get().get(i);
|
||||||
|
try{
|
||||||
|
runnable.run();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("Failed to execute runnable at transaction ROLLBACK " + runnable, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RUNNABLES.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Runnable> getRunnables() {
|
||||||
|
return RUNNABLES.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,7 @@ public class RocketMQEventProducer extends AbstractEventProducer {
|
|||||||
* 一个扩展点.让外部可以在发送消息后做特殊逻辑处理. 比如发送消息统计, 消息元数据监控
|
* 一个扩展点.让外部可以在发送消息后做特殊逻辑处理. 比如发送消息统计, 消息元数据监控
|
||||||
*/
|
*/
|
||||||
private BiConsumer<Event, Context<RocketMQMessageMeta>> sendCallback;
|
private BiConsumer<Event, Context<RocketMQMessageMeta>> sendCallback;
|
||||||
|
public static final String MQ_MESSAGE_ID = "messageId";
|
||||||
|
|
||||||
public RocketMQEventProducer(RocketMQTemplate rocketMQTemplate,
|
public RocketMQEventProducer(RocketMQTemplate rocketMQTemplate,
|
||||||
String defaultModule,
|
String defaultModule,
|
||||||
@ -73,9 +74,10 @@ public class RocketMQEventProducer extends AbstractEventProducer {
|
|||||||
try {
|
try {
|
||||||
// 同步发送超时时间支持在properties中设置
|
// 同步发送超时时间支持在properties中设置
|
||||||
SendResult sendResult = rocketMQTemplate.syncSendOrderly(destination, messageBuilder.build(), getMessageShardingKey(event, context));
|
SendResult sendResult = rocketMQTemplate.syncSendOrderly(destination, messageBuilder.build(), getMessageShardingKey(event, context));
|
||||||
log.info("====MQ PRODUCER SYNC====, context={}, message = {}, queueId = {}",
|
log.info("====MQ PRODUCER SYNC====, context={}, message = {}, messageId = {} queueId = {}",
|
||||||
context, event.toPrettyJsonString(), sendResult.getMessageQueue().getQueueId());
|
context, event.toPrettyJsonString(), sendResult.getMsgId(), sendResult.getMessageQueue().getQueueId());
|
||||||
if (sendCallback != null) {
|
if (sendCallback != null) {
|
||||||
|
context.getHeaders().put(MQ_MESSAGE_ID, sendResult.getMsgId());
|
||||||
sendCallback.accept(event, context);
|
sendCallback.accept(event, context);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user