Browse Source

fix: cache로 사용하는 DroneStorage의 잘못된 참조 수정

- 참조하는 객체에 의하여 무한증식하는 현상 수정
feature/socket
지대한 7 months ago
parent
commit
32b003ec24
  1. 48
      app/kac-app/src/main/resources/application.yml
  2. 4
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/SandboxDroneCommandImpl.java
  3. 50
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/storage/DroneStorage.java
  4. 72
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/ScheduledService.java
  5. 3
      app/kac-socket-app/src/main/resources/application.yml
  6. 6
      socket-test/pav-100-dron.js

48
app/kac-app/src/main/resources/application.yml

@ -12,6 +12,28 @@ server.port: 8000
logging: logging:
level: level:
kr.co.palnet: DEBUG kr.co.palnet: DEBUG
# jdbc:
# audit: OFF
# resultset: OFF
# resultsettable: INFO #SQL 결과 데이터 Table을 로그로 남긴다.
# sqlonly: OFF #SQL만 로그로 남긴다.
# sqltiming: info #SQL과 소요시간을 표기한다.
# connection : trace # 커넥션 확인가능
# org.hibernate:
# SQL: off
# type.descriptor.sql.BasicBinder: TRACE
# com.zaxxer.hikari.HikariConfig: DEBUG
# com.zaxxer.hikari: TRACE
# file:
# name: ./logs/kac-app/data.log
# logback:
# rollingpolicy:
# clean-history-on-start: false
# # file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
# file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.log
# max-file-size: 10MB
# max-history: 60
# total-size-cap: 100MB
management: management:
endpoints: endpoints:
@ -28,19 +50,19 @@ spring:
logging: logging:
level: level:
kr.co.palnet: DEBUG kr.co.palnet: info
jdbc: jdbc: off
audit: OFF # audit: OFF
resultset: OFF # resultset: OFF
resultsettable: INFO #SQL 결과 데이터 Table을 로그로 남긴다. # resultsettable: INFO #SQL 결과 데이터 Table을 로그로 남긴다.
sqlonly: OFF #SQL만 로그로 남긴다. # sqlonly: OFF #SQL만 로그로 남긴다.
sqltiming: info #SQL과 소요시간을 표기한다. # sqltiming: info #SQL과 소요시간을 표기한다.
connection : trace # 커넥션 확인가능 # connection : trace # 커넥션 확인가능
org.hibernate: org.hibernate: off
SQL: off # SQL: off
type.descriptor.sql.BasicBinder: TRACE # type.descriptor.sql.BasicBinder: TRACE
com.zaxxer.hikari.HikariConfig: DEBUG # com.zaxxer.hikari.HikariConfig: off
com.zaxxer.hikari: TRACE com.zaxxer.hikari: off
file: file:
name: ./logs/kac-app/data.log name: ./logs/kac-app/data.log
logback: logback:

4
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/SandboxDroneCommandImpl.java

