Reactive思考

news2024/11/19 7:43:06

 最近在学习响应式编程,由此翻看了很多资料,在此把思考过程记录下

目录

来由

规范

具体实现

历史

1. Servlet的尝试

1.1 Async实现demo

2. web应用层

2.1 web-flux

2.2 web-flux/web-mvc 与 spring-web架构关系

3. DBConnection层

相关可能混淆的概念

附:关于Servlet历史的描述来源于:讨论


来由

The Reactive Manifesto:https://www.reactivemanifesto.org/

规范

Jvm实现规范:https://github.com/reactive-streams/reactive-streams-jvm

具体实现

  1. web层:web-flux
  2. DB层:r2dbc

历史

1. Servlet的尝试

看以上资料过程中,得知Servlet也对此做过尝试,痛点来源于以下两点:

  1. client端发送量大且发送速度(比如带宽较小)无法于server端处理速度匹配时(server能够处理过来)
  2. server端在处理过程中有io(network或disk)延迟较高的处理

以上两种情况都会导致在传统servlet及其web container(经典的比如tomcat)下造成线程阻塞,比如tomcat采用线程池处理request请求,一旦client端数量较多且以上两种情况发生时,会造成server端无法处理request请求(等线程资源)。

历史如下:

  1. 3.0之前:servlet接收到请求后,释放来自于web container的thread,开启新的线程,处理request和response,但这些由具体的web container实现,Tomcat’s Comet, WebLogic’s FutureResponseServlet and WebSphere’s Asynchronous Request Dispatcher
  2. 3.0:支持Async,在Servlet spec层面支持一个单独的线程池处理此类请求
  3. 3.1:NIO,本质就是在对3.0的一个补充,在I/O层面使用了非阻塞IO模型,将原来会阻塞在IO等待上的线程进一步释放,request可读的listener和response可write的listener,也是在spec层面支持,并由spring-web进行了具体实现(未明白和IO多路复用的关系,待深入学习,看样子是拷贝到user-space的监听?

1.1 Async实现demo

package org.example;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.AsyncContext;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// todo 该注解在springboot下无效,或者是没发现别的机制
// @WebServlet(urlPatterns = "/asyncservlet", asyncSupported = true)

/**
 * 主要作用就是把server端servlet的thread释放出来,弄一个新线程,去从input读或response写,
 * 进而将container中的thread释放出来,能够再处理更多的这样的连接,不至于卡死web container(如典型的tomcat)
 * 好像servlet 3.0已经支持(todo 在头里加ASYNC,而开发者不用管? 待验证)
 * 深入想一下,
 *  1. tomcat是什么?实现了http协议解析并基于servlet规范实现
 *  2. webmvc是什么?实现了具体的servlet,并且基于spring-web规范实现
 *  初步总结,他俩和一起能够使用spring mvc,抽象层次不同
 *  3. webflux是什么?实现了具体的reactive,并且基于spring-web规范实现
 *  所以,
 *      第一点,webflux不排斥servlet,只要做好adapter就行
 *      第二点,没想通的是,controller和flux提供者该如何写?
 *          flux提供者需要stream式/async式提供,不能在main中实现,否则就是sync
 *      第三点,基于reactive的webflux在:
 *          netty下,已经是基于recator实现的
 *          servlet-container下,在具体的servlet实现基础上,尽早的释放了原先会block的thread(web container的http-nio命名的线程池中线程),
 *          将result处理过程改为pub-sub的方式,并在处理完后对response做commit处理(与servlet的async机制类似,只不过抽象层级为spring-web)
 * httpserver层次的IO多路复用+应用层次的reactive(附带IO部分的NIO)+后端处理层面的r2dbc/r2...整体才是最佳方案。
 */
@Slf4j
public class AsyncServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response) {
        response.setContentType("text/html;charset=UTF-8");
        final AsyncContext acontext = request.startAsync();
        log.info("main, thread:{}", Thread.currentThread());
        acontext.start(new Runnable() {
            @SneakyThrows
            public void run() {
                String param = acontext.getRequest().getParameter("param");
                log.info("async,param:{}, thread:{}", param, Thread.currentThread());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ServletResponse response = acontext.getResponse();
                /* ... print to the response ... */
                response.getOutputStream().write("success1".getBytes());
                // 执行此,会立即将数据发送给client
                response.getOutputStream().flush();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                response.getOutputStream().write("success2".getBytes());
                // 未执行flush,故会与3一起返回
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                response.getOutputStream().write("success3".getBytes());
                // 以上print信息:说明是异步
                // 2023-01-07 11:06:15.398  INFO 32164 --- [nio-8080-exec-1] org.example.AsyncServlet                 : main, thread:Thread[http-nio-8080-exec-1,5,main]
                // 2023-01-07 11:06:15.404  INFO 32164 --- [nio-8080-exec-2] org.example.AsyncServlet                 : async,param:gxx, thread:Thread[http-nio-8080-exec-2,5,main]
                // 中间是10s的等待
                // 2023-01-07 11:06:45.894  INFO 32164 --- [nio-8080-exec-3] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
                // 2023-01-07 11:06:45.894  INFO 32164 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
                // 2023-01-07 11:06:45.897  INFO 32164 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms
                // 2023-01-07 11:06:45.989 ERROR 32164 --- [nio-8080-exec-3] s.e.ErrorMvcAutoConfiguration$StaticView : Cannot render error page for request [/asyncservlet] as the response has already been committed. As a result, the response may have the wrong status code.
                // 此处无关
                response.getOutputStream().flush();
                // 主要是此处complete能及时告知完成了(todo timeout=20s后,也会由client端发起complete??)
                acontext.complete();
            }
        });
    }
}

