Code Monkey home page Code Monkey logo

gs-reactor-thumbnailer's Introduction

tags projects
reactor
rest
netty
microservice
reactor

This guide walks you through the process of creating a RESTful image thumbnailing service using Spring Boot, Reactor, and Netty.

What you’ll build

You’ll build a service that thumbnails an uploaded JPEG image and stores it for serving to a web browser. You’ll use Reactor’s TCP support (specifically, the Netty implementation) in order to provide excellent scalability in highly-concurrent deployments. Although similar systems could be built in a more traditional MVC style, it’s not very straightforward to harness the fully non-blocking capabilities that are only available in Servlet 3.1 and later containers.

Create the Thumbnailer

To do the work of resizing the image, you’ll register a function on a Reactor that will:

  1. Accept a Path to an uploaded, full-size image.

  2. Resize this image to 250px on the long side.

  3. Return a Path to the resized (thumbnailed) image.

Note
There is another Getting Started Guide that discusses in more detail publishing and handling events in Reactor.

Although the image quality will be quite poor, for simplicity’s sake you can use the JDK’s built-in Graphics2D engine to do the image thumbnailing. Specifically that means applying an AffineTransform to a BufferedImage.

src/main/java/hello/BufferedImageThumbnailer.java

link:complete/src/main/java/hello/BufferedImageThumbnailer.java[role=include]

The thumbnailer implements Reactor’s Function interface by implementing the apply(Event<Path>) method. It accepts an Event whose payload is the Path to the uploaded, full-size image. It then reads in the image using ImageIO and determines its current dimensions. It has to calculate the scale it needs to use to shrink the image from its present dimensions to 250px on the longest side. If the image is square or horizontal, it uses the image’s width to determine that scale factor, otherwise it uses the image’s height.

After creating a Graphics2D context, the thumbnailer applies a transformation to the image that scales it according to the scaling factor previously calculated to arrive at a dimension of 250px.

Note
If you have the GraphicsMagick package installed on your machine and want a higher-quality thumbnail, you can use the alternative implementation of the BufferedImageThumbnailer included in this guide: GraphicsMagickThumbnailer.java

Once the image is resized, it is written to disk and a Path returned that points to that thumbnailed image.

Create a REST API

To handle incoming HTTP requests using Reactor, you need to create a helper class. Create static methods that return stateless consumers which accept the Netty FullHttpRequest object for the REST request. The helper methods should accept as parameters a Reactor NetChannel to write a response to and an AtomicReference to the shared thumbnail Path.

src/main/java/hello/ImageThumbnailerRestApi.java

link:complete/src/main/java/hello/ImageThumbnailerRestApi.java[role=include]
Note
For simplicity’s sake, the upload handler expects raw binary data, not urlencoded data like in an upload form. This means you’ll need to use a command-line tool like curl to POST the image to the server in binary mode.

Configure the Reactor NetServer

To bootstrap a Reactor NetServer, you need to add the appropriate options that configure Netty’s pipeline to handle HTTP. Reactor has a special subclass of ServerSocketOptions that expose the Netty ChannelPipeline so you can add Netty built-in HTTP handlers.

Also required are some housekeeping objects like a CountDownLatch to keep the main thread alive since Reactor’s TCP support is fully asynchronous and would not prevent the test app from exiting immediately after starting the server.

src/main/java/hello/ImageThumbnailerApp.java

link:complete/src/main/java/hello/ImageThumbnailerApp.java[role=include]

The Reactor NetServer is configured in the restApi() @Bean method.

Note
The Environment parameter referenced in that method is coming from Reactor’s @EnableReactor bootstrap code. It’s not something you need to create yourself. The ServerSocketOptions, Reactor, and CountDownLatch parameters are all configured in other @Bean methods to keep the server configuration code clean from unnecessary clutter.

Reactor’s TCP support is channel-based. In order to respond to requests, you need to use the incoming Stream of HTTP requests and attach the consumers you’re returning from the helper class you created in Create a REST API.

You can build a single executable JAR file that contains all the necessary dependencies, classes, and resources. This makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.

./gradlew build

Then you can run the JAR file:

java -jar build/libs/gs-reactor-thumbnailer-0.1.0.jar

If you are using Maven, you can run the application using mvn spring-boot:run. Or you can build the JAR file with mvn clean package and run the JAR by typing:

java -jar target/gs-reactor-thumbnailer-0.1.0.jar

The console should report the application starting.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.0.1.RELEASE)

2014-04-14 18:10:55.456  INFO 64002 --- [ailerApp.main()] hello.ImageThumbnailerApp                : Starting ImageThumbnailerApp on dev.home with PID 64002 (/Users/jbrisbin/Development/Projects/reactor/master/gs-reactor-thumbnailer/complete/target/classes started by jbrisbin)
2014-04-14 18:10:55.491  INFO 64002 --- [ailerApp.main()] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@79ea1e26: startup date [Mon Apr 14 18:10:55 CDT 2014]; root of context hierarchy
2014-04-14 18:10:56.276  INFO 64002 --- [entExecutor-1-1] reactor.net.netty.tcp.NettyTcpServer     : BIND /0:0:0:0:0:0:0:0:3000
2014-04-14 18:10:56.350  INFO 64002 --- [ailerApp.main()] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-04-14 18:10:56.365  INFO 64002 --- [ailerApp.main()] hello.ImageThumbnailerApp                : Started ImageThumbnailerApp in 1.087 seconds (JVM running for 4.66)

When you see "Started ImageThumbnailerApp" in the console log, upload the image using a curl command like the following:

curl -v -XPOST -H "Content-Type: image/jpeg" --data-binary @cute_kittens.jpg http://localhost:3000/thumbnail

You should get a 301 response back from the server with the Location header pointing to the URI to pull up in your browser to see the thumbnailed image.

* About to connect() to localhost port 3000 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 3000 (#0)
> POST /thumbnail HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: localhost:3000
> Accept: */*
> Content-Type: image/jpeg
> Content-Length: 1002634
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 301 Moved Permanently
< Content-Length: 0
< Location: /image/thumbnail.jpg

Summary

Although this is about as simple a multi-component setup as you can get, this guide demonstrates:

  • How to serve HTTP requests to power a REST API

  • How to distribute requests to a Reactor and use it as an event bus

  • How to send the response from an asynchronous task executed in a Reactor to an HTTP client that’s waiting on a result

gs-reactor-thumbnailer's People

Contributors

gregturn avatar jbrisbin avatar royclarkson avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.