Browse Source

racket: tweak responses, leverage multiple cores (#5727)

* racket: pin Racket 7.7

* racket: ensure all responses have content lengths

Throughput was previously limited because all responses were using
chunked transfer encoding.

* racket: use the CS variant of Racket

* racket: run multiple procs behind a lb
Bogdan Popa 5 years ago
parent
commit
f0f79e3c62

+ 30 - 0
frameworks/Racket/racket/config/nginx.conf

@@ -0,0 +1,30 @@
+daemon off;
+worker_processes 4;
+error_log stderr error;
+
+events {
+  worker_connections 16384;
+}
+
+http {
+  access_log  off;
+
+  sendfile    on;
+  tcp_nopush  on;
+  tcp_nodelay on;
+
+  include /racket/config/upstream.conf;
+
+  server {
+    listen 8080 default;
+
+    location / {
+      proxy_pass            http://app;
+      proxy_http_version    1.1;
+      proxy_connect_timeout 90s;
+      proxy_send_timeout    90s;
+      proxy_read_timeout    90s;
+      proxy_buffers         32 8k;
+    }
+  }
+}

+ 11 - 9
frameworks/Racket/racket/racket.dockerfile

@@ -11,28 +11,32 @@ RUN echo 'APT::Get::Install-Recommends "false";' > /etc/apt/apt.conf.d/00-genera
 
 FROM debian AS racket
 
-ARG RACKET_VERSION=7.6
+ARG RACKET_VERSION=7.7
 
 RUN apt-get update -q \
     && apt-get install --no-install-recommends -q -y \
          ca-certificates curl libcurl3-gnutls \
     && rm -rf /var/lib/apt/lists/* \
     && curl -L -o racket-install.sh \
-         -O http://mirror.racket-lang.org/installers/${RACKET_VERSION}/racket-minimal-${RACKET_VERSION}-x86_64-linux-natipkg.sh \
+         -O http://mirror.racket-lang.org/installers/${RACKET_VERSION}/racket-minimal-${RACKET_VERSION}-x86_64-linux-cs.sh \
     && echo "yes\n1\n" | sh racket-install.sh --create-dir --unix-style --dest /usr/ \
     && rm racket-install.sh
 
 ENV SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"
 ENV SSL_CERT_DIR="/etc/ssl/certs"
 
+RUN apt-get update -q \
+  && apt-get install --no-install-recommends -q -y nginx
+
 
 FROM racket AS builder
 
+RUN raco pkg install --auto compiler-lib db-lib threading-lib web-server-lib
+
 WORKDIR /racket
 ADD  . .
 
-RUN raco pkg install --auto compiler-lib db-lib threading-lib web-server-lib \
-  && raco make servlet.rkt \
+RUN raco make servlet.rkt \
   && raco exe servlet.rkt
 
 
@@ -40,11 +44,9 @@ FROM racket
 
 WORKDIR /racket
 COPY --from=builder /racket/servlet .
-
-RUN ["chmod", "+x", "./servlet"]
+ADD config config
+ADD scripts scripts
 
 EXPOSE 8080
 
-ENV PLT_INCREMENTAL_GC=1
-
-CMD ["/racket/servlet"]
+CMD ["/racket/scripts/run"]

+ 36 - 0
frameworks/Racket/racket/scripts/run

@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+export PLT_INCREMENTAL_GC=1
+
+CORES=$(("$(nproc --all)" * 2))
+CONF="/racket/config/upstream.conf"
+
+cat >"$CONF" <<EOF
+upstream app {
+  least_conn;
+EOF
+
+for i in $(seq 0 $CORES); do
+    port=$((8081 + i))
+    /racket/servlet "$port" &
+    echo "  server 127.0.0.1:$port;" >> "$CONF"
+done
+
+cat >>"$CONF" <<EOF
+  keepalive 512;
+}
+EOF
+
+for i in $(seq 0 $CORES); do
+    port=$((8081 + i))
+    filename="/racket/$port.ready"
+    while [ ! -f "$filename" ]; do
+        echo "Waiting for $filename..."
+        sleep 0.25;
+    done
+done
+
+cat /racket/config/upstream.conf
+nginx -c /racket/config/nginx.conf

+ 58 - 14
frameworks/Racket/racket/servlet.rkt

@@ -2,13 +2,16 @@
 
 (require db
          json
+         racket/port
          threading
          web-server/dispatch
-         web-server/http)
+         web-server/http
+         web-server/http/response
+         xml)
 
 ;; db ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define max-db-conns 1024)
+(define max-db-conns 128)
 (define db-conn-pool
   (connection-pool
    #:max-connections max-db-conns
@@ -41,12 +44,30 @@
 
 ;; helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (response/json e)
+(define (response/bytes bs
+                        #:code [code 200]
+                        #:headers [headers null]
+                        #:mime-type [mime-type #"text/plain"])
+  (define len:bs (string->bytes/utf-8 (number->string (bytes-length bs))))
   (response/output
-   #:mime-type #"application/json; charset=utf-8"
+   #:code code
+   #:mime-type mime-type
+   #:headers (cons (make-header #"Content-Length" len:bs) headers)
    (lambda (out)
-     (write-json e out))))
+     (write-bytes bs out))))
 
+(define (response/json e)
+  (response/bytes
+   #:mime-type #"application/json; charset=utf-8"
+   (jsexpr->bytes e)))
+
+(define (response/xexpr xe)
+  (response/bytes
+   #:mime-type #"text/html; charset=utf-8"
+   (call-with-output-bytes
+    (lambda (out)
+      (write-bytes #"<!DOCTYPE html>" out)
+      (write-xexpr xe out)))))
 
 ;; world ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -132,10 +153,7 @@
   (dispatch-rules
    [("plaintext")
     (lambda (_req)
-      (response/output
-       #:mime-type #"text/plain"
-       (lambda (out)
-         (display "Hello, World!" out))))]
+      (response/bytes #"Hello, World!"))]
 
    [("json")
     (lambda (_req)
@@ -159,7 +177,6 @@
         (call-with-db-conn all-fortunes))
 
       (response/xexpr
-       #:preamble #"<!DOCTYPE html>"
        `(html
          (head
           (title "Fortunes"))
@@ -200,14 +217,41 @@
        (map world->hash worlds)))]))
 
 (module+ main
-  (require web-server/servlet-dispatch
+  (require racket/async-channel
+           racket/cmdline
+           racket/format
+           web-server/http/response
+           web-server/safety-limits
            web-server/web-server)
 
+  (define port
+    (command-line
+     #:args (port)
+     (string->number port)))
+
+  (define (app c req)
+    (output-response c (dispatch req)))
+
+  (define ch (make-async-channel))
   (define stop
     (serve
-     #:dispatch (dispatch/servlet dispatch)
-     #:listen-ip "0.0.0.0"
-     #:port 8080))
+     #:dispatch app
+     #:listen-ip "127.0.0.1"
+     #:port port
+     #:confirmation-channel ch
+     #:safety-limits (make-safety-limits
+                      #:max-waiting 4096
+                      #:request-read-timeout 16
+                      #:response-timeout 16
+                      #:response-send-timeout 16)))
+
+  (define ready-or-exn (sync ch))
+  (when (exn:fail? ready-or-exn)
+    (raise ready-or-exn))
+
+  (call-with-output-file (build-path (~a port ".ready"))
+    (lambda (out)
+      (displayln "ready" out)))
 
   (with-handlers ([exn:break?
                    (lambda (_e)