feat: 유량제어 그룹 버킷 분리 및 현황 화면 개선
- CustomGroupBucket 클래스 신규 생성: 초당/추가 임계치를 별도 LocalBucket으로 분리하여 개별 토큰 조회 가능 - InflowControlManager 수정: groupBucketList 타입을 CustomGroupBucket으로 변경, makeGroupBucket() 메서드 추가 - InflowGroupBucketService 단순화: 복잡한 리플렉션 로직 제거, CustomGroupBucket 직접 메서드 사용 - 현황 탭에 전체 인스턴스 합산 요약 추가 (활성 인스턴스 수, 초당/추가 임계치 합산) - 추가 임계치 시간단위에서 '초(SEC)' 옵션 제거 - 저장 완료 후 리스트 화면 이동 제거
This commit is contained in:
@@ -21,6 +21,8 @@
|
|||||||
base-package="com.eactive.eai.authserver" />
|
base-package="com.eactive.eai.authserver" />
|
||||||
<context:component-scan
|
<context:component-scan
|
||||||
base-package="com.eactive.eai.adapter" />
|
base-package="com.eactive.eai.adapter" />
|
||||||
|
<context:component-scan
|
||||||
|
base-package="com.eactive.eai.manage" />
|
||||||
<context:annotation-config />
|
<context:annotation-config />
|
||||||
<mvc:annotation-driven />
|
<mvc:annotation-driven />
|
||||||
<mvc:default-servlet-handler />
|
<mvc:default-servlet-handler />
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- 유량제어 그룹 테이블 DDL
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Oracle
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- 유량제어 그룹 테이블
|
||||||
|
CREATE TABLE inflow_control_group (
|
||||||
|
group_id VARCHAR2(36) NOT NULL, -- UUID
|
||||||
|
group_name VARCHAR2(100),
|
||||||
|
threshold NUMBER(10),
|
||||||
|
threshold_per_second NUMBER(10),
|
||||||
|
threshold_time_unit VARCHAR2(12),
|
||||||
|
use_yn VARCHAR2(1) DEFAULT '1',
|
||||||
|
modified_by VARCHAR2(50),
|
||||||
|
modified_at TIMESTAMP,
|
||||||
|
CONSTRAINT pk_inflow_control_group PRIMARY KEY (group_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE inflow_control_group IS '유량제어 그룹';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.group_id IS '그룹 ID (UUID)';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.group_name IS '그룹명';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.threshold IS '임계치';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.threshold_per_second IS '초당 임계치';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.threshold_time_unit IS '임계치 TimeUnit (SEC, MIN, HOU, DAY, MON)';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.use_yn IS '사용여부 (1:사용, 0:미사용)';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.modified_by IS '수정자';
|
||||||
|
COMMENT ON COLUMN inflow_control_group.modified_at IS '수정일시';
|
||||||
|
|
||||||
|
-- 그룹-인터페이스 매핑 테이블
|
||||||
|
CREATE TABLE inflow_control_group_mapping (
|
||||||
|
interface_id VARCHAR2(100) NOT NULL,
|
||||||
|
group_id VARCHAR2(50) NOT NULL,
|
||||||
|
use_yn VARCHAR2(1) DEFAULT '1',
|
||||||
|
modified_by VARCHAR2(50),
|
||||||
|
modified_at TIMESTAMP,
|
||||||
|
CONSTRAINT pk_inflow_ctrl_grp_mapping PRIMARY KEY (interface_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE inflow_control_group_mapping IS '유량제어 그룹 매핑';
|
||||||
|
COMMENT ON COLUMN inflow_control_group_mapping.interface_id IS '인터페이스 ID';
|
||||||
|
COMMENT ON COLUMN inflow_control_group_mapping.group_id IS '그룹 ID';
|
||||||
|
COMMENT ON COLUMN inflow_control_group_mapping.use_yn IS '사용여부 (1:사용, 0:미사용)';
|
||||||
|
COMMENT ON COLUMN inflow_control_group_mapping.modified_by IS '수정자';
|
||||||
|
COMMENT ON COLUMN inflow_control_group_mapping.modified_at IS '수정일시';
|
||||||
|
|
||||||
|
-- 인덱스
|
||||||
|
CREATE INDEX idx_inflow_ctrl_grp_map_grp ON inflow_control_group_mapping (group_id);
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.eactive.eai.manage.inflow;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GroupBucketStatusDTO {
|
||||||
|
private String groupId;
|
||||||
|
private String groupName;
|
||||||
|
private boolean activate;
|
||||||
|
private List<BucketInfo> buckets;
|
||||||
|
|
||||||
|
public static class BucketInfo {
|
||||||
|
private String type; // "perSecond" | "threshold"
|
||||||
|
private long capacity; // 최대 토큰 수
|
||||||
|
private long availableTokens; // 현재 사용 가능한 토큰 수
|
||||||
|
private String timeUnit; // threshold인 경우 시간 단위
|
||||||
|
private double usagePercent; // 사용률 (100 - availableTokens/capacity * 100)
|
||||||
|
|
||||||
|
public BucketInfo() {}
|
||||||
|
|
||||||
|
public BucketInfo(String type, long capacity, long availableTokens, String timeUnit) {
|
||||||
|
this.type = type;
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.availableTokens = availableTokens;
|
||||||
|
this.timeUnit = timeUnit;
|
||||||
|
this.usagePercent = capacity > 0 ?
|
||||||
|
Math.round((1.0 - (double) availableTokens / capacity) * 10000) / 100.0 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCapacity() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCapacity(long capacity) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAvailableTokens() {
|
||||||
|
return availableTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvailableTokens(long availableTokens) {
|
||||||
|
this.availableTokens = availableTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTimeUnit() {
|
||||||
|
return timeUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeUnit(String timeUnit) {
|
||||||
|
this.timeUnit = timeUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getUsagePercent() {
|
||||||
|
return usagePercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsagePercent(double usagePercent) {
|
||||||
|
this.usagePercent = usagePercent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(String groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupName() {
|
||||||
|
return groupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupName(String groupName) {
|
||||||
|
this.groupName = groupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActivate() {
|
||||||
|
return activate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivate(boolean activate) {
|
||||||
|
this.activate = activate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BucketInfo> getBuckets() {
|
||||||
|
return buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuckets(List<BucketInfo> buckets) {
|
||||||
|
this.buckets = buckets;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.eactive.eai.manage.inflow;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/manage/inflow/group")
|
||||||
|
public class InflowGroupBucketController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InflowGroupBucketService bucketService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 그룹의 버킷 상태 조회
|
||||||
|
* GET /manage/inflow/group/{groupId}/bucket-status
|
||||||
|
*/
|
||||||
|
@GetMapping("/{groupId}/bucket-status")
|
||||||
|
public ResponseEntity<?> getGroupBucketStatus(@PathVariable String groupId) {
|
||||||
|
GroupBucketStatusDTO status = bucketService.getGroupBucketStatus(groupId);
|
||||||
|
|
||||||
|
if (status == null) {
|
||||||
|
Map<String, Object> error = new HashMap<>();
|
||||||
|
error.put("success", false);
|
||||||
|
error.put("message", "그룹을 찾을 수 없습니다: " + groupId);
|
||||||
|
return ResponseEntity.ok(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("data", status);
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모든 그룹의 버킷 상태 조회
|
||||||
|
* GET /manage/inflow/group/bucket-status
|
||||||
|
*/
|
||||||
|
@GetMapping("/bucket-status")
|
||||||
|
public ResponseEntity<?> getAllGroupBucketStatus() {
|
||||||
|
List<GroupBucketStatusDTO> statusList = bucketService.getAllGroupBucketStatus();
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("data", statusList);
|
||||||
|
result.put("count", statusList.size());
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.eactive.eai.manage.inflow;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.eactive.eai.common.inflow.CustomGroupBucket;
|
||||||
|
import com.eactive.eai.common.inflow.InflowControlManager;
|
||||||
|
import com.eactive.eai.common.inflow.InflowGroupVO;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class InflowGroupBucketService {
|
||||||
|
|
||||||
|
public GroupBucketStatusDTO getGroupBucketStatus(String groupId) {
|
||||||
|
InflowControlManager manager = InflowControlManager.getInstance();
|
||||||
|
|
||||||
|
InflowGroupVO groupVo = manager.getGroupInflowThreshold(groupId);
|
||||||
|
if (groupVo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupBucketStatusDTO dto = new GroupBucketStatusDTO();
|
||||||
|
dto.setGroupId(groupId);
|
||||||
|
dto.setGroupName(groupVo.getGroupName());
|
||||||
|
dto.setActivate(groupVo.isActivate());
|
||||||
|
|
||||||
|
List<GroupBucketStatusDTO.BucketInfo> buckets = new ArrayList<>();
|
||||||
|
|
||||||
|
// CustomGroupBucket에서 직접 토큰 조회
|
||||||
|
CustomGroupBucket customGroupBucket = getCustomGroupBucket(groupId);
|
||||||
|
|
||||||
|
// perSecond bucket
|
||||||
|
if (groupVo.getThresholdPerSecond() > 0) {
|
||||||
|
long capacity = groupVo.getThresholdPerSecond();
|
||||||
|
long available = capacity;
|
||||||
|
if (customGroupBucket != null) {
|
||||||
|
long tokens = customGroupBucket.getPerSecondAvailableTokens();
|
||||||
|
if (tokens >= 0) {
|
||||||
|
available = Math.min(tokens, capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buckets.add(new GroupBucketStatusDTO.BucketInfo(
|
||||||
|
"perSecond",
|
||||||
|
capacity,
|
||||||
|
available,
|
||||||
|
"SEC"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// threshold bucket
|
||||||
|
if (groupVo.getThreshold() > 0) {
|
||||||
|
long capacity = groupVo.getThreshold();
|
||||||
|
long available = capacity;
|
||||||
|
if (customGroupBucket != null) {
|
||||||
|
long tokens = customGroupBucket.getThresholdAvailableTokens();
|
||||||
|
if (tokens >= 0) {
|
||||||
|
available = Math.min(tokens, capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buckets.add(new GroupBucketStatusDTO.BucketInfo(
|
||||||
|
"threshold",
|
||||||
|
capacity,
|
||||||
|
available,
|
||||||
|
groupVo.getThresholdTimeUnit()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setBuckets(buckets);
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 그룹 버킷 조회 (리플렉션 사용)
|
||||||
|
*/
|
||||||
|
public CustomGroupBucket getCustomGroupBucket(String groupId) {
|
||||||
|
try {
|
||||||
|
InflowControlManager manager = InflowControlManager.getInstance();
|
||||||
|
Field field = InflowControlManager.class.getDeclaredField("groupBucketList");
|
||||||
|
field.setAccessible(true);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, CustomGroupBucket> groupBucketList =
|
||||||
|
(Map<String, CustomGroupBucket>) field.get(manager);
|
||||||
|
return groupBucketList.get(groupId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GroupBucketStatusDTO> getAllGroupBucketStatus() {
|
||||||
|
InflowControlManager manager = InflowControlManager.getInstance();
|
||||||
|
String[] groupIds = manager.getGroupAllKeys();
|
||||||
|
|
||||||
|
List<GroupBucketStatusDTO> result = new ArrayList<>();
|
||||||
|
for (String groupId : groupIds) {
|
||||||
|
GroupBucketStatusDTO dto = getGroupBucketStatus(groupId);
|
||||||
|
if (dto != null) {
|
||||||
|
result.add(dto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user