关于postgresql:为什么我的Clojure应用程序需要_minutes_才能连接到Postgres?

Why does my Clojure application take _minutes_ to connect to Postgres?

我的项目使用Docker Compose创建两个服务(app和postgres)。 在本地,应用程序(使用Compojure,JDBC,Korma,Ragtime等的Clojure应用程序)可以立即连接到postgres,而不会出现问题。 但是,当我出于测试目的将应用程序部署到Digital Ocean Droplet(1 GB RAM / 30 GB磁盘/ Ubuntu 16.04.2 x64)时,应用程序似乎需要几分钟才能连接到Postgres-例如 Korma插入会挂起很多分钟,然后最终开始正常工作。 Droplet很小,但是似乎并没有资源短缺(基于htop的输出)。

以下是我申请的相关部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
;; project.clj
(defproject backend"0.1.0-SNAPSHOT"
  :min-lein-version"2.0.0"
  :dependencies [[com.grammarly/perseverance"0.1.2"]
                 [commons-codec/commons-codec"1.4"]
                 [compojure"1.4.0"]
                 [environ"1.0.3"]
                 [clj-http"2.3.0"]
                 [korma"0.4.3"]
                 [lock-KEY"1.4.1"]
                 [me.raynes/fs"1.4.6"]
                 [midje"1.6.3"]
                 [org.clojure/clojure"1.8.0"]
                 [org.clojure/core.async"0.3.441"]
                 [org.clojure/java.jdbc"0.7.0-alpha2"]
                 [postgresql"9.3-1102.jdbc41"]
                 [ragtime"0.6.0"]
                 [ring-cors"0.1.7"]
                 [ring-mock"0.1.5"]
                 [ring/ring-defaults"0.1.5"]
                 [ring/ring-json"0.4.0"]]
  :plugins [[lein-environ"1.0.3"]
            [lein-midje"3.1.3"]
            [lein-ring"0.9.7"]]
  :aliases {"migrate"  ["run""-m""backend.db/ragtime-migrate"]
           "rollback" ["run""-m""backend.db/ragtime-rollback"]}
  :ring {:handler backend.handler/app}
  :profiles
  {:dev {:dependencies [[javax.servlet/servlet-api"2.5"]
                        [ring/ring-mock"0.3.0"]]}})

;; db.clj
(ns backend.db
  (:USE [korma.core]
        [korma.db])
  (:require [clojure.string :AS string]
            [environ.core :AS environ]
            [lock-KEY.core :refer [encrypt-as-base64 decrypt-from-base64]
                           :RENAME {encrypt-as-base64 encrypt
                                    decrypt-from-base64 decrypt}]
            [ragtime.jdbc :AS jdbc]
            [ragtime.repl :AS repl]))

(def database-host (environ/env :postgres-port-5432-tcp-addr)) ;; SET BY Docker
(def database-name (environ/env :database-name))
(def database-password (environ/env :database-password))
(def database-port (environ/env :postgres-port-5432-tcp-port)) ;; SET BY Docker
(def database-sslmode (environ/env :database-sslmode))
(def database-USER (environ/env :database-USER))
(def database-url (str"jdbc:postgresql://"
                       database-host
                      ":"
                       database-port
                      "/"
                       database-name
                      "?user="
                       database-USER
                      "&password="
                       database-password))

(defn load-config []
    {:datastore  (jdbc/sql-DATABASE {:connection-uri database-url})
     :migrations (jdbc/load-resources"migrations")})

(defn ragtime-migrate []
  (repl/migrate (load-config)))

(defn ragtime-ROLLBACK []
  (repl/ROLLBACK (load-config)))

(defdb db (postgres {:db database-name
                     :host database-host
                     :password database-password
                     :port database-port
                     :USER database-USER
                     :sslmode database-sslmode}))

(defentity engagements)

(def LOCK (environ/env :LOCK))

(defn query-engagement [id]
  (let [engagement (FIRST
                     (SELECT
                       engagements
                       (WHERE {:id (read-string id)})))
        decrypted-email (->
                          (:email_address engagement)
                          (decrypt LOCK))]
    (conj engagement {:email_address decrypted-email})))

(defn create-engagement [email-address image-path]
  (let [encrypted-email (encrypt email-address LOCK)]
    (INSERT engagements
      (VALUES [{:email_address encrypted-email
                :image_path image-path}]))))

;; docker-compose.yml
app:
  build: .
  volumes:
    - .:/app
  ports:
    -"127.0.0.1:3000:3000"
  links:
    - postgres
postgres:
  build: .
  dockerfile: Dockerfile-postgres
  expose:
    -"5432"

我做错了什么吗? 这可能是JDBC连接池的问题吗? 是否有调试此类问题的约定?

更新:如果我直接在Digital Ocean Droplet上运行应用程序,而不是通过Docker,我可以确认问题仍然存在。


TLDR;

将以下标志添加到project.clj解决了我的问题::jvm-opts ["-Djava.security.egd=file:/dev/urandom"](HT到Redditor / u / fitzoh!)

据我了解,我看到的问题是由JVM向/dev/random发出随机数的阻止请求引起的。因为Droplet并没有执行任何操作(IO,网络请求等),所以要花费足够长的时间(以分钟为单位)来生成/dev/random的熵,才能开始生成随机数。

一种解决方法是使用/dev/urandom,它不等待熵累积,并会愉快地生成(低质量)随机数。通过这个出色的Digital Ocean教程,

... however, since it's a non-blocking device, it will continue producing"random" data, even when the entropy pool runs out. This can result in lower quality random data, as repeats of previous data are much more likely. Lots of bad things can happen when the available entropy runs low on a production server, especially when this server performs cryptographic functions.

另一个看似更健壮的解决方法(同样,来自出色的DO教程)是使用软件解决方案,例如Haveged。

Based on the HAVEGE principle, and previously based on its associated library, haveged allows generating randomness based on variations in code execution time on a processor. Since it's nearly impossible for one piece of code to take the same exact time to execute, even in the same environment on the same hardware, the timing of running a single or multiple programs should be suitable to seed a random source. The haveged implementation seeds your system's random source (usually /dev/random) using differences in your processor's time stamp counter (TSC) after executing a loop repeatedly.


我在Hibernate中也遇到了类似的问题,并且设法缩小它的范围,以减慢JDBC驱动程序中元数据的获取速度,尤其是在数据库服务器具有许多不同数据库的情况下(无论是否可以访问它们)。

在这种情况下,可能会有些类似,所以我建议您看一下是否可以在Korma中禁用元数据获取。

我尚未在JDBC驱动程序本身中找到它的设置,但是您应该可以通过将logLevel=2参数添加到jdbc-url中来从数据库本身获取更多日志记录,因为它可能会向您显示更多有关何处的详细信息问题出在哪里。