Last active
December 11, 2022 01:17
-
-
Save chenlilyd/42e7995349f7e8db5ccc3f1e7df5491b to your computer and use it in GitHub Desktop.
Test concurrent requests to Jetty
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns garfield.ring-jetty-project | |
(:gen-class) | |
(:require | |
[cheshire.core :refer [parse-string]] | |
[clj-http.client :as client] | |
[clojure.java.io :as io] | |
[clojure.java.shell :refer [sh]] | |
[clojure.string :as s] | |
[com.climate.claypoole :as cp] | |
[ring.adapter.jetty :refer [run-jetty]] | |
[taoensso.timbre :as log] | |
[taoensso.timbre.appenders.core :as appenders]) | |
(:import | |
[java.time ZoneId ZonedDateTime])) | |
(log/merge-config! | |
{:appenders {:spit (appenders/spit-appender {:fname "jetty.log"})}}) | |
;; a handler which could take a long time to respond | |
(defn handler [_req] | |
(Thread/sleep (if (> (rand-int 100) 95) | |
(+ 5000 (rand-int 4000)) ;; 5% chance of possible slow response | |
(rand-int 150))) | |
{:status 200 | |
:headers {"Content-Type" "text/plain"} | |
:body "Hello World"}) | |
(defonce server-store (atom nil)) | |
(defn jetty-thread-stats [server] | |
(let [pool (.getThreadPool server)] | |
{:threads (.getThreads pool) | |
:max-threads (.getMaxThreads pool) | |
:queue-size (.getQueueSize pool)})) | |
(defn log-middleware [handler] | |
(fn [{:keys [request-method uri] :as req}] | |
(let [start (System/currentTimeMillis)] | |
(try | |
(handler req) | |
(finally | |
(let [time (- (System/currentTimeMillis) start)] | |
(log/info request-method uri time (jetty-thread-stats @server-store)))))))) | |
(defn -main [& _] | |
(reset! | |
server-store | |
(run-jetty | |
(->> handler log-middleware) | |
{:port 4000 | |
:join? false}))) | |
(defn stop! [] | |
(when-let [s @server-store] | |
(try | |
(.stop s) | |
(finally (reset! server-store nil))))) | |
(comment | |
;; start server | |
(-main) | |
;; Simulate 200 concurrent users | |
(def unlimited-pool (cp/threadpool 200)) | |
;; Delete existing log file if exists | |
(io/delete-file (io/file "jetty.log") true) | |
;; these users send 10k 2000 requests in total, wait for a maximum time of 10 seconds | |
(time | |
(do | |
(def xs (cp/upmap unlimited-pool (partial make-http-request-with-timeout 10000) (range 2000))) | |
(last xs))) | |
;; Time taken for the longest running requests | |
(->> xs (map :time) sort reverse (take 10)) | |
;; => (10011 10011 10011 10010 10009 10008 10005 9981 9957 9951) | |
;; On the client side, all requests are success except those aborted by the client | |
(->> xs (map (juxt :http-status :error)) distinct) | |
;; => ([200 nil] [nil "Read timed out"]) | |
;; On the server side, the longest response time. | |
;; Notice the max time is less than 9 second. | |
(with-open [r (io/reader "jetty.log")] | |
(->> | |
(line-seq r) | |
(map (fn [line] ;; extract time from `/request-{id} {time-in-millis}` | |
(some->> line | |
(re-seq #"/request-\d+\s+(\d+)") | |
(first) | |
(second) | |
(read-string)))) | |
(filterv identity) | |
(sort) | |
(reverse) | |
(take 10))) | |
;; => (8983 8965 8939 8917 8868 8859 8837 8832 8832 8800) | |
;; Check if server handles all the requests, even the client aborted the requests | |
(with-open [r (io/reader "jetty.log")] | |
(->> | |
(line-seq r) | |
(map (fn [line] ;; extract id from `/request-{id}` | |
(some->> line | |
(re-seq #"/request-(\d+)") | |
(first) | |
(second) | |
(read-string)))) | |
(filterv identity) | |
(sort) | |
(= (range 2000)))) | |
;; => true | |
;; stop server | |
(stop!) | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And
deps.edn