2. web应用层

2.1 web-flux

见官网

2.2 web-flux/web-mvc 与 spring-web架构关系

3. DBConnection层

r2dbc 

相关可能混淆的概念

  1. reactor&&proactor(此为IO多路复用技术,可查看链接学习)

附:关于Servlet历史的描述来源于:讨论

Prior to Servlet 3.0:
The problem with synchronous processing of requests is that it resulted in threads (doing heavy-lifting) running for a long time before the response goes out. If this happens at scale, the servlet container eventually runs out of threads - long running threads lead to thread starvation.

Prior to Servlet 3.0, there were container specific solutions for these long running threads where we can spawn a separate worker thread to do the heavy task and then return the response to client. The servlet thread returns to the servlet pool after starting the worker thread. Tomcat’s Comet, WebLogic’s FutureResponseServlet and WebSphere’s Asynchronous Request Dispatcher are some of the example of implementation of asynchronous processing.
(See link 1 for more info.)

Servlet 3.0 Async:
The actual work could be delegated to a thread pool implementation (independent of the container specific solutions). The Runnable implementation will perform the actual processing and will use the AsyncContext to either dispatch the request to another resource or write the response. We can also add AsyncListener implementation to the AsyncContext object to implement callback methods.
(See link 1 for more info.)

Servlet 3.1 NIO:
As described above, Servlet 3.0 allowed asynchronous request processing but only traditional I/O (as opposed to NIO) was permitted. Why is traditional I/O a problem?

In traditional I/O, there are two scenarios to consider:

  • If the data coming into the server (I/O) is blocking or streamed slower than the server can read, then the server thread that is trying to read this data has to wait for that data.
  • On the other hand, if the response data from the server written to ServletOutputStream is slow, the client thread has to wait. In both cases, the server thread doing the traditional I/O (for requests/responses) blocks.

In other words, with Servlet 3.0, only the request processing part became async, but not the I/O for serving the requests and responses. If enough threads block, this results in thread starvation and affects performance.

With Servlet 3.1 NIO, this problem is solved by ReadListener and WriteListener interfaces. These are registered in ServletInputStream and ServletOutputStream. The listeners have callback methods that are invoked when the content is available to be read or can be written without the servlet container blocking on the I/O threads. So these I/O threads are freed up and can now serve other request increasing performance. (See link 2 for more info.)

相关资料:

https://blog.csdn.net/GAOXINXINGgaoxinxing/article/details/128591628

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/147939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STL中vector怎么扩容

STL vector扩容 vector容器 vector被称为向量容器,该容器擅长在尾部插入或删除元素,时间复杂度为O(1);而对于在vector容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间)&#xf…

十九、Kubernetes中Pod调度第一篇

1、概述 在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些…

Gradle学习笔记之插件

文章目录插件的作用插件的分类和使用脚本插件对象插件第三方插件用户自定义插件buildSrc项目编写步骤其他项目/模块引用步骤java插件常用属性插件的作用 插件可以帮助我们促进代码的重用、项目更高程度的模块化、自动化和便捷化,减少功能类似代码的编写&#xff0c…

【数据结构与算法】 | 用队列实现栈

用队列实现栈 链接: https://leetcode.cn/problems/implement-stack-using-queues/ 1. 要求 2. 思路 栈的性质是先进后出,队列的性质是先进先出。 题目要求是用两个队列来完成栈的实现,那么大思路就是通过倒腾数据来完成。 3. 代码实现…

JAVA系列 多线程 Thread类 耦合 线程调度 如何创建线程 弱耦合 完全解耦 思维结构整理

这估计是我JAVA系列 基础知识点梳理的最后一篇文章了吧 之后一段时间 我打算去看看之前承诺更新的JAVA的一些项目了 然后我要好好玩一段时间游戏 然后跟某些好朋友见面 所以后面会忙一点 然后就是很多同学 六级还没有考 估计很多人是开学再考把 这个假期就不要玩的太厉害了 没事…

人员超员识别系统 yolov5

人员超员识别系统通过yolov5深度学习网络模型对现场画面进行实时监测,监测到画面中区域人数超过规定人数时,立即抓拍存档预警。YOLO系列算法是一类典型的one-stage目标检测算法,其利用anchor box将分类与目标定位的回归问题结合起来&#xff…

