Skip to content

Commit

Permalink
Various changes including Spring Cloud upgrade and improved instructi…
Browse files Browse the repository at this point in the history
…ons.

Improved instructions on how to build and run the application
Upgraded to latest Spring Cloud
Front-end web application uses Hystrix when invoking backend service
Added Eureka Server
Backend service validates parameters
Switched Selenium web tests to use Chrome
Improved shell scripts
Added end to end tests for application launched using Docker
  • Loading branch information
cer committed May 31, 2016
1 parent 5919b7a commit 7858ff0
Show file tree
Hide file tree
Showing 57 changed files with 1,266 additions and 296 deletions.
171 changes: 160 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,171 @@
A simple example of microservices that is described in this [blog post](http://plainoldobjects.com/2014/11/16/deploying-spring-boot-based-microservices-with-docker/).
A simple example of microservices that is described in this [series of blog posts](http://plainoldobjects.com/2014/11/16/deploying-spring-boot-based-microservices-with-docker/).

The services are built using
There are two services:
* RESTful service - exposes a REST API for registering a user.
It saves the user registration in MongoDB and posts a message to RabbitMQ.
It is the example code for the article [Building microservices with Spring Boot - part 1](http://plainoldobjects.com/2014/04/01/building-microservices-with-spring-boot-part1/).

* Web application - implements the user registration UI and invokes the RESTful service.
It is the example code for the article [Building microservices with Spring Boot - part 2](https://plainoldobjects.com/2014/05/05/building-microservices-with-spring-boot-part-2/).

The services are written in Scala and use the following technologies.

* Spring Boot
* Spring Cloud
* Netflix OSS Eureka
* RabbitMQ

There is also a [microservices example that uses event sourcing](https://github.com/cer/event-sourcing-examples).
Note: There are [other example microservice applications](http://eventuate.io/exampleapps.html).

# Building and running the microservices

This project uses with [Docker Compose](https://docs.docker.com/compose/) to run the services as well as RabbitMQ and MongoDB.

The `spring-boot-webapp` project uses Selenium to test the web UI using the Chrome browser.
You will need to install [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/getting-started).
On Mac OSX you can run `brew install chromedriver`.


## The quick way

The quickest way to build and run the services on Mac OSX is the script `build-test-and-run-all.sh`.

Otherwise, follow these instructions.

## Running MongoDB and RabbitMQ

The RESTful service uses RabbitMQ and MongoDB.
The easier way to run them is to using Docker:

```
docker-compose up -d mongodb rabbitmq
```

You also need to set some environment variables so that the services can connect to them:

```
export DOCKER_HOST_IP=$(docker-machine ip default 2>/dev/null)
export SPRING_DATA_MONGODB_URI=mongodb://${DOCKER_HOST_IP}/userregistration
export SPRING_RABBITMQ_HOST=${DOCKER_HOST_IP}
```

## Build the Eureka server

This application uses Netflix OSS Eureka for service discovery.
Build the Spring Cloud based Eureka server using the following commands:

```
cd eureka-server
./gradlew build
```

## Building the RESTful service

Use the following commands to build the RESTful service:

```
cd spring-boot-restful-service
./gradlew build
```

## Running the RESTful service

You can run the service by using the following command in the top-level directory:

```
docker-compose up -d restfulservice
```

## Using the RESTful service

Once the service has started, you can send a registration request using:

```
./register-user.sh
```

You can examine the MongoDB database using the following commands

```
$ ./mongodb-cli.sh
> show dbs;
local 0.031GB
mydb 0.031GB
userregistration 0.031GB
> use userregistration;
switched to db userregistration
>
>
> show collections;
registeredUser
system.indexes
>
>
> db.registeredUser.find()
{ "_id" : ObjectId("55a99b0993860551c6020e9d"), "_class" : "net.chrisrichardson.microservices.restfulspringboot.backend.RegisteredUser", "emailAddress" : "[email protected]", "password" : "secret" }
> exit
$
```

## Building the web application

Since the web application invokes the RESTful service you must set the following environment variable:

```
export USER_REGISTRATION_URL=http://${DOCKER_HOST_IP}:8081/user
```

Next, use the following commands to build the web application:

```
cd spring-boot-webapp
./gradlew build
```

## Running the web application

Run the web application using the following command in the top-level directory:

```
docker-compose up -d web
```

## Using the web application

You can access the web application by visiting the following URL: `http://${DOCKER_HOST_IP?}:8080/register.html`

There are also other URLs that you can visit:

* `http://${DOCKER_HOST_IP?}:8761` - Eureka console
* `http://${DOCKER_HOST_IP?}:8081/swagger-ui.html` - the Swagger UI

# Building and running Docker images

The previous instructions deployed the services as Docker containers without actually packaging the services as Docker images.
The `docker-compose.yml` file ran the image `java:openjdk-8u91-jdk` and used volume mapping to make the Spring Boot jar files accessible.
Follow these instructions to build and run the Docker images.

## Building the images

You can build the images by running the following command:

```
./build-docker-images.sh
```

This script is a simple wrapper around `docker build`.

## Running the images

Building and running the microservices
===
You can now run the Docker images using the `docker-compose` command with `docker-compose-images.yml`:

You can find instructions for building the services in the READMEs.
Start by [building the RESTful backend service](spring-boot-restful-service).
Once you have build the services, you can launch them by running `docker-compose up -d`.
```
docker-compose -f docker-compose-images.yml up -d
```

The quick version
==
The following command will wait until the services are available and displays the URLs:

The script `build-and-test-all.sh` might work for you depending on your machine.
```
./show-urls.sh
```
54 changes: 14 additions & 40 deletions build-and-test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@ if [ -z "$DOCKER_HOST_IP" ] ; then
echo set DOCKER_HOST_IP $DOCKER_HOST_IP
fi

docker-compose up -d --no-recreate rabbitmq mongodb
docker-compose stop
docker-compose rm -v --force

docker-compose up -d rabbitmq mongodb

cd eureka-server
./gradlew build

cd ..

cd spring-boot-restful-service

Expand All @@ -22,56 +30,22 @@ export SPRING_DATA_MONGODB_URI=mongodb://${DOCKER_HOST_IP?}/userregistration

cd ..

docker-compose up -d --no-deps restfulservice
docker-compose up -d restfulservice

echo -n waiting for restfulservice to start..

set +e

echo Launching RESTful service in Docker container
echo This takes about 30 seconds...

while [[ true ]]; do
nc -z -w 4 ${DOCKER_HOST_IP?} 8081
if [[ "$?" -eq "0" ]]; then
echo connected
break
fi
echo -n .
sleep 1
done

set -e
./wait-for-services.sh ${DOCKER_HOST_IP?} /health 8081
./wait-for-services.sh ${DOCKER_HOST_IP?} /eureka/apps/REGISTRATION-SERVICE 8761

cd spring-boot-webapp

export USER_REGISTRATION_URL=http://${DOCKER_HOST_IP?}:8081/user

./gradlew build

cd ..

set +e

echo Launching web app in Docker container
echo This takes about 30 seconds...

docker-compose up -d --no-deps web

while [[ true ]]; do
nc -z -w 4 ${DOCKER_HOST_IP?} 8080
if [[ "$?" -eq "0" ]]; then
echo connected
break
fi
echo -n .
sleep 1
done

set -e

echo The microservices are running
echo You can visit these URLS
echo http://${DOCKER_HOST_IP?}:8080/register.html - registration
echo http://${DOCKER_HOST_IP?}:8761 - Eureka console
echo http://${DOCKER_HOST_IP?}:8081/swagger-ui.html - the Swagger UI
docker-compose stop
docker-compose rm -v --force
15 changes: 15 additions & 0 deletions build-and-test-e2e.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#! /bin/bash

set -e

# Build and test the services

./build-test-and-run-all.sh

echo ================== Test the services launched with Docker Compose

./run-e2e-test.sh

echo ================== Test the services packaged as Docker images

./run-test-e2e-test-images.sh
6 changes: 6 additions & 0 deletions build-docker-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#! /bin/bash

set -e

(cd spring-boot-restful-service/docker ; ./build.sh)
(cd spring-boot-webapp/docker ; ./build.sh)
14 changes: 14 additions & 0 deletions build-test-and-run-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#! /bin/bash

set -e

./build-and-test-all.sh

echo Launching services in Docker containers
echo This can take a while

docker-compose up -d

./wait-for-services.sh ${DOCKER_HOST_IP?} /health 8080 8081

./show-urls.sh
14 changes: 11 additions & 3 deletions docker-compose-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ rabbitmq:
- "15672:15672"

eureka:
image: springcloud/eureka
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./eureka-server/build/libs:/app
command: java -jar /app/eureka-server.jar --server.port=8761
ports:
- "8761:8761"

Expand All @@ -27,7 +31,9 @@ restfulservice:
SPRING_DATA_MONGODB_URI: mongodb://mongodb/userregistration
SPRING_RABBITMQ_HOST: rabbitmq
SPRING_APPLICATION_NAME: registration-service
EUREKA_INSTANCE_PREFER_IP_ADDRESS: true
EUREKA_INSTANCE_PREFER_IP_ADDRESS: "true"
SPRING_PROFILES_ACTIVE: enableEureka
EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka:8761/eureka/

web:
image: sb_web
Expand All @@ -41,4 +47,6 @@ web:
# For some reason this didn't work
# EUREKA_CLIENT_SERVICE_URL_DEFAULT_ZONE: http://eureka:8761/eureka/
# EUREKA_INSTANCE_NON_SECURE_PORT: 8080
EUREKA_INSTANCE_PREFER_IP_ADDRESS: true
EUREKA_INSTANCE_PREFER_IP_ADDRESS: "true"
EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka:8761/eureka/
SPRING_PROFILES_ACTIVE: enableEureka
14 changes: 9 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ rabbitmq:
- "15672:15672"

eureka:
image: springcloud/eureka
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./eureka-server/build/libs:/app
command: java -jar /app/eureka-server.jar --server.port=8761
ports:
- "8761:8761"

Expand All @@ -16,7 +20,7 @@ mongodb:
command: mongod --smallfiles

restfulservice:
image: java:openjdk-8u45-jdk
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./spring-boot-restful-service/build/libs:/app
Expand All @@ -35,10 +39,10 @@ restfulservice:
# EUREKA_CLIENT_SERVICE_URL_DEFAULT_ZONE: http://eureka:8761/
# EUREKA_INSTANCE_NON_SECURE_PORT: 8080
# EUREKA_INSTANCE_NON_SECURE_PORT: 8081
EUREKA_INSTANCE_PREFER_IP_ADDRESS: true
EUREKA_INSTANCE_PREFER_IP_ADDRESS: "true"

web:
image: java:openjdk-8u45-jdk
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./spring-boot-webapp/build/libs:/app
Expand All @@ -53,4 +57,4 @@ web:
# For some reason this didn't work
# EUREKA_CLIENT_SERVICE_URL_DEFAULT_ZONE: http://eureka:8761/eureka/
# EUREKA_INSTANCE_NON_SECURE_PORT: 8080
EUREKA_INSTANCE_PREFER_IP_ADDRESS: true
EUREKA_INSTANCE_PREFER_IP_ADDRESS: "true"
18 changes: 18 additions & 0 deletions e2e-test/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

apply plugin: 'scala'
dependencies {
compile "org.scala-lang:scala-library:2.10.2"

testCompile "junit:junit:4.11"
testCompile "org.scalatest:scalatest_2.10:2.1.0"
testCompile "org.seleniumhq.selenium:selenium-java:2.45.0"
}


repositories {
mavenCentral()
}

task wrapper(type: Wrapper) {
gradleVersion = '2.11'
}
Binary file added e2e-test/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
6 changes: 6 additions & 0 deletions e2e-test/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#Thu Mar 03 01:04:28 PST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-2.11-all.zip
Loading

0 comments on commit 7858ff0

Please sign in to comment.