当前位置: 首页 > article >正文

从java角度对比nodejs、fastapi,同步和异步区别

 我之前一直用java语言编程,最近一年用python fastapi和nodejs nestjs开发了一些项目,站在java程序员的角度谈谈异步编程和同步编程的区别,主要在两方面

  1. 处理请求,java常用的tomcat是多线程处理请求并执行代码,同步阻塞型。而nodejs是单线程处理请求并执行代码,异步非阻塞。
  2. 编程思想,同步利用多线程完成非阻塞目的,写代码按顺序写。而异步天生能完成非阻塞,比同步多了回调、async await等,写代码也不是顺序、没那么容易理解。

参考这两章节重要

Node.js 中文网 — Node.js 事件循环

Node.js 中文网 — 不要阻塞事件循环(或工作池)

一。同步编程

习惯了java开发,会觉得同步编程易于理解,符合常见顺序思维。

同步tomcat如何处理请求

我们知道tomcat默认用150个线程,每个线程处理一个客户端请求,堆积的客户端请求会进入队列。某个客户端处理请求的线程被io或大量计算堵塞,不影响tomcat的剩余149处理请求,所以tomcat依然能处理其他客户端的请求。

同步编程该如何完成非阻塞需求

在主线程执行期间,如果想同时执行另一个任务,就新建线程去做,并通过callable get方法、线程阀、中断等办法得知线程执行结果。

以下面代码为例,我们想让第二件IO事和第三件IO事同时进行、不想主线程被第二件IO事堵塞住,所以用新线程执行第二件IO事,但想获得第二件事结果要在主线程堵塞等待get。注意在编程中,第二件事应该是阻塞型代码比如IO、长时间计算型任务利用空闲的cpu执行,否则新建线程无意义。

        System.out.println("第一件事");
        FutureTask futureTask = new FutureTask<>((Callable) () -> {
            System.out.println("第二件事IO");
            return 1;
        });
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("第三件事IO");
        Object o = futureTask.get();
        System.out.println("第二三件事IO都完成了,第二件事结果"+o);

二。异步编程

nodejs

nodejs的思想是用较少的线程处理更多的客户端请求。

nodejs如何处理请求和io

nodejs有两种线程,事件循环线程和工作池线程,事件循环线程是单线程,工作池线程有多个。

事件循环线程用来干嘛?看看官网文档,如下Node.js 中文网 — 不要阻塞事件循环(或工作池)

我认为nodejs中文网翻译的不对,估计是工具翻译的,还是自己看英文。

第一段话我反复看了好几次 1.nodejs初始化模块和注册事件回调,然后执行事件循环,nodejs通过回调的方式响应处理客户端请求,这个处理客户端请求的回调是以同步方式执行的,并且可能注册异步请求继续处理。(这段话意思是事件循环线程用来处理客户端请求,处理方式是注册事件和回调,在处理客户端请求回调的过程中,它可能注册新的异步事件,我这里翻译为事件,但原文用的是may register asynchronous requests ,requests不是指客户端http请求、而是异步操作请求。比如平常@get等接口接受请求、并在接口中写新的异步io操作就会注册到事件里)

2.第二段又重复说时事件循环会处理在回调中发起的异步非阻塞请求,比如网络io,我理解是进一步解释了

3.总结,事件循环线程干了两件事  (1)处理事件回调 ,这里包含了处理客户端请求,因为它也是通过回调处理;同时回调中如果注册了新事件,新事件同样由事件循环线程处理 (2)处理非阻塞异步事件如网络IO,为什么要强调网络io呢?看了下面工作线程的作用就明白了,因为文件io是工作线程处理的。

举个例子再理解下,平常写代码@get接口,里面打一行日志、然后查数据库、有查询结果后处理。事件循环线程处理请求,然后用回调执行代码,回调里打一行日志,查数据库是网络io,于是事件循环线程发起了这次网络io,但不等待结果,有结果后回调继续在事件循环中完成。

工作池线程用来干嘛的?当碰到一下api时,会自动使用工作池线程执行

  1. I/O 密集型

    1. DNSdns.lookup()dns.lookupService().

    2. 文件系统:除 fs.FSWatcher() 之外的所有文件系统 API 以及明确同步的 API 都使用 libuv 的线程池。

  2. CPU 密集型

    1. 加密crypto.pbkdf2()crypto.scrypt()crypto.randomBytes()crypto.randomFill()crypto.generateKeyPair().

    2. Zlib:除了明确同步的 zlib API 之外,所有 zlib API 都使用 libuv 的线程池。

