Clojure建立多个数据库连接

时间:2016-07-29 10:51:50

标签: clojure database-connection luminus connman

我们有一个Clojure Web应用程序,由多个项目(> 20)使用,这些项目有多个用户同时登录。所有项目都有自己的MySQL数据库。我们试图找出一种方法来使用一个应用程序实例来处理从项目数据库传递的用户的请求。

以下脚本显示了我们的多个连接的原则,并且应该可以在REPL中执行(使用正确的数据库设置)。

(ns testmultiple.core
  (:require
    [clojure.java.jdbc :as jdbc]
    [compojure.core :refer [defroutes GET ANY routes context]]
    [conman.core :as conman]
    [mount.core :refer [defstate]]))

(def database-urls {:DB1 "jdbc:mysql://localhost:3306/DB1?user=DB1_user&password=DB1_password"
                    :DB2 "jdbc:mysql://localhost:3306/DB2?user=DB2_user&password=DB2_password"})

;; Connects to all databases in pool-specs
(defn connect!
  [pool-specs]
  (reduce merge (map (fn [pool-spec]
                       {(keyword (key pool-spec)) (conman/connect! {:jdbc-url (val pool-spec)})}) pool-specs)))

;; Disconnect from all databases in db-connections
(defn disconnect!
  [db-connections]
  (map (fn [db] (conman/disconnect! (val db))) db-connections))

;; Establish connections to all databases
;; and store connections in *dbs*
(defstate ^:dynamic *dbs*
          :start (connect!
                   database-urls)
          :stop (disconnect! *dbs*))

;; Bind queries to *db* dynamic variable which is bound
;; to each clients database before executing queries
;; The queries file defines the query get-user which
;; returns user by user id
(def ^:dynamic *db* nil)
(conman/bind-connection *db* "sql/queries.sql")

(mount.core/start)

; Define function that executes in current *db* binding
(defn getuser [id] (get-user {:id id}))

; Works, the user with Id 670 is returned from DB1
(with-bindings {#'*db* (:DB1 *dbs*)} (getuser 670))

; Works, the user with Id 670 is returned from DB2
(with-bindings {#'*db* (:DB2 *dbs*)} (getuser 670))

更具体地说,该项目是从路由器中的URL请求推断出来的。以下代码显示了路由器的原理。访问www.example.com/DB1/page1和www.example.com/DB2/page2将分别显示page1,其中包含来自DB1和page2的数据以及来自DB2的数据。

(defn serve-page1 [] (str "page1" (getuser 670)))
(defn serve-page2 [] (str "page2" (getuser 670)))

(def home-routes
  (context "/:project" [project]
    (if (contains? *dbs* (keyword project))
      (routes
        (GET "/page1" []
          (with-bindings {#'*db* ((keyword project) *dbs*)}
            (serve-page1)))
        (GET "/page2" []
          (with-bindings {#'*db* ((keyword project) *dbs*)}
            (serve-page2))))
      (ANY "*" [] (str "Project not found")))))

这将是一个拥有大量流量的应用程序。值得注意的是,我们仍处于开发阶段,因此无法使用在localhost上运行的多个数据库来测试此解决方案。我们的问题是

  • 建立多个这样合理,稳定且可扩展的连接?
  • 是否有其他更好的方法用于项目数据库的路由和动态绑定?

1 个答案:

答案 0 :(得分:2)

  

建立多个这样合理,稳定且可扩展的连接?

是的,这是一种非常合理的方法。很少有数据库系统受传出连接数量的限制。 JDBC和Korma都会在clojure中处理这个问题。在构建监视和操作相关组件时,您确实需要了解哪些请求依赖于哪个DB。因此,您可以确定哪个数据库导致了问题。

  

项目数据库的路由和动态绑定还有其他更好的方法吗?

我唯一的建议是明确地将DB传递给每个函数而不是使用绑定,尽管这是个人风格的意见,你的方法显然会有效。