浅谈一下Sanic和Nodejs事件循环的区别
最近做了几个python的sanic项目发现sanic的事件循环有些无法理解的问题,钻研了一下。
二者事件循环的主要区别
Sanic 是基于 Python 异步框架,而 Node.js 是以 JavaScript 为编程语言并内置了异步事件驱动的架构。它们都采用了事件循环来处理异步操作,但实现机制和相关概念不尽相同。
Node.js 事件循环:
- Node.js 中的事件循环由 libuv 库提供,处理异步I/O操作。
- JavaScript 是单线程语言,在 Node.js 中通过事件循环来实现非阻塞I/O。
- 所有的异步任务都会被放入事件队列中,在主线程之外执行。
- 当异步操作执行完毕后,它们的回调函数会被加入到一个特定的队列,称为事件队列。
- 事件循环负责从事件队列中取出消息并执行对应的回调。
- Node.js 的 async/await 是基于 Promises 实现的,可以使异步代码看起来像同步代码。
Sanic 事件循环:
- Sanic 是建立在 Python 的 asyncio 库之上的,利用 Python 的原生协程(coroutines)和 async/await 语法来进行异步非阻塞编程。
- Python 的 asyncio 提供了自己的事件循环实现,用来调度协程的执行。
- 在 Sanic 中,异步请求处理函数通常以 async 关键字开头,表示它们是一个协程。
- 当你在 Sanic 的异步函数中使用同步代码(比如常规的阻塞 I/O 操作)时,它会阻塞整个事件循环,因为它的事件循环在等待这个操作完成。
- 为了进行异步I/O操作,你通常会使用提供了异步接口的库(如 asyncpg 直接访问 PostgreSQL,aiohttp 发送 HTTP 请求等)。
如果在 Sanic 中遇到代码阻塞问题,可能的原因如下:
- 你可能在异步函数中使用了阻塞的I/O调用或者长时间运行的代码,这会占据事件循环并导致其他操作无法即时处理。
- 应该使用异步兼容的库来执行I/O操作,如果你试图使用同步的库,就算是在异步函数中,也会造成阻塞。
例如,需要使用异步数据库库来进行数据库操作,使用 aiohttp 来执行HTTP请求,而不是使用 requests 这种同步的库。
一种常见的错误模式是在异步处理程序中不加区分地使用同步代码,这会导致整个服务性能下降。解决这个问题的关键是识别出哪些操作会阻塞事件循环,并将这些操作转换为非阻塞的异步调用。此外,你可能需要熟悉 Python 中的 asyncio.sleep()
替代 time.sleep()
,以及其他类似的异步替代函数和模式来避免不小心插入阻塞代码。
为什么node中可以相对随意的使用I/O操作,而sanic就要避免使用阻塞I/O
Node.js 和 Sanic (基于 Python 的 asyncio) 都是异步框架,但是它们处理异步操作的方法不同。这些差异导致了在使用 I/O 操作时需要采用不同的考虑方式。
在 Node.js 中:
- Node.js 的设计是单线程的,它利用 JavaScript 事件循环来执行异步操作。所有的 I/O 操作在 Node.js 中被设计为非阻塞,这是 Node.js 的核心优势之一。
- 当 I/O 操作被执行时,它们会被委托给底层的系统内核,而 JavaScript 事件循环会继续执行其他任务。
- 当 I/O 操作完成时,回调函数将会被放进事件队列中,并在事件循环可以处理它们的时候被调用。
- 这样做的结果是,开发者可以较为自由地在 Node.js 中执行 I/O 操作而无需担心阻塞整个应用,因为所有的 I/O 默认就是异步且非阻塞的。
在 Sanic 和 asyncio 中:
- asyncio 需要明确地将事情声明为异步。如果在协程中使用同步代码或者库,那么它会阻塞正在运行的协程,从而阻塞整个事件循环。
- 这就意味着在 Sanic(或任何基于 asyncio 的框架)中,你需要确保所有的 I/O 操作都是通过使用
async
和await
关键字,以及利用可以异步执行 I/O 操作的库(例如aiohttp
替代requests
,aiomysql
替代PyMySQL
等)。 - 如果在异步函数中执行阻塞的 I/O 或者长时间执行的同步代码,它会暂停事件循环,阻塞事件循环上的其他协程,直到该操作完成。这会造成整个应用性能的下降,因为其他的并发请求无法被及时处理。
总结来说,Node.js 让所有的 I/O 操作变得默认非阻塞,并通过事件驱动的模型简化了异步编程模式。而在 Sanic/asyncio 中,由于 Python 是多线程语言,所以需要开发者显式地使用异步 I/O 来获得相同的非阻塞行为。这是为什么在 Sanic 中要避免使用阻塞 I/O,而在 Node.js 中可以相对自由使用 I/O 操作的主要原因。