node.js 学习笔记004:使用eventproxy控制并发

原文地址:lesson4 : 使用eventproxy控制并发
在上一篇博客中达到了使用superagentcheerio实现了简单网络爬虫的程序,现在的新需求是在上一个程序的基础上获取到每一个文章的第一条评论,这样难度就稍稍有些增加了


一、安装eventproxy

eventproxynodejs中控制并发的一大利器
eventproxygithub:https://github.com/JacksonTian/eventproxy

npm install eventproxy --save

二、使用eventproxy的步骤

  1. 创建eventproxy对象
var ep=new eventproxy();

当然,前提是已经引入了eventproxy

var eventproxy=require("eventproxy");
  1. 告诉它需要监听哪些事件,并提供回调函数
ep.all('event_name',function(result){
});
  1. 在适当的时候
ep.emit('event_name',data);

三、eventproxy实现并发的原理

线程并发实际上是非常复杂的一个东西,它需要考虑到相当多的因素,比如线程间通信、死锁等,在这里则暂且都不考虑
后续可能会使用nodejs实现多生产者多消费者问题,敬请期待

  1. 不使用evenproxy的时候如何实现并发控制
    比较经典的做法就是使用计数器控制并发,比如一共有3个并发线程,希望3个并发线程都完成之后才进行下一步操作,那么可以这么做
var count=0;
var result={};
$.get("http://example.com/1.html",function(data){
    result.data1=data;
    count++;
    handler();
});
$.get("http://example.com/2.html",function(data){
    result.data2=data;
    count++;
    handler();
});
$.get("http://example.com/3.html",function(data){
    result.data3=data;
    count++;
    handler();
});
function handler(){
    if(count==3){
        var html=getHtml(result);
        return html;
    }
}
  1. 如果使用eventproxy该如何做
var ep=new eventproxy();
ep.all('event1','event2','event3',function(data1,data2,data3){
    var html=getHtml(data1,data2,data3);
});
$.get("http://example.com/1.html",function(data){
    ep.emit('event1',data);
});
$.get("http://example.com/2.html",function(data){
    ep.emit('event2',data);
});
$.get("http://example.com/3.html",function(data){
    ep.emit('event3',data);
});
  1. 以上两个程序的原理几乎相同,eventproxy只是透明了计数器count的使用而已。

四、目标程序实现

目标程序的实现需要重点强调 重复异步协作 的使用方法,具体说明见链接。

  1. 重复异步协作
    此处以读取目录下的所有文件为例,在异步操作中,我们需要在所有异步调用结束后,执行某些操作。
var ep = new EventProxy();
ep.after('got_file', files.length, function (list) {
  // 在所有文件的异步执行结束后将被执行
  // 所有文件的内容都存在list数组中
});
for (var i = 0; i < files.length; i++) {
  fs.readFile(files[i], 'utf-8', function (err, content) {
    // 触发结果事件
    ep.emit('got_file', content);
  });
}

after方法适合重复的操作,比如读取10个文件,调用5次数据库等。将handler注册到N次相同事件的触发上。达到指定的触发数,handler将会被调用执行,每次触发的数据,将会按触发顺序,存为数组作为参数传入。

  1. 目标代码实现
