node.js 學習筆記004:使用eventproxy控制并發(fā)

原文地址:lesson4 : 使用eventproxy控制并發(fā)
在上一篇博客中達到了使用superagentcheerio實現(xiàn)了簡單網(wǎng)絡爬蟲的程序,現(xiàn)在的新需求是在上一個程序的基礎(chǔ)上獲取到每一個文章的第一條評論,這樣難度就稍稍有些增加了


一、安裝eventproxy

eventproxynodejs中控制并發(fā)的一大利器
eventproxygithub:https://github.com/JacksonTian/eventproxy

npm install eventproxy --save

二、使用eventproxy的步驟

  1. 創(chuàng)建eventproxy對象
var ep=new eventproxy();

當然,前提是已經(jīng)引入了eventproxy

var eventproxy=require("eventproxy");
  1. 告訴它需要監(jiān)聽哪些事件,并提供回調(diào)函數(shù)
ep.all('event_name',function(result){
});
  1. 在適當?shù)臅r候
ep.emit('event_name',data);

三、eventproxy實現(xiàn)并發(fā)的原理

線程并發(fā)實際上是非常復雜的一個東西,它需要考慮到相當多的因素,比如線程間通信、死鎖等,在這里則暫且都不考慮
后續(xù)可能會使用nodejs實現(xiàn)多生產(chǎn)者多消費者問題,敬請期待

  1. 不使用evenproxy的時候如何實現(xiàn)并發(fā)控制
    比較經(jīng)典的做法就是使用計數(shù)器控制并發(fā),比如一共有3個并發(fā)線程,希望3個并發(fā)線程都完成之后才進行下一步操作,那么可以這么做
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只是透明了計數(shù)器count的使用而已。

四、目標程序?qū)崿F(xiàn)

目標程序的實現(xiàn)需要重點強調(diào) 重復異步協(xié)作 的使用方法,具體說明見鏈接。

  1. 重復異步協(xié)作
    此處以讀取目錄下的所有文件為例,在異步操作中,我們需要在所有異步調(diào)用結(jié)束后,執(zhí)行某些操作。
var ep = new EventProxy();
ep.after('got_file', files.length, function (list) {
  // 在所有文件的異步執(zhí)行結(jié)束后將被執(zhí)行
  // 所有文件的內(nèi)容都存在list數(shù)組中
});
for (var i = 0; i < files.length; i++) {
  fs.readFile(files[i], 'utf-8', function (err, content) {
    // 觸發(fā)結(jié)果事件
    ep.emit('got_file', content);
  });
}

after方法適合重復的操作,比如讀取10個文件,調(diào)用5次數(shù)據(jù)庫等。將handler注冊到N次相同事件的觸發(fā)上。達到指定的觸發(fā)數(shù),handler將會被調(diào)用執(zhí)行,每次觸發(fā)的數(shù)據(jù),將會按觸發(fā)順序,存為數(shù)組作為參數(shù)傳入。

  1. 目標代碼實現(xiàn)
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. 結(jié)果示例
  { 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: '寫了篇有關(guān)CSRF的博客,大家多批評~',
    href: 'https://cnodejs.org/topic/56c833f9d1e0945c614187e6',
    firstcomment: '做安全的么。。。' },
  { title: '精華\n\n\n\n        React-Native 客戶端【v1.0.0-alpha2】【安卓已發(fā)布】【最后更新:2016.4.14】',
    href: 'https://cnodejs.org/topic/559bd1b91e5c761761468884',
    firstcomment: '不錯,頂起' },
  { title: '置頂\n\n\n\n        cnode 社區(qū)也切換到 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        國內(nèi)Nodejs 2015匯總',
    href: 'https://cnodejs.org/topic/5696e43e6272216e51bff67e',
    firstcomment: '桑大大,很贊?' },
  { title: 'timer 的 unref 函數(shù)',
    href: 'https://cnodejs.org/topic/570924d294b38dcb3c09a7a0',
    firstcomment: '6666' },
  { title: '一個基于NodeJs + ReactJs構(gòu)建的輕量級博客系統(tǒng)',
    href: 'https://cnodejs.org/topic/57126364238ae0ac1e3a6a4f',
    firstcomment: '支持' },
  { title: '精華\n\n\n\n        cnode社區(qū)app客戶端(Ionic2.0)',
    href: 'https://cnodejs.org/topic/57111863434cfcfa52684a8e',
    firstcomment: 'good job' },
  { title: 'node的web應用被黑,求指導該如何處理',
    href: 'https://cnodejs.org/topic/5707198b8a612c5559d16d26',
    firstcomment: '你是什么服務器啊? 這么不安全?' } ]

有些請求發(fā)生了錯誤,很有可能是并發(fā)請求數(shù)量太多遭到服務器拒絕服務,得到的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"       
}

解決方法就是限制并發(fā)訪問數(shù)量,下一篇講解如何控制并發(fā)連接數(shù)量。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,544評論 19 139
  • 你總是這樣,起床后他對我說 你又夢到了什么? 我靜默,把頭埋進被窩 我扛著一輛小自行車,在夜里 獨自過一條窄橋 橋...
    叮咚的你閱讀 372評論 1 5
  • R:書中原文。 非暴力溝通鼓勵我們表達自己最深的感受和需要,因此,我們有時也許會發(fā)現(xiàn)運用非暴力溝通是富有挑戰(zhàn)性的。...
    虎皮寶寶閱讀 776評論 0 50
  • 1. 爸爸是孩子最好的玩伴。應該發(fā)揮自身體能好、愛運動的優(yōu)勢,帶孩子多做體育運動。運動影響孩子的時間感知、動作預測...
    真的凜風閱讀 197評論 0 0
  • 這是一首關(guān)于夢想的歌。但是不是關(guān)于一個杰出青年實現(xiàn)夢想的樣子,而是關(guān)于一個普普通通的憨人、甚至只是一條被陽光暴曬過...
    燃世閱讀 637評論 0 0

友情鏈接更多精彩內(nèi)容