首页 > 其他分享 >【Spring Security OAuth2】- 【使用Spring MVC开发RESTful API】 异步处理rest服务

【Spring Security OAuth2】- 【使用Spring MVC开发RESTful API】 异步处理rest服务

时间:2024-10-09 08:53:45浏览次数:10  
标签:OAuth2 Spring private API 线程 DeferredResult 源码 logger public

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

异步处理rest服务

  • 使用Callable异步处理rest服务
  • 使用DeferredResult异步处理rest服务
  • 异步处理配置

异步处理就是主线程使用委托副线程去处理业务,然后主线程去接纳其他的请求。提高性能

Callable

202306181906543851.png

    @RestController
    public class AsyncController {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @RequestMapping("/order")
        public Callable<String> order() {
            logger.info("主线程开始");
            Callable<String> result = () -> {
                logger.info("副线程开始");
                TimeUnit.SECONDS.sleep(1);
                logger.info("副线程返回");
                return "success";
            };
            logger.info("主线程返回");
            return result;
        }
    }

输出

    2023-08-02 17:43:24.406  INFO 15644 --- [nio-8080-exec-2] c.e.demo.web.async.AsyncController       : 主线程开始
    2023-08-02 17:43:24.407  INFO 15644 --- [nio-8080-exec-2] c.e.demo.web.async.AsyncController       : 主线程返回
    2023-08-02 17:43:24.414  INFO 15644 --- [      MvcAsync1] c.e.demo.web.async.AsyncController       : 副线程开始
    2023-08-02 17:43:25.414  INFO 15644 --- [      MvcAsync1] c.e.demo.web.async.AsyncController       : 副线程返回

很奇怪。这个是spring mvc提供的支持吗?如果是不在spring框架中使用。自己写个测试例子,是在同一个线程中执行的

DeferredResult

在实际场景中可能会非常复杂,假如如下入这样;下单服务的一个流程

202306181906599342.png

线程1和线程2是隔离的。

业务实现逻辑如下:

  1. 请求下单
  2. 发送消息到消息队列中
  3. 另外一个应用(假如是订单系统)处理下单,并返回处理结果

编写下单请求api

    @RestController
    public class AsyncController {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private MockQueue mockQueue;
        @Autowired
        private DeferredResultHolder deferredResultHolder;
    
        @RequestMapping("/order")
        public DeferredResult<String> order() {
            logger.info("主线程开始");
            // 发送消息到消息队列,请求订单生成
            String orderNumber = RandomStringUtils.randomNumeric(8);
            mockQueue.setPlaceOrder(orderNumber);
            DeferredResult<String> deferredResult = new DeferredResult<>();
            // holder 只是用来存储 DeferredResult
            // 方便监听器线程拿到 DeferredResult
            deferredResultHolder.getMap().put(orderNumber, deferredResult);
            logger.info("主线程返回");
            return deferredResult;
        }
    }

编写模拟队列

    @Component
    public class MockQueue {
        private Logger logger = LoggerFactory.getLogger(getClass());
        private String placeOrder; // 请求下单
        private String completeOrder;  // 下单完成
    
        public String getPlaceOrder() {
            return placeOrder;
        }
    
        public void setPlaceOrder(String placeOrder) {
            // 模拟另外一个线程去执行耗时操作
            new Thread(() -> {
                logger.info("接到下单请求");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.completeOrder = "下单请求处理完毕";
                this.placeOrder = placeOrder;
                logger.info(completeOrder + " : " + placeOrder);
            }).start();
        }
    
        public String getCompleteOrder() {
            return completeOrder;
        }
    
        public void setCompleteOrder(String completeOrder) {
            this.completeOrder = completeOrder;
        }
    }

编写一个装载 DeferredResult 的缓存类;用来存储已经产生的DeferredResult

    @Component
    public class DeferredResultHolder {
        private Map<String, DeferredResult<String>> map = new HashMap<>();
    
        public Map<String, DeferredResult<String>> getMap() {
            return map;
        }
    
        public void setMap(Map<String, DeferredResult<String>> map) {
            this.map = map;
        }
    }

