nodejs + nginx + redis cluster 高并發(fā)解決方案

20160226210838859.jpg

nodejs :應(yīng)用服務(wù)器黔牵。
nginx :負(fù)載均衡反向代理。
redis cluster : 非主從的集群方案。

背景:
每當(dāng)我們的應(yīng)用服務(wù)大刊,在網(wǎng)絡(luò)上有許許多多的用戶時(shí)穷缤。我們首先考慮到的是敌蜂,我們的服務(wù)器能否承載同一時(shí)間高吞吐量的處理,我們的應(yīng)用服務(wù)器架構(gòu)應(yīng)該如何去搭建津肛。本文就是作者自己對(duì)于高性能web服務(wù)器的部分見(jiàn)解章喉。
一、數(shù)據(jù)。
針對(duì)于用戶使用數(shù)據(jù)而言秸脱,我們應(yīng)該會(huì)對(duì)數(shù)據(jù)進(jìn)行分級(jí)落包。例如簡(jiǎn)單的兩級(jí),用戶會(huì)經(jīng)常訪問(wèn)的(例如自己的id撞反,地理信息)妥色;用戶不會(huì)經(jīng)常訪問(wèn)的(例如歷史訂單)。因此我們?cè)O(shè)計(jì)系統(tǒng)時(shí)就應(yīng)該去考慮消除冗余遏片,讓經(jīng)常使用的數(shù)據(jù)有更多的訪問(wèn)速度資源嘹害,不會(huì)經(jīng)常使用的數(shù)據(jù)盡可能少的速度資源。最通俗的做法就是吮便,經(jīng)常訪問(wèn)的放在內(nèi)存中笔呀,不經(jīng)常訪問(wèn)的放入持久層,當(dāng)然髓需,經(jīng)常訪問(wèn)的也應(yīng)該在持久層中存根许师,不過(guò)用戶訪問(wèn)的時(shí)候會(huì)先去訪問(wèn)內(nèi)存中的數(shù)據(jù),看是否是自己需要的僚匆。

傳統(tǒng)的java 處理辦法是在service中加個(gè)變量微渠,缺點(diǎn):
1.服務(wù)器掛掉后,登錄狀態(tài)和會(huì)話數(shù)據(jù)丟失咧擂,體驗(yàn)差逞盆。
2.java虛擬機(jī)自身缺陷,存儲(chǔ)空間有限松申,需要進(jìn)行jvm優(yōu)化云芦。
3.丟失后不可恢復(fù),風(fēng)險(xiǎn)大贸桶。

面對(duì)傳統(tǒng)的缺陷應(yīng)運(yùn)而生的 redis 主從處理方案出現(xiàn)了舅逸,它支持雙機(jī)熱備,1臺(tái)處理皇筛,1臺(tái)備份琉历,在一定程度上可以防止數(shù)據(jù)丟失,對(duì)的你沒(méi)看錯(cuò)水醋,是一定程度上旗笔。為什么,分析下离例,在高并發(fā)環(huán)境中换团,一次吞吐量很大悉稠,主redis 正在處理新數(shù)據(jù)的時(shí)候掛掉了宫蛆,從redis能一定保證把最新的數(shù)據(jù)備份下來(lái)了么?很顯然不可能,所以一定會(huì)丟失耀盗。而且一臺(tái)redis如果和n臺(tái)web服務(wù)器交互想虎,壓力是很大的,掛掉的概率叛拷,嘖嘖嘖舌厨。當(dāng)然后來(lái)出現(xiàn)了keepalived 做負(fù)載均衡的,一定程度上減少了服務(wù)器的壓力忿薇,但是這種方案裙椭,始終是下策(為什么是下策,如果兩臺(tái)服務(wù)器都有對(duì)方?jīng)]有的新數(shù)據(jù)署浩,突然掛掉了揉燃。。筋栋。再或者承載keepalived的這太機(jī)器出了問(wèn)題炊汤。。弊攘。抢腐。)。風(fēng)險(xiǎn)還是太大了襟交。

