簡體   English   中英

Haskell數據庫連接

[英]Haskell database connections

請查看這個scotty應用程序(它直接取自2014年的這個舊答案 ):

import Web.Scotty
import Database.MongoDB
import qualified Data.Text.Lazy as T
import Control.Monad.IO.Class

runQuery :: Pipe -> Query -> IO [Document]
runQuery pipe query = access pipe master "nutrition" (find query >>= rest) 

main = do
  pipe <- connect $ host "127.0.0.1"
  scotty 3000 $ do
    get "/" $ do
      res <- liftIO $ runQuery pipe (select [] "stock_foods")
      text $ T.pack $ show res

您將看到如何在啟動Web應用程序時僅一次創建數據庫連接( pipe )。 隨后,成千上萬(如果不是數百萬)的訪問者將同時點擊“ /”路由,並使用相同的連接( pipe )從數據庫中讀取數據。

我對如何正確使用Database.MongoDB有疑問:

  1. 這是設置事情的正確方法嗎? 相對於每次訪問“ /”都創建數據庫連接。 在后一種情況下,我們可以一次擁有數百萬個連接。 灰心嗎? 這種方法的優缺點是什么?
  2. 在上面的應用程序中,如果由於某種原因數據庫連接丟失並且需要重新創建該怎么辦? 您將如何恢復呢?
  3. 如何使用auth函數進行身份auth 應該在創建pipe之后僅將auth函數調用一次,還是應該在每次命中“ /”時調用auth函數?
  4. 有人說我應該使用一個池( Data.Pool )。 看起來這只會幫助限制同時使用同一數據庫連接的訪問​​者數量。 但是我為什么要這樣做呢? MongoDB連接是否沒有對同時使用的內置支持?
  1. 即使您為每個客戶端創建連接,您也將無法創建過多的連接。 您將擊中ulimit。 一旦您擊中該ulimit,則擊中該ulimit的客戶端將獲得運行時錯誤。 之所以沒有意義,是因為mongodb服務器將花費太多時間輪詢所有這些連接,並且它的有意義的工作人員將與db服務器擁有的CPU數量一樣多。 一個連接不是一個壞主意,因為mongodb旨在發送多個請求並等待響應。 因此,它將僅使用一個限制就可以使用mongodb所擁有的盡可能多的資源-您只有一個寫入管道,並且如果意外關閉,則需要您自己重新創建此管道。 因此,擁有一個連接池更有意義。 它不必很大。 我有一個可以驗證用戶身份並給他們令牌的應用程序。 每秒有2500個並發用戶,它與數據庫只有3-4個並發連接。

以下是連接池為您帶來的好處:

  • 如果達到池連接限制,則將等待下一個可用連接,並且不會出現運行時錯誤。 因此,您的應用將稍等片刻,而不是拒絕您的客戶端。

  • 池將為您重新建立連接。 您可以配置池以關閉過多的連接,並根據需要創建更多的連接,直到達到特定限制。 如果您在讀取或寫入連接時斷開連接,則只需從池中建立另一個連接。 如果您不將斷開的連接返回到池中,池將為您創建另一個連接。

    1. 如果數據庫連接已關閉,則:此連接上的mongodb監聽器將退出,並在終端上顯示一條錯誤消息,您的應用將收到IO錯誤。 為了處理此錯誤,您將需要創建另一個連接,然后重試。 在處理這種情況時,您了解使用數據庫池更容易。 因為最終您對此的解決方案將非常類似於連接池。

    2. 作為連接的一部分,我只進行一次身份驗證。 如果以后需要認證其他用戶,可以隨時進行。

    3. 是的,mongodb可以同時使用,但是就像我說的那樣,它只寫一個管道,很快就成為瓶頸。 如果您創建的連接數至少等於mongodb服務器所能負擔的用於處理它們的線程數(CPU數),則它們將全速運行。

如果我錯過了任何事情,請隨時澄清。 謝謝你的問題。

您真正想要的是一個數據庫連接池。 看一下另一個答案中的代碼。

如果您的MongoDB服務器處於安全模式,則可以使用withMongoDBPool代替auth

這是設置事情的正確方法嗎? 相對於每次訪問“ /”都創建數據庫連接。 在后一種情況下,我們可以一次擁有數百萬個連接。 灰心嗎? 這種方法的優缺點是什么?