@ -128,8 +128,8 @@ public class SandboxDroneCommandImpl implements DroneCommand {
// kacAppService.asyncSendData(drone); // kacAppService.asyncSendData(drone);
// 저장 해 놓았다가 한거번에 전송 - 필요한 곳에 전송(HISTORY, UTM) // 저장 해 놓았다가 한거번에 전송 - 필요한 곳에 전송(HISTORY, UTM)
DroneStorage dronStorage = DroneStorage.getInstance(); DroneStorage droneStorage = DroneStorage.getInstance();
dronStorage.add(drone); droneStorage.add(drone);
} catch (Exception e) { } catch (Exception e) {
log.error("ERROR : {}", e.getMessage(), e); log.error("ERROR : {}", e.getMessage(), e);

50
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/storage/DroneStorage.java

@ -51,34 +51,42 @@ public class DroneStorage {
return; return;
} }
List<DroneDto> list = droneMap.get(drone.getObjectId()); List<DroneDto> list = this.droneMap.get(drone.getObjectId());
if (list == null) { if (list == null) {
list = new ArrayList<>(); list = new ArrayList<>();
// list = new CopyOnWriteArrayList<>();
// list = new ConcurrentLinkedQueue<>();
// droneMap.put(drone.getObjectId(), list);
} }
// log.info("add :: {}::{}::{}", drone.getObjectId(), droneMap.keySet().size(), list.size());
list.add(drone); list.add(drone);
droneMap.put(drone.getObjectId(), list); this.droneMap.put(drone.getObjectId(), list);
// list.offer(drone);
if ("PA-DRON-004-000".equals(drone.getObjectId()))
log.info("add :: {}::{}::{}", drone.getObjectId(), droneMap.keySet().size(), list.size());
} }
// 1분 이상된 데이터 삭제 또는 처리가 끝난 데이터 삭제 // 1분 이상된 데이터 삭제 또는 처리가 끝난 데이터 삭제
public void removeByCondition() { public void removeByCondition() {
for (String key : droneMap.keySet()) { for (String objectId : droneMap.keySet()) {
List<DroneDto> list = droneMap.get(key); List<DroneDto> list = droneMap.get(objectId);
if (list == null) { if (list == null) {
continue; continue;
} else if (list.isEmpty()) { } else if (list.isEmpty()) {
droneMap.remove(key); droneMap.remove(objectId);
continue; continue;
} }
// 1분 전 시간 // 1분 전 시간
Instant compareTime = Instant.now().minusSeconds(60); Instant compareTime = Instant.now().minusSeconds(60);
// 마지막 데이터가 1분 이상된 데이터라면 삭제 // 마지막 데이터가 1분 이상된 데이터라면 삭제
DroneDto lastDronDto = list.getLast(); DroneDto lastDronDto = list.stream().toList().getLast();
if ((lastDronDto.isSendHistroy() && lastDronDto.isSendUtm()) || compareTime.isAfter(lastDronDto.getRegDt())) { if ((lastDronDto.isSendHistroy() && lastDronDto.isSendUtm()) || compareTime.isAfter(lastDronDto.getRegDt())) {
list.remove(lastDronDto); // list.remove(lastDronDto);
droneMap.remove(objectId);
continue; continue;
} }
@ -89,11 +97,18 @@ public class DroneStorage {
public List<DroneDto> getAllByUtm() { public List<DroneDto> getAllByUtm() {
// 보내지 않은 모든 데이터 추출 // 보내지 않은 모든 데이터 추출
return droneMap.values().stream().reduce((list, result) -> { // return droneMap.values().stream().reduce((list, result) -> {
List<DroneDto> yetSendData = list.stream().filter(droneDto -> !droneDto.isSendUtm()).toList(); // log.info("list: {}, result: {}",list, result);
result.addAll(yetSendData); // List<DroneDto> yetSendData = list.stream().filter(droneDto -> !droneDto.isSendUtm()).toList();
return result; // result.addAll(yetSendData);
}).orElse(new ArrayList<>()); // return result;
// }).orElse(new ArrayList<>());
List<DroneDto> resultList = new ArrayList<>();
droneMap.values().forEach(droneDtoList -> {
List<DroneDto> list = droneDtoList.stream().filter(droneDto -> !droneDto.isSendUtm()).toList();
resultList.addAll(list);
});
return resultList;
} }
// public List<DroneDto> getAllByHistory() { // public List<DroneDto> getAllByHistory() {
@ -110,9 +125,12 @@ public class DroneStorage {
Map<String, List<DroneDto>> sendList = new HashMap<>(); Map<String, List<DroneDto>> sendList = new HashMap<>();
for (String objectId : droneMap.keySet()) { for (String objectId : droneMap.keySet()) {
List<DroneDto> droneDtoList = droneMap.get(objectId); List<DroneDto> droneDtoList = droneMap.get(objectId);
List<DroneDto> list = droneDtoList.stream().filter(droneDto -> !droneDto.isSendHistroy()).toList(); if (droneDtoList != null) {
if (list != null && !list.isEmpty()) { // ConcurrentLinkedQueue<DroneDto> list = droneDtoList.stream().filter(droneDto -> !droneDto.isSendHistroy()).collect(Collectors.toCollection(ConcurrentLinkedQueue::new));
sendList.put(objectId, list); List<DroneDto> list = droneDtoList.stream().filter(droneDto -> !droneDto.isSendHistroy()).toList();
if (!list.isEmpty()) {
sendList.put(objectId, list);
}
} }
} }
return sendList; return sendList;

72
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/ScheduledService.java

@ -11,7 +11,6 @@ import org.springframework.stereotype.Service;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -26,13 +25,18 @@ public class ScheduledService {
@Scheduled(fixedDelay = 2 * 1000) @Scheduled(fixedDelay = 2 * 1000)
public void sendDataByHistory() { public void sendDataByHistory() {
DroneStorage droneStorage = DroneStorage.getInstance(); DroneStorage droneStorage = DroneStorage.getInstance();
Map<String, List<DroneDto>> history = droneStorage.getAllByHistory(); Map<String, List<DroneDto>> history = droneStorage.getAllByHistory();
log.info("sendDataByHistory size : {}", history.keySet().size());
// list 합
history.values().stream().map(List::size).reduce(Integer::sum).ifPresent(s -> log.info("list size sum : {}", s));
if (!history.isEmpty()) { if (!history.isEmpty()) {
if (kacAppService.sendDataAll(history)) { if (kacAppService.sendDataAll(history)) {
history.values().forEach(droneDtoList -> droneDtoList.forEach(droneDto -> droneDto.setSendHistroy(true))); history.values().forEach(droneDtoList -> droneDtoList.forEach(droneDto -> droneDto.setSendHistroy(true)));
} }
history.values().forEach(droneDtoList -> droneDtoList.forEach(droneDto -> droneDto.setSendHistroy(true)));
} }
} }
@ -44,58 +48,56 @@ public class ScheduledService {
DroneStorage droneStorage = DroneStorage.getInstance(); DroneStorage droneStorage = DroneStorage.getInstance();
List<DroneDto> list = droneStorage.getAllByUtm(); List<DroneDto> list = droneStorage.getAllByUtm();
// 가공 try {
List<UtmDto.DroneInfo> droneInfoList = list.stream().map(model -> {
UtmDto.DroneInfo droneInfo = UtmDto.DroneInfo.builder()
.id(model.getObjectId())
.latitude(model.getLat().toString())
.longitude(model.getLon().toString())
.height(model.getElev().toString())
.build();
return droneInfo;
}).collect(Collectors.toList());
if (droneInfoList.isEmpty()) return; List<UtmDto.DroneInfo> droneInfoList = list.stream().map(model -> {
UtmDto.DroneInfo droneInfo = UtmDto.DroneInfo.builder()
.id(model.getObjectId())
.latitude(model.getLat().toString())
.longitude(model.getLon().toString())
.height(model.getElev().toString())
.build();
return droneInfo;
}).collect(Collectors.toList());
// 가공
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss") if (droneInfoList.isEmpty()) return;
.withZone(ZoneId.of("Asia/Seoul"));
String dateStr = formatter.format(Instant.now());
UtmDto utmDto = UtmDto.builder() DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
.GPSime(dateStr) .withZone(ZoneId.of("Asia/Seoul"));
.droneInfo(droneInfoList) String dateStr = formatter.format(Instant.now());
.build();
UtmDto utmDto = UtmDto.builder()
.GPSime(dateStr)
.droneInfo(droneInfoList)
.build();
// if (externalService.sendDataToUtm(utmDto)) { // if (externalService.sendDataToUtm(utmDto)) {
// list.forEach(drone -> drone.setSendUtm(true)); // list.forEach(drone -> drone.setSendUtm(true));
// } // }
// TODO 현재 통신이 안되므로 모두 보낸다는 가정으로 진행 // TODO 현재 통신이 안되므로 모두 보낸다는 가정으로 진행
list.forEach(drone -> drone.setSendUtm(true)); list.forEach(drone -> drone.setSendUtm(true));
} catch (Exception e) {
log.error("ERROR : {}", e.getMessage(), e);
if (list != null) {
log.warn("list : {}", list.size());
} else {
log.warn("list is null");
}
}
} }
// 사용을 다한 데이터 제거 // 사용을 다한 데이터 제거
@Scheduled(fixedDelay = 1000 * 30) @Scheduled(fixedDelay = 30 * 1000)
public void removeDrone() { public void removeDrone() {
log.debug(">>>> removeDrone <<<<<"); log.info(">>>> removeDrone <<<<<");
DroneStorage droneStorage = DroneStorage.getInstance(); DroneStorage droneStorage = DroneStorage.getInstance();
Map<String, List<DroneDto>> all = droneStorage.getAll(); Map<String, List<DroneDto>> all = droneStorage.getAll();
if (all != null) {
log.debug(">>>> remove drone before size : {}::{}", all.keySet().size(), all.values().stream().reduce((list, result) -> {
result.addAll(list);
return result;
}).orElse(new ArrayList<>()).size());
}
droneStorage.removeByCondition(); droneStorage.removeByCondition();
if (all != null) {
log.debug(">>>> remove drone before size : {}::{}", all.keySet().size(), all.values().stream().reduce((list, result) -> {
result.addAll(list);
return result;
}).orElse(new ArrayList<>()).size());
}
} }

3
app/kac-socket-app/src/main/resources/application.yml

@ -33,6 +33,9 @@ spring:
activate: activate:
on-profile: local on-profile: local
logging:
level:
kr.co.palnet: info
--- ---

6
socket-test/pav-100-dron.js

@ -2,8 +2,8 @@ const { getConnection, writeData } = require('./pav-client');
const { dumyData } = require('./pav-utils'); const { dumyData } = require('./pav-utils');
const host = "localhost" const host = "localhost"
// const port = 8003; const port = 8003;
const port = 18003; // const port = 18003;
const prefix = 'PA-DRON-004-'; const prefix = 'PA-DRON-004-';
const severalDrones = cnt => { const severalDrones = cnt => {
@ -56,7 +56,7 @@ const severalDrones = cnt => {
return clients; return clients;
}; };
// //
const clients = severalDrones(10); const clients = severalDrones(100);
const dist = 0.01; const dist = 0.01;
const sendData = cnt => { const sendData = cnt => {
const direction = Math.floor(cnt / 10) % 4; const direction = Math.floor(cnt / 10) % 4;

Loading…
Cancel
Save