启动一个线程专门来监听消息队列处理结果,并且调用 deferredResult 返回结果

    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    import java.util.concurrent.TimeUnit;
    
    // ContextRefreshedEvent 当应用程序上下文初始化或刷新时引发的事件。
    @Component
    public class QueueLinstener implements ApplicationListener<ContextRefreshedEvent> {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private MockQueue mockQueue;
        @Autowired
        private DeferredResultHolder deferredResultHolder;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            // 在线程中执行否则会阻塞监听器回调的线程
            new Thread(() -> {
                while (true) {
                    // 当有订单完成时
                    if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
                        String orderNumber = mockQueue.getPlaceOrder();
                        logger.info("返回订单处理结果:" + orderNumber);
                        deferredResultHolder.getMap().get(orderNumber).setResult("place order success");
                        mockQueue.setCompleteOrder(null);
                        deferredResultHolder.getMap().remove(orderNumber);
                    } else {
                        try {
                            TimeUnit.MILLISECONDS.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }

访问api输出的结果如下:

    2023-08-02 21:53:45.919  INFO 16944 --- [nio-8080-exec-2] c.e.demo.web.async.AsyncController       : 主线程开始
    2023-08-02 21:53:45.920  INFO 16944 --- [nio-8080-exec-2] c.e.demo.web.async.AsyncController       : 主线程返回
    2023-08-02 21:53:45.921  INFO 16944 --- [       Thread-8] com.example.demo.web.async.MockQueue     : 接到下单请求
    2023-08-02 21:53:46.922  INFO 16944 --- [       Thread-8] com.example.demo.web.async.MockQueue     : 下单请求处理完毕 : 34633453
    2023-08-02 21:53:47.269  INFO 16944 --- [       Thread-2] c.example.demo.web.async.QueueLinstener  : 返回订单处理结果:34633453

可以看到请求线程已经结束了,由其他线程进行处理并返回结果的;
这个套路就如同 socket里面reactor模型,主线程只接收请求,其他线程去执行逻辑;
但是有一个问题就是,容器里面肯定有一个线程接收请求,然后启用子线程去分发处理;然后这里还有搞多线程去处理,这个是什么套路呢?对性能有提高吗?

总结下

DeferredResult 在效果上实现了一个让主线程可以立即返回,但是连接不断开,可以通过DeferredResult设置返回结果来让连接返回并断开的功能

异步功能配置

在WebConfig中重写异步支持配置;从上面的例子中也看到了,主线程返回了。所以同步拦截器可能就不那么好用了;可以在这里配置

    public class WebConfig implements WebMvcConfigurer {
        @Autowired
        private TimeInterceptor timeInterceptor;
    
        @Override
        public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
            // 异步支持
    //        configurer.registerCallableInterceptors(); // callable 拦截器
    //        configurer.registerDeferredResultInterceptors(); // deferredResult拦截器
    //        configurer.setTaskExecutor(); // 应该是自定义线程池
    //        configurer.setDefaultTimeout() // 超时设置
        }

标签:OAuth2,Spring,private,API,线程,DeferredResult,源码,logger,public
From: https://blog.csdn.net/smart_an/article/details/142773608

相关文章

  • 【Spring Security OAuth2】- 【使用Spring MVC开发RESTful API】 使用切片拦截rest服
    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬学习必须往深处挖,挖的越深,基础越扎实!阶段1、深入多线程阶段2、深入多线程设计模式阶段3、深入juc源码解析阶段4、深入jdk其余源码解析......
  • 【含文档+源码】基于SpringBoot的健身工作室管理系统的设计与实现
    项目背景与意义随着健身行业的快速发展,越来越多的消费者开始注重健康和身材管理,这使得健身房成为了一个蓬勃发展的行业。然而,随着客户数量的不断增加,如何有效管理客户信息,提供个性化服务,以及提升客户满意度,成为了健身房面临的重要挑战。传统的客户信息管理方式往往依赖于纸质......
  • springboot-网站开发-linux服务器部署jar格式图片存档路径问题
    springboot-网站开发-linux服务器部署jar格式图片存档路径问题!近期在部署自己的网站源码,使用的是jar格式的编码格式。发布到远程服务器后,发现客户捐款的证书图片存在异常。经过排查代码,找到了原因。下面分享给大家。1:首先,在linux服务器内部,存档图片,文件等资源的时候,本地java......
  • springboot-网站开发-thymeleaf引擎报错找不到指定的页面模板文件
    springboot-网站开发-thymeleaf引擎报错找不到指定的页面模板文件!这种错误的情况,发生,一般都是因为,我们自己的html模板文件,存档位置并不是在默认的templates下面。而是我们自己新建的一个子目录里面。然后,我们在java代码里面,控制器方法体内,return,返回模板的时候,我们多写了一个......
  • Google:敏感信息访问权限和 API 政策更新
    目录公布时间公布内容内容有关GooglePlay照片和视频权限政策的详细信息截止时间相关问题公布时间公布日期:2023-10-25公布内容内容为向用户提供更注重隐私保护的体验,我们将推出“照片和视频访问权限”政策,以减少获准针对照片/视频请求广泛权限(READ_......
  • 基于数据可视化+Java+SpringBoot+Vue实现的高校食堂移动预约点餐系统设计与实现
    文章目录前言系统演示录像论文参考代码运行展示图技术框架SpringBoot技术介绍系统测试系统测试的目的系统功能测试推荐选题:代码参考实现案例找我做程序,有什么保障?联系我们前言......
  • springboot+vue基于工作流的会议和督办管理系统【开题+程序+论文】
    系统程序文件列表开题报告内容研究背景在当今快节奏的商业环境中,高效会议与事务督办成为企业日常运营不可或缺的一部分。然而,传统会议管理方式往往存在流程繁琐、效率低下、资源分配不均等问题,影响了企业的决策速度和执行力。随着信息技术的飞速发展,工作流技术在各类管理系......
  • springboot+vue基于的个人健康管理系统【开题+程序+论文】
    系统程序文件列表开题报告内容研究背景在信息化高速发展的时代,个人健康管理已成为现代社会关注的焦点。随着人们生活水平的提高和健康意识的增强,越来越多的人开始重视个人健康状况的监测与管理。然而,传统的健康管理方式存在诸多不便,如信息记录不完整、健康数据分散、健康咨......
  • springboot+vue基于SpringBoot的个人健康管理系统【开题+程序+论文】
    系统程序文件列表开题报告内容研究背景随着现代生活节奏的加快,人们对个人健康管理的重视程度日益提升。在信息化时代背景下,利用互联网技术实现个人健康信息的有效管理与跟踪已成为一种趋势。传统的健康管理方式大多依赖于纸质记录或零散的电子文档,难以形成系统化的健康档案......
  • springboot+vue基于大数据的疫情追踪系统的设计和实现【开题+程序+论文】
    系统程序文件列表开题报告内容研究背景随着全球信息化技术的飞速发展,大数据已成为推动社会进步和产业升级的重要力量。近年来,全球范围内频繁爆发的疫情事件,对公共卫生体系构成了严峻挑战。传统的疫情追踪方式存在信息滞后、数据孤岛等问题,难以有效应对疫情的快速传播。因此......