优化国标设备点播的信息的Redis缓存策略

pull/1669/head
648540858 2024-10-25 14:46:51 +08:00
parent 193f80963d
commit 34cc8251e2
5 changed files with 122 additions and 88 deletions

View File

@ -31,6 +31,8 @@ public class InviteInfo {
private String mediaServerId; private String mediaServerId;
private Long expirationTime;
public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId, public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId,
String receiveIp, Integer receivePort, String streamMode, String receiveIp, Integer receivePort, String streamMode,

View File

@ -39,7 +39,7 @@ public interface IInviteStreamService {
*/ */
void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, Integer channelId); void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, Integer channelId);
List<InviteInfo> getAllInviteInfo(InviteSessionType type, Integer channelId, String stream); List<InviteInfo> getAllInviteInfo();
/** /**
* *

View File

@ -10,11 +10,12 @@ import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper;
import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService;
import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent;
import com.genersoft.iot.vmp.service.bean.ErrorCallback; import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -23,7 +24,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
@Slf4j @Slf4j
@Service @Service
@ -61,11 +61,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
} }
} }
} }
@Override @Override
public void updateInviteInfo(InviteInfo inviteInfo) { public void updateInviteInfo(InviteInfo inviteInfo) {
if (InviteSessionStatus.ready == inviteInfo.getStatus()) { if (InviteSessionStatus.ready == inviteInfo.getStatus()) {
updateInviteInfo(inviteInfo, Long.valueOf(userSetting.getPlayTimeout()) * 2); updateInviteInfo(inviteInfo, Long.valueOf(userSetting.getPlayTimeout()) * 2);
}else { } else {
updateInviteInfo(inviteInfo, null); updateInviteInfo(inviteInfo, null);
} }
} }
@ -114,16 +115,15 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
inviteInfoForUpdate = inviteInfoInRedis; inviteInfoForUpdate = inviteInfoInRedis;
} }
String key = VideoManagerConstants.INVITE_PREFIX + String key = VideoManagerConstants.INVITE_PREFIX;
":" + inviteInfoForUpdate.getType() + String objectKey = inviteInfoForUpdate.getType() +
":" + inviteInfoForUpdate.getChannelId() + ":" + inviteInfoForUpdate.getChannelId() +
":" + inviteInfoForUpdate.getStream()+ ":" + inviteInfoForUpdate.getStream() +
":" + inviteInfoForUpdate.getSsrcInfo().getSsrc(); ":" + inviteInfoForUpdate.getSsrcInfo().getSsrc();
if (time != null && time > 0) { if (time != null && time > 0) {
redisTemplate.opsForValue().set(key, inviteInfoForUpdate, time, TimeUnit.SECONDS); inviteInfoForUpdate.setExpirationTime(time);
}else {
redisTemplate.opsForValue().set(key, inviteInfoForUpdate);
} }
redisTemplate.opsForHash().put(key, objectKey, inviteInfoForUpdate);
} }
@Override @Override
@ -134,8 +134,8 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
return null; return null;
} }
removeInviteInfo(inviteInfoInDb); removeInviteInfo(inviteInfoInDb);
String key = VideoManagerConstants.INVITE_PREFIX + String key = VideoManagerConstants.INVITE_PREFIX;
":" + inviteInfo.getType() + String objectKey = inviteInfo.getType() +
":" + inviteInfo.getChannelId() + ":" + inviteInfo.getChannelId() +
":" + stream + ":" + stream +
":" + inviteInfo.getSsrcInfo().getSsrc(); ":" + inviteInfo.getSsrcInfo().getSsrc();
@ -144,46 +144,43 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
inviteInfoInDb.getSsrcInfo().setStream(stream); inviteInfoInDb.getSsrcInfo().setStream(stream);
} }
if (InviteSessionStatus.ready == inviteInfo.getStatus()) { if (InviteSessionStatus.ready == inviteInfo.getStatus()) {
redisTemplate.opsForValue().set(key, inviteInfoInDb, userSetting.getPlayTimeout() * 2, TimeUnit.SECONDS); inviteInfoInDb.setExpirationTime((long) (userSetting.getPlayTimeout() * 2));
}else {
redisTemplate.opsForValue().set(key, inviteInfoInDb);
} }
redisTemplate.opsForHash().put(key, objectKey, inviteInfoInDb);
return inviteInfoInDb; return inviteInfoInDb;
} }
@Override @Override
public InviteInfo getInviteInfo(InviteSessionType type, Integer channelId, String stream) { public InviteInfo getInviteInfo(InviteSessionType type, Integer channelId, String stream) {
String key = VideoManagerConstants.INVITE_PREFIX + String key = VideoManagerConstants.INVITE_PREFIX;
":" + (type != null ? type : "*") + String keyPattern = (type != null ? type : "*") +
":" + (channelId != null ? channelId : "*") + ":" + (channelId != null ? channelId : "*") +
":" + (stream != null ? stream : "*") ":" + (stream != null ? stream : "*")
+ ":*"; + ":*";
List<Object> scanResult = RedisUtil.scan(redisTemplate, key); ScanOptions options = ScanOptions.scanOptions().match(keyPattern).count(20).build();
if (scanResult.isEmpty()) { try (Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, options)) {
return null; if (cursor.hasNext()) {
} InviteInfo inviteInfo = (InviteInfo) cursor.next().getValue();
if (scanResult.size() != 1) { cursor.close();
log.warn("[获取InviteInfo] 发现 key: {}存在多条", key); return inviteInfo;
}
return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); }
} catch (Exception e) {
log.error("[Redis-InviteInfo] 查询异常: ", e);
}
return null;
} }
@Override @Override
public List<InviteInfo> getAllInviteInfo(InviteSessionType type, Integer channelId, String stream) { public List<InviteInfo> getAllInviteInfo() {
String key = VideoManagerConstants.INVITE_PREFIX +
":" + (type != null ? type : "*") +
":" + (channelId != null ? channelId : "*") +
":" + (stream != null ? stream : "*")
+ ":*";
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
if (scanResult.isEmpty()) {
return new ArrayList<>();
}
List<InviteInfo> result = new ArrayList<>(); List<InviteInfo> result = new ArrayList<>();
for (Object keyObj : scanResult) { String key = VideoManagerConstants.INVITE_PREFIX;
result.add((InviteInfo) redisTemplate.opsForValue().get(keyObj)); List<Object> values = redisTemplate.opsForHash().values(key);
if(values.isEmpty()) {
return result;
}
for (Object value : values) {
result.add((InviteInfo)value);
} }
return result; return result;
} }
@ -200,22 +197,21 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
@Override @Override
public void removeInviteInfo(InviteSessionType type, Integer channelId, String stream) { public void removeInviteInfo(InviteSessionType type, Integer channelId, String stream) {
String scanKey = VideoManagerConstants.INVITE_PREFIX + String key = VideoManagerConstants.INVITE_PREFIX;
":" + (type != null ? type : "*") + if (type == null && channelId == null && stream == null) {
":" + (channelId != null ? channelId : "*") + redisTemplate.opsForHash().delete(key);
":" + (stream != null ? stream : "*") + return;
":*";
List<Object> scanResult = RedisUtil.scan(redisTemplate, scanKey);
if (!scanResult.isEmpty()) {
for (Object keyObj : scanResult) {
String key = (String) keyObj;
InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key);
if (inviteInfo == null) {
continue;
} }
redisTemplate.delete(key); InviteInfo inviteInfo = getInviteInfo(type, channelId, stream);
inviteErrorCallbackMap.remove(buildKey(type,channelId, inviteInfo.getStream())); if (inviteInfo != null) {
String objectKey = inviteInfo.getType() +
":" + inviteInfo.getChannelId() +
":" + stream +
":" + inviteInfo.getSsrcInfo().getSsrc();
redisTemplate.opsForHash().delete(key, objectKey);
} }
if (redisTemplate.opsForHash().size(key) == 0) {
redisTemplate.opsForHash().delete(key);
} }
} }
@ -249,7 +245,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
@Override @Override
public void clearInviteInfo(String deviceId) { public void clearInviteInfo(String deviceId) {
List<InviteInfo> inviteInfoList = getAllInviteInfo(null, null, null); List<InviteInfo> inviteInfoList = getAllInviteInfo();
for (InviteInfo inviteInfo : inviteInfoList) { for (InviteInfo inviteInfo : inviteInfoList) {
if (inviteInfo.getDeviceId().equals(deviceId)) { if (inviteInfo.getDeviceId().equals(deviceId)) {
removeInviteInfo(inviteInfo); removeInviteInfo(inviteInfo);
@ -260,14 +256,13 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
@Override @Override
public int getStreamInfoCount(String mediaServerId) { public int getStreamInfoCount(String mediaServerId) {
int count = 0; int count = 0;
String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*"; String key = VideoManagerConstants.INVITE_PREFIX;
List<Object> scanResult = RedisUtil.scan(redisTemplate, key); List<Object> values = redisTemplate.opsForHash().values(key);
if (scanResult.isEmpty()) { if (values.isEmpty()) {
return 0; return count;
}else { }
for (Object keyObj : scanResult) { for (Object value : values) {
String keyStr = (String) keyObj; InviteInfo inviteInfo = (InviteInfo)value;
InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(keyStr);
if (inviteInfo != null if (inviteInfo != null
&& inviteInfo.getStreamInfo() != null && inviteInfo.getStreamInfo() != null
&& inviteInfo.getStreamInfo().getMediaServer() != null && inviteInfo.getStreamInfo().getMediaServer() != null
@ -278,7 +273,6 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
count++; count++;
} }
} }
}
return count; return count;
} }
@ -309,13 +303,16 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
@Override @Override
public InviteInfo getInviteInfoBySSRC(String ssrc) { public InviteInfo getInviteInfoBySSRC(String ssrc) {
String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:" + ssrc; List<InviteInfo> inviteInfoList = getAllInviteInfo();
List<Object> scanResult = RedisUtil.scan(redisTemplate, key); if (inviteInfoList.isEmpty()) {
if (scanResult.size() != 1) {
return null; return null;
} }
for (InviteInfo inviteInfo : inviteInfoList) {
return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); if (inviteInfo.getSsrcInfo() != null && ssrc.equals(inviteInfo.getSsrcInfo().getSsrc())) {
return inviteInfo;
}
}
return null;
} }
@Override @Override
@ -325,15 +322,15 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
return null; return null;
} }
removeInviteInfo(inviteInfoInDb); removeInviteInfo(inviteInfoInDb);
String key = VideoManagerConstants.INVITE_PREFIX + String key = VideoManagerConstants.INVITE_PREFIX;
":" + inviteInfo.getType() + String objectKey = inviteInfo.getType() +
":" + inviteInfo.getChannelId() + ":" + inviteInfo.getChannelId() +
":" + inviteInfo.getStream() + ":" + inviteInfo.getStream() +
":" + ssrc; ":" + ssrc;
if (inviteInfoInDb.getSsrcInfo() != null) { if (inviteInfoInDb.getSsrcInfo() != null) {
inviteInfoInDb.getSsrcInfo().setSsrc(ssrc); inviteInfoInDb.getSsrcInfo().setSsrc(ssrc);
} }
redisTemplate.opsForValue().set(key, inviteInfoInDb); redisTemplate.opsForHash().put(key, objectKey, inviteInfoInDb);
return inviteInfoInDb; return inviteInfoInDb;
} }
} }

View File

@ -1566,10 +1566,11 @@ public class PlayServiceImpl implements IPlayService {
@Override @Override
public void stop(InviteSessionType type, Device device, DeviceChannel channel, String stream) { public void stop(InviteSessionType type, Device device, DeviceChannel channel, String stream) {
InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(type, stream); InviteInfo inviteInfo = inviteStreamService.getInviteInfo(type, channel.getId(), stream);
if (inviteInfo == null) { if (inviteInfo == null) {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到");
} }
inviteStreamService.removeInviteInfoByDeviceAndChannel(inviteInfo.getType(), channel.getId());
if (InviteSessionStatus.ok == inviteInfo.getStatus()) { if (InviteSessionStatus.ok == inviteInfo.getStatus()) {
try { try {
log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId());
@ -1579,7 +1580,7 @@ public class PlayServiceImpl implements IPlayService {
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
} }
} }
inviteStreamService.removeInviteInfoByDeviceAndChannel(inviteInfo.getType(), channel.getId());
if (inviteInfo.getType() == InviteSessionType.PLAY) { if (inviteInfo.getType() == InviteSessionType.PLAY) {
deviceChannelService.stopPlay(channel.getId()); deviceChannelService.stopPlay(channel.getId());
} }

View File

@ -1,16 +1,21 @@
package com.genersoft.iot.vmp.vmanager; package com.genersoft.iot.vmp.vmanager;
import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
@RestController @RestController
@RequestMapping("/api/test") @RequestMapping("/api/test")
@ -19,13 +24,42 @@ public class TestController {
@Autowired @Autowired
private HookSubscribe subscribe; private HookSubscribe subscribe;
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@GetMapping("/hook/list") @GetMapping("/hook/list")
@Operation(summary = "查询角色", security = @SecurityRequirement(name = JwtUtils.HEADER))
public List<Hook> all(){ public List<Hook> all(){
return subscribe.getAll(); return subscribe.getAll();
} }
@GetMapping("/redis")
public List<InviteInfo> redis(){
InviteSessionType type = InviteSessionType.PLAY;
String channelId = null;
String stream = null;
String key = VideoManagerConstants.INVITE_PREFIX;
String keyPattern = (type != null ? type : "*") +
":" + (channelId != null ? channelId : "*") +
":" + (stream != null ? stream : "*")
+ ":*";
ScanOptions options = ScanOptions.scanOptions().match(keyPattern).count(20).build();
Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, options);
List<InviteInfo> result = new ArrayList<>();
try {
while (cursor.hasNext()) {
System.out.println(cursor.next().getKey());
result.add((InviteInfo) cursor.next().getValue());
}
}catch (Exception e) {
}finally {
cursor.close();
}
return result;
}
// @Bean // @Bean
// public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() { // public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
// ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); // ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");