欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

async.mapLimit 并发请求限制的一点实践

程序员文章站 2022-05-13 20:53:03
...

前言

上回参考《使用 eventproxy 控制并发》学习未能解决网站对并发请求进行限制的问题。这回继续学习《使用 async 控制并发》,目标是输出 CNode(https://cnodejs.org/ ) 社区首页的所有(40个)主题的标题,链接和第一条评论。

一个简单的callback实例

function add_callback(p1, p2 ,callback) {
    var my_number = p1 + p2;
    callback(my_number);
}
add_callback(5, 15, function(num){
    console.log("call " + num);
});
//call 20

add_callback函数的第三个参数是回调函数。当函数执行时,传递的结果作为callback的参数。

async.mapLimit

async是一个流程控制工具包,提供了直接而强大的异步功能。基于Javascript为Node.js设计,同时也可以直接在浏览器中使用。

async异步流程控制模式包括,串行(series),并行(parallel),瀑布(waterfall)等。

async主要实现了三个部分的流程控制功能:

  • 集合: Collections
  • 流程控制: Control Flow
  • 工具类: Utils

这次用到的是集合中的mapLimit(coll, limit, iteratee, callback opt)
每次以指定的最大limit数执行异步操作。参数如下:

名称 类型 描述
coll Array / Iterable / object 要迭代的集合。
limit number 一次异步操作的最大数量。
iteratee AsyncFunction 对于 coll 中的每一个item,迭代执行该异步函数。用(item, callback)调用,callback可选。
callback [ function ] 所有iteratee 函数完成后或发生错误时触发的回调函数。用(err, results)调用。results可以是iteratee 函数完成后触发callback时传递的项。

具体实现:

var superagent = require('superagent')
var cheerio = require('cheerio')
var async = require('async')
var url = require('url') 

var cnodeUrl = 'https://cnodejs.org/';

superagent.get(cnodeUrl).end(function(err, res) {
    if (err) {
        return console.error(err)
    }
    // 存放标题url的数组
    var topicUrls = [];
    var $ = cheerio.load(res.text);
    //获取首页所有的链接
    $('#topic_list .topic_title').each(function (idx, el) {
        if (idx < 40) {
            var $el = $(el);
            var href = url.resolve(cnodeUrl, $el.attr('href'));
            topicUrls.push(href);           
        }
    });
    //并发连接数的计数器
    var concurrencyCount = 0;
    var fetch = function (url, callback) {
        console.time('  耗时');
        concurrencyCount++;
        superagent.get(url).end( function (err, res) {
            console.log('并发数:', concurrencyCount--, 'fetch', url);
            //var $ = cheerio.load(res.text);
            callback(null, [url, res.text]);
        });

    }
    async.mapLimit(topicUrls, 11, function (topicUrl, callback) {
        fetch(topicUrl, callback);
        console.timeEnd("  耗时");
    }, function (err, result) {
        result = result.map( function (pair) {
            var $ = cheerio.load(pair[1]);
            return ({
                title: $('.topic_full_title').text().trim(),
                href: pair[0],
                comment1: $('.reply_content').eq(0).text().trim(),
                author1: $('.reply_author').eq(0).text().trim() || "评论不存在",    
            });
        });
        console.log('final:\n',result);
    });
});
$ node app.js
  耗时: 2.108ms
  耗时: 0.748ms
  耗时: 0.641ms
  耗时: 0.596ms
  耗时: 1.060ms
  耗时: 1.429ms
  耗时: 0.728ms
  耗时: 0.653ms
  耗时: 1.177ms
  耗时: 0.820ms
  耗时: 3.520ms
并发数: 11 fetch https://cnodejs.org/topic/5a54a8a4afa0a121784a8ab0
  耗时: 0.802ms
并发数: 11 fetch https://cnodejs.org/topic/5a61ad4ece45d44045146172
  耗时: 0.703ms
并发数: 11 fetch https://cnodejs.org/topic/5a66e5899288dc81532880b0
  耗时: 0.694ms

......
并发数: 11 fetch https://cnodejs.org/topic/5a37c17dd92f2f5b185acfc4
  耗时: 0.684ms
并发数: 11 fetch https://cnodejs.org/topic/5a62125cce45d44045146198
  耗时: 0.674ms
并发数: 11 fetch https://cnodejs.org/topic/5a668f8fafa0a121784a8e9b
  耗时: 0.682ms
并发数: 11 fetch https://cnodejs.org/topic/5a61e5d8afa0a121784a8dc4
并发数: 10 fetch https://cnodejs.org/topic/5a65adc39288dc8153288058
并发数: 9 fetch https://cnodejs.org/topic/5a5b7638a3692d014f4f146e
并发数: 8 fetch https://cnodejs.org/topic/59523d04984e31dd458c14a4
并发数: 7 fetch https://cnodejs.org/topic/5a6335bbafa0a121784a8dfe
并发数: 6 fetch https://cnodejs.org/topic/5a55617299d207fa49f5cd85
并发数: 5 fetch https://cnodejs.org/topic/5a659dc5afa0a121784a8e6f
并发数: 4 fetch https://cnodejs.org/topic/5a637604ce45d440451461c5
并发数: 3 fetch https://cnodejs.org/topic/5a5c8d2eafa0a121784a8c05
并发数: 2 fetch https://cnodejs.org/topic/59c0d4038812ce51127a8fe5
并发数: 1 fetch https://cnodejs.org/topic/5a4db120ebc575dc49b27108
final:
 [ { title: '置顶\n\n\n\n        玉伯《从前端技术到体验科技(附演讲视频)》',
    href: 'https://cnodejs.org/topic/5a54a8a4afa0a121784a8ab0',
    comment1: '已经过玉伯本人授权,稍后会同步发到node全栈公众号',
    author1: 'i5ting' },
  { title: '置顶\n\n\n\n        企业级 Node.js 框架 Egg 发布 2.0,性能提升 30%,拥抱 Async',
    href: 'https://cnodejs.org/topic/5a2403226190c8912ebaceeb',
    comment1: '顺便拉下票,OSChina 开源中国正在举办 2017年度最受欢迎中国开源软件评选,请为 Egg 打 Call~\n另
外,顶楼『分享交流』章节的两个 Slide 推荐看下~',
    author1: 'atian25' },
  { title: '置顶\n\n\n\n        测试请发到客户端测试专区,违规影响用户的,直接封号',
    href: 'https://cnodejs.org/topic/592917b59e32cc84569a7458',
    comment1: '其实发到招聘板块,首页是看不到的\nsource vue-cnode mobile 2.0',
    author1: '1340641314' },
......

参考
《使用 async 控制并发》
mapLimit(coll, limit, iteratee, callbackopt)
Javascript中的Callback方法浅析

相关标签: async