一篇彻底解决:Fatal error compiling: 无效的目标发行版: 11 -> [Help 1]

先在这声明,如果我的方法没有解决你的问题,那你直接私信我,我第一时间帮你解决,送佛送到西!!! 这个问题,主要原因就是JDK的版本问题,“无效的目标发行版:11”的意思是你在某个位置配置了JDK11,但是在其他位置配置的并不是11&…

靶机测试Gears of War笔记

靶机介绍Its a CTF machine that deals with the history of gears of war, where we must try to escape from prison and obtain root privileges. it has some rabbit holes, so you have to try to connect the tracks to get access.This works better with VirtualBox ra…

[ 数据结构 ] 二叉树详解--------前序、中序、后序、存储、线索化

0 前言 why?为什么需要树结构? 数组虽然查找快(通过下标访问),但无法动态扩容(拷贝到新的数组),而链表相反,树结构刚好结合两者优点 浅谈树? 树的存储和读取效率都很高,比如二叉排序树,理解树的抽象模型有助于理解递归的原理,树的模型接近于单向链表,父节点指向左右子树,而…

【因果发现】 针对时序数据的因果图学习

文章目录 一、任务二、数据集说明三、专业名词CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、任务 This track focuses on solving a causal structure learning problem in AIOps. AIOps 相关:主要目标是从事件序列数据中挖掘因果图关系,并辅助定位根因。 主要需要解…

php学习笔记-php数组的创建和使用,数组常用函数-day03

php数组的创建和使用,数组常用函数数组的概念一维数组的创建和使用1.直接将变量声明为数组元素2.通过array()函数来创建一维数组3.数组的元素值的访问4.数组元素的赋值方式5.数组的注意事项二维数组的创建和使用1.二维数组的创建2.二维数组的数组元素值访问3.二维元…

ArcGIS基础实验操作100例--实验67设置标注样式

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台:ArcGIS 10.6 实验数据:请访问实验1(传送门) 高级编辑篇--实验67 设置标注样式 目录 一、实验背景 二、实验数据 三、实验步骤 (1&…

Java+Swing可视化图像处理软件

JavaSwing可视化图像处理软件一、系统介绍二、功能展示1.图片裁剪2.图片缩放3.图片旋转4.图像灰度处理5.图像变形6.图像扭曲7.图像移动三、项目相关3.1 乱码问题3.2 如何将GBK编码系统修改为UTF-8编码的系统?四、其它1.其他系统实现2.获取源码一、系统介绍 该系统实…

Allegro174版本新功能介绍之和172版本兼容设置

Allegro174版本新功能介绍之和172版本兼容设置 Allegro升级到了174版本的时候,如果按照常规操作用174版本保存PCB之后,用172版本是无法打开的。 但是174版本开放了一个和172版本兼容的窗口,即便是174版本保存了PCB,172同样还是能够打开 具体操作如下 选择Setup选择User p…

[Linux]git命令行

🥁作者: 华丞臧. 📕​​​​专栏:【LINUX】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文…

前端入门笔记 05 —— js基础

基于ES6 生成html文件&#xff0c;script标签引入js文件 简单语句直接用console面板 <!DOCTYPE html> <html><head><title>js核心语法</title><meta charset"UTF-8" /></head><body><script src "./1.5j…

多线程(4)

文章目录1.单例模式2. 阻塞队列3. 定时器4.线程池前言 :   前面的一些文章&#xff0c;我们已经将有关多线的基础知识了解了&#xff0c; 下面我们来写一些关于多线程的代码案例&#xff0c;来强化对多线程的理解&#xff0c;另外通过这些案例补充一下其他方面的知识。 1.单例…

印度如何在云计算中抓住千载难逢的机会

云对印度的影响大流行和后大流行时期的虚拟和混合世界越来越依赖于云&#xff0c;并随之提高灵活性、敏捷性和创新性。在这场快速的数字化转型中&#xff0c;印度企业也纷纷加入云解决方案以促进业务增长。NASSCOM-EY的调查显示&#xff0c;78%的印度IT公司、53%的医疗保健和BF…

【Javascript】数字和字符串常用方法

数字常用方法&#xff1a; toFixed&#xff1a;保留小数位数&#xff0c;不够自动补0. 注意&#xff1a;返回值是字符串&#xff0c;不能和数字直接相加减&#xff0c;需要把字符串转化为数字&#xff1a;-0即可 取整&#xff1a; // round:四舍五入取整console.log(Math.roun…

SQL IN 操作符

IN 操作符 IN 操作符允许您在 WHERE 子句中规定多个值。 SQL IN 语法 SELECT column1, column2, ... FROM table_name WHERE column IN (value1, value2, ...); 参数说明&#xff1a; column1, column2, ...&#xff1a;要选择的字段名称&#xff0c;可以为多个字段。如果…