From 9540050f633b8d77b7d6cc001c516c7fc86a9de7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=86=B7=E5=86=B7?= <2270033969@qq.com>
Date: Thu, 22 Aug 2024 09:42:13 +0800
Subject: [PATCH] =?UTF-8?q?refactor(sequence):=20=E9=87=8D=E6=9E=84sequenc?=
=?UTF-8?q?e=20=E4=BD=BF=E7=94=A8anyline=20=E5=AE=9E=E7=8E=B0=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=88=9B=E8=A1=A8=E6=8A=BD=E8=B1=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pigx-common/pigx-common-sse/pom.xml | 33 +++++++++
.../LocalMessageDistributorConfiguration.java | 30 ++++++++
.../MessageDistributorTypeConstants.java | 26 +++++++
.../RedisMessageDistributorConfiguration.java | 70 +++++++++++++++++++
.../config/SseEmitterAutoConfiguration.java | 21 ++++++
.../common/sse/config/SseEmitterEndpoint.java | 48 +++++++++++++
.../sse/config/SseEmitterMessageSender.java | 54 ++++++++++++++
.../sse/config/SseEmitterProperties.java | 28 ++++++++
.../distribute/LocalMessageDistributor.java | 20 ++++++
.../pigx/common/sse/distribute/MessageDO.java | 39 +++++++++++
.../sse/distribute/MessageDistributor.java | 17 +++++
.../common/sse/distribute/MessageSender.java | 34 +++++++++
.../distribute/RedisMessageDistributor.java | 32 +++++++++
.../RedisSseEmitterMessageListener.java | 41 +++++++++++
.../common/sse/holder/SseEmitterHolder.java | 65 +++++++++++++++++
...ot.autoconfigure.AutoConfiguration.imports | 3 +
16 files changed, 561 insertions(+)
create mode 100755 pigx-common/pigx-common-sse/pom.xml
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/LocalMessageDistributorConfiguration.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/MessageDistributorTypeConstants.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/RedisMessageDistributorConfiguration.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterAutoConfiguration.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterEndpoint.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterMessageSender.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterProperties.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/LocalMessageDistributor.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/MessageDO.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/MessageDistributor.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/MessageSender.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/RedisMessageDistributor.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/RedisSseEmitterMessageListener.java
create mode 100644 pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/holder/SseEmitterHolder.java
create mode 100644 pigx-common/pigx-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
diff --git a/pigx-common/pigx-common-sse/pom.xml b/pigx-common/pigx-common-sse/pom.xml
new file mode 100755
index 000000000..27feeefd3
--- /dev/null
+++ b/pigx-common/pigx-common-sse/pom.xml
@@ -0,0 +1,33 @@
+
+
+ 4.0.0
+
+ com.pig4cloud
+ pigx-common
+ 5.5.0
+
+
+ pigx-common-sse
+ jar
+
+ pigx sse 服务端推送
+
+
+
+ org.springframework.data
+ spring-data-redis
+
+
+
+ cn.hutool
+ hutool-json
+
+
+
+ com.pig4cloud
+ pigx-common-security
+
+
+
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/LocalMessageDistributorConfiguration.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/LocalMessageDistributorConfiguration.java
new file mode 100644
index 000000000..0d30ad835
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/LocalMessageDistributorConfiguration.java
@@ -0,0 +1,30 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+import com.pig4cloud.pigx.common.sse.distribute.LocalMessageDistributor;
+import com.pig4cloud.pigx.common.sse.distribute.MessageDistributor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 本地的消息分发器配置
+ *
+ * @author lengleng
+ */
+@ConditionalOnProperty(prefix = SseEmitterProperties.PREFIX, name = "message-distributor",
+ havingValue = MessageDistributorTypeConstants.LOCAL)
+@Configuration(proxyBeanMethods = false)
+public class LocalMessageDistributorConfiguration {
+
+ /**
+ * 本地基于内存的消息分发,不支持集群
+ * @return LocalMessageDistributor
+ */
+ @Bean
+ @ConditionalOnMissingBean(MessageDistributor.class)
+ public LocalMessageDistributor messageDistributor() {
+ return new LocalMessageDistributor();
+ }
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/MessageDistributorTypeConstants.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/MessageDistributorTypeConstants.java
new file mode 100644
index 000000000..6ae8cee0b
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/MessageDistributorTypeConstants.java
@@ -0,0 +1,26 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+/**
+ * @author lengleng
+ */
+public final class MessageDistributorTypeConstants {
+
+ private MessageDistributorTypeConstants() {
+ }
+
+ /**
+ * 本地
+ */
+ public static final String LOCAL = "local";
+
+ /**
+ * 基于 Redis PUB/SUB
+ */
+ public static final String REDIS = "redis";
+
+ /**
+ * 自定义
+ */
+ public static final String CUSTOM = "custom";
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/RedisMessageDistributorConfiguration.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/RedisMessageDistributorConfiguration.java
new file mode 100644
index 000000000..83ac005f1
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/RedisMessageDistributorConfiguration.java
@@ -0,0 +1,70 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+import com.pig4cloud.pigx.common.sse.distribute.MessageDistributor;
+import com.pig4cloud.pigx.common.sse.distribute.RedisMessageDistributor;
+import com.pig4cloud.pigx.common.sse.distribute.RedisSseEmitterMessageListener;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.listener.PatternTopic;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 基于 Redis Pub/Sub 的消息分发器
+ *
+ * @author lengleng
+ */
+@ConditionalOnClass(StringRedisTemplate.class)
+@ConditionalOnProperty(prefix = SseEmitterProperties.PREFIX, name = "message-distributor",
+ havingValue = MessageDistributorTypeConstants.REDIS, matchIfMissing = true)
+@Configuration(proxyBeanMethods = false)
+public class RedisMessageDistributorConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(MessageDistributor.class)
+ public RedisMessageDistributor messageDistributor(StringRedisTemplate stringRedisTemplate) {
+ return new RedisMessageDistributor(stringRedisTemplate);
+ }
+
+ @Bean
+ @ConditionalOnBean(RedisMessageDistributor.class)
+ @ConditionalOnMissingBean
+ public RedisSseEmitterMessageListener redisWebsocketMessageListener(StringRedisTemplate stringRedisTemplate) {
+ return new RedisSseEmitterMessageListener(stringRedisTemplate);
+ }
+
+ @Bean
+ @ConditionalOnBean(RedisMessageDistributor.class)
+ @ConditionalOnMissingBean
+ public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
+ RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+ container.setConnectionFactory(connectionFactory);
+ return container;
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnMissingBean(MessageDistributor.class)
+ @RequiredArgsConstructor
+ static class RedisMessageListenerRegisterConfiguration {
+
+ private final RedisMessageListenerContainer redisMessageListenerContainer;
+
+ private final RedisSseEmitterMessageListener redisWebsocketMessageListener;
+
+ @PostConstruct
+ public void addMessageListener() {
+ redisMessageListenerContainer.addMessageListener(redisWebsocketMessageListener,
+ new PatternTopic(RedisSseEmitterMessageListener.CHANNEL));
+ }
+
+ }
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterAutoConfiguration.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterAutoConfiguration.java
new file mode 100644
index 000000000..c2fa9cdec
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterAutoConfiguration.java
@@ -0,0 +1,21 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * websocket自动配置
+ *
+ * @author Yakir
+ */
+@EnableConfigurationProperties(SseEmitterProperties.class)
+public class SseEmitterAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public SseEmitterEndpoint sseEmitterEndpoint() {
+ return new SseEmitterEndpoint();
+ }
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterEndpoint.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterEndpoint.java
new file mode 100644
index 000000000..397a81f1f
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterEndpoint.java
@@ -0,0 +1,48 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+import com.pig4cloud.pigx.common.security.util.SecurityUtils;
+import com.pig4cloud.pigx.common.sse.holder.SseEmitterHolder;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.Objects;
+
+/**
+ * @author lengleng
+ * @date 2023/12/11
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/sse")
+public class SseEmitterEndpoint {
+
+ @SneakyThrows
+ @RequestMapping("/info")
+ public SseEmitter info() {
+ SseEmitter sseEmitter = new SseEmitter(0L);
+
+ if (Objects.isNull(SecurityUtils.getUser())) {
+ log.debug("MSG: SseConnectError");
+ return null;
+ }
+ log.info("MSG: SseConnect | EmitterHash: {} | ID: {}", sseEmitter.hashCode(), SecurityUtils.getUser().getId());
+ sseEmitter.onCompletion(() -> {
+ log.info("MSG: SseConnectCompletion | EmitterHash: {} |ID: {}", sseEmitter.hashCode(), SecurityUtils.getUser().getId());
+ });
+ sseEmitter.onTimeout(() -> {
+ log.error("MSG: SseConnectTimeout | EmitterHash: {} |ID: {} ", sseEmitter.hashCode(), SecurityUtils.getUser().getId());
+ });
+ sseEmitter.onError(t -> {
+ log.error("MSG: SseConnectError | EmitterHash: {} |ID: {}", sseEmitter.hashCode(), SecurityUtils.getUser().getId());
+ });
+ SseEmitterHolder.addSseEmitter(SecurityUtils.getUser().getId(), sseEmitter);
+ sseEmitter.send("pong");
+ return sseEmitter;
+ }
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterMessageSender.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterMessageSender.java
new file mode 100644
index 000000000..cee119b5d
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterMessageSender.java
@@ -0,0 +1,54 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+import cn.hutool.core.util.ReflectUtil;
+import com.pig4cloud.pigx.common.sse.holder.SseEmitterHolder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * @author lengleng 2021/1/4
+ * @version 1.0
+ */
+@Slf4j
+public class SseEmitterMessageSender {
+
+ public static void broadcast(String message) {
+ Collection sseEmitters = SseEmitterHolder.getSseEmitters();
+ for (SseEmitter sseEmitter : sseEmitters) {
+ send(sseEmitter, message);
+ }
+ }
+
+ public static boolean send(Object sseEmitterKey, String message) {
+ SseEmitter sseEmitter = SseEmitterHolder.getSseEmitter(sseEmitterKey);
+ if (sseEmitter == null) {
+ log.info("[send] 当前 sseEmitterKey:{} 对应 sseEmitter 不在本服务中", sseEmitterKey);
+ return false;
+ }
+ else {
+ return send(sseEmitter, message);
+ }
+ }
+
+ public static boolean send(SseEmitter sseEmitter, String message) {
+ if (sseEmitter == null) {
+ log.error("[send] sseEmitter 为 null");
+ return false;
+ }
+
+
+ try {
+ sseEmitter.send(message);
+ }
+ catch (IOException e) {
+ log.error("[send] sseEmitter({}) 发送消息({}) 异常", sseEmitter, message, e);
+ SseEmitterHolder.removeSseEmitter(sseEmitter);
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterProperties.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterProperties.java
new file mode 100644
index 000000000..238969416
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/config/SseEmitterProperties.java
@@ -0,0 +1,28 @@
+package com.pig4cloud.pigx.common.sse.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * websocket props
+ *
+ * @author Yakir
+ */
+@Data
+@ConfigurationProperties(SseEmitterProperties.PREFIX)
+public class SseEmitterProperties {
+
+ public static final String PREFIX = "pigx.sse";
+
+ /**
+ * 路径: 无参: /ws 有参: PathVariable: 单参: /ws/{test} 多参: /ws/{test1}/{test2} query:
+ * /ws?uid=1&name=test
+ */
+ private String path = "/sse/info";
+
+ /**
+ * 消息分发器:local | redis,默认 local, 如果自定义的话,可以配置为其他任意值
+ */
+ private String messageDistributor = MessageDistributorTypeConstants.LOCAL;
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/LocalMessageDistributor.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/LocalMessageDistributor.java
new file mode 100644
index 000000000..9a7d92867
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/LocalMessageDistributor.java
@@ -0,0 +1,20 @@
+package com.pig4cloud.pigx.common.sse.distribute;
+
+/**
+ * 本地消息分发,直接进行发送
+ *
+ * @author lengleng 2021/1/12
+ * @version 1.0
+ */
+public class LocalMessageDistributor implements MessageDistributor, MessageSender {
+
+ /**
+ * 消息分发
+ * @param messageDO 发送的消息
+ */
+ @Override
+ public void distribute(MessageDO messageDO) {
+ doSend(messageDO);
+ }
+
+}
diff --git a/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/MessageDO.java b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/MessageDO.java
new file mode 100644
index 000000000..e1fd87688
--- /dev/null
+++ b/pigx-common/pigx-common-sse/src/main/java/com/pig4cloud/pigx/common/sse/distribute/MessageDO.java
@@ -0,0 +1,39 @@
+package com.pig4cloud.pigx.common.sse.distribute;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * @author lengleng 2021/1/12
+ * @version 1.0
+ */
+@Data
+@Accessors(chain = true)
+public class MessageDO {
+
+ /**
+ * 是否广播
+ */
+ private Boolean needBroadcast;
+
+ /**
+ * sessionKeys
+ */
+ private List