feat: DualInflowControlManager DAO 이중 호출 제거 및 타겟 메트릭 추가
- 베이스 맵 메서드 오버라이드(getAdapterAllKeys, getInterfaceAllKeys, getAdapterInflowThreashold, getInterfaceInflowThreashold, removeAdapter, removeInterface)로 DAO 이중 호출 제거 - super.reload*() 호출 제거 - 어댑터/인터페이스/클라이언트별 TargetMetrics 수집 추가 (allowed, rejectedPerSecond, rejectedThreshold) - 메트릭 조회/초기화 API 메서드 추가 - 단위테스트: DualInflowControlManagerBucketMethodsTest(18건), DualInflowControlManagerMetricsTest 확장 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,26 +1,22 @@
|
||||
package com.eactive.eai.common.inflow.dual;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
|
||||
import com.eactive.eai.common.inflow.Bucket;
|
||||
import com.eactive.eai.common.inflow.CustomGroupBucket;
|
||||
import com.eactive.eai.common.inflow.InflowControlDAO;
|
||||
import com.eactive.eai.common.inflow.InflowControlManager;
|
||||
import com.eactive.eai.common.inflow.InflowTargetVO;
|
||||
import com.eactive.eai.common.inflow.InflowType;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleException;
|
||||
import com.eactive.eai.common.message.EAIMessage;
|
||||
import com.eactive.eai.common.server.EAIServerManager;
|
||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
|
||||
@@ -41,7 +37,7 @@ import io.github.bucket4j.local.LocalBucket;
|
||||
* <pre>inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.DualInflowControlManager</pre>
|
||||
*/
|
||||
@Component
|
||||
public class DualInflowControlManager extends InflowControlManager implements DualBucket {
|
||||
public class DualInflowControlManager extends AbstractInflowControlManager implements DualBucket {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
||||
|
||||
@@ -50,18 +46,14 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
return ApplicationContextProvider.getContext().getBean(DualInflowControlManager.class);
|
||||
}
|
||||
|
||||
public static Boolean isTargetOfInflowControl(EAIServerManager eaiServerManager, EAIMessage eaiMessage) {
|
||||
return InflowControlManager.isTargetOfInflowControl(eaiServerManager, eaiMessage);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private InflowControlDAO dualDao;
|
||||
|
||||
private volatile Map<String, DualCustomBucket> adapterBucketMap = new ConcurrentHashMap<>();
|
||||
private volatile Map<String, DualCustomBucket> interfaceBucketMap = new ConcurrentHashMap<>();
|
||||
private volatile Map<String, DualCustomBucket> clientBucketMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, TargetMetrics> groupMetricsMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, TargetMetrics> adapterMetricsMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, TargetMetrics> interfaceMetricsMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, TargetMetrics> clientMetricsMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, TargetMetrics> groupMetricsMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static class TargetMetrics {
|
||||
public final AtomicLong allowed = new AtomicLong();
|
||||
@@ -114,7 +106,7 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
|
||||
private Map<String, DualCustomBucket> loadBucketMap(InflowType type) throws Exception {
|
||||
Map<String, DualCustomBucket> newMap = new HashMap<>();
|
||||
for (InflowTargetVO vo : dualDao.getInflowList(type)) {
|
||||
for (InflowTargetVO vo : inflowControlDAO.getInflowList(type)) {
|
||||
DualCustomBucket bucket = makeBucket(vo);
|
||||
if (bucket != null) newMap.put(vo.getName(), bucket);
|
||||
}
|
||||
@@ -123,38 +115,44 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
|
||||
@Override
|
||||
public synchronized void reloadAdapter() throws Exception {
|
||||
super.reloadAdapter();
|
||||
initAdapters();
|
||||
adapterMetricsMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reloadAdapter(String adapter) throws Exception {
|
||||
super.reloadAdapter(adapter);
|
||||
reloadBucketMap(adapterBucketMap, InflowType.ADAPTER, adapter);
|
||||
TargetMetrics m = adapterMetricsMap.get(adapter);
|
||||
if (m != null) m.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reloadInterface() throws Exception {
|
||||
super.reloadInterface();
|
||||
initInterfaces();
|
||||
interfaceMetricsMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reloadInterface(String inter) throws Exception {
|
||||
super.reloadInterface(inter);
|
||||
reloadBucketMap(interfaceBucketMap, InflowType.INTERFACE, inter);
|
||||
TargetMetrics m = interfaceMetricsMap.get(inter);
|
||||
if (m != null) m.reset();
|
||||
}
|
||||
|
||||
public synchronized void reloadClient() throws Exception {
|
||||
initClients();
|
||||
clientMetricsMap.clear();
|
||||
}
|
||||
|
||||
public synchronized void reloadClient(String clientId) throws Exception {
|
||||
reloadBucketMap(clientBucketMap, InflowType.CLIENT, clientId);
|
||||
TargetMetrics m = clientMetricsMap.get(clientId);
|
||||
if (m != null) m.reset();
|
||||
}
|
||||
|
||||
public synchronized void removeClient(String clientId) {
|
||||
clientBucketMap.remove(clientId);
|
||||
clientMetricsMap.remove(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,7 +169,7 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
}
|
||||
|
||||
private void reloadBucketMap(Map<String, DualCustomBucket> targetMap, InflowType type, String name) throws Exception {
|
||||
Map<String, InflowTargetVO> updated = dualDao.getInflowMap(type, name);
|
||||
Map<String, InflowTargetVO> updated = inflowControlDAO.getInflowMap(type, name);
|
||||
if (updated == null) return;
|
||||
for (InflowTargetVO vo : updated.values()) {
|
||||
DualCustomBucket bucket = makeBucket(vo);
|
||||
@@ -180,6 +178,26 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 어댑터/인터페이스/클라이언트 메트릭 접근자
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public TargetMetrics getAdapterMetrics(String adapter) { return adapterMetricsMap.get(adapter); }
|
||||
public TargetMetrics getInterfaceMetrics(String inter) { return interfaceMetricsMap.get(inter); }
|
||||
public TargetMetrics getClientMetrics(String clientId) { return clientMetricsMap.get(clientId); }
|
||||
|
||||
public Map<String, TargetMetrics> getAllAdapterMetrics() { return Collections.unmodifiableMap(adapterMetricsMap); }
|
||||
public Map<String, TargetMetrics> getAllInterfaceMetrics() { return Collections.unmodifiableMap(interfaceMetricsMap); }
|
||||
public Map<String, TargetMetrics> getAllClientMetrics() { return Collections.unmodifiableMap(clientMetricsMap); }
|
||||
|
||||
public void resetAdapterMetrics(String adapter) { TargetMetrics m = adapterMetricsMap.get(adapter); if (m != null) m.reset(); }
|
||||
public void resetInterfaceMetrics(String inter) { TargetMetrics m = interfaceMetricsMap.get(inter); if (m != null) m.reset(); }
|
||||
public void resetClientMetrics(String clientId) { TargetMetrics m = clientMetricsMap.get(clientId); if (m != null) m.reset(); }
|
||||
|
||||
public void resetAllAdapterMetrics() { adapterMetricsMap.values().forEach(TargetMetrics::reset); }
|
||||
public void resetAllInterfaceMetrics() { interfaceMetricsMap.values().forEach(TargetMetrics::reset); }
|
||||
public void resetAllClientMetrics() { clientMetricsMap.values().forEach(TargetMetrics::reset); }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 그룹 메트릭 — isGroupPass() 카운팅 및 접근자
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -222,19 +240,35 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
@Override
|
||||
public String isAdapterPassDetail(String adapter) {
|
||||
DualCustomBucket b = adapterBucketMap.get(adapter);
|
||||
return (b == null) ? DualCustomBucket.RESULT_PASS : b.tryConsume();
|
||||
if (b == null) return DualCustomBucket.RESULT_PASS;
|
||||
String result = b.tryConsume();
|
||||
recordMetrics(adapterMetricsMap, adapter, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String isInterfacePassDetail(String inter) {
|
||||
DualCustomBucket b = interfaceBucketMap.get(inter);
|
||||
return (b == null) ? DualCustomBucket.RESULT_PASS : b.tryConsume();
|
||||
if (b == null) return DualCustomBucket.RESULT_PASS;
|
||||
String result = b.tryConsume();
|
||||
recordMetrics(interfaceMetricsMap, inter, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String isClientPassDetail(String clientId) {
|
||||
DualCustomBucket b = clientBucketMap.get(clientId);
|
||||
return (b == null) ? DualCustomBucket.RESULT_PASS : b.tryConsume();
|
||||
if (b == null) return DualCustomBucket.RESULT_PASS;
|
||||
String result = b.tryConsume();
|
||||
recordMetrics(clientMetricsMap, clientId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void recordMetrics(ConcurrentHashMap<String, TargetMetrics> metricsMap, String key, String result) {
|
||||
TargetMetrics m = metricsMap.computeIfAbsent(key, k -> new TargetMetrics());
|
||||
if (result == null) m.allowed.incrementAndGet();
|
||||
else if (DualCustomBucket.RESULT_BLOCKED_PER_SECOND.equals(result)) m.rejectedPerSecond.incrementAndGet();
|
||||
else m.rejectedThreshold.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -259,6 +293,49 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
return isInterfacePassDetail(inter) == DualCustomBucket.RESULT_PASS;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 어댑터/인터페이스 키·VO·삭제 — Dual 맵 기준으로 오버라이드
|
||||
// (base의 adapterBucketList / interfaceBucketList 는 읽지 않아 DAO 이중 호출 제거)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public String[] getAdapterAllKeys() {
|
||||
String[] keys = adapterBucketMap.keySet().toArray(new String[0]);
|
||||
Arrays.sort(keys);
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInterfaceAllKeys() {
|
||||
String[] keys = interfaceBucketMap.keySet().toArray(new String[0]);
|
||||
Arrays.sort(keys);
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InflowTargetVO getAdapterInflowThreashold(String adapter) {
|
||||
DualCustomBucket b = adapterBucketMap.get(adapter);
|
||||
return (b == null) ? null : b.getInflowTargetVo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InflowTargetVO getInterfaceInflowThreashold(String inter) {
|
||||
DualCustomBucket b = interfaceBucketMap.get(inter);
|
||||
return (b == null) ? null : b.getInflowTargetVo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeAdapter(String adapter) {
|
||||
adapterBucketMap.remove(adapter);
|
||||
adapterMetricsMap.remove(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void removeInterface(String inter) {
|
||||
interfaceBucketMap.remove(inter);
|
||||
interfaceMetricsMap.remove(inter);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 모니터링용 접근자
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -292,7 +369,7 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private DualCustomBucket makeBucket(InflowTargetVO vo) {
|
||||
if (!InflowControlManager.isInflowTarget(vo)) {
|
||||
if (!AbstractInflowControlManager.isInflowTarget(vo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -308,7 +385,7 @@ public class DualInflowControlManager extends InflowControlManager implements Du
|
||||
if (vo.getThreshold() > 0 && StringUtils.isNotBlank(vo.getThresholdTimeUnit())) {
|
||||
thresholdBucket = Bucket4j.builder()
|
||||
.addLimit(Bandwidth.simple(vo.getThreshold(),
|
||||
InflowControlManager.getPeriod(vo.getThresholdTimeUnit())))
|
||||
AbstractInflowControlManager.getPeriod(vo.getThresholdTimeUnit())))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user