|
|
@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil; |
|
|
|
import cn.hutool.core.collection.ConcurrentHashSet; |
|
|
|
import cn.hutool.core.collection.ConcurrentHashSet; |
|
|
|
import cn.hutool.core.util.StrUtil; |
|
|
|
import cn.hutool.core.util.StrUtil; |
|
|
|
import com.cloud.kicc.common.core.exception.CommonException; |
|
|
|
import com.cloud.kicc.common.core.exception.CommonException; |
|
|
|
|
|
|
|
import com.cloud.kicc.common.core.util.BaseUtil; |
|
|
|
import com.cloud.kicc.common.data.entity.KiccUser; |
|
|
|
import com.cloud.kicc.common.data.entity.KiccUser; |
|
|
|
import com.cloud.kicc.common.security.util.SecurityUtils; |
|
|
|
import com.cloud.kicc.common.security.util.SecurityUtils; |
|
|
|
import com.cloud.kicc.commonbiz.api.entity.SseSignalContainer; |
|
|
|
import com.cloud.kicc.commonbiz.api.entity.SseSignalContainer; |
|
|
@ -17,6 +18,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
|
|
|
|
import java.util.Iterator; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Collectors; |
|
|
@ -42,43 +44,41 @@ public class MapLogisticSseServiceImpl implements IMapLogisticSseService { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@XxlJob("doHeartbeat") |
|
|
|
@XxlJob("doHeartbeat") |
|
|
|
public void doHeartbeat() { |
|
|
|
public void doHeartbeat() { |
|
|
|
sseSignalContainers.forEach(item -> { |
|
|
|
doMaintenance(); |
|
|
|
try { |
|
|
|
|
|
|
|
item.getSseEmitter().send(SseEmitter.event().comment("保持心跳 " + LocalDateTime.now()).reconnectTime(1000)); |
|
|
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
|
|
log.debug("当前用户Id为:{}发送心跳包失败,正在删除当前的建立通道对象", item.getUserId()); |
|
|
|
|
|
|
|
sseSignalContainers.removeIf(sseSignalContainer -> StrUtil.equals(item.getUserId(), sseSignalContainer.getUserId()) && StrUtil.equals(item.getTenantId(), sseSignalContainer.getTenantId())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@SneakyThrows |
|
|
|
@SneakyThrows |
|
|
|
public SseEmitter SseSubscribe() { |
|
|
|
public SseEmitter SseSubscribe(String clientId) { |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
// 超时时间设置为20秒
|
|
|
|
// 设置超时时间为1小时
|
|
|
|
SseEmitter sseEmitter = new SseEmitter(20000L); |
|
|
|
SseEmitter sseEmitter = new SseEmitter(3600_000L); |
|
|
|
SseSignalContainer sseSignalContainer =new SseSignalContainer( |
|
|
|
if(sseSignalContainers.stream() |
|
|
|
kiccUser.getId(), |
|
|
|
.filter(item -> StrUtil.equals(item.getClientId(),clientId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())) |
|
|
|
sseEmitter, |
|
|
|
.collect(Collectors.toSet()).isEmpty()) { |
|
|
|
kiccUser.getTenantId() |
|
|
|
SseSignalContainer sseSignalContainer =new SseSignalContainer( |
|
|
|
); |
|
|
|
clientId, |
|
|
|
// 设置如果网络出错前端请求的重试时间为1s
|
|
|
|
kiccUser.getId(), |
|
|
|
sseEmitter.send(SseEmitter.event().reconnectTime(1000).data("创建通道连接成功")); |
|
|
|
sseEmitter, |
|
|
|
sseSignalContainers.add(sseSignalContainer); |
|
|
|
kiccUser.getTenantId() |
|
|
|
log.info("当前建立的用户Id为:{}", kiccUser.getId()); |
|
|
|
); |
|
|
|
sseEmitter.onTimeout(() -> { |
|
|
|
// 设置如果网络出错前端请求的重试时间为1s
|
|
|
|
log.info("当前用户Id为:{}的SSE长轮询已经超时,正在删除当前的建立通道对象", kiccUser.getId()); |
|
|
|
sseEmitter.send(SseEmitter.event().reconnectTime(1000).data("创建通道连接成功")); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getUserId(), kiccUser.getId()) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
sseSignalContainers.add(sseSignalContainer); |
|
|
|
}); |
|
|
|
log.info("clientId:{},建立的用户Id为:{}", clientId, kiccUser.getId()); |
|
|
|
sseEmitter.onCompletion(() -> { |
|
|
|
sseEmitter.onTimeout(() -> { |
|
|
|
log.info("当前用户Id为:{}的SSE长轮询已经返回响应关闭,正在删除当前的建立通道对象", kiccUser.getId()); |
|
|
|
log.info("clientId:{},用户Id为:{},的SSE长轮询已经超时,正在删除当前的建立通道对象", clientId, kiccUser.getId()); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getUserId(), kiccUser.getId()) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getClientId(), clientId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
}); |
|
|
|
}); |
|
|
|
sseEmitter.onError(e -> { |
|
|
|
sseEmitter.onCompletion(() -> { |
|
|
|
log.info("当前用户Id为:{}的SSE长轮询出现异常,正在删除当前的建立通道对象,错误信息{}", kiccUser.getId(), e.getLocalizedMessage()); |
|
|
|
log.info("clientId:{},用户Id为:{}的SSE长轮询已经返回响应关闭,正在删除当前的建立通道对象", clientId, kiccUser.getId()); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getUserId(), kiccUser.getId()) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getClientId(), clientId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
sseEmitter.onError(e -> { |
|
|
|
|
|
|
|
log.info("clientId:{},当前用户Id为:{}的SSE长轮询出现异常,正在删除当前的建立通道对象,错误信息{}", clientId, kiccUser.getId(), e.getLocalizedMessage()); |
|
|
|
|
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getClientId(), clientId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} else new CommonException("客户端id重复,请重新设置确保唯一"); |
|
|
|
return sseEmitter; |
|
|
|
return sseEmitter; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -87,57 +87,58 @@ public class MapLogisticSseServiceImpl implements IMapLogisticSseService { |
|
|
|
public void sendMessage(String userId, String json) { |
|
|
|
public void sendMessage(String userId, String json) { |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
.filter(item -> StrUtil.equals(item.getUserId(), userId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())).collect(Collectors.toSet()); |
|
|
|
.filter(item -> StrUtil.equals(item.getUserId(), userId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())) |
|
|
|
if (CollectionUtil.isNotEmpty(sendSseSignalContainers)) { |
|
|
|
.collect(Collectors.toSet()); |
|
|
|
SseSignalContainer sendSseSignalContainer = sendSseSignalContainers.stream().findFirst().get(); |
|
|
|
Iterator<SseSignalContainer> it = sendSseSignalContainers.iterator(); |
|
|
|
sendSseSignalContainer.getSseEmitter().send(json); |
|
|
|
while (it.hasNext()) { |
|
|
|
|
|
|
|
SseSignalContainer item = it.next(); |
|
|
|
|
|
|
|
item.getSseEmitter().send(json); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
|
|
|
|
@SneakyThrows |
|
|
|
public void sendTenantMessage(String json) { |
|
|
|
public void sendTenantMessage(String json) { |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
.filter(item -> StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())).collect(Collectors.toSet()); |
|
|
|
.filter(item -> StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())) |
|
|
|
sendSseSignalContainers.forEach(item -> { |
|
|
|
.collect(Collectors.toSet()); |
|
|
|
try { |
|
|
|
Iterator<SseSignalContainer> it = sendSseSignalContainers.iterator(); |
|
|
|
item.getSseEmitter().send(json); |
|
|
|
while (it.hasNext()) { |
|
|
|
} catch (IOException e) { |
|
|
|
SseSignalContainer item = it.next(); |
|
|
|
throw new RuntimeException(e); |
|
|
|
item.getSseEmitter().send(json); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void disconnect() { |
|
|
|
public void disconnect(String clientId) { |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
.filter(item -> StrUtil.equals(item.getUserId(), kiccUser.getId()) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())).collect(Collectors.toSet()); |
|
|
|
.filter(item -> StrUtil.equals(item.getClientId(), clientId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())) |
|
|
|
if (CollectionUtil.isNotEmpty(sendSseSignalContainers)) { |
|
|
|
.collect(Collectors.toSet()); |
|
|
|
SseSignalContainer sendSseSignalContainer = sendSseSignalContainers.stream().findFirst().get(); |
|
|
|
sendSseSignalContainers.forEach(item -> item.getSseEmitter().complete()); |
|
|
|
sendSseSignalContainer.getSseEmitter().complete(); |
|
|
|
sseSignalContainers.removeIf(sseSignalContainer -> StrUtil.equals(sseSignalContainer.getClientId(), clientId) && StrUtil.equals(sseSignalContainer.getTenantId(), kiccUser.getTenantId())); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getUserId(), kiccUser.getId()) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void disconnectTenant() { |
|
|
|
public void disconnectTenant() { |
|
|
|
KiccUser kiccUser = SecurityUtils.getUser(); |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
.filter(item -> StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())).collect(Collectors.toSet()); |
|
|
|
.filter(item -> StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())) |
|
|
|
|
|
|
|
.collect(Collectors.toSet()); |
|
|
|
sendSseSignalContainers.forEach(item -> item.getSseEmitter().complete()); |
|
|
|
sendSseSignalContainers.forEach(item -> item.getSseEmitter().complete()); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
/** |
|
|
|
public void remove(String userId) { |
|
|
|
* 执行心跳维护,避免 sse 膨胀容量问题 |
|
|
|
KiccUser kiccUser = getUser(); |
|
|
|
*/ |
|
|
|
Set<SseSignalContainer> sendSseSignalContainers = sseSignalContainers.stream() |
|
|
|
@SneakyThrows |
|
|
|
.filter(item -> StrUtil.equals(item.getUserId(), userId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())).collect(Collectors.toSet()); |
|
|
|
private void doMaintenance() { |
|
|
|
if (CollectionUtil.isNotEmpty(sendSseSignalContainers)) { |
|
|
|
Iterator<SseSignalContainer> it = sseSignalContainers.iterator(); |
|
|
|
SseSignalContainer sendSseSignalContainer = sendSseSignalContainers.stream().findFirst().get(); |
|
|
|
while (it.hasNext()) { |
|
|
|
sendSseSignalContainer.getSseEmitter().complete(); |
|
|
|
SseSignalContainer item = it.next(); |
|
|
|
sseSignalContainers.removeIf(item -> StrUtil.equals(item.getUserId(), userId) && StrUtil.equals(item.getTenantId(), kiccUser.getTenantId())); |
|
|
|
item.getSseEmitter().send(SseEmitter.event().comment("保持心跳" + LocalDateTime.now()).reconnectTime(1000)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|