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