Java处理大数据量的同步超时和接口调用次数限制

问题背景:

页面触发同步按钮,后台通过duubo调用服务的多个接口,进行读取写入,中转的时间也比较久,这种情况,如果只是在后台swagger触发接口,数据量十万以上,加了配置,倒是不会超时了,但是在页面点击同步时,直接卡主了,这时dubbo配置超时已经是不能够用的了,时间长的话会导致504网关超时,会很影响用户体验。

目标:

数据同步按钮点击后,页面直接返给友好提示,不再给用户展示调用过程的等待期;且由于数据量会很大,为保证数据同步的完整性,我这里采用对接口触发频率做限制。

执行方案:

使用多线程处理数据;AOP监测接口触发频率。

Show Code:

异步方案:

private static Semaphore sbytStatusSemaphore = new Semaphore(10, true);
private static ThreadPoolExecutor assignSbytStatusPoolExecutor =
            new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(10));

public Result executeFuncA(){
		//异步获取
        try {
            sbytStatusSemaphore.acquire();
        } catch (InterruptedException e) {
            log.error("CenterServiceImpl.executeFuncA:入队失败");
        }
        CompletableFuture.runAsync(() -> {
                    try {
		                 //异步的方法
		                 Result result = getHttzRecursion();
						if (!result.isSuccess()) {
                           log.error("CenterServiceImpl.executeFuncA:同步失败" + 					result.getMessage());
                        }
                    } catch (Exception e) {
                        log.error("CenterServiceImpl.executeFuncA:同步异常" + e.getMessage());
                    } catch (Throwable e) {
                        log.error("CenterServiceImpl.executeFuncA:同步抛出异常" + e.getMessage());

                    } finally {
                        sbytStatusSemaphore.release();
                    }
                }
                , assignSbytStatusPoolExecutor);
        //优先执行此返回结果
       return Result.ok();
}

监听接口调用次数方案:

创建两个类,分别是注解类LimitTime 和工具类LimitTimeAspectUtil

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LimitTime {
    // 访问次数,默认为10次
    int time() default 10;
    // 过期时间,时间戳间隔
    long timeout() default 1;
}
@Component
@Order
@Aspect
@Slf4j
public class LimitTimeAspectUtil {

    private ConcurrentHashMap<String, LimitReqDTO> limitMap = new ConcurrentHashMap<>();

    @Pointcut("@annotation(limitTime)")
    public void limit(LimitTime limitTime) {
    }

    @Around("limit(limitTime)")
    public Object aroundLog(ProceedingJoinPoint joinpoint, LimitTime limitTime) throws Throwable {
        //获取传入的最大访问次数
        int time = limitTime.time();
        //获取计算时间
        long timeout = limitTime.timeout();
        //获取访问方法
        Object target = joinpoint.getTarget().getClass().getName();
        String key = target.toString();
        //如果第一次访问该方法
        if (limitMap.get(key) == null) {
            //新建一次对象存放访问信息
            LimitReqDTO limitDTO = new LimitReqDTO();
            limitDTO.setTime(time - 1);
            limitDTO.setRefreshTime(System.currentTimeMillis());
            limitMap.put(key, limitDTO);
        } else {
            //如果不是第一次访问,获取上次访问的信息
            LimitReqDTO limitDTO = limitMap.get(key);
            //如果和上次刷新时间比已经过期
            if (System.currentTimeMillis() - limitDTO.getRefreshTime() > timeout) {
                //将对象中的刷新时间和访问次数刷新
                limitDTO.setRefreshTime(System.currentTimeMillis());
                limitDTO.setTime(time);
                limitMap.put(key, limitDTO);
            }
            //获取当前访问对象中的剩余访问次数
            int t = (int) limitMap.get(key).getTime();
            //如果访问次数大于0
            if (t > 0) {
                //允许访问,并将访问次数-1
                limitDTO.setTime(--t);
            } else {
                //如果已经没有访问次数,返回错误信息
                return Result.error("操作频繁,请稍后再试。");
            }
        }
        //打印信息
        log.error("剩余次数:" + limitMap.get(key).getTime() + " 方法名称:" + key);
        return joinpoint.proceed();
    }

}

此注解类,应用在controller的方法上

 	@PostMapping("/executeFuncA")
    @LimitTime(time = 1, timeout = 60000)
    public Result executeFuncA() {
        return service.executeFuncA();
    }

你可能感兴趣的:(Java,json)