"I have a limit of a 100 threads per process. So when I go over that limit, I get: java.lang.OutOfMemoryError: unable to create new native thread.
Compojure is using the jetty ring adapter. Is there away of configuring the
the server to only accept a hundred threads to the servlet at a time?"
There seems to be a succinct solution here but it appears to be outdated:
Limiting the number of threads Compojure spawns
I put this in my project.clj:
:dependencies [[org.clojure/clojure "1.6.0"]
[compojure "1.1.8"]
[ring/ring-json "0.3.1"]
[c3p0/c3p0 "0.9.1.2"]
[org.clojure/java.jdbc "0.2.3"]
[cheshire "5.4.0"]
[ring/ring-jetty-adapter "1.1.8"]
[environ "0.2.1"]
[hiccup "1.0.0"]
[com.novemberain/monger "2.0.0"]
[org.clojure/data.json "0.2.5"]]
and I put this in my controller file:
(:import [org.mortbay.thread.QueuedThreadPool])
...
(defn -main [] (run-jetty app {:port (if (nil? (System/getenv
"PORT"))
8000 ; localhost or heroku?
(Integer/parseInt (System/getenv "PORT")))
:configurator #(.setThreadPool % (QueuedThreadPool. 100))
}) )
When I try that I get:
Exception in thread "main" java.lang.IllegalArgumentException: Unable to resolve classname: QueuedThreadPool, compiling:
Can you please offer me a recommendation of what clojure plugin to inject into my library in order not to startle the 100 thread per process limit with Heroku?
You seem to be using an outdated version of ring/ring-jetty-adapter (1.1.8), the latest release version is 1.3.2.
In the latest version you can simply specify :max-threads in the options to ring.adapter.jetty/run-jetty.
So to limit the maximum number of threads used by jetty, you can do the following:
(defn -main [] (run-jetty #'app
{:port (Integer/parseInt (or (System/getenv "PORT") "8000"))
:max-threads 100}) )
There may, of course, exist other threads in your JVM, which means that you may have to lower the number of threads allocated to jetty in order to not exceed the Heroku limit overall.
If you still want to use QueuedThreadPool, then you would need to use:
(:import [org.eclipse.jetty.util.thread QueuedThreadPool])
instead of
(:import [org.mortbay.thread.QueuedThreadPool])
Related
How do you make a ring server reload during development whenever a file changes?
Add this dependency to your project.clj:
[ring/ring-devel "1.8.0"]
You can get the latest version number from Clojars.
Then require the following in the file where your request handler lives:
(:require [ring.middleware.reload :refer [wrap-reload]])
The wrap your handler:
(wrap-reload handler)
Example from a server using multiple wrappers:
(def handler
(compojure/routes
(GET "/" [] "hello world")
(route/not-found "No such page.")))
(defn -main []
(server/run-server
(-> handler
params/wrap-params
wrap-reload)
{:port 8080}))
You can find the documentation on the reload middleware here, and another example on how to use it here.
I am using swagger to provide an API for a db access program. During development I normally have 2 versions running, the dev version and the prod version that I automatically start on login. I want to have a different title visible on the swagger front page so I don't accidentally trash my live database. So far I have been hand editing the title field in the swagger setup, but this is error prone, I often forget to change it before I run lein uberjar to build the prod version.
The env setup seems like an ideal way to do this. The luminus lein template already uses an env map that is built from dev and prod config files which works fine, allowing me to automatically specify different ports for the 2 builds. I added an entry to these that gives me a title that is different in prod and dev versions. I can see it from the repl, but including it in the swagger specification just gives null.
This is the :swagger definition from the start of my photo-api.routes.services.clj file:
(ns photo-api.routes.services
(:require [cheshire.core :as json]
[compojure.api.sweet :refer :all]
[image-lib.images :as ilim]
[image-lib.preferences :as ilpf]
[image-lib.projects :as ilpr]
[image-lib.write :as ilwr]
[photo-api.db.core :as db]
[photo-api.config :refer [env]]
[photo-api.routes.helpers.build :as build]
[photo-api.routes.helpers.keywords :as keywords]
[photo-api.routes.helpers.open :as open]
[photo-api.routes.helpers.photos :as photos]
[photo-api.routes.helpers.projects :as projects]
[ring.util.codec :refer [url-decode]]
[ring.util.http-response :refer [ok]]
[schema.core :as s]
[clojure.string :as str]))
(defapi service-routes
{:swagger {:ui "/swagger-ui"
:spec "/swagger.json"
:data
{:info
{:version "1.0.1"
;; Switch to correct title before lein uberjar
;; TODO Automate this so swagger page always shows dev or prod version
;;:title "Photo API"
:title (:title env)
:description "Access a mongo database containing details of photos"}}}}
The commented out :title specification works fine, but the (:title env) call doesn't although it is the exact same call I can successfully use from the repl. I believe the env map is built as part of photo-api.config and from the startup messages when I start the server it looks like this is being successfully started before the http-server:
{:started
["#'photo-api.config/env"
"#'photo-api.db.core/db*"
"#'photo-api.db.core/db"
"#'photo-api.handler/init-app"
"#'photo-api.handler/app"
"#'photo-api.core/http-server"]}
user>
This is photo-api.config, unchanged from the luminus default:
(ns photo-api.config
(:require [cprop.core :refer [load-config]]
[cprop.source :as source]
[mount.core :refer [args defstate]]))
(defstate env :start (load-config
:merge
[(args)
(source/from-system-props)
(source/from-env)]))
and the dev config.edn file:
{:title "**** Photos Development API ****"
:dev true
:port 31999
;; when :nrepl-port is set the application starts the nREPL server on load
:nrepl-port 57251}
Am I missing something obvious here? Is there another step necessary to make the env map visible to the swagger setup?
EDIT:
Changing the call from (:title env) to (env :title) causes cider-jack-in to fail with a long error message/stack trace that includes:
Caused by: java.lang.ClassCastException: mount.core.DerefableState cannot be cast to clojure.lang.IFn
Changing it again to (#env :title) then gives a similarly long error message/stack trace that contains:
Caused by: java.lang.ClassCastException: mount.core.NotStartedState cannot be cast to clojure.lang.IFn, compiling:(services.clj:29:23)
So it looks like env is not being started till after the call to it from the swagger setup. I still have no idea why as the when cider-jack-in did work it clearly showed the config.env state starting before the http-server. (see above)
It looks like (defstate env) is an atom that needs to be derefed. Mount's README points to the tests to for some examples .
You might try (:title #env) in service-routes
(defapi service-routes
{:swagger {:ui "/swagger-ui"
:spec "/swagger.json"
:data
{:info
{:version "1.0.1"
;; Switch to correct title before lein uberjar
;; TODO Automate this so swagger page always shows dev or prod version
;;:title "Photo API"
:title (:title #env) ;;;--------> UPDATED
:description "Access a mongo database containing details of photos"}}}}
EDIT --
Not an atom. See this issue for the same deref error, which, presumably means that no deref is needed.
I'm running Debian Wheezy, openjdk-7-jre, clojure 1.4.0 and leiningen-1.7.1, all installed from official repo.
I have a project.clj like this:
(defproject guess-film "1.0.0-SNAPSHOT"
:description "FIXME"
:dependencies [
[org.clojure/clojure "1.4.0"],
[clj-http "0.5.0"],
[clj-tagsoup "0.3.0" :exclusions [net.java.dev.stax-utils/stax-utils]]
]
:main guess-film.core
)
As you can see on that stage I excluded stax-utils, actually this is described on clj-tagsoup Github page.
Then I have this src/guess-film/core.clj:
ns guess-film.core
(:require
[clj-http.client :as client]
[pl.danieljanus.tagsoup :as parser :refer [parse parse-string]] :verbose
)
)
And when I try to
lein repl
it crashes, I presume because of unsatisfied stax-utils:
...
(clojure.core/load "/pl/danieljanus/tagsoup")
(clojure.core/load "/clojure/zip")
(clojure.core/in-ns 'pl.danieljanus.tagsoup)
(clojure.core/alias 'zip 'clojure.zip)
(clojure.core/load "/clojure/xml")
(clojure.core/in-ns 'pl.danieljanus.tagsoup)
(clojure.core/alias 'xml 'clojure.xml)
(clojure.core/load "/clojure/data/xml")
(clojure.core/in-ns 'clojure.data.xml)
(clojure.core/alias 'str 'clojure.string)
(clojure.core/in-ns 'pl.danieljanus.tagsoup)
(clojure.core/alias 'lazy-xml 'clojure.data.xml)
ClassNotFoundException javanet.staxutils.ContentHandlerToXMLEventWriter java.net.URLClassLoader$1.run (URLClassLoader.java:366)
I tried to evade loading of lazy-xml by using :refer, but that doesn't work I presume. Also I tried ":use ... :only [parse parse-string]" with no success either.
I just don't like to load what I am not going to use :)
Is it clj-tagsoup failure that it tries to load lazy-xml anyway?
If I can't escape from that dependency, will it be a perfomance hit?
It is clj-tagsoup the one that is importing the javanet.staxutils.Content... class, so I don't think there is much that you can do about it.
It won't be any performance hit, loading just another class it is not going to make much of a difference and it is not going to use much memory either.
When I was calling the jetty handler directly, I was able to pass in a configurator like so:
(def header-buffer-size 8388608)
(defn start [port]
(ring/run-jetty
(var app)
{:port port
:join? false
:host "127.0.0.1"
:configurator
(fn [jetty]
(doseq [connector (.getConnectors jetty)]
(.setHeaderBufferSize connector header-buffer-size)))}))
I had to do this because I kept getting a FULL HEAD error when posting. Now I refactored things to use > lein ring server directly, which gets called from the command line.
> lein ring server
This uses some configuration specified in my project.clj:
:ring {:handler caribou.api.core/app
:servlet-name "caribou-api"
:init caribou.api.core/init
:port 33443}
This works great, but now I am getting the FULL HEAD issue again. So I tried to add a configurator in there:
:ring {:handler caribou.api.core/app
:servlet-name "caribou-api"
:init caribou.api.core/init
:configurator
(fn [jetty]
(doseq [connector (.getConnectors jetty)]
(.setHeaderBufferSize connector 8388608)))
:port 33443})
And this fails with this stacktrace:
Exception in thread "main" java.lang.ClassCastException:
clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
at ring.adapter.jetty$run_jetty.invoke(jetty.clj:66)
at ring.server.standalone$serve$fn__833.invoke(standalone.clj:78)
at ring.server.standalone$try_port.invoke(standalone.clj:12)
at ring.server.standalone$serve.doInvoke(standalone.clj:75)
at clojure.lang.RestFn.invoke(RestFn.java:423)
at ring.server.leiningen$serve.invoke(leiningen.clj:20)
I figured this had to do with putting the function directly in the map like that, so I defined it outside the project (in caribou.api.core) and tried referring to it like I do the rest of the functions defined elsewhere:
;; in caribou/api/core.clj
(def header-buffer-size 8388608)
(defn full-head-avoidance
[jetty]
(doseq [connector (.getConnectors jetty)]
(.setHeaderBufferSize connector header-buffer-size)))
;; in project.clj
:ring {:handler caribou.api.core/app
:servlet-name "caribou-api"
:init caribou.api.core/init
:configurator caribou.api.core/full-head-avoidance
:port 33443})
This spins up the app, but I still get the 413: FULL HEAD error when posting. Any ideas? Thanks!
Stuff written inside a defproject form is not evaluated by default, but you can use ~ (unquote) in defproject when you need it to:
(defproject foo "1.2.3"
...
:some-fn-key ~(fn [& args] ...))
In this particular situation, you'd unquote the fn form defining your configurator function (which without unquoting is being used as a list containing a bunch of symbols, rather than compiled into a function).
Alternatively, you can define the configurator outside the defproject form and refer to its name inside defproject as in your second approach, but then you need to unquote the name inside defproject -- otherwise it'll be treated as a symbol. NB. in the latter case no exception is produced, because symbols are in fact callable and accept arbitrary arguments (but only ever return nil when called with something other than a map or a set; with maps or sets, they look themselves up in their arguments).
The answer is not to use ring server and just start jetty directly with ring/ring-jetty-adapter. That way I can still pass in the configurator, which BTW has changed to this:
(defn full-head-avoidance
[jetty]
(doseq [connector (.getConnectors jetty)]
(.setRequestHeaderSize connector header-buffer-size)))
I've written a small Swing App before in Clojure and now I'd like to create an Ajax-style Web-App. Compojure looks like the best choice right now, so that's what I'm going to try out.
I'd like to have a real tiny edit/try feedback-loop, so I'd prefer not to restart the web server after each small change I do.
What's the best way to accomplish this? By default my Compojure setup (the standard stuff with ant deps/ant with Jetty) doesn't seem to reload any changes I do. I'll have to restart with run-server to see the changes. Because of the Java-heritage and the way the system is started etc. This is probably perfectly normal and the way it should be when I start the system from command-line.
Still, there must be a way to reload stuff dynamically while the server is running. Should I use Compojure from REPL to accomplish my goal? If I should, how do I reload my stuff there?
This is quite an old question, and there have been some recent changes that make this much easier.
There are two main things that you want:
Control should return to the REPL so you can keep interacting with your server. This is accomplished by adding {:join? false} to options when starting the Jetty server.
You'd like to automatically pick up changes in certain namespaces when the files change. This can be done with Ring's "wrap-reload" middleware.
A toy application would look like this:
(ns demo.core
(:use webui.nav
[clojure.java.io]
[compojure core response]
[ring.adapter.jetty :only [run-jetty]]
[ring.util.response]
[ring.middleware file file-info stacktrace reload])
(:require [compojure.route :as route] view)
(:gen-class))
; Some stuff using Fleet omitted.
(defroutes main-routes
(GET "/" [] (view/layout {:body (index-page)})
(route/not-found (file "public/404.html"))
)
(defn app
[]
(-> main-routes
(wrap-reload '(demo.core view))
(wrap-file "public")
(wrap-file-info)
(wrap-stacktrace)))
(defn start-server
[]
(run-jetty (app) {:port 8080 :join? false}))
(defn -main [& args]
(start-server))
The wrap-reload function decorates your app routes with a function that detects changes in the listed namespaces. When processing a request, if those namespaces have changed on disk, they are reloaded before further request processing. (My "view" namespace is dynamically created by Fleet, so this auto-reloads my templates whenever they change, too.)
I added a few other pieces of middleware that I've found consistently useful. wrap-file handles static assets. wrap-file-info sets the MIME type on those static assets. wrap-stacktrace helps in debugging.
From the REPL, you could start this app by using the namespace and calling start-server directly. The :gen-class keyword and -main function mean that the app can also be packaged as an uberjar for startup from outside the REPL, too. (There's a world outside the REPL? Well, some people have asked for it anyway...)
Here's an answer I got from James Reeves in the Compojure Google Group (the answer's here with his permission):
You can reload a namespace in Clojure using the :reload key on the use
or require commands. For example, let's say you have a file "demo.clj" that contains your routes:
(ns demo
(:use compojure))
(defroutes demo-routes
(GET "/"
"Hello World")
(ANY "*"
[404 "Page not found"]))
At the REPL, you can use this file and start a server:
user=> (use 'demo)
nil
user=> (use 'compojure)
nil
user=> (run-server {:port 8080} "/*" (servlet demo-routes))
...
You could also put the run-server command in another clojure file.
However, you don't want to put it in the same file as the stuff you want to reload.
Now make some changes to demo.clj. At the REPL type:
user=> (use 'demo :reload)
nil
And your changes should now show up on http://localhost:8080
I wanted to add an answer, since things have changed a bit since the newest answer and I had spent a bit of time looking for this myself.
Install leiningen (just follow the instructions there)
Create project
lein new compojure compojure-test
Edit the ring section of project.clj
:ring {:handler compojure-test.handler/app
:auto-reload? true
:auto-refresh? true}
Start the server on whatever port you want
lein ring server-headless 8080
Check that the server is running in your browser, the default base route should just say "Hello world". Next, go modify your handler (it's in src/project_name). Change the hello world text, save the file and reload the page in your browser. It should reflect the new text.
Following up on Timothy's link to Jim Downing's setup, I recently posted on a critical addition to that baseline that I found was necessary to enable automatic redeployment of compojure apps during development.
I have a shell script that looks like this:
#!/bin/sh
CLASSPATH=/home/me/install/compojure/compojure.jar
CLASSPATH=$CLASSPATH:/home/me/clojure/clojure.jar
CLASSPATH=$CLASSPATH:/home/me/clojure-contrib/clojure-contrib.jar
CLASSPATH=$CLASSPATH:/home/me/elisp/clojure/swank-clojure
for f in /home/me/install/compojure/deps/*.jar; do
CLASSPATH=$CLASSPATH:$f
done
java -server -cp $CLASSPATH clojure.lang.Repl /home/me/code/web/web.clj
web.clj looks like this
(use '[swank.swank])
(swank.swank/ignore-protocol-version "2009-03-09")
(start-server ".slime-socket" :port 4005 :encoding "utf-8")
Whenever I want to update the server I create an ssh tunnel from my local machine to the remote machine.
Enclojure and Emacs (running SLIME+swank-clojure) can connect to the remote REPL.
This is highly configuration dependent but works for me and I think you can adapt it:
Put compojure.jar and the jars under the compojure/deps directory are in your classpath. I use clojure-contrib/launchers/bash/clj-env-dir to do this, all you need to do is set the directory in CLOJURE_EXT and it will find the jars.
CLOJURE_EXT Colon-delimited list of paths to directories whose top-level
contents are (either directly or as symbolic links) jar
files and/or directories whose paths will be in Clojure's
classpath.
Launch clojure REPL
Paste in hello.clj example from compojure root directory
Check localhost:8080
Re-define the greeter
(defroutes greeter
(GET "/"
(html [:h1 "Goodbye World"])))
Check localhost:8080
There are also methods for attaching a REPL to an existing process, or you could keep a socket REPL embedded in your server or you could even define a POST call that will eval on the fly to allow you to redefine functions from the browser itself! There are lots of ways to approach this.
I'd like to follow up on mtnygard's answer and post the full project.clj file and core.clj file that got the given functionality working. A few modifications were made, and it's more barebones
pre-setup commands
lein new app test-web
cd test-web
mkdir resources
project.clj
(defproject test-web "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[compojure "1.1.6"]
[ring "1.2.1"]]
:main ^:skip-aot test-web.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
core.clj
(ns test-web.core
(:use
[clojure.java.io]
[compojure core response]
[ring.adapter.jetty :only [run-jetty]]
[ring.util.response]
[ring.middleware file file-info stacktrace reload])
(:require [compojure.route :as route])
(:gen-class))
(defroutes main-routes
(GET "/" [] "Hello World!!")
(GET "/hello" [] (hello))
(route/not-found "NOT FOUND"))
(def app
(-> main-routes
(wrap-reload '(test-web.core))
(wrap-file "resources")
(wrap-file-info)
(wrap-stacktrace)))
(defn hello []
(str "Hello World!"))
(defn start-server
[]
(run-jetty #'app {:port 8081 :join? false}))
(defn -main [& args]
(start-server))
Pay Attention to the change from (defn app ...) to (def app ...)
This was crucial to getting the jetty server to work correctly
Compojure uses ring internally (by the same author), the ring web server options allow automatic realoading. So two alternatives would be :
lein ring server
lein ring server-headless
lein ring server 4000
lein ring server-headless 4000
Note that :
You need to have a line in your project.clj file that looks like:
:ring {:handler your.app/handler}