package com.eactive.eai.common.inflow; import java.time.Duration; import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import com.eactive.eai.common.exception.ExceptionUtil; import com.eactive.eai.common.lifecycle.Lifecycle; import com.eactive.eai.common.lifecycle.LifecycleException; import com.eactive.eai.common.lifecycle.LifecycleListener; import com.eactive.eai.common.lifecycle.LifecycleSupport; import com.eactive.eai.common.util.Logger; import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket4j; import io.github.bucket4j.Refill; import io.github.bucket4j.local.LocalBucket; import io.github.bucket4j.local.LocalBucketBuilder; public abstract class AbstractInflowControlManager implements Lifecycle, Bucket { static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); public static final String QUOTA_TIMEUNIT_SECOND = "SEC"; public static final String QUOTA_TIMEUNIT_MINUTE = "MIN"; public static final String QUOTA_TIMEUNIT_HOUR = "HOU"; public static final String QUOTA_TIMEUNIT_DAY = "DAY"; public static final String QUOTA_TIMEUNIT_MONTH = "MON"; private volatile Map adapterBucketList = new ConcurrentHashMap<>(); private volatile Map interfaceBucketList = new ConcurrentHashMap<>(); private volatile Map groupBucketList = new ConcurrentHashMap<>(); private volatile Map interfaceToGroupMap = new ConcurrentHashMap<>(); private volatile Map groupVoMap = new ConcurrentHashMap<>(); private boolean started; private LifecycleSupport lifecycle = new LifecycleSupport(this); @Autowired protected InflowControlDAO inflowControlDAO; public void start() throws LifecycleException { if (started) throw new LifecycleException("RECEAICMM201"); lifecycle.fireLifecycleEvent(STARTING_EVENT, this); try { init(); } catch (Exception e) { throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAICMM202")); } started = true; lifecycle.fireLifecycleEvent(STARTED_EVENT, this); } private void init() throws Exception { initAdapter(); initInterface(); initGroup(); } private void initAdapter() throws Exception { Map newMap = new HashMap<>(); List inflowAdapterVoList = inflowControlDAO.getInflowAdapterList(); for (InflowTargetVO inflowVo : inflowAdapterVoList) { if (AbstractInflowControlManager.isInflowTarget(inflowVo)) { CustomBucket bucket = makeBucketUsingInflowVO(inflowVo); if (bucket != null) { newMap.put(inflowVo.getName(), bucket); } } } this.adapterBucketList = newMap; } private void initInterface() throws Exception { Map newMap = new HashMap<>(); List inflowInterfaceVoList = inflowControlDAO.getInflowInterfaceList(); for (InflowTargetVO inflowVo : inflowInterfaceVoList) { if (AbstractInflowControlManager.isInflowTarget(inflowVo)) { CustomBucket bucket = makeBucketUsingInflowVO(inflowVo); if (bucket != null) { newMap.put(inflowVo.getName(), bucket); } } } this.interfaceBucketList = newMap; } private void initGroup() throws Exception { Map newGroupBucketList = new HashMap<>(); Map newInterfaceToGroupMap = new HashMap<>(); Map newGroupVoMap = new HashMap<>(); List inflowGroupVoList = inflowControlDAO.getInflowGroupList(); for (InflowGroupVO groupVo : inflowGroupVoList) { if (isInflowGroupTarget(groupVo)) { CustomGroupBucket bucket = makeGroupBucket(groupVo); if (bucket != null) { newGroupBucketList.put(groupVo.getGroupId(), bucket); newGroupVoMap.put(groupVo.getGroupId(), groupVo); for (String interfaceId : groupVo.getInterfaceList()) { newInterfaceToGroupMap.put(interfaceId, groupVo.getGroupId()); } } } } if (logger.isInfo()) { logger.info("InflowControlManager] initGroup completed. groups=" + newGroupBucketList.size() + ", mappings=" + newInterfaceToGroupMap.size()); } this.groupBucketList = newGroupBucketList; this.interfaceToGroupMap = newInterfaceToGroupMap; this.groupVoMap = newGroupVoMap; } public synchronized void reloadAdapter() throws Exception { if (logger.isWarn()) { logger.warn("InflowControlManager] reload all Started ..."); } initAdapter(); if (logger.isWarn()) { logger.warn("InflowControlManager] reload all finished ..."); } } public synchronized void reloadInterface() throws Exception { if (logger.isWarn()) { logger.warn("InflowControlManager] reload all Started ..."); } initInterface(); if (logger.isWarn()) { logger.warn("InflowControlManager] reload all finished ..."); } } public synchronized void reloadAdapter(String adapter) throws Exception { if (logger.isWarn()) { logger.warn("InflowControlManager] reload Started ..."); } Map map = inflowControlDAO.getInflowTargetByAdater(adapter); if (map != null) { Iterator it = map.keySet().iterator(); String key = ""; while (it.hasNext()) { key = it.next(); InflowTargetVO inflowTarget = map.get(key); if (AbstractInflowControlManager.isInflowTarget(inflowTarget)) { CustomBucket bucket = makeBucketUsingInflowVO(inflowTarget); if (bucket != null) { adapterBucketList.put(inflowTarget.getName(), bucket); } else { adapterBucketList.remove(inflowTarget.getName()); } } else { adapterBucketList.remove(inflowTarget.getName()); } } } if (logger.isWarn()) { logger.warn("InflowControlManager] reload finished ..."); } } public synchronized void reloadInterface(String inter) throws Exception { if (logger.isWarn()) { logger.warn("InflowControlManager] reload Started ..."); } Map map = inflowControlDAO.getInflowTargetByInterface(inter); if (map != null) { Iterator it = map.keySet().iterator(); String key = ""; while (it.hasNext()) { key = it.next(); InflowTargetVO inflowTarget = map.get(key); if (AbstractInflowControlManager.isInflowTarget(inflowTarget)) { CustomBucket bucket = makeBucketUsingInflowVO(inflowTarget); if (bucket != null) { interfaceBucketList.put(inflowTarget.getName(), bucket); } else { interfaceBucketList.remove(inflowTarget.getName()); } } else { interfaceBucketList.remove(inflowTarget.getName()); } } } if (logger.isWarn()) { logger.warn("InflowControlManager] reload finished ..."); } } public synchronized void reloadGroup() throws Exception { if (logger.isWarn()) { logger.warn("InflowControlManager] reloadGroup all Started ..."); } initGroup(); if (logger.isWarn()) { logger.warn("InflowControlManager] reloadGroup all finished ..."); } } public synchronized void reloadGroup(String groupId) throws Exception { if (logger.isWarn()) { logger.warn("InflowControlManager] reloadGroup Started ... groupId=" + groupId); } Map map = inflowControlDAO.getInflowTargetByGroup(groupId); if (map != null) { Iterator> mappingIt = interfaceToGroupMap.entrySet().iterator(); while (mappingIt.hasNext()) { Map.Entry entry = mappingIt.next(); if (groupId.equals(entry.getValue())) { mappingIt.remove(); } } Iterator it = map.keySet().iterator(); while (it.hasNext()) { String key = it.next(); InflowGroupVO groupVo = map.get(key); if (isInflowGroupTarget(groupVo)) { CustomGroupBucket bucket = makeGroupBucket(groupVo); if (bucket != null) { groupBucketList.put(groupVo.getGroupId(), bucket); groupVoMap.put(groupVo.getGroupId(), groupVo); for (String interfaceId : groupVo.getInterfaceList()) { interfaceToGroupMap.put(interfaceId, groupVo.getGroupId()); } } else { groupBucketList.remove(groupVo.getGroupId()); groupVoMap.remove(groupVo.getGroupId()); } } else { groupBucketList.remove(groupVo.getGroupId()); groupVoMap.remove(groupVo.getGroupId()); } } } if (logger.isWarn()) { logger.warn("InflowControlManager] reloadGroup finished ..."); } } public void stop() throws LifecycleException { if (!started) throw new LifecycleException("RECEAICMM203"); lifecycle.fireLifecycleEvent(STOPING_EVENT, this); adapterBucketList = new ConcurrentHashMap<>(); interfaceBucketList = new ConcurrentHashMap<>(); groupBucketList = new ConcurrentHashMap<>(); interfaceToGroupMap = new ConcurrentHashMap<>(); groupVoMap = new ConcurrentHashMap<>(); started = false; lifecycle.fireLifecycleEvent(STOPPED_EVENT, this); } public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } public LifecycleListener[] findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); } public boolean isStarted() { return this.started; } public String[] getAdapterAllKeys() { Iterator it = this.adapterBucketList.keySet().iterator(); String[] svcCd = new String[this.adapterBucketList.size()]; for (int i = 0; it.hasNext(); i++) { svcCd[i] = it.next(); } Arrays.sort(svcCd); return svcCd; } public String[] getInterfaceAllKeys() { Iterator it = this.interfaceBucketList.keySet().iterator(); String[] svcCd = new String[this.interfaceBucketList.size()]; for (int i = 0; it.hasNext(); i++) { svcCd[i] = it.next(); } Arrays.sort(svcCd); return svcCd; } public synchronized void removeAdapter(String adapter) { adapterBucketList.remove(adapter); } public synchronized void removeInterface(String inter) { interfaceBucketList.remove(inter); } public synchronized void removeGroup(String groupId) { groupBucketList.remove(groupId); groupVoMap.remove(groupId); Iterator> it = interfaceToGroupMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); if (groupId.equals(entry.getValue())) { it.remove(); } } } public String[] getGroupAllKeys() { Iterator it = this.groupBucketList.keySet().iterator(); String[] groupIds = new String[this.groupBucketList.size()]; for (int i = 0; it.hasNext(); i++) { groupIds[i] = it.next(); } Arrays.sort(groupIds); return groupIds; } public CustomGroupBucket getGroupBucket(String groupId) { return groupBucketList.get(groupId); } private CustomBucket makeBucketUsingInflowVO(InflowTargetVO inflowTarget) { if (AbstractInflowControlManager.isInflowTarget(inflowTarget)) { LocalBucketBuilder builder = Bucket4j.builder(); if (inflowTarget.getThresholdPerSecond() > 0) { builder.addLimit(Bandwidth.classic(inflowTarget.getThresholdPerSecond(), Refill.greedy(inflowTarget.getThresholdPerSecond(), Duration.ofSeconds(1)))); } if (inflowTarget.getThreshold() > 0 && StringUtils.isNotBlank(inflowTarget.getThresholdTimeUnit())) { builder.addLimit(Bandwidth.classic(inflowTarget.getThreshold(), Refill.intervally(inflowTarget.getThreshold(), AbstractInflowControlManager.getPeriod(inflowTarget.getThresholdTimeUnit())))); } return new CustomBucket(builder.build(), inflowTarget); } return null; } private CustomGroupBucket makeGroupBucket(InflowGroupVO groupVo) { if (!isInflowGroupTarget(groupVo)) { return null; } LocalBucket perSecondBucket = null; LocalBucket thresholdBucket = null; if (groupVo.getThresholdPerSecond() > 0) { perSecondBucket = Bucket4j.builder() .addLimit(Bandwidth.classic(groupVo.getThresholdPerSecond(), Refill.greedy(groupVo.getThresholdPerSecond(), Duration.ofSeconds(1)))) .build(); } if (groupVo.getThreshold() > 0 && StringUtils.isNotBlank(groupVo.getThresholdTimeUnit())) { thresholdBucket = Bucket4j.builder() .addLimit(Bandwidth.classic(groupVo.getThreshold(), Refill.intervally(groupVo.getThreshold(), AbstractInflowControlManager.getPeriod(groupVo.getThresholdTimeUnit())))) .build(); } return new CustomGroupBucket(perSecondBucket, thresholdBucket, groupVo); } @Override public boolean isAdapterPass(String adapter) { CustomBucket b = adapterBucketList.get(adapter); if (b == null) return true; return b.getLocalBucket().tryConsume(1); } @Override public boolean isInterfacePass(String inter) { CustomBucket b = interfaceBucketList.get(inter); if (b == null) return true; return b.getLocalBucket().tryConsume(1); } @Override public InflowTargetVO getAdapterInflowThreashold(String adapter) { CustomBucket b = adapterBucketList.get(adapter); if (b == null) return null; return b.getInflowTargetVo(); } @Override public InflowTargetVO getInterfaceInflowThreashold(String inter) { CustomBucket b = interfaceBucketList.get(inter); if (b == null) return null; return b.getInflowTargetVo(); } @Override public InflowTargetVO getInflowThreashold(String inter) { return null; } public InflowTargetVO getAdapterInflow(String adapter) { return getAdapterInflowThreashold(adapter); } public InflowTargetVO getInterfaceInflow(String inter) { return getInterfaceInflowThreashold(inter); } @Override public String isGroupPass(String groupId) { CustomGroupBucket b = groupBucketList.get(groupId); if (b == null) return null; return b.tryConsume(); } @Override public InflowGroupVO getGroupInflowThreshold(String groupId) { return groupVoMap.get(groupId); } @Override public String getGroupIdByInterface(String interfaceId) { return interfaceToGroupMap.get(interfaceId); } public InflowGroupVO getGroupInflow(String groupId) { return getGroupInflowThreshold(groupId); } public static boolean isInflowTarget(InflowTargetVO inflowTarget) { if (inflowTarget == null || !inflowTarget.isActivate()) { return false; } return inflowTarget.getThresholdPerSecond() > 0 || (inflowTarget.getThreshold() > 0 && StringUtils .equalsAnyIgnoreCase(inflowTarget.getThresholdTimeUnit(), QUOTA_TIMEUNIT_DAY, QUOTA_TIMEUNIT_SECOND, QUOTA_TIMEUNIT_MINUTE, QUOTA_TIMEUNIT_HOUR, QUOTA_TIMEUNIT_DAY, QUOTA_TIMEUNIT_MONTH)); } public static boolean isInflowGroupTarget(InflowGroupVO groupVo) { if (groupVo == null || !groupVo.isActivate()) { return false; } return groupVo.getThresholdPerSecond() > 0 || (groupVo.getThreshold() > 0 && StringUtils .equalsAnyIgnoreCase(groupVo.getThresholdTimeUnit(), QUOTA_TIMEUNIT_DAY, QUOTA_TIMEUNIT_SECOND, QUOTA_TIMEUNIT_MINUTE, QUOTA_TIMEUNIT_HOUR, QUOTA_TIMEUNIT_DAY, QUOTA_TIMEUNIT_MONTH)); } public static Duration getPeriod(String timeUnit) { if (StringUtils.equalsIgnoreCase(timeUnit, QUOTA_TIMEUNIT_SECOND)) { return Duration.ofSeconds(1); } else if (StringUtils.equalsIgnoreCase(timeUnit, QUOTA_TIMEUNIT_MINUTE)) { return Duration.ofMinutes(1); } else if (StringUtils.equalsIgnoreCase(timeUnit, QUOTA_TIMEUNIT_HOUR)) { return Duration.ofHours(1); } else if (StringUtils.equalsIgnoreCase(timeUnit, QUOTA_TIMEUNIT_DAY)) { return Duration.ofDays(1); } else if (StringUtils.equalsIgnoreCase(timeUnit, QUOTA_TIMEUNIT_MONTH)) { Calendar calendar = Calendar.getInstance(); return Duration.ofDays(calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); } else { return null; } } }