var express=require("express");
var superagent=require("superagent");
var cheerio=require("cheerio");
var url=require("url");
var eventproxy=require("eventproxy");
var app=express();
var baseUrl='https://cnodejs.org/';
function output(arr){
    for(var i=0;i<arr.length;i++){
        console.log(arr[i]);
    }
}
superagent.get(baseUrl).end(function(err,resp){
    if(err){
        return console.error(err);
    }
    var arr=[];
    var $=cheerio.load(resp.text);
    $("#topic_list .topic_title").each(function(idx,element){
        $element=$(element);
        var _url=url.resolve(baseUrl,$element.attr("href"));
        arr.push(_url);
    });
    //验证得到的所有文章链接集合
    output(arr);
    //接下来遍历arr,解析每一个页面中需要的信息
    var ep=new eventproxy();
    ep.after('destEvent',arr.length,function(topics){
        topics=topics.map(function(topic){
            var _url=topic[0];
            var message=topic[1];
            var $=cheerio.load(message);
            return {
                title:$(".topic_full_title").text().trim(),
                href:_url,
                firstcomment:$("#reply1 .markdown-text").text().trim()
            };
        });
        console.log("result :");
        console.log(topics);
    });
    arr.forEach(function(_url){
        superagent.get(_url).end(function(err,mes){
            if(err){
                console.log("get \""+_url+"\" error !"+err);
                console.log("message info:"+JSON.stringify(mes));
            }
            console.log('fetch '+_url+" succeful !");
            ep.emit('destEvent',[_url,mes.text]);
        });
    });
});
  1. 结果示例
  { title: '',
    href: 'https://cnodejs.org/topic/57108b8f0a1e9da252f1e347',
    firstcomment: '' },
  { title: '',
    href: 'https://cnodejs.org/topic/571054ae8b5ce7be52adc019',
    firstcomment: '' },
  { title: '',
    href: 'https://cnodejs.org/topic/570ce12704e7772f4eb639d6',
    firstcomment: '' },
  { title: 'nodejs报错Cannot find module \'./lib/topologies/server\'',
    href: 'https://cnodejs.org/topic/571308a6434cfcfa52684ae2',
    firstcomment: '问题没有上下文大家看了,都没啥感觉啊, 让人摸不着头脑的感觉~' },
  { title: '写了篇有关CSRF的博客,大家多批评~',
    href: 'https://cnodejs.org/topic/56c833f9d1e0945c614187e6',
    firstcomment: '做安全的么。。。' },
  { title: '精华\n\n\n\n        React-Native 客户端【v1.0.0-alpha2】【安卓已发布】【最后更新:2016.4.14】',
    href: 'https://cnodejs.org/topic/559bd1b91e5c761761468884',
    firstcomment: '不错,顶起' },
  { title: '置顶\n\n\n\n        cnode 社区也切换到 Let\'s encrypt 了',
    href: 'https://cnodejs.org/topic/5711f1816a2d2bda52de962a',
    firstcomment: 'ssllab 也拿到 A 了 https://www.ssllabs.com/ssltest/analyze.html?d=cnodejs.org' },
  { title: '置顶\n\n\n\n        前端资源教程',
    href: 'https://cnodejs.org/topic/56ef3edd532839c33a99d00e',
    firstcomment: '感谢大神分享!\n来自酷炫的 CNodeMD' },
  { title: '置顶\n\n\n\n        国内Nodejs 2015汇总',
    href: 'https://cnodejs.org/topic/5696e43e6272216e51bff67e',
    firstcomment: '桑大大,很赞�' },
  { title: 'timer 的 unref 函数',
    href: 'https://cnodejs.org/topic/570924d294b38dcb3c09a7a0',
    firstcomment: '6666' },
  { title: '一个基于NodeJs + ReactJs构建的轻量级博客系统',
    href: 'https://cnodejs.org/topic/57126364238ae0ac1e3a6a4f',
    firstcomment: '支持' },
  { title: '精华\n\n\n\n        cnode社区app客户端(Ionic2.0)',
    href: 'https://cnodejs.org/topic/57111863434cfcfa52684a8e',
    firstcomment: 'good job' },
  { title: 'node的web应用被黑,求指导该如何处理',
    href: 'https://cnodejs.org/topic/5707198b8a612c5559d16d26',
    firstcomment: '你是什么服务器啊? 这么不安全?' } ]

有些请求发生了错误,很有可能是并发请求数量太多遭到服务器拒绝服务,得到的error信息如下

{
"req":
    {
        "method":"GET",
        "url":"https://cnodejs.org/topic/5711e7ea6a2d2bda52de9627"
    },
"header":
    {
        "server":"nginx/1.4.6 (Ubuntu)",
        "date":"Sun, 17 Apr 2016 07:09:59 GMT",
        "content-type":"text/html",
        "content-length":"221",
        "connection":"close"
    },
"status":503,
"text":"<html>\r\n<head><title>503 Service Temporarily Unavailable</title></head>\r\n<body bgcolor=\"white\">\r\n<center><h1>503
Service Temporarily Unavailable</h1></center>\r\n<hr><center>nginx/1.4.6 (Ubuntu)</center>\r\n</body>\r\n</html>\r\n"       
}

解决方法就是限制并发访问数量,下一篇讲解如何控制并发连接数量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 你总是这样,起床后他对我说 你又梦到了什么? 我静默,把头埋进被窝 我扛着一辆小自行车,在夜里 独自过一条窄桥 桥...
    叮咚的你阅读 342评论 1 5
  • R:书中原文。 非暴力沟通鼓励我们表达自己最深的感受和需要,因此,我们有时也许会发现运用非暴力沟通是富有挑战性的。...
    虎皮宝宝阅读 721评论 0 50
  • 1. 爸爸是孩子最好的玩伴。应该发挥自身体能好、爱运动的优势,带孩子多做体育运动。运动影响孩子的时间感知、动作预测...
    真的凛风阅读 173评论 0 0
  • 这是一首关于梦想的歌。但是不是关于一个杰出青年实现梦想的样子,而是关于一个普普通通的憨人、甚至只是一条被阳光暴晒过...
    燃世阅读 530评论 0 0