2016-07-29 131 views
2

我們有一個Clojure Web應用程序,由多個項目(> 20)使用,它們有多個用戶同時登錄。所有項目都有自己的MySQL數據庫。我們試圖找出一種方法來使用一個應用程序實例來爲來自其項目數據庫的用戶請求提供服務。Clojure建立多個數據庫連接

以下腳本顯示了我們的多個連接的原理,並且應該可以在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將分別顯示來自DB1和來自DB2的數據的頁1。

(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"))))) 

這將是一個具有相當流量的應用程序。值得注意的是,我們仍處於開發階段,因此無法使用本地主機上運行的多個數據庫來測試此解決方案。我們的問題是

  • 正在建立像這樣合理,穩定和可擴展的多個連接?
  • 有沒有其他更好的方法來路由和動態綁定項目的數據庫?

回答

1

正在建立像這樣合理,穩定和可擴展的多重連接嗎?

是的,這是一個非常合理的方法。很少數據庫系統受到傳出連接數量的限制。 JDBC和Korma都會在clojure中處理這個問題。您確實需要了解哪些請求取決於在構建監視和操作相關組件時使用哪個數據庫。所以你可以知道哪個數據庫導致了問題。

是否還有其他更好的方法用於項目數據庫的路由和動態綁定?

我唯一的建議是明確地將數據庫傳遞給每個函數,而不是使用綁定,雖然這是個人風格的意見,你的方法將明確工作。

+0

謝謝你的迴應!我來自PHP,每次發出http請求時都會重新建立數據庫連接,因此長期持續連接的想法對我來說是新的。連接是否有任何風險?如果是這樣 - 它會自動重新建立還是必須進行監控?解釋如何建立和維護連接的任何鏈接都非常出色。 –