您不想打開一個連接然后再使用它。 支持Scotty的HTTP服務器稱為Warp。 Warp具有多核,多綠線設計 允許您在所有線程之間共享相同的連接,因為Database.MongoDB直截了當地說連接是線程安全的,但是將發生的事情是,當一個線程被阻塞以等待響應時( MongoDB協議遵循簡單的請求-響應設計 ),您的Web服務中的所有線程都會阻塞。 這是不幸的。

相反,我們可以在每個請求上創建一個連接。 這瑣碎地解決了一個線程阻塞另一個線程的問題,但導致了自己的問題。 建立TCP連接的開銷雖然不大,但也不為零。 回想一下,每次我們想要打開或關閉套接字時,我們都必須從用戶跳到內核,等待內核更新其內部數據結構,然后跳回(上下文切換)。 我們還必須處理TCP握手和告別。 在高負載下,我們還將耗盡文件描述符或內存。

如果我們之間有解決方案,那就太好了。 解決方案應該是

  • 線程安全
  • 讓我們最大程度地限制連接數,這樣我們就不會耗盡操作系統的有限資源
  • 在正常負載下跨線程共享連接
  • 隨着負載的增加,創建新的連接
  • 在減少負載的情況下刪除連接時,請允許我們清理資源(如關閉句柄)
  • 希望已經由其他生產系統編寫並經過了實戰測試

資源池解決的正是這個問題。

有人說我應該使用一個池(Data.Pool)。 看起來這只會幫助限制同時使用同一數據庫連接的訪問​​者數量。 但是我為什么要這樣做呢? MongoDB連接是否沒有對同時使用的內置支持?

目前尚不清楚同時使用是什么意思。 我可以猜測一種解釋:您的意思是類似HTTP / 2的東西,該協議已內置流水線。

流水線的標准圖片http://research.worksap.com/wp-content/uploads/2015/08/pipeline.png

在上方,我們看到客戶端向服務器發出多個請求,而無需等待響應,然后客戶端可以按一定順序接收響應。 (時間從上到下流動。)此MongoDB沒有。 這是一個相當復雜的協議設計,沒有比僅要求您的客戶端使用連接池好多少了。 而且MongoDB並不孤單:Postgres,MySQL,SQL Server和大多數其他數據庫都采用了簡單的請求和響應設計。

並且:的確,連接池限制了在阻止所有線程並且用戶僅看到加載欄之前可以作為Web服務承擔的負載。 但是,在三種情況下(連接池,一個共享連接,每個請求一個連接),都會存在此問題! 計算機具有有限的資源,在某些情況下,某些東西會在足夠的負載下崩潰。 連接池的優點是可以正常擴展直到無法擴展為止。 處理更多流量的正確解決方案是增加計算機數量。 我們不應該僅僅因為這個問題就避免合並。

在上面的應用程序中,如果由於某種原因數據庫連接丟失並且需要重新創建該怎么辦? 您將如何恢復呢?

我認為,這些假設分析超出了Stack Overflow的范圍,沒有比“嘗試一下然后看”更好的答案了。 Buuuuuuut假​​設服務器終止了連接,我可以對可能發生的情況take之以鼻:假設Warp為每個請求派生一個綠色線程(我認為確實如此),則每個線程在嘗試寫入時都會遇到未經檢查的IOException TCP連接已關閉。 Warp會捕獲此異常並將其用作HTTP 500,希望也可以為日志編寫一些有用的東西。 假設像現在這樣的單連接模型,您可以在“重新啟動” main功能並建立第二個連接的地方做一些聰明的事情(但是代碼行很多)。 我為愛好項目所做的事情:萬一發生異常情況(例如斷開連接),我請主管進程(如systemd)觀看日志並重新啟動Web服務。 雖然顯然不是一個生產,賺錢的網站的好解決方案,但它對於小型應用程序已經足夠好了。

使用auth函數進行身份auth怎么辦? 應該僅在創建管道之后才將auth函數調用一次,還是在每次命中“ /”時都調用auth函數?

創建連接后應調用一次。 MongoDB身份驗證是按連接的。 您可以在此處看到db.auth()命令如何db.auth()與當前客戶端連接相對應的MongoDB服務器的數據結構的示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM