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();
    }
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
针对SpringBoot+JPA后端代码逻辑过大、执行时间过长、效率慢的问题,可以从以下几个方面进行优化: 1. 数据库优化:可以对数据库进行索引、分表、分库等优化操作,以提高查询效率。 2. 代码优化:可以对代码进行重构,采用分层架构、缓存等技术,减少查询次数和数据传输。 3. 异步处理:可以采用异步处理技术,将耗时的操作放入消息队列进行异步处理,从而减少请求响应时间。 4. 分批查询:对于大数据查询,可以采用分批查询的方式,每次查询一部分数据,减少一次查询的数据。 针对gateway timeout超时问题,可以参考以下代码实现: ```java @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/users") public ResponseEntity<List<User>> getUsers() { List<User> users = userService.getUsers(); return ResponseEntity.ok(users); } @GetMapping("/users/async") public DeferredResult<ResponseEntity<List<User>>> getUsersAsync() { DeferredResult<ResponseEntity<List<User>>> deferredResult = new DeferredResult<>(); CompletableFuture.supplyAsync(() -> userService.getUsers()) .whenCompleteAsync((users, throwable) -> { if (throwable != null) { deferredResult.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()); } else { deferredResult.setResult(ResponseEntity.ok(users)); } }); deferredResult.onTimeout(() -> deferredResult.setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).build())); return deferredResult; } } ``` 上述代码,提供了两个接口,`/users` 和 `/users/async` ,前者是同步接口,后者是异步接口。 在同步接口,直接调用`userService.getUsers()`方法获取用户信息,并返回结果。 在异步接口,使用`DeferredResult`对象作为返回结果,将查询操作放入`CompletableFuture`进行异步处理。当查询完成后,通过`DeferredResult`对象返回结果。同时,设置`onTimeout`方法处理请求超时的情况,返回超时错误信息。 通过异步查询的方式,可以避免长时间同步查询阻塞请求,从而避免`gateway timeout`超时问题的发生。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值