7. A Simple Server Using the Pedestal Framework

The goal of this chapter is to understand why the Pedestal server was developed and why you’d use it (over Compojure). We also want to get a very basic Pedestal server website up and running.

Assumptions

In this chapter we assume the following:

Image You understand that rich web applications can exhaust web server resources if the server isn’t implemented properly.

Image You understand that by default the Ring framework under Compojure allocates a thread to each request.

Benefits

Benefits include the following:

Image You’ll get a simple Pedestal application working.

Image You’ll come to understand the reasons to choose the Pedestal framework over another.

Context

A number of the members of the Relevance/Cognitect group were writing a high scalability Clojure website for a client that involved long polling, server sent events, and requests waiting for long-running processes. The traditional pattern in Ring/Compojure is to associate one request with one thread. The authors wanted a way to decouple the processing of a request and a thread. This means the state of a request is stored in a context that keeps the request information.

Keeping in mind the ideas about software development in Clojure from Rich Hickey’s “Value of Values” talk1 the authors designed a completely decoupled web framework. This separated out the Data Model, the Application Model, and the DOM.

1. Rich Hickey’s talk was presented at the JaxConf 2012 in London: https://jaxenter.com/jaxconf-keynotes-hickey-analyses-the-value-of-value-humble-on-continuous-delivery-104746.html. See also http://www.infoq.com/presentations/Value-Values.

You would choose Pedestal over Compojure when you are working with a rich client website that triggers a significant number of long-running processes on the server. Unlike Compojure, in Pedestal each request is not treated as a thread instance but as data on a queue within the server. This means that the server (when using Pedestal) is much more scalable for rich client sites.

Now we’ll build a very simple Pedestal web server to get started. (Note that the Pedestal client has been deprecated in favor of other data-driven web frameworks like Om.)

The Recipe—Code

Follow these steps to get the simple Pedestal web server built. First we’ll create the application using a special Leiningen template. Then we’ll create a static file to load. Then we’ll customize the server to display the file. Then we’ll view it in a web browser.

1. We’ll start by using Leiningen to create a new pedestal-service project:

lein new pedestal-service pedestal-hello-world

2. Create the directory resources/public inside pedestal-hello-world.

3. Create the HTML file resources/public/index.html with the following contents:

<html> <head>
  <title>Home page</title>
</head>
  <body>
    <h1>This is my first Pedestal webapp</h1>
  </body>
</html>

4. Next we’ll modify the file /src/pedestal_hello_world/service.clj to look like the following:

(ns pedestal-hello-world.service
    (:require [io.pedestal.service.http :as bootstrap]
              [io.pedestal.service.http.route :as route]
              [io.pedestal.service.http.body-params :as body-params]
              [io.pedestal.service.http.route.definition :refer [defroutes]]
              [ring.util.response :as ring-resp]
              [clojure.java.io :as io]))


(defn about-page
  [request]
  (ring-resp/response (format "Clojure %s" (-served from %s"
                                           clojure-version)
                                           (route/url-for ::about-page))))

(defn home-page
  [request]
  (ring-resp/response "Hello World!"))

(defn index-page
  [request]
  (assoc-in (ring-resp/response (io/input-stream (io/file
  "resources/public/index.html")))
            [:headers "Content-type"]
            "text/html"))

(defroutes routes
  [[["/" {:get index-page}
          ^:interceptors [(body-params/body-params) bootstrap/html-body]
     ["/about" {:get about-page}]]]])

;; Consumed by pedestal-hello-world.server/create-server
(def service {:env :prod
              ;; You can bring your own non-default interceptors. Make
              ;; sure you include routing and set it up right for
              ;; dev-mode. If you do, many other keys for configuring
              ;; default interceptors will be ignored.
              ;; :bootstrap/interceptors []
              ::bootstrap/routes routes

              ;; Uncomment next line to enable CORS support, add
              ;; string(s) specifying scheme, host and port for
              ;; allowed source(s):
              ;;
              ;; "http://localhost:8080"
              ;;
              ;;::bootstrap/allowed-origins ["scheme://host:port"]

              ;; Root for resource interceptor that is available by default.
              ::bootstrap/resource-path "/public"

              ;; Either :jetty or :tomcat (see comments in project.clj
              ;; to enable Tomcat)
              ;;::bootstrap/host "localhost"
              ::bootstrap/type :jetty
              ::bootstrap/port 8080})

Testing the Solution

Next we test the solution:

1. Now on the command prompt at the root level of the application, run the application with Leiningen:

pedestal-hello-world>lein run

You should get the following (or similar) output:

...
Creating your server...
INFO  org.eclipse.jetty.server.Server - jetty-9.2.0.v20140526
INFO  o.e.j.server.handler.ContextHandler - Started o.e.j.s.ServletContextHan
dler@602fb3d0{/,null,AVAILABLE}
INFO  o.e.jetty.server.ServerConnector - Started ServerConnector@7d8dbfd5{H
TTP/1.1}{0.0.0.0:8080}
INFO  org.eclipse.jetty.server.Server - Started @9802ms

So we know the server has started on port 8080.

2. In your web browser, navigate to http://localhost:8080/. You should get a response like the one shown in Figure 7.1.

Image

Figure 7.1 Expected home page for first Pedestal webapp

3. Now browse to http://localhost/about. You should get a response similar to that shown in Figure 7.2.

Image

Figure 7.2 Calling Clojure functions in the response for your Pedestal webapp

Here we’re using the existing route definition for "/about" that is calling a function about page that runs the function clojure-version.

Notes on the Recipe

When we created the directory resources/public this was for static content.

Our response “This is my First Pedestal webapp” came from our definition in the index.html file.

Let’s take a look at the file service.clj. Note in particular the addition to the namespace:

[clojure.java.io :as io]))

This enabled us to load a file from the Clojure project path.

We loaded the index.html file using the following function:

(defn index-page
  [request]
  (assoc-in (ring-resp/response (io/input-stream (io/file "resources/public/
index.html")))
            [:headers "Content-type"]
            "text/html"))

This function tells Ring to load our index file using the function ring-resp/file-response.

The Clojure version response came from the function:

(defn about-page
  [request]
  (ring-resp/response (format "Clojure %s" (clojure-version))))

We mapped the index-page function to the default route, and the about-page function to the /about URL in the routes function as above. This resolves the URL request coming in to the function.

(defroutes routes
  [[["/" {:get index-page}
          ^:interceptors [(body-params/body-params) bootstrap/html-body]
["/about" {:get about-page}]]]])

It’s worth discussing the difference between middleware, seen in Ring and Compojure, and the concept of interceptors, seen in the code above. Broadly speaking, they’re directed at the same goal—reusable component functions for a web framework for goals like session or error handling.

The benefit of middleware in Ring is simplicity: you compose functions together that operate on the results of other functions. The disadvantage of this is that it is thread bound. If you have many requests running in parallel, you need a thread for each request running.

The advantage of interceptors is that they are data-driven: the context they operate on is a data structure, and so their execution can be delayed, and the execution of many http requests can be done in one thread. This is useful in a scenario where you have many long-running threads.

Conclusion

We’ve warmed up with the Pedestal framework and are ready to start with some of its more advanced features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.78.136