nodejs高並發大流量的設計實現,控制並發的三種方法
eventproxy、async.mapLimit、async.queue控制並發
Node.js是建立在Google V8 JavaScript引擎之上的網絡服務器框架,允許開發者能夠用客戶端使用的語言JavaScript在服務器端編碼。
----------------
node.js優缺點:
優點: 高並發,io密集型處理, 可以作為單頁面應用,便於爬蟲抓取。
缺點:不適合cpu計算密集型, 對關系數據庫支持不好
nodejs高並發大流量的設計實現
原理:非阻塞事件驅動實現異步開發,通過事件驅動的I/O來操作完成跨平台數據密集型實時應用
傳統的server 每個請求生成一個線程, nodejs是一個單線程的,使用libuv保持數萬並發
libuv原理:
c語言編寫的基礎庫實現主循環,文件,網絡即可
libuv的改進:
回傳上下文信息,其它線程不能訪問缺省主循環,loop不支持多線程
代碼可讀性維護改進:
async:
async.waterfall([getcatalog, getaticle,getTigle])
promise 的方法
koa寫法
es6寫法使用yield
var titles = []
co(function *() {
var catalog = yield getCatalog(gid)
var articles = yield getArticles(catalog)
titles = yield getTitles(articles)
});
前端優化問題:移除iscorll,合並請求,tcp優化,http優化 ,localstorate,html5離線緩存
api優化:restfulapi,標准輸入輸出
ui優化: 使用同一的框架,前端組件化
異常處理: log監控,避免大文件處理,retry處理
===================
用eventproxy、async.mapLimit、async.queue控制並發
1.用eventproxy實現控制並發 var EventProxy = require('eventproxy'); const most = 5;//並發數5 var urllist = [....];//待抓取url列表,100個 function foo(start){ var ep = new EventProxy(); ep.after('ok',most,function(){ foo(start+most);//一個批次任務完成,遞歸進行下一批任務 }); var q=0; for(var i=start;i<urllist.length;i++){ if(q>=most){ break;//最多添加most個任務 } http.get(urllist[i],function(res){ //.... res.on('end',function(){ ep.emit('ok');//一個任務完成,觸發一次ok事件 }); }); q++; } } foo(0); 2.使用 async.mapLimit 控制並發 var async = require('async'); //模擬一組連接地址 var urls = []; for(var i = 0; i < 30; i++) { urls.push('http://datasource_' + i); } console.log(urls); // 並發連接數的計數器 var concurrencyCount = 0; // 並發抓取數據的過程 var fetchUrl = function (url, callback) { // delay 的值在 2000 以內,是個隨機的整數 var delay = parseInt((Math.random() * 10000000) % 2000, 10); concurrencyCount++; console.log('現在的並發數是', concurrencyCount, ',正在抓取的是', url, ',耗時' + delay + '毫秒'); setTimeout(function () { concurrencyCount--; //抓取成功,調用回調函數 callback(null, url + ' html content'); }, delay); }; //使用 async.mapLimit 來 5 個並發抓取,並獲取結果 async.mapLimit(urls, 5, function (url, callback) { fetchUrl(url, callback); }, function (err, result) { //所有連接抓取成功,返回回調結果列表 console.log('final:'); console.log(result); }); 3.使用async.queue 控制並發 "use strict" var http = require('http'); var cheerio = require('cheerio'); var URL = require('url'); var path = require('path'); var fs = require('fs'); var async = require('async'); var baseUrl = "http://cnodejs.org/"; var targetUrl = "http://cnodejs.org/"; var stime = new Date(); function sGet(url,callback){ var chunks = []; http.get(url,(res)=>{ if (res.statusCode != '200') { callback({message:"抓取失敗,狀態碼:"+res.statusCode,url:url}); return; } res.on('data',(chunk)=>{ chunks.push(chunk); }); res.on('end',()=>{ callback(null,Buffer.concat(chunks).toString()); }); }).on('error',(e)=>{ callback({message:"抓取失敗",url:url,err:e}); }); } sGet(targetUrl,(err,data)=>{ if (err) { console.log(err); return false; } var $ = cheerio.load(data); var anchors = $("#topic_list a.topic_title"); console.log('共'+anchors.length+'個任務'); const most=5;//並發數 //創建隊列並指定並發數 var q=async.queue(function(url,callback){ var filename = path.basename(url)+'.txt'; sGet(url, (err, data)=> { if (err) { callback(err); return false; } fs.writeFile('./html/' + filename, data, function (err) { if (err) { throw err; } callback(null,filename); }); }); },most); q.drain = function() { console.log('任務全部完成,共耗時:'+(new Date()-stime)+'ms'); } anchors.each(function(){ var url = URL.resolve(baseUrl,$(this).attr('href')); q.push(url,function(err,filename){ if (err) { console.log(err); return; } console.log("finished:"+filename); }); }); });