|
@@ -20,8 +20,12 @@ import com.nb.core.utils.ExceptionUtil;
|
|
|
import com.nb.web.api.feign.IDeviceClient;
|
|
import com.nb.web.api.feign.IDeviceClient;
|
|
|
import com.nb.web.api.utils.Items;
|
|
import com.nb.web.api.utils.Items;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+
|
|
|
|
|
+import javax.jms.JMSException;
|
|
|
import javax.jms.Message;
|
|
import javax.jms.Message;
|
|
|
import javax.jms.MessageListener;
|
|
import javax.jms.MessageListener;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
import java.util.concurrent.*;
|
|
import java.util.concurrent.*;
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
@@ -47,6 +51,12 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
private static final String MESSAGEID = "messageId";
|
|
private static final String MESSAGEID = "messageId";
|
|
|
private static final String STATUS = "status";
|
|
private static final String STATUS = "status";
|
|
|
|
|
|
|
|
|
|
+ // 批量提交日志的定时任务间隔(毫秒)
|
|
|
|
|
+ private static final long BATCH_COMMIT_INTERVAL = 5000; // 5秒
|
|
|
|
|
+
|
|
|
|
|
+ // 批量提交日志的最大数量
|
|
|
|
|
+ private static final int BATCH_MAX_SIZE = 100;
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
private IDeviceClient deviceService;
|
|
private IDeviceClient deviceService;
|
|
@@ -55,12 +65,47 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
|
|
|
|
|
|
|
|
|
|
private IHospitalLogClient hospitalLogService;
|
|
private IHospitalLogClient hospitalLogService;
|
|
|
|
|
+
|
|
|
|
|
+ // 日志批量存储队列
|
|
|
|
|
+ private final BlockingQueue<BusHospitalLogEntity> logQueue = new LinkedBlockingQueue<>(10000);
|
|
|
|
|
+
|
|
|
|
|
+ // 批量提交日志的调度器
|
|
|
|
|
+ private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
|
|
|
|
|
|
|
public NBAndFourGConsumerGroupService(AliIotSubscribeClient client,AliIotConsumerPojo consumer) {
|
|
public NBAndFourGConsumerGroupService(AliIotSubscribeClient client,AliIotConsumerPojo consumer) {
|
|
|
super(client,consumer);
|
|
super(client,consumer);
|
|
|
this.deviceService= SpringUtil.getBean(IDeviceClient.class);
|
|
this.deviceService= SpringUtil.getBean(IDeviceClient.class);
|
|
|
this.iotMsgHandler= SpringUtil.getBean(IIotMsgHandler.class);
|
|
this.iotMsgHandler= SpringUtil.getBean(IIotMsgHandler.class);
|
|
|
this.hospitalLogService= SpringUtil.getBean(IHospitalLogClient.class);
|
|
this.hospitalLogService= SpringUtil.getBean(IHospitalLogClient.class);
|
|
|
|
|
+
|
|
|
|
|
+ // 启动定时批量提交任务
|
|
|
|
|
+ startBatchCommitTask();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 启动定时批量提交日志任务
|
|
|
|
|
+ */
|
|
|
|
|
+ private void startBatchCommitTask() {
|
|
|
|
|
+ scheduledExecutorService.scheduleWithFixedDelay(this::batchCommitLogs,
|
|
|
|
|
+ BATCH_COMMIT_INTERVAL, BATCH_COMMIT_INTERVAL, TimeUnit.MILLISECONDS);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 批量提交日志到数据库
|
|
|
|
|
+ */
|
|
|
|
|
+ private void batchCommitLogs() {
|
|
|
|
|
+ List<BusHospitalLogEntity> logsToCommit = new ArrayList<>();
|
|
|
|
|
+ int drainedCount = logQueue.drainTo(logsToCommit, BATCH_MAX_SIZE);
|
|
|
|
|
+
|
|
|
|
|
+ if (drainedCount > 0) {
|
|
|
|
|
+ log.info("开始批量提交 {} 条日志数据", drainedCount);
|
|
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
|
|
+ hospitalLogService.batchSave(logsToCommit);
|
|
|
|
|
+ // 由于 IHospitalLogClient 只提供了单条保存接口,所以仍然需要逐条保存
|
|
|
|
|
+ // 在实际生产环境中,建议提供批量保存接口以进一步优化性能
|
|
|
|
|
+ long endTime = System.currentTimeMillis();
|
|
|
|
|
+ log.info("完成批量提交 {} 条日志数据,耗时 {} ms", drainedCount, (endTime - startTime));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -81,7 +126,7 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
*/
|
|
*/
|
|
|
private final static ExecutorService executorService = new ThreadPoolExecutor(
|
|
private final static ExecutorService executorService = new ThreadPoolExecutor(
|
|
|
Runtime.getRuntime().availableProcessors(),
|
|
Runtime.getRuntime().availableProcessors(),
|
|
|
- Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS,
|
|
|
|
|
|
|
+ Runtime.getRuntime().availableProcessors(), 60, TimeUnit.SECONDS,
|
|
|
new LinkedBlockingQueue(50000),
|
|
new LinkedBlockingQueue(50000),
|
|
|
new AliYunThreadFactory());
|
|
new AliYunThreadFactory());
|
|
|
|
|
|
|
@@ -117,12 +162,20 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
private MessageListener messageListener = (message) -> {
|
|
private MessageListener messageListener = (message) -> {
|
|
|
try {
|
|
try {
|
|
|
//1.收到消息之后一定要ACK。
|
|
//1.收到消息之后一定要ACK。
|
|
|
- // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
|
|
|
|
|
- // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
|
|
|
|
|
- // message.acknowledge();
|
|
|
|
|
- //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
|
|
|
|
|
- // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
|
|
|
|
|
- executorService.submit(()-> processMessage(message));
|
|
|
|
|
|
|
+ ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
|
|
|
|
|
+ int queueSize = threadPoolExecutor.getQueue().size();
|
|
|
|
|
+ log.info("当前消息队列中待处理的任务数量: {}", queueSize);
|
|
|
|
|
+ executorService.submit(()-> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ processMessage(message);
|
|
|
|
|
+ }finally {
|
|
|
|
|
+ try {
|
|
|
|
|
+ message.acknowledge();
|
|
|
|
|
+ } catch (JMSException e) {
|
|
|
|
|
+ throw new RuntimeException(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("{},submit task occurs exception ", this.getConsumer().getName(),e);
|
|
log.error("{},submit task occurs exception ", this.getConsumer().getName(),e);
|
|
|
}
|
|
}
|
|
@@ -176,7 +229,6 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
// 创建设备
|
|
// 创建设备
|
|
|
BusDeviceEntity device = new BusDeviceEntity();
|
|
BusDeviceEntity device = new BusDeviceEntity();
|
|
|
device.setDeviceId(deviceName);
|
|
device.setDeviceId(deviceName);
|
|
|
-
|
|
|
|
|
// 配置信息
|
|
// 配置信息
|
|
|
AliIotConfig config = new AliIotConfig();
|
|
AliIotConfig config = new AliIotConfig();
|
|
|
config.setDeviceName(deviceName);
|
|
config.setDeviceName(deviceName);
|
|
@@ -197,7 +249,7 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
}
|
|
}
|
|
|
hospitalLog.setSuccess(true);
|
|
hospitalLog.setSuccess(true);
|
|
|
} catch (CustomException c){
|
|
} catch (CustomException c){
|
|
|
-
|
|
|
|
|
|
|
+ c.printStackTrace();
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
hospitalLog.setSuccess(false);
|
|
hospitalLog.setSuccess(false);
|
|
|
hospitalLog.setMessage(ExceptionUtil.getExceptionMsg(e));
|
|
hospitalLog.setMessage(ExceptionUtil.getExceptionMsg(e));
|
|
@@ -213,9 +265,17 @@ public class NBAndFourGConsumerGroupService extends AbstractAliConsumer {
|
|
|
if(CharSequenceUtil.isEmpty(hospitalLog.getTenantId())){
|
|
if(CharSequenceUtil.isEmpty(hospitalLog.getTenantId())){
|
|
|
log.warn("日志【{}】医院为空,进行自动填充",JSONUtil.toJsonStr(hospitalLog));
|
|
log.warn("日志【{}】医院为空,进行自动填充",JSONUtil.toJsonStr(hospitalLog));
|
|
|
}
|
|
}
|
|
|
- hospitalLogService.save(hospitalLog);
|
|
|
|
|
|
|
+ // 改为异步批量提交日志
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!logQueue.offer(hospitalLog, 100, TimeUnit.MILLISECONDS)) {
|
|
|
|
|
+ log.warn("日志队列已满,丢弃日志: {}", hospitalLog);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ log.error("添加日志到队列被中断: {}", hospitalLog, e);
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-}
|
|
|
|
|
|
|
+}
|