于是出現(xiàn)了redis cluster --集群迈倍,集群首先考慮的就是數(shù)據(jù)最小丟失,因此婿着,采用的是數(shù)據(jù)分片技術(shù)授瘦,每臺(tái)redis上存儲(chǔ)的都是不同的數(shù)據(jù),redis越多竟宋,數(shù)據(jù)分離的越細(xì)提完,一次丟失內(nèi)容更少,沒(méi)有代理層丘侠,性能穩(wěn)定徒欣。XXXX等等一大堆優(yōu)勢(shì),就不說(shuō)了蜗字。

二打肝、服務(wù)器集群只做數(shù)據(jù)處理,不做狀態(tài)維持挪捕。

數(shù)據(jù)處理是很消耗計(jì)算機(jī)資源的粗梭,而狀態(tài)維持相對(duì)較少。因此级零,大多高性能服務(wù)器都做狀態(tài)和數(shù)據(jù)分離断医。nginx就是用來(lái)做會(huì)話狀態(tài)維持,如圖,整個(gè)系統(tǒng)中只有一個(gè)nginx鉴嗤。而會(huì)話狀態(tài)中的數(shù)據(jù)斩启,存儲(chǔ)在redis集群中。

這里有個(gè)梗醉锅,如之前所說(shuō)redis集群中兔簇,數(shù)據(jù)是以分片的形式存儲(chǔ)在幾臺(tái)redis中,如果我需要的數(shù)據(jù)所在的redis正好掛掉了硬耍,怎么辦垄琐。這就需要架構(gòu)師對(duì)應(yīng)用層編碼進(jìn)行規(guī)范,如何規(guī)范经柴,需要專門的人員進(jìn)行編寫這種redis數(shù)據(jù)此虑,數(shù)據(jù)提取策略是,如果這個(gè)數(shù)據(jù)從redis中獲取不到口锭,則直接從數(shù)據(jù)庫(kù)中再次讀取朦前,再存儲(chǔ)進(jìn)入redis。

例如session對(duì)象內(nèi)存儲(chǔ)的數(shù)據(jù)鹃操,存儲(chǔ)了一個(gè)user(點(diǎn)到為止韭寸,看不懂這個(gè)的,還需要多寫寫代碼)荆隘。

廢話不說(shuō)了恩伺,上nginx配置代碼(nginx如何配置安裝,百度有大巴高手寫過(guò)椰拒,小弟也是抄抄晶渠,不敢多寫):

worker_processes  1;  
  
events {  
    worker_connections  1024;  
}  
  
  
http {  
    include       mime.types;  
    default_type  application/octet-stream;  
    sendfile        on;     
    keepalive_timeout  65;  
  
    # 集群服務(wù)器配置   
    upstream sxt.com{  
  
        # weight 表示權(quán)重,其實(shí)就是被訪問(wèn)的概率燃观。例如3002是3000的6倍是3001的3倍褒脯。  
        server 127.0.0.1:3000 weight=1;  
        server 127.0.0.1:3001 weight=2;  
        server 127.0.0.1:3002 weight=6;  
  
    }  
  
    server {  
      
        listen       80;  
        server_name  localhost;  
         
        location / {  
  
            # 配置代理  
            proxy_pass http://sxt.com;  
            proxy_redirect default;  
  
        }  
         
        error_page   500 502 503 504  /50x.html;  
        location = /50x.html {  
            root   html;  
        }  
          
    }  
  
}  

作者只對(duì)跟負(fù)載均衡,反向代理相關(guān)的地方進(jìn)行了注釋缆毁。

這樣配置之后番川,當(dāng)訪問(wèn)nginx所在機(jī)器(例如192.168.1.200)的80端口時(shí)(http://192.168.1.200)

啟動(dòng)阿婆主的 nodejs服務(wù)器和 nginx服務(wù)器 并訪問(wèn):

20160226220328211.jpg

20160226220426391.jpg

得到如上圖中的結(jié)果:

發(fā)現(xiàn)---一共3次請(qǐng)求,3臺(tái)nodejs服務(wù)器接受到的請(qǐng)求數(shù)目不相同脊框。
一共請(qǐng)求了一個(gè)html 一個(gè)css文件和一個(gè)js文件颁督。

這就是weight (權(quán)重)起到的作用。此時(shí)浇雹,面向客戶的負(fù)載均衡和反向代理沉御,已經(jīng)部署完畢。由3臺(tái)服務(wù)器共同承擔(dān)一個(gè)定向地址的請(qǐng)求昭灵。

那么問(wèn)題來(lái)了吠裆,如何知道這個(gè)用戶是否登錄過(guò)了呢聂儒?這個(gè)問(wèn)題就是會(huì)話狀態(tài)維持,會(huì)話狀態(tài)實(shí)際上硫痰,是由session-cookies 來(lái)綁定維持的,做過(guò)移動(dòng)的webView的同學(xué)應(yīng)該都知道這個(gè)窜护,所以效斑,前端nginx和后端任意一臺(tái)nodejs服務(wù)器都不用關(guān)心會(huì)話,因?yàn)檫@個(gè)數(shù)據(jù)是存儲(chǔ)在redis集群上的柱徙,nodejs直接去集群上獲取比對(duì)session-cookies數(shù)據(jù)缓屠,相同則登陸過(guò),不同則沒(méi)有登陸過(guò)护侮,放心session-cookies是不會(huì)變的敌完,因?yàn)橛脩羰俏ㄒ慌cnginx進(jìn)行交互的,只要nodejs或者如果java服務(wù)器程序猿羊初,不去自己手動(dòng)獲取session對(duì)象滨溉,就能保證會(huì)話一致。

搞定了nginx 和 nodejs應(yīng)用服務(wù)器之后长赞,接下來(lái)就是nodejs服務(wù)器獲取redis集群的部分了晦攒。
配置redis集群的方式,本文就不詳述了得哆,百度好多meet分片的文章脯颜。其實(shí)就是修改conf中幾個(gè)參數(shù),再手動(dòng)修改分片文件進(jìn)行分片就可以贩据。當(dāng)然也可以命令分片栋操。

20160226221719021.jpg

筆者在自己的虛擬機(jī)里,配置并啟動(dòng)了三臺(tái)redis饱亮,構(gòu)建成了一個(gè)cluster集群矾芙。
分別是:
192.168.1.200:7000
192.168.1.200:7001
192.168.1.200:7002

接下來(lái)就是需要注意的地方:
cluster集群與主從的redis有很大出入的地方:
1,java代碼中:

import java.util.HashSet;  
import java.util.Set;  
  
  
import redis.clients.jedis.HostAndPort;  
  
import redis.clients.jedis.JedisCluster;  
  
/*** 
 * 測(cè)試redis 
 * @author sxt 
 * 
 */  
public class TestJedis {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
          
        // 集群地址  
        Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();  
        jedisClusterNodes.add(new HostAndPort("192.168.1.200", 7000));    
        jedisClusterNodes.add(new HostAndPort("192.168.1.200", 7001));    
        jedisClusterNodes.add(new HostAndPort("192.168.1.200", 7002));    
          
        // 實(shí)例化集群  
        JedisCluster jedis = new JedisCluster(jedisClusterNodes);  
          
        // 向集群中綁定值  
        jedis.set("name","shixiaotian");  
        // 從集群中獲取值  
        String result = jedis.get("name");  
        // 輸出結(jié)果  
        System.out.println(result);  
          
    }  
  
} 

如上圖代碼中所示近上,官方給出的最簡(jiǎn)單的獲取方式中蠕啄,cluster并沒(méi)有讓我們指定從哪臺(tái)redis中獲取數(shù)據(jù)。為什么戈锻?

redis cluster 采用的是數(shù)據(jù)分片技術(shù)歼跟,并沒(méi)有熱備份。每個(gè)數(shù)據(jù)都是存在固定的位置的格遭,因此你訪問(wèn)任何一臺(tái)機(jī)器哈街,都會(huì)給你重新定向到存儲(chǔ)你需要數(shù)據(jù)的那臺(tái)redis上,所以不存在并發(fā)問(wèn)題拒迅,因?yàn)樯兀銠M豎都得到這臺(tái)機(jī)器上來(lái)她倘。這與之前接觸到的主從完全不一樣,這里講究的是分片后的最小丟失作箍,主從講究的是不丟失(想法總是很好的)硬梁。

