繁体   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