package com.eactive.eai.custom.inflow; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.stereotype.Component; import com.eactive.eai.common.inflow.Bucket; import com.eactive.eai.common.inflow.InflowTargetVO; import com.eactive.eai.common.inflow.InflowType; import com.eactive.eai.common.inflow.dual.DualCustomBucket; import com.eactive.eai.common.inflow.dual.DualInflowControlManager; import com.eactive.eai.common.lifecycle.LifecycleException; import com.eactive.eai.common.util.ApplicationContextProvider; import com.eactive.eai.common.util.Logger; /** * 어댑터/인터페이스 이중 버킷(DualInflowControlManager)에 클라이언트 유량제어를 추가한 관리자. * *

적용 방법: INFLOW.properties에 아래 한 줄 추가. *

inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager
*/ @Component public class ClientDualInflowControlManager extends DualInflowControlManager implements ClientDualBucket { private static final Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); public static synchronized Bucket getBucket() { return ApplicationContextProvider.getContext().getBean(ClientDualInflowControlManager.class); } private volatile Map clientBucketMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap clientMetricsMap = new ConcurrentHashMap<>(); // ------------------------------------------------------------------------- // Lifecycle // ------------------------------------------------------------------------- @Override public void start() throws LifecycleException { super.start(); try { initClients(); } catch (Exception e) { throw new LifecycleException("ClientDualInflowControlManager init failed: " + e.getMessage()); } } // ------------------------------------------------------------------------- // 초기화 / 리로드 // ------------------------------------------------------------------------- private void initClients() throws Exception { this.clientBucketMap = loadBucketMap(InflowType.CLIENT); if (logger.isInfo()) logger.info("ClientDualInflowControlManager] initClients: " + clientBucketMap.size() + " buckets"); } 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); } // ------------------------------------------------------------------------- // 클라이언트 메트릭 접근자 // ------------------------------------------------------------------------- public TargetMetrics getClientMetrics(String clientId) { return clientMetricsMap.get(clientId); } public Map getAllClientMetrics() { return Collections.unmodifiableMap(clientMetricsMap); } public void resetClientMetrics(String clientId) { TargetMetrics m = clientMetricsMap.get(clientId); if (m != null) m.reset(); } public void resetAllClientMetrics() { clientMetricsMap.values().forEach(TargetMetrics::reset); } // ------------------------------------------------------------------------- // ClientDualBucket — 클라이언트 유량제어 // ------------------------------------------------------------------------- @Override public String isClientPassDetail(String clientId) { DualCustomBucket b = clientBucketMap.get(clientId); if (b == null) return DualCustomBucket.RESULT_PASS; String result = b.tryConsume(); recordMetrics(clientMetricsMap, clientId, result); return result; } @Override public InflowTargetVO getClientInflowThreshold(String clientId) { DualCustomBucket b = clientBucketMap.get(clientId); return (b == null) ? null : b.getInflowTargetVo(); } // ------------------------------------------------------------------------- // 모니터링용 접근자 // ------------------------------------------------------------------------- public DualCustomBucket getClientBucket(String clientId) { return clientBucketMap.get(clientId); } public Map getClientBucketMap() { return clientBucketMap; } }