到这里就明白了,nodejs用事件循环处理请求,处理请求的回调中有异步操作会注册为新事件、网络io则继续由事件循环线程处理,一旦有文件io或加密等规定api会自动由工作线程处理。

所以nodejs官网一再强调,不要阻塞事件循环,这是开发者干的事。详细阅读文档,它说了哪些操作容易堵塞、如何避免堵塞客户端请求,分区卸载等Node.js 中文网 — 不要阻塞事件循环(或工作池)

nodejs处理事件循环的过程

Node.js 中文网 — Node.js 事件循环

在文档这章里讲各个事件循环的各阶段

nodejs完成非阻塞需求代码

以下代码完成了上面java代码相同的事,第二件事用异步执行,想获取第二件事的结果用await。  这里与tomcat不同的是,执行线程只有1个,await并没有阻塞到计算线程,计算线程可以继续执行其他不阻塞的代码,直到等待事件回调。


async function a(){
console.log("第一件事")
    
const promise = b();
    
console.log("第三件事")
const result = await promise;
console.log("第二件事结果"+ result);
}
async function b(){
console.log("第二件事");
    return 1;
}
a();

关于nodejs执行宏任务微任务 

以上的代码里,先打印第二件事还是第三件事,这与宏任务微任务有关,也不难,可以参考

node【一文搞懂:浏览器和node的事件循环机制】【微任务和宏任务】_node事件循环-CSDN博客

当然用异步不是为了打印日志,这是没意义的,异步是为了io

await怎么实现的 

await是个promise语法糖,await 一个promise,和promise.then()差不多。虽然await看起来像是一种特殊的操作,但本质上它也是基于Promise机制的。

await一个Promise时,实际上是等待这个Promise的状态转换。如果Promise的状态还处于pending,那么与这个Promise最终状态转换相关的回调(例如resolvereject后的回调)就会在微任务队列中处于等待状态。在事件循环中,微任务队列会在宏任务之间被检查和执行。一旦Promise的状态发生改变,微任务队列中的相应回调就会被触发,从而使得await后的代码能够继续执行(如果Promiseresolve)或者抛出异常(如果Promisereject)。

python fastapi

fastapi的异步处理请求过程跟node一样,只是fastapi同时支持异步和同步,当接口方法加了async时,走异步处理流程,当不加async时请求由线程池进行处理、跟tomcat类似!

参考这篇文章python - uvicorn 如何调节线程池大小 - SegmentFault 思否

这篇文章也很好的体现了fastapi异步和同步编程的区别FastAPI到底用不用async?_fastapi async-CSDN博客


http://www.kler.cn/a/534573.html

相关文章:

  • DeepSeek写的lammps反应势断键动态显示程序
  • 专门记录台式电脑常见问题
  • JDK9新特性
  • CTP查询资金费率和手续费没响应
  • ArrayList和Araay数组区别
  • MySQL UNION 操作详解
  • 【hot100】073矩阵置零
  • FFmpeg 头文件完美翻译之 libavfilter 模块
  • 怎么实现AI思考过程
  • 【前端】【Ts】TypeScript的关键知识点
  • css小知识
  • Windows图形界面(GUI)-QT-C/C++ - QT Dock Widget
  • 【12】深入理解Golang值传递与引用传递:避坑指南与性能优化
  • 前端学习数据库知识
  • React组件中的列表渲染与分隔符处理技巧
  • YOLOv11实时目标检测 | 摄像头视频图片文件检测
  • ZZNUOJ(C/C++)基础练习1061——1070(详解版)
  • 《redis的pub/sub机制》
  • Vue 3 中的 el-tooltip 详解:语法、示例及与其他框架对比
  • 谈谈对IOC的理解
  • 反向代理模块anns
  • 笔记:新能源汽车零部件功率级测试怎么进行?
  • 文心一言指令词宝典之职场效率篇
  • Java 大视界 -- Java 大数据在智慧文旅中的应用与体验优化(74)
  • 快速幂,错位排序笔记
  • 【字节青训营-6】:Gorm的基础使用