對于搜索系統(tǒng)大量并發(fā)訪問的通常解決方案是分布式搜索和智能緩存相結(jié)合,這些功能HubbleDotNet 將在后續(xù)版本中開發(fā)。不過分布式搜索和智能緩存服務(wù)往往會提高系統(tǒng)的軟硬件成本,很多中小型網(wǎng)站的站內(nèi)搜索的訪問量還沒有達(dá)到大型門戶網(wǎng)站的數(shù)量級別,他們往往希望采用單機(jī)系統(tǒng)來最大限度的滿足自身系統(tǒng)的需要。為了這些系統(tǒng)可以最大限度的在低成本下運(yùn)行,HubbleDotNet 的V1.0.4.0提供了一個(gè)折中的解決方案,可以在一定程度上緩解單機(jī)搜索系統(tǒng)大量并發(fā)的問題。
首先我們來看看 Too many connects on server 這個(gè)錯(cuò)誤是怎么產(chǎn)生的。
HubbleDotNet 是以服務(wù)形式存在的,HubbleDotNet 提供的客戶端組件 Hubble.SqlClient.dll 通過TCP 方式向HubbleDotNet 服務(wù)發(fā)出查詢請求,并從HubbleDotNet 服務(wù)獲得查詢結(jié)果。每個(gè)HubbleConnection 實(shí)例都會保持一個(gè)和服務(wù)器之間的TCP連接,直到執(zhí)行HubbleConnection.Close 方法關(guān)閉連接,這個(gè)原理和SQL SERVER 的訪問原理是相似的。HubbleDotNet 在 Setting.xml 中有如下配置
<MaxConnectNum>32</MaxConnectNum>
這個(gè)配置用于設(shè)置HubbleDotNet 服務(wù)同時(shí)最多可以接受的連接數(shù),默認(rèn)為32個(gè)連接。如果同時(shí)連接數(shù)超過了這個(gè)設(shè)置指定的數(shù),老版本就會報(bào) Too many connects on server這樣的錯(cuò)誤。
那么是不是我們把這個(gè)連接數(shù)加大就可以解決大并發(fā)的問題了呢?回答是不一定。
這里面有兩個(gè)問題
1. 單機(jī)的TCP連接數(shù)是有限的,曾經(jīng)有個(gè)用戶將這個(gè)值設(shè)置為10000,結(jié)果導(dǎo)致其機(jī)器上其他的TCP連接都無法連接了,為什么會造成這個(gè)問題,請看我以前寫過的一篇文章
Windows 下單機(jī)最大TCP連接數(shù)
2. 機(jī)器的處理能力有限
機(jī)器的處理能力就像一個(gè)池子,如果流進(jìn)的水一直比流出的水多,那么這個(gè)池子遲早會溢出。比如機(jī)器的處理能力為每秒可以搜索100次,那么當(dāng)并發(fā)數(shù)量超過每秒100次時(shí),哪怕最大連接數(shù)設(shè)置很大,溢出也是遲早會發(fā)生的,只是這個(gè)值設(shè)置的大比設(shè)置的小發(fā)生溢出所用時(shí)間會長。
下面談?wù)?V1.0.4.0 所做的改進(jìn)
改進(jìn)1 等待機(jī)制
等待機(jī)制就是當(dāng)同時(shí)連接數(shù)超過最大連接數(shù)時(shí),后面的連接會等待一段時(shí)間,而不是直接報(bào)錯(cuò)。在這個(gè)等待的時(shí)間內(nèi),HubbleConnection 會多次嘗試和服務(wù)器連接,一旦服務(wù)器有空閑的連接,等待的連接就可以連接成功。
等待的時(shí)間由 HubbleConnection.ConnectionTimeout 這個(gè)參數(shù)設(shè)置,單位為秒,默認(rèn)為300秒。如果超過這個(gè)等待時(shí)間依然無法連接成功,則會觸發(fā)連接超時(shí)的錯(cuò)誤。
這個(gè)改進(jìn)主要是針對短時(shí)間內(nèi)大量訪問的情況。很多中小網(wǎng)站,搜索的訪問量不會持續(xù)很大,但可能在某個(gè)瞬間很高,比如在100ms內(nèi)同時(shí)有50個(gè)用戶訪問,最大連接數(shù)為32,這時(shí)即使機(jī)器的處理能力達(dá)到每秒500次,老版本依然可能出錯(cuò),V1.0.4.0 以后版本,在出現(xiàn)這種問題時(shí)就不會再出錯(cuò)了,大量并發(fā)時(shí)訪問時(shí)間可能會稍微長一點(diǎn)比如幾秒鐘,這個(gè)視機(jī)器性能和緩存設(shè)置情況而定。但不會出錯(cuò),這樣對于應(yīng)用來說會更友好一些。
等待機(jī)制和數(shù)據(jù)緩存超時(shí)同時(shí)使用,會達(dá)到比較好的效果
HubbleDotNet 的數(shù)據(jù)緩存原理是將查詢數(shù)據(jù)緩存在客戶端組件中,查詢時(shí)如果緩存未超時(shí),則直接從客戶端組件讀取數(shù)據(jù),這樣就大大緩解了搜索服務(wù)器的壓力。這個(gè)超時(shí)時(shí)間通過HubbleCommand.CacheTimeout 這個(gè)屬性設(shè)置,單位為秒,默認(rèn)為-1,超時(shí)時(shí)間小于0,表示不緩存。等于0表示每次都向服務(wù)器詢問一下緩存是否還有效,如果在兩次訪問間服務(wù)器沒有更新過,則服務(wù)器返回緩存依然有效,這樣就繼續(xù)使用客戶端的緩存,否則服務(wù)器將重新查詢一邊。如果大于0,那么在緩存超時(shí)前將不再詢問服務(wù)器是否有效,直到緩存超時(shí)后才詢問。
HubbleCommand 還有一個(gè)屬性 ResetDataCacheAfterTimeout 這個(gè)屬性如果設(shè)置為 true ,則在緩存超時(shí)后強(qiáng)制刷新緩存,不管服務(wù)器是否更新過,這個(gè)設(shè)置一般用在被動模式下非索引字段更新問題的解決上,這里不做過多解釋。
很顯然,如果我們設(shè)置了較長的緩存超時(shí)時(shí)間,服務(wù)器的壓力就會大大降低(除非每次搜索都是不同的關(guān)鍵字,這種應(yīng)用不是特別多,大部分應(yīng)用還是集中在一些熱門關(guān)鍵字上,網(wǎng)站上也可以通過類似百度,google 的關(guān)鍵字提示功能來誘導(dǎo)用戶使用熱門關(guān)鍵字以提高緩存的命中率,減輕服務(wù)器的壓力)
改進(jìn)2 服務(wù)器忙時(shí)強(qiáng)制使用緩存
HubbleConnection 這個(gè)類新增了一個(gè) TryConnectTimeout 屬性,這個(gè)屬性設(shè)置嘗試連接的超時(shí)時(shí)長,單位為毫秒。當(dāng)嘗試連接時(shí)間超過這個(gè)設(shè)置時(shí)讓暫時(shí)讓這個(gè)連接通過,即HubbleConnection.Open 這個(gè)函數(shù)返回(此時(shí)并沒有連接到服務(wù)器)。然后后面在HubbleCommand 執(zhí)行 SQL 查詢時(shí),將判斷當(dāng)前是否有緩存,如果有,不管這個(gè)緩存是否超時(shí),都強(qiáng)制使用這個(gè)緩存,如果沒有將繼續(xù)嘗試連接,直到連接超時(shí)(這時(shí)不再判斷這個(gè) TryConnectTimeout 超時(shí)了,而是判斷 ConnectionTimeout 超時(shí)了)。這個(gè)參數(shù)的默認(rèn)值為0,就是不強(qiáng)制使用緩存。
這個(gè)設(shè)置的好處是在大并發(fā)時(shí)最大限度的減少響應(yīng)時(shí)間。下面舉個(gè)例子
比如我們設(shè)置數(shù)據(jù)緩存超時(shí)為300秒,某個(gè)搜索條件 A 在300秒前執(zhí)行過一次,300秒后又要查詢這個(gè)A,這時(shí)網(wǎng)站出現(xiàn)訪問量洪峰,同時(shí)搜索的訪問量超過32次(假設(shè)最大連接數(shù)為32),假設(shè)A為第33個(gè)連接,平均搜索時(shí)長為50毫秒,那么A最長會在 1.6 秒后才得到執(zhí)行。如果設(shè)置TryConnectTimeout= 200ms 那么200ms后,A就直接返回300秒前的緩存數(shù)據(jù)了,這樣就大大提高了服務(wù)器忙時(shí)的搜索響應(yīng)時(shí)間。
應(yīng)用這個(gè)參數(shù)時(shí)必須特別注意
由于數(shù)據(jù)緩存只針對 select 語句,對于存儲過程是不會緩存的,那么根據(jù)我上面說的,如果在 select 語句前執(zhí)行存儲過程,那么這個(gè)設(shè)置就失效了,比如我寫的那個(gè)asp.net 的例子,在select 執(zhí)行之前會執(zhí)行一個(gè)存儲過程獲取搜索關(guān)鍵字的分詞結(jié)果,如果要用這個(gè)設(shè)置,那么我們就不能這樣去獲取搜索關(guān)鍵字的分詞結(jié)果了,要用本地的分詞組件在asp.net 代碼中直接進(jìn)行分詞才行,也就是說如果要使用這個(gè)參數(shù),我們只能在 HubbleCommand 中執(zhí)行 select 語句,不能執(zhí)行存儲過程,否則這個(gè)參數(shù)就會失效,效果和不用這個(gè)參數(shù)的效果一樣。