menu 不会旅行的兔子不是好青蛙
search self_improvement
目录

Spring Boot 异步通信实现扫码登陆

旅行青蛙
旅行青蛙 2023年10月08日  ·  阅读 1,903

需求很简单,就是为了实现PC站点的扫码登陆,方案有很多,比如WebSocket搞一个长链接,或者短链接轮训,考虑来考虑去,都感觉不是很好,最后选择的伪长链接方案,既可以解决短链接的实时问题,有可以降低服务器长链接资源占用问题,居于两者之间

检索了一下SpringBoot的文档,实现异步接口方便的很,使用@Async或者返回Callable接口都可以快速的解决问题,但是最终没有选择这两个方案,这两个方案封装无法做到按需返回,都是在子线程实现事物完成后,返回,对于主线程来说是异步的,但是无法做到按需返回结果,所以最终选择了HttpServletRequest对象中的startAsync();实现如下

AsyncContext context = request.startAsync();
       context.getResponse().setCharacterEncoding("utf-8");
       context.getResponse().setContentType("text/html;charset=UTF-8");
       //缓存AsyncContext,方便扫码后的处理
       map.put(key, context);
       context.addListener(new AsyncListener() {
           @Override
           public void onTimeout(AsyncEvent event) throws IOException {
               System.out.println("超时了...");
               PrintWriter writer = context.getResponse().getWriter();
               writer.print(JSON.toJSONString(CommonResult.errorReturn(400006, "超时")));
               writer.flush();
               context.complete();
           }

           @Override
           public void onStartAsync(AsyncEvent event) throws IOException {
               System.out.println("线程开始");
           }

           @Override
           public void onError(AsyncEvent event) throws IOException {
               log.error("error", event.getThrowable());
               clearQrCode(key);
           }

           @Override
           public void onComplete(AsyncEvent event) throws IOException {
               clearQrCode(key);
           }
       });
       //设置超时时间
       context.setTimeout(20000);

这里的异步被设置了20秒超时,并且在超时的方法内实现了complete(),主要就是为了解决线程超时后正常返回结果,比如在20秒内扫码后,会执行如下操作

@Override
    public void setQrCodeState(String key, String appId, String openId) {
        AsyncContext context = map.get(key);
        if (context == null) {
            return;
        }
        context.start(() -> {
            try {
               //扫码登陆逻辑处理
               sleep(1000);
            } catch (Exception e) {
                log.error("exception:", e);
            } finally {
                context.complete();
                //移除缓存map中的AsyncContext,防止资源泄漏
                map.remove(key);
            }
        });
    }
分类:
标签: Spring Boot Java