|
@@ -2,30 +2,23 @@ package com.chelvc.framework.rocketmq.context;
|
|
|
|
|
|
import java.lang.reflect.Type;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Objects;
|
|
|
-import java.util.function.BiFunction;
|
|
|
import java.util.function.Consumer;
|
|
|
-import java.util.function.Function;
|
|
|
import java.util.function.Predicate;
|
|
|
-import java.util.stream.Collectors;
|
|
|
|
|
|
import com.chelvc.framework.base.context.ApplicationContextHolder;
|
|
|
import com.chelvc.framework.base.context.Session;
|
|
|
-import com.chelvc.framework.base.context.SessionContextHolder;
|
|
|
import com.chelvc.framework.common.util.JacksonUtils;
|
|
|
import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
import com.chelvc.framework.common.util.StringUtils;
|
|
|
+import com.chelvc.framework.rocketmq.producer.EmptySendReceipt;
|
|
|
+import com.chelvc.framework.rocketmq.producer.ProducerMessage;
|
|
|
import lombok.NonNull;
|
|
|
import org.apache.rocketmq.client.apis.ClientException;
|
|
|
-import org.apache.rocketmq.client.apis.ClientServiceProvider;
|
|
|
import org.apache.rocketmq.client.apis.message.Message;
|
|
|
-import org.apache.rocketmq.client.apis.message.MessageBuilder;
|
|
|
import org.apache.rocketmq.client.apis.message.MessageId;
|
|
|
import org.apache.rocketmq.client.apis.message.MessageView;
|
|
|
import org.apache.rocketmq.client.apis.producer.Producer;
|
|
|
+import org.apache.rocketmq.client.apis.producer.SendReceipt;
|
|
|
import org.apache.rocketmq.client.apis.producer.Transaction;
|
|
|
|
|
|
/**
|
|
@@ -35,21 +28,11 @@ import org.apache.rocketmq.client.apis.producer.Transaction;
|
|
|
* @date 2024/1/30
|
|
|
*/
|
|
|
public final class RocketMQContextHolder {
|
|
|
- /**
|
|
|
- * topic、tag分隔符
|
|
|
- */
|
|
|
- public static final char TOPIC_TAG_DELIMITER = ':';
|
|
|
-
|
|
|
/**
|
|
|
* 消息生产者
|
|
|
*/
|
|
|
private static volatile Producer PRODUCER;
|
|
|
|
|
|
- /**
|
|
|
- * 客户端服务提供者
|
|
|
- */
|
|
|
- private static volatile ClientServiceProvider SERVICE_PROVIDER;
|
|
|
-
|
|
|
private RocketMQContextHolder() {
|
|
|
}
|
|
|
|
|
@@ -69,96 +52,16 @@ public final class RocketMQContextHolder {
|
|
|
return PRODUCER;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取服务提供者
|
|
|
- *
|
|
|
- * @return 服务提供者实例
|
|
|
- */
|
|
|
- public static ClientServiceProvider getServiceProvider() {
|
|
|
- if (SERVICE_PROVIDER == null) {
|
|
|
- synchronized (ClientServiceProvider.class) {
|
|
|
- if (SERVICE_PROVIDER == null) {
|
|
|
- SERVICE_PROVIDER = ApplicationContextHolder.getBean(ClientServiceProvider.class);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return SERVICE_PROVIDER;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取消息主题
|
|
|
- *
|
|
|
- * @param message 消息实例
|
|
|
- * @return 消息主题
|
|
|
- */
|
|
|
- public static String topic(@NonNull Message message) {
|
|
|
- String namespace = ApplicationContextHolder.getProfile();
|
|
|
- String topic = message.getTopic(), tag = message.getTag().orElse(null);
|
|
|
- if (StringUtils.notEmpty(namespace) && StringUtils.notEmpty(topic) && topic.startsWith(namespace)
|
|
|
- && topic.length() > namespace.length() && topic.charAt(namespace.length()) == '-') {
|
|
|
- topic = topic.substring(namespace.length() + 1);
|
|
|
- }
|
|
|
- return StringUtils.isEmpty(tag) ? topic : (topic + TOPIC_TAG_DELIMITER + tag);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取消息主题
|
|
|
- *
|
|
|
- * @param message 消息实例
|
|
|
- * @return 消息主题
|
|
|
- */
|
|
|
- public static String topic(@NonNull MessageView message) {
|
|
|
- String namespace = ApplicationContextHolder.getProfile();
|
|
|
- String topic = message.getTopic(), tag = message.getTag().orElse(null);
|
|
|
- if (StringUtils.notEmpty(namespace) && StringUtils.notEmpty(topic) && topic.startsWith(namespace)
|
|
|
- && topic.length() > namespace.length() && topic.charAt(namespace.length()) == '-') {
|
|
|
- topic = topic.substring(namespace.length() + 1);
|
|
|
- }
|
|
|
- return StringUtils.isEmpty(tag) ? topic : (topic + TOPIC_TAG_DELIMITER + tag);
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取消息体内容
|
|
|
*
|
|
|
* @param message 消息实例
|
|
|
* @return 消息体内容
|
|
|
*/
|
|
|
- public static String body(@NonNull Message message) {
|
|
|
+ public static String getBody(@NonNull MessageView message) {
|
|
|
return StandardCharsets.UTF_8.decode(message.getBody()).toString();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取消息体内容
|
|
|
- *
|
|
|
- * @param message 消息实例
|
|
|
- * @return 消息体内容
|
|
|
- */
|
|
|
- public static String body(@NonNull MessageView message) {
|
|
|
- return StandardCharsets.UTF_8.decode(message.getBody()).toString();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 环境隔离
|
|
|
- *
|
|
|
- * @param original 原始标记
|
|
|
- * @return 环境隔离标记
|
|
|
- */
|
|
|
- public static String isolate(@NonNull String original) {
|
|
|
- String namespace = ApplicationContextHolder.getProfile();
|
|
|
- return StringUtils.isEmpty(namespace) ? original : (namespace + "-" + original);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取会话信息
|
|
|
- *
|
|
|
- * @param message 消息实例
|
|
|
- * @return 会话实例
|
|
|
- */
|
|
|
- public static Session getSession(@NonNull Message message) {
|
|
|
- String json = message.getProperties().get(Session.NAMING);
|
|
|
- return StringUtils.isEmpty(json) ? null : JacksonUtils.deserialize(json, Session.class);
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取会话信息
|
|
|
*
|
|
@@ -170,34 +73,6 @@ public final class RocketMQContextHolder {
|
|
|
return StringUtils.isEmpty(json) ? null : JacksonUtils.deserialize(json, Session.class);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 消息序列化
|
|
|
- *
|
|
|
- * @param topic 消息主题
|
|
|
- * @param payload 消息内容
|
|
|
- * @param ordering 顺序消息标识
|
|
|
- * @return 消息对象实例
|
|
|
- */
|
|
|
- public static Message serialize(@NonNull String topic, @NonNull Object payload, String ordering) {
|
|
|
- byte[] body = JacksonUtils.serialize(payload).getBytes(StandardCharsets.UTF_8);
|
|
|
- String session = JacksonUtils.serialize(SessionContextHolder.getSession(false));
|
|
|
- MessageBuilder builder = getServiceProvider().newMessageBuilder().setBody(body);
|
|
|
- int delimiter = topic.indexOf(TOPIC_TAG_DELIMITER);
|
|
|
- if (delimiter <= 0) {
|
|
|
- builder.setTopic(isolate(topic));
|
|
|
- } else {
|
|
|
- builder.setTopic(isolate(topic.substring(0, delimiter)));
|
|
|
- builder.setTag(topic.substring(delimiter + 1));
|
|
|
- }
|
|
|
- if (Objects.nonNull(session)) {
|
|
|
- builder.addProperty(Session.NAMING, session);
|
|
|
- }
|
|
|
- if (StringUtils.notEmpty(ordering)) {
|
|
|
- builder.setMessageGroup(ordering);
|
|
|
- }
|
|
|
- return builder.build();
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 消息反序列化
|
|
|
*
|
|
@@ -206,78 +81,31 @@ public final class RocketMQContextHolder {
|
|
|
* @return 消息体实例
|
|
|
*/
|
|
|
public static Object deserialize(@NonNull MessageView message, @NonNull Type type) {
|
|
|
- return JacksonUtils.deserialize(body(message), type);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 消息反序列化
|
|
|
- *
|
|
|
- * @param message 消息视图
|
|
|
- * @param function 消息处理函数
|
|
|
- * @param <T> 消息类型
|
|
|
- * @return 消息处理结果
|
|
|
- */
|
|
|
- public static <T> T deserialize(@NonNull MessageView message, @NonNull Function<MessageView, T> function) {
|
|
|
- SessionContextHolder.setSession(getSession(message));
|
|
|
- try {
|
|
|
- return function.apply(message);
|
|
|
- } finally {
|
|
|
- SessionContextHolder.removeSessionContext();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 消息反序列化
|
|
|
- *
|
|
|
- * @param message 消息视图
|
|
|
- * @param type 消息类型
|
|
|
- * @param function 消息处理函数
|
|
|
- * @param <T> 消息类型
|
|
|
- * @return 消息处理结果
|
|
|
- */
|
|
|
- public static <T> T deserialize(@NonNull MessageView message, @NonNull Type type,
|
|
|
- @NonNull Function<Object, T> function) {
|
|
|
- return deserialize(message, view -> function.apply(deserialize(message, type)));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 消息反序列化
|
|
|
- *
|
|
|
- * @param messages 消息视图集合
|
|
|
- * @param type 消息类型
|
|
|
- * @param function 消息处理函数
|
|
|
- * @param <T> 消息类型
|
|
|
- * @return 消息列表
|
|
|
- */
|
|
|
- public static <T> List<T> deserialize(@NonNull Collection<MessageView> messages, @NonNull Type type,
|
|
|
- @NonNull BiFunction<Object, Session, T> function) {
|
|
|
- if (ObjectUtils.isEmpty(messages)) {
|
|
|
- return Collections.emptyList();
|
|
|
- }
|
|
|
- return messages.stream().map(message -> function.apply(deserialize(message, type), getSession(message)))
|
|
|
- .filter(Objects::nonNull).collect(Collectors.toList());
|
|
|
+ return JacksonUtils.deserialize(getBody(message), type);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 发送消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
*/
|
|
|
- public static void send(@NonNull String topic, @NonNull Object payload) {
|
|
|
- send(topic, payload, (String) null);
|
|
|
+ public static void send(@NonNull String topic, String tag, @NonNull Object payload) {
|
|
|
+ send(topic, tag, payload, (String) null);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 发送消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
* @param ordering 顺序消息标识
|
|
|
*/
|
|
|
- public static void send(@NonNull String topic, @NonNull Object payload, String ordering) {
|
|
|
+ public static void send(@NonNull String topic, String tag, @NonNull Object payload, String ordering) {
|
|
|
Producer producer = getProducer();
|
|
|
- Message message = serialize(topic, payload, ordering);
|
|
|
+ Message message = new ProducerMessage(topic, tag, payload, ordering, null);
|
|
|
try {
|
|
|
producer.send(message);
|
|
|
} catch (ClientException e) {
|
|
@@ -289,38 +117,43 @@ public final class RocketMQContextHolder {
|
|
|
* 发送事务消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
* @param callback 本地事务回调函数
|
|
|
* @param <T> 消息类型
|
|
|
*/
|
|
|
- public static <T> void send(@NonNull String topic, @NonNull T payload, @NonNull Consumer<MessageId> callback) {
|
|
|
- send(topic, payload, null, callback);
|
|
|
+ public static <T> void send(@NonNull String topic, String tag, @NonNull T payload,
|
|
|
+ @NonNull Consumer<String> callback) {
|
|
|
+ send(topic, tag, payload, null, callback);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 发送事务消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
* @param callback 本地事务回调函数
|
|
|
* @param <T> 消息类型
|
|
|
*/
|
|
|
- public static <T> void send(@NonNull String topic, @NonNull T payload, @NonNull Predicate<MessageId> callback) {
|
|
|
- send(topic, payload, null, callback);
|
|
|
+ public static <T> void send(@NonNull String topic, String tag, @NonNull T payload,
|
|
|
+ @NonNull Predicate<String> callback) {
|
|
|
+ send(topic, tag, payload, null, callback);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 发送事务消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
* @param ordering 顺序消息标识
|
|
|
* @param callback 本地事务回调函数
|
|
|
* @param <T> 消息类型
|
|
|
*/
|
|
|
- public static <T> void send(@NonNull String topic, @NonNull T payload, String ordering,
|
|
|
- @NonNull Consumer<MessageId> callback) {
|
|
|
- send(topic, payload, ordering, id -> {
|
|
|
+ public static <T> void send(@NonNull String topic, String tag, @NonNull T payload, String ordering,
|
|
|
+ @NonNull Consumer<String> callback) {
|
|
|
+ send(topic, tag, payload, ordering, id -> {
|
|
|
callback.accept(id);
|
|
|
return true;
|
|
|
});
|
|
@@ -330,34 +163,38 @@ public final class RocketMQContextHolder {
|
|
|
* 发送事务消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
* @param ordering 顺序消息标识
|
|
|
* @param callback 本地事务回调函数
|
|
|
* @param <T> 消息类型
|
|
|
*/
|
|
|
- public static <T> void send(@NonNull String topic, @NonNull T payload, String ordering,
|
|
|
- @NonNull Predicate<MessageId> callback) {
|
|
|
+ public static <T> void send(@NonNull String topic, String tag, @NonNull T payload, String ordering,
|
|
|
+ @NonNull Predicate<String> callback) {
|
|
|
// 发送事务半消息
|
|
|
- MessageId id;
|
|
|
+ SendReceipt receipt;
|
|
|
Transaction transaction;
|
|
|
Producer producer = getProducer();
|
|
|
- Message message = serialize(topic, payload, ordering);
|
|
|
+ Message message = new ProducerMessage(topic, tag, payload, ordering, null);
|
|
|
try {
|
|
|
transaction = producer.beginTransaction();
|
|
|
- id = producer.send(message, transaction).getMessageId();
|
|
|
+ receipt = producer.send(message, transaction);
|
|
|
} catch (ClientException e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
|
|
|
// 处理本地事务并执行事务消息提交或回滚
|
|
|
- try {
|
|
|
- if (callback.test(id)) {
|
|
|
- transaction.commit();
|
|
|
- } else {
|
|
|
- transaction.rollback();
|
|
|
+ if (receipt != EmptySendReceipt.NONE) {
|
|
|
+ String id = ObjectUtils.ifNull(receipt.getMessageId(), MessageId::toString);
|
|
|
+ try {
|
|
|
+ if (callback.test(id)) {
|
|
|
+ transaction.commit();
|
|
|
+ } else {
|
|
|
+ transaction.rollback();
|
|
|
+ }
|
|
|
+ } catch (ClientException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
}
|
|
|
- } catch (ClientException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -365,21 +202,22 @@ public final class RocketMQContextHolder {
|
|
|
* 发送异步消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
*/
|
|
|
- public static void sendAsync(@NonNull String topic, @NonNull Object payload) {
|
|
|
- sendAsync(topic, payload, null);
|
|
|
+ public static void sendAsync(@NonNull String topic, String tag, @NonNull Object payload) {
|
|
|
+ sendAsync(topic, tag, payload, null);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 发送异步消息
|
|
|
*
|
|
|
* @param topic 消息主题
|
|
|
+ * @param tag 消息标签
|
|
|
* @param payload 消息内容
|
|
|
* @param ordering 顺序消息标识
|
|
|
*/
|
|
|
- public static void sendAsync(@NonNull String topic, @NonNull Object payload, String ordering) {
|
|
|
- Message message = serialize(topic, payload, ordering);
|
|
|
- getProducer().sendAsync(message);
|
|
|
+ public static void sendAsync(@NonNull String topic, String tag, @NonNull Object payload, String ordering) {
|
|
|
+ getProducer().sendAsync(new ProducerMessage(topic, tag, payload, ordering, null));
|
|
|
}
|
|
|
}
|