异步编程语言的常见坑
天生支持异步编程的语言如 NodeJS, Golang 等, 创建一个异步 routine 的成本非常小, 这确实是一个非常方便的功能. 比如用在网络爬虫程序的开发, 对于每一个要抓取的 URL 就启动一个 routine, 类似启动一个线程, 既能充分利用 CPU 多核, 代码也很简洁.
正因为太方便, 所以常常被滥用, 并引发许多严重坑. 下面分析一下.
1. 拖垮了所依赖的服务
写异步编程的程序员是爽了, 但维护数据库的 DBA 却要哭了. 异步编程程序员每一个查询可以轻松的异步, 但数据库的处理能力就那么多, 最直接的后果是数据库挂了. 回到网络爬虫的例子, 如果爬虫本身不做控制, 可能就把对方的网站拖垮了.
2. 用完了 socket 连接数
一般的异步编程语言的 runtime 会对每个程序只起一个进程, 每一个进程的最大连接数是有限制的, 比如一般的桌面系统是 256, 所以, 虽然异步的成本很小, 但很快就会用完 socket 连接数, 想再增加并发也增加不了了. 而且, 用完了最大连接数, 还经常会触发其它严重的问题.
3. 用完了本机端口
这个坑和前一个有些类似. 常见的程序一般只连一台数据库服务器的一个实例, 这样, 程序最多只能连 3 万多个连接, 这时端口就会被用光, 因为操作系统一般从 3 万多开始分配临时端口号, 最大 65535. 如果并发太多导致保持了太多的网络连接, 这时, 你就会看到你的异步编程开发出来的程序报错: Cannot assign requested address. 这时, 整个操作系统都会受不良影响!
如何解决?
其实, 要解决这些坑, 必须异步变同步. 什么? 异步变同步?
其实, 就是通过一个同步队列, 控制一下异步的并发量. 所以, 异步编程是和队列(排队)紧密相连, 如果你的异步编程没有用到队列, 那么你的程序就必然存在隐患! 除非你能确保异步调用不会爆炸性增长. 这是一条真理!
例如网络爬虫的例子, 为了控制抓取的并发量, 就要通过某种策略, 把要抓取的 URL 放到队列中, 控制在队列中同时存在的同一个域名的 URL 数量, 然后再异步地从这个队列领取 URL 进行抓取, 这就不会把别人的网站拖垮了.
不过, 具体用哪个策略, 是和业务相关的. 其实这个策略的实现, 开发的工作量不一定小, 有时, 异步不异步根本解决不了太大的问题, 这个策略才是工作量的大头.
Related posts:
- Lighttpd mod_fastcgi源码分析
- TCP网络协议及其思想的应用
- Windows Python select标准输入输出
- SSH ProxyCommand及其思想
- 结合IO多路复用的多线程服务器模型
你现在看的文章是: 异步编程语言的常见坑