因此,你完全不需要對(duì)cluster做負(fù)載均衡胞得,集群自己處理(很贊荧止,這才是合格的產(chǎn)品,屏蔽讓別人覺(jué)得麻煩的東西阶剑,不需要自己去掛個(gè)代理了)跃巡。

nodejs中讀取redis集群的方式也要進(jìn)行相應(yīng)的改變,為了區(qū)分兩種的編碼方式牧愁,下面都給出:

1.主從讀取方式

//redis 鏈接  
var redis   = require('redis');  
var client  = redis.createClient('7000', '192.168.1.200');  
// redis 鏈接錯(cuò)誤  
client.on("error", function(error) {  
    console.log(error);  
});  
  
// 向特定的redis機(jī)器上綁定數(shù)據(jù)  
cluster.set('foo', 'bar');  
// 從特定redis上獲取數(shù)據(jù)  
cluster.get('foo', function (err, res) {  
    
  console.log(res);  
  
});  

2.cluster集群獲取方式:

// 注意素邪,不一樣的模塊  
var Redis = require('ioredis');  
  
// 不一樣的創(chuàng)建方式,多臺(tái)獲取猪半,出來(lái)就是集群  
var cluster = new Redis.Cluster(  
    [{  
      port: 7000,  
      host: '192.168.1.200'  
    }, {  
      port: 7001,  
      host: '192.168.1.200'  
    }, {  
      port: 7002,  
      host: '192.168.1.200'  
    }]  
);  
// 設(shè)置數(shù)據(jù)相同  
cluster.set('foo', 'bar');  
// 獲取數(shù)據(jù)相同  
cluster.get('foo', function (err, res) {  
    
  console.log(res);  
    
}); 

我們這里使用的是第二種方式兔朦,因?yàn)槭莄luster集群。

接下來(lái)磨确,編寫應(yīng)用程序烘绽,所有使用到的session 數(shù)據(jù),全部使用cluster中獲取到的俐填,或者存入的安接,即可。
來(lái)源:http://blog.csdn.net/a7178077/article/details/50752413

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末英融,一起剝皮案震驚了整個(gè)濱河市盏檐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驶悟,老刑警劉巖胡野,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異痕鳍,居然都是意外死亡硫豆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門笼呆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)熊响,“玉大人,你說(shuō)我怎么就攤上這事诗赌『骨眩” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵铭若,是天一觀的道長(zhǎng)洪碳。 經(jīng)常有香客問(wèn)我递览,道長(zhǎng),這世上最難降的妖魔是什么瞳腌? 我笑而不...
    開(kāi)封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任绞铃,我火速辦了婚禮,結(jié)果婚禮上嫂侍,老公的妹妹穿的比我還像新娘儿捧。我一直安慰自己,他們只是感情好吵冒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著西剥,像睡著了一般痹栖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞭空,一...
    開(kāi)封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天揪阿,我揣著相機(jī)與錄音,去河邊找鬼咆畏。 笑死南捂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旧找。 我是一名探鬼主播溺健,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钮蛛!你這毒婦竟也來(lái)了鞭缭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤魏颓,失蹤者是張志新(化名)和其女友劉穎岭辣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體甸饱,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沦童,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叹话。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷遗。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驼壶,靈堂內(nèi)的尸體忽然破棺而出鹦肿,到底是詐尸還是另有隱情,我是刑警寧澤辅柴,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布箩溃,位于F島的核電站瞭吃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏涣旨。R本人自食惡果不足惜歪架,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望霹陡。 院中可真熱鬧和蚪,春花似錦、人聲如沸烹棉。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浆洗。三九已至催束,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伏社,已是汗流浹背抠刺。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摘昌,地道東北人速妖。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像聪黎,于是被迫代替她去往敵國(guó)和親罕容。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359