理解session和cookie之間的聯(lián)系
????當(dāng)程序需要為客戶端創(chuàng)建一個(gè)session前吧碾,會(huì)先在客戶端發(fā)送過來(lái)的消息內(nèi)查找是否包含cookie信息烟阐,cookie信息里一般會(huì)報(bào)存一個(gè)session標(biāo)識(shí)(也就是sessionId)忍燥,如果沒有這個(gè)sessionId就創(chuàng)建一個(gè)新的session和一個(gè)與該session關(guān)聯(lián)的sessionId拧晕,將sessionId返回給客戶端保存。(sessionId不重復(fù))
? ? 若上面的cookie被人為禁止了梅垄,那么我們就采用url重寫的方式將標(biāo)識(shí)附在url路徑后(兩種方式:1.作為url的附加消息厂捞;2.作為查詢字符串附加在url后)
為什么session需要設(shè)置失效時(shí)間?
? ? 要先明白一個(gè)概念:只有客戶端通知服務(wù)器刪除session队丝,服務(wù)器才會(huì)刪除session靡馁。但是我們關(guān)閉瀏覽器前并不會(huì)給服務(wù)器發(fā)送通知“我要關(guān)閉瀏覽器了”,所以服務(wù)器根本不知道我們何時(shí)關(guān)閉瀏覽器机久。所以服務(wù)器就要設(shè)置一個(gè)session失效時(shí)間臭墨,避免session的長(zhǎng)期保留占用存儲(chǔ)空間。
如何避免表單重復(fù)提交膘盖?(這里只考慮session方法)
? ? 在服務(wù)端隨機(jī)生成一個(gè)唯一的標(biāo)識(shí)符胧弛,存入session和表單的隱藏字段中尤误。當(dāng)用戶點(diǎn)擊提交,把表單信息發(fā)送給服務(wù)器叶圃,服務(wù)器先檢測(cè)標(biāo)識(shí)符字段是否相等,若不等践图,則說(shuō)明是重復(fù)提交掺冠,不再進(jìn)行處理。
如何用session共享實(shí)現(xiàn)單點(diǎn)登錄码党?
? ? 將sessionID存儲(chǔ)在Redis上德崭。
????1.用戶訪問某頁(yè)面,該頁(yè)面需要登錄揖盘。用戶向sso發(fā)起登錄請(qǐng)求眉厨,sso跳轉(zhuǎn)到登錄界面。接收用戶名兽狭、密碼憾股,判斷用戶名密碼是否正確,正確就生成一個(gè)token箕慧,將用戶信息保存至Redis上并設(shè)置有效期服球,返回登錄成功,將token保存在cookie中颠焦,返回到該頁(yè)面斩熊。
? ? 2.在用戶已經(jīng)登錄的前提下訪問B頁(yè)面,從cookie中取出token伐庭,sso服務(wù)根據(jù)token查詢用戶的登錄信息粉渠,redis根據(jù)key獲取內(nèi)容,然后返回給sso圾另,sso判斷token值是否存在霸株,存在就調(diào)整生存期,不存在則返回登錄界面集乔。
? ? 3.若用戶退出登錄淳衙,那么sso將token銷毀,并且取出所有由該token注冊(cè)的地址饺著,將所有注冊(cè)系統(tǒng)發(fā)起注銷請(qǐng)求箫攀,各子系統(tǒng)收到請(qǐng)求后銷毀局部會(huì)話。用戶完成單點(diǎn)注銷
sso部署方式:
? ? ? ? sso認(rèn)證系統(tǒng)(只有認(rèn)證系統(tǒng)提供登錄頁(yè)面)作為sso-server端幼衰,而其它子系統(tǒng)作為sso-client端靴跛,它們之間靠著token維持聯(lián)系。
既然存在cookie渡嚣,那么為什么不直接用cookie直接進(jìn)行單點(diǎn)登錄梢睛?
? ? cookie的域是有限制的肥印。出于安全的考慮,只有與創(chuàng)建cookie的頁(yè)面處于同一個(gè)目錄下绝葡,或子目錄下才可以訪問cookie深碱。
? ? 不過cookie的跨域限制是可以解決的。
? ? 解決方法:
? ? ? ? ? ? ? ? ? ? 1.使用nginx進(jìn)行反向代理藏畅,客戶端的請(qǐng)求都經(jīng)過nginx敷硅,再發(fā)送給服務(wù)端,nginx獲得返回內(nèi)容后再轉(zhuǎn)交給客戶端
? ? ? ? ? ? ? ? ? ? 2.使用jsonp愉阎,在ajax的url里寫入需要跳轉(zhuǎn)的網(wǎng)址绞蹦,并且設(shè)置發(fā)送cookie(因?yàn)槟J(rèn)為不發(fā)送cookie)
$.ajax({
url:'http://www.wp.com/getData.php'?
type:'GET',?//jsonp?類型下只能使用GET,不能用POST,這里不寫默認(rèn)為GET
dataType:'jsonp',?//指定為jsonp類型
data:{"name":"Zjmainstay"},?//數(shù)據(jù)參數(shù)
jsonp:'callback',
//服務(wù)器端獲取回調(diào)函數(shù)名的key,對(duì)應(yīng)后臺(tái)有$_GET['callback']='‘getName';callback是默值
jsonpCallback:'getName',?//回調(diào)函數(shù)名
success:function(result){?//成功執(zhí)行處理榜旦,對(duì)應(yīng)后臺(tái)返回的getName(data)方法幽七。}
});
? ? ? ?3.nodejs的superagent
?但是即使cookie共享了,也還是存在著很多問題
問題一:各子系統(tǒng)的web服務(wù)器必須相同溅呢,不同的服務(wù)器cookie的key值不同
問題二:共享cookie無(wú)法實(shí)現(xiàn)跨語(yǔ)言登錄
問題三:cookie存在一定的安全隱患
什么是Redis澡屡?
redis其實(shí)就是一個(gè)緩存工具,可以存儲(chǔ)大量數(shù)據(jù)咐旧。但是redis又不同于關(guān)系型數(shù)據(jù)庫(kù)挪蹭,因?yàn)樗荒艽鎯?chǔ)key-value,常用于主鍵查詢休偶。
Redis的主要應(yīng)用場(chǎng)景:
? ? ? ? 秒殺商品梁厉、熱點(diǎn)新聞、熱門商品等(其實(shí)也就是需要頻繁查詢的東西放在redis里踏兜,因?yàn)橄啾容^mysql词顾,redis查詢速度更快,性能更好)碱妆、聊天記錄緩存(將聊天記錄先緩存到redis肉盹,等達(dá)到一定數(shù)量再存到數(shù)據(jù)庫(kù)中,這比一條一條直接存入數(shù)據(jù)庫(kù)來(lái)說(shuō)性能更佳)
Redis查詢?yōu)槭裁春芸欤?/b>
? ? 雖然redis是單線程程序疹尾,但是因?yàn)樗羌儍?nèi)存數(shù)據(jù)庫(kù)上忍,只操作內(nèi)存(而我們的mysql是硬盤操作),并且使用了異步非阻塞IO
Redis的優(yōu)勢(shì)(從數(shù)據(jù)結(jié)構(gòu)層面討論)
? ? 1.它使用了動(dòng)態(tài)字符串纳本,但是這個(gè)動(dòng)態(tài)字符串最大的特點(diǎn)就是它的實(shí)際長(zhǎng)度遠(yuǎn)比它的存儲(chǔ)長(zhǎng)度長(zhǎng)窍蓝。這樣在一定范圍的字符串存入,就不需要頻繁的進(jìn)行內(nèi)存的再分配
? ? 2.它使用了dict(也就是字典)繁成,類似于java的hashmap(插入知識(shí):hashmap的hash算法就是將當(dāng)前要查找的key值進(jìn)行hash吓笙,然后將得到的hash值與原數(shù)組的索引值進(jìn)行對(duì)比,找到對(duì)應(yīng)的value)巾腕,但是與hashmap不同的一點(diǎn)就是dict包含兩個(gè)數(shù)組面睛。
????????兩個(gè)數(shù)組主要是用于擴(kuò)容的絮蒿,初始的dict數(shù)組長(zhǎng)度不會(huì)過長(zhǎng),當(dāng)超過了一定長(zhǎng)度后叁鉴,就會(huì)進(jìn)行擴(kuò)容土涝。(插入知識(shí):hashmap的擴(kuò)容就是新生成一個(gè)數(shù)組,在把舊數(shù)組的值向新數(shù)組遷移)幌墓。而dict的擴(kuò)容是漸進(jìn)式的但壮,就是一點(diǎn)點(diǎn)的向另一個(gè)數(shù)組遷移,不會(huì)影響當(dāng)前的dict的使用克锣。
? ? 3.關(guān)系型數(shù)據(jù)庫(kù)往往都是采用樹型結(jié)構(gòu)(例如mysql的B+樹茵肃,B+樹的搜索非常的快速腔长,但是B+樹最大的弱點(diǎn)就在于插入袭祟,因?yàn)樾枰鞣N的左旋右旋,拖了性能)捞附,而我們的非關(guān)系型Redis采用的是跳躍表巾乳。
? ? ? ? 什么是跳躍表?
? ? ? ? 首先先明白數(shù)組鸟召、鏈表帶來(lái)的插入數(shù)據(jù)的復(fù)雜
? ? ? ? 比如:當(dāng)數(shù)組里存有 1,2,3,4,6,7,8,9,10,11 時(shí)胆绊,我們向數(shù)組內(nèi)插入5,使用二分查找法快速查找到與3最相近的數(shù)字(O(logN))欧募,然后再將其他數(shù)字右移(O(N))压状,那么數(shù)組的插入算法的時(shí)間復(fù)雜度就是O(N)
? ? ? ? ? ? ? ? ? ?當(dāng)鏈表里存有 1,2,3,4,6,7,8,9,10,11時(shí),往鏈表中插入5跟继,那么就需要逐一比較种冬,時(shí)間復(fù)雜度為O(N)
? ? ? ? 這樣簡(jiǎn)單的數(shù)字看起來(lái)插入查詢似乎并沒有什么性能的影響,但是思考一下舔糖,如果此時(shí)數(shù)據(jù)庫(kù)存在10萬(wàn)條數(shù)據(jù)娱两,對(duì)這個(gè)數(shù)據(jù)進(jìn)行查詢或者插入,是非常耗費(fèi)性能的金吗。
? ? ? ? 所以這個(gè)時(shí)候提出來(lái)跳躍表:
? ? ? ? ? ? 例子:我們將原數(shù)據(jù)1,2,3,4,6,7,8,9,10,11提煉出來(lái)十兢,變成兩層:1,4,8,11(2,3,6,7,9,10),那我們就只需要先對(duì)頂層數(shù)據(jù)進(jìn)行查詢摇庙,然后查找到與插入數(shù)據(jù)最接近的值4旱物,那么就知道數(shù)據(jù)應(yīng)該插入4-8之間,那么繼續(xù)往下查詢卫袒,知道插入的數(shù)據(jù)應(yīng)該在4-6之間异袄,確定了索引位置就返回原鏈表,迅速定位到該位置進(jìn)行插入玛臂。
? ? ? ? ? ? ? ? ? ? 那么問題又來(lái)了?就伞封孙!如果這個(gè)時(shí)候已經(jīng)有大量新節(jié)點(diǎn)插入,那么原本的索引節(jié)點(diǎn)就會(huì)不夠用了讽营,這個(gè)時(shí)候要怎么辦呢虎忌?
? ? ? ? ? 跳躍表的提出者想了一個(gè)辦法:既然我們無(wú)法確定增加和刪除的節(jié)點(diǎn),那么我們就采用拋硬幣法(概率50%)橱鹏,為正就把節(jié)點(diǎn)提到上一層索引膜蠢,然后再拋硬幣,若為正繼續(xù)提到上一層索引莉兰,直至為反挑围。
? ? ? ? 好了又逼逼太多了,總結(jié)一下跳躍表的插入
? ? ? ? 1.將新節(jié)點(diǎn)與各層節(jié)點(diǎn)逐一比較糖荒,確定插入的位置杉辙,把索引插入到原鏈表
? ? ? ? 2.采用“拋硬幣”法,決定節(jié)點(diǎn)是否提為為上一層的索引捶朵,硬幣為正就往上提蜘矢,為負(fù)就停止。
????????逼逼太多甚至忘了自己剛才寫到了哪里
? ? 4.上面說(shuō)的數(shù)據(jù)結(jié)構(gòu)的優(yōu)勢(shì)都沒有比下面我們要說(shuō)的這個(gè)牛皮W劭础品腹!就是單線程+多路復(fù)用IO模型
? ? ? ? 一聽到單線程是不是就覺得坑了(什么鬼單線程查詢速度能這么快?)
? ? ? ? 首先红碑,單線程肯定不可能比多線程快舞吭,但是單線程的模式避免了數(shù)據(jù)并發(fā)安全(因?yàn)槎嗑€程的數(shù)據(jù)并發(fā)安全,所以出現(xiàn)了各種鎖)析珊。那么這個(gè)時(shí)候我們單線程的優(yōu)點(diǎn)就來(lái)了:不需要考慮數(shù)據(jù)并發(fā)的安全羡鸥,也不用出現(xiàn)頻繁的線程調(diào)度,切換上下文唾琼。
? ? ? ? 上下文是啥兄春?
????????線程每次執(zhí)行時(shí)都需要將數(shù)據(jù)從主內(nèi)存讀入到工作內(nèi)存中。當(dāng)線程阻塞時(shí)锡溯,工作內(nèi)存的數(shù)據(jù)就需要被放到線程上下文中赶舆,逼逼了這么多,其實(shí)線程上下文就是一個(gè)存儲(chǔ)結(jié)構(gòu)祭饭,當(dāng)線程被喚醒的時(shí)候芜茵,就去讀取上下文的內(nèi)容,就稱為切換上下文倡蝙。
? ? ? ? 那么再說(shuō)一說(shuō)多路復(fù)用IO九串,其實(shí)就是當(dāng)一個(gè)請(qǐng)求訪問redis時(shí),redis去取數(shù)據(jù)給這個(gè)請(qǐng)求的時(shí)間段內(nèi),請(qǐng)求的入口不是阻塞的猪钮,也就是說(shuō)依舊可以接收請(qǐng)求品山,直到redis取到數(shù)據(jù),再一一響應(yīng)這些請(qǐng)求烤低。(其實(shí)我覺得有點(diǎn)像消息隊(duì)列)
? ? ? ? 我們拿redis常常用來(lái)做緩存肘交,用它會(huì)碰到一個(gè)問題“緩存擊穿”
? ? ? ? 什么叫做緩存擊穿?
? ? ? ? 我們緩存里存有一定的數(shù)據(jù)扑馁,如果緩存中查找不到數(shù)據(jù)涯呻,就到服務(wù)器里查找數(shù)據(jù)再更新redis緩存。那么如果有人惡意拿請(qǐng)求緩存中沒有的數(shù)據(jù)腻要,造成redis壓力過大复罐,redis往往會(huì)被整崩潰了。
? ? ? ? 如何解決緩存擊穿雄家?
? ? ? ? 定時(shí)更新redis緩存效诅,緩存中查找不到的數(shù)據(jù)不允許再去服務(wù)器里查找,所有的查詢以緩存中的數(shù)據(jù)為主咳短。(雖然這會(huì)存在數(shù)據(jù)延遲的問題填帽,但是緩存容量夠大蛛淋,定時(shí)更新時(shí)間合理咙好,延遲的時(shí)間就微乎其微了)