Building a Microservice Federation with Grails
Grails... microservices...
you must be crazy man!
About me
20 years Java
12+ years Spring
7+ years Groovy/Grails
PowerBuilder
C/C++
FORTRAN
1st career in aviation/aerospace
twitter: @jackfrosch linkedin: ../in/jackfrosch
email: jackfrosch@gmail.com groovy-community.slack.com: @jackfrosch
The Story I'm About to Tell You is True
- Microservices are here
- Doing it the easy way
- Building a small microservice federation
Microservices are here
- Foundational concepts
- Microservice advantages
- Microservice disadvantages
What are microservices?
Microservices are small, autonomous services that work together.
Sam Newman Building Microservices, O'Reilly Media
Small is key
Small in functional scope...
http://www.whattofix.com/images/ComplexERDExample.gif
Probably not small functional scope...
... not necesarily size
Smaller is simpler
Everything should be made as simple as possible, but not simpler*
* though attributed to Einstein, this simple quote is actually from
Roger Sessions paraphrasing (and simplifying!) a statement by Albert Einstein
Simpler is better
A system that is hard to understand is hard to change.
— Eric Evans, Domain-Driven Design
Autonomy is key
Development autonomy*
*Caveat: You can't break my stuff
http://www.memes.com/meme/498049
Consumer Driven Contracts
http://bit.ly/thoughtworks-consumer-driven-contracts
Testing: How much & what kind?
http://famouswonders.com/wp-content/gallery/pyramids-of-egypt/pyramid-of-khafre.jpg
Data Autonomy
http://martinfowler.com/articles/microservices.html
Build/Deploy autonomy
http://www.openmakesoftware.com/images/ReleaseEngineer/CI-CD.png
Scale where the load is...
Operational Autonomy
https://media.licdn.com/mpr/mpr/p/8/005/083/1a8/257d716.jpg
All this sounds great, but...
...what about our monolith?
Identify bounded contexts...
... and divide along the seams
Favor choreography over orchestration
http://kennysilva.net/wp-content/uploads/2010/12/orchestra-conductor.jpg
Monolith
Microservice
class Passenger {
String accountNo
String firstName
String lastName
Address billingAddress
Payment paymentPreference
List<Payment> paymentHistory
Phone home
Phone mobile
Phone work
BloodType bloodType
...
}
What about shared domain?
// For Trip Management
class Passenger {
String accountNo
String firstName
String lastName
Phone mobile
...
}
// For Billing
class Passenger {
String accountNo
String firstName
String lastName
Address billingAddress
List<Payment> paymentHistory
Phone home
Phone work
...
}
The evils of too much coupling between services are far worse than the problems caused by code duplication.
- Sam Newman, Building Microservices
Microservices Federation Cross-cutting Concerns
- Logging
- Security
- Metrics and monitoring
Logging Tips
- Use a Correlation ID
- Use a consistent log message format
- Use log aggregation
Logging Aggregators
"ELK Stack"
Security
- None
- At the gateway only
- At every microservice
Security at the gateway only
"They're inside the room!"
"Yikes!"
"Mmm, you look tasty."
Perimeter security reminds me of the movie, Aliens
Security at every microservice
Metrics, Monitoring & More
- Microservices need to report metrics
- Resiliency requires monitoring & circuit breakers
- Automatic service discovery
- Gateway / reverse proxy
- Load balancing
Coming up:
Spring Boot & Spring Cloud to the rescue!
Microservice Architecture Advantages
- Scalable
- Adaptable
- Resilient
- Testable
- More future-proof
- More greenfield
Microservice Architecture Disadvantages
- Solid domain understanding
- Development more complicated
- More builds
- More databases
- Log aggregation is essential
- Metrics, monitoring & more essential
Doing it the easy way
- Spring Boot
- Spring Cloud with Netflix OSS
- Grails
Spring Boot includes metrics endpoint
There are many metrics out of the box, but you can create your own.
http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html
Spring Boot includes many endpoints
http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html
Spring Cloud provides monitoring
... and much more
Service discovery with Eureka
Gateway /Rev Proxy with Zuul
http://bit.ly/vignette2_wikia_nocookie_net_ghostbusters
Gateway /Rev Proxy with Zuul
Hystrix for Resilience
Monitoring & Circuit Breakers w/ Hystrix
http://martinfowler.com/bliki/CircuitBreaker.html
Grails plays well
- Grails full stack development
- Grails REST
- Grails plugin architecture
Full stack != monolith
Grails full stack development
Web UI, SQL / NOSQL Database Support, Spring, Hibernate, Security, REST, Java, Groovy...
Create App
$grails create-app HelloWorld
Hello World app... easy as 1 - 2 - 3
grails> create-controller demo.Hello
package demo
class HelloController {
def index() {
render 'Hello SpringOne2GX 2015!'
}
}
Full stack CRUD apps are about as easy...
1. Create controller
2. Make it do something
3. See it to believe it!
Create a Domain Class
Define demo.Person
package demo
class Person {
String firstName
String lastName
String email
String twitterHandle
static constraints = {
firstName blank:false, maxSize: 32
lastName blank: false, maxSize: 64
email blank: false, maxSize: 128, email:true
twitterHandle nullable:true, maxSize:64
}
}
Generate Scaffolding...
.. to bootstrap CRUD app
After generation
Run App
grails> run-app
Retrieve Persons List
Create Person form
Create a Person record
Show a Person record
Retrieve Persons List
Delete a Person record
Grails REST URI patterns
What if I want to do more than CRUD?
package demo
import grails.rest.RestfulController
class PersonController extends RestfulController<Person> {
static responseFormats =['json', 'xml']
PersonController() {
super(Person)
}
def findPersonsWithLastNameLike(String likeness) {
respond Person.findByLastNameLike("${likeness}%")
}
}
class UrlMappings {
static mappings = {
"/persons"(resources:'person')
"/persons/search/withLastNameLike/$likeness"(controller: 'person',
action: 'findPersonsWithLastNameLike')
}
}
Restfully create a Person...
... yields a 201 created response
Test Rest
$ curl -i -X GET http://localhost:8080/persons.xml
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 07 Sep 2015 21:26:16 GMT
<?xml version="1.0" encoding="UTF-8"?><list />
$
200 OK good ... XML weird, but you wanted weird
Test Rest
$ curl -i -X GET http://localhost:8080/persons
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 07 Sep 2015 21:26:16 GMT
[]
$
200 OK good ... JSON good.
Change @Resource to prefer JSON
package demo
import grails.rest.Resource
@Resource(uri='/persons', formats=['json', 'xml'])
class Person {
String firstName
String lastName
String email
String twitterHandle
static constraints = {
firstName blank:false, maxSize: 32
lastName blank: false, maxSize: 64
email blank: false, maxSize: 128, email:true
twitterHandle nullable:true, maxSize:64
}
}
Grails content negotiation
$ curl -i -X GET --header "Accept:application/json" http://localhost:8080/persons
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 07 Sep 2015 21:26:16 GMT
[]
$
200 OK good ... JSON good.
Grails content negotiation
$ curl -i -X GET http://localhost:8080/persons.json
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 07 Sep 2015 21:26:16 GMT
[]
$
200 OK good ... JSON good.
Besides UI CRUD, what about REST?
package demo
import grails.rest.Resource
@Resource(uri='/persons')
class Person {
String firstName
String lastName
String email
String twitterHandle
static constraints = {
firstName blank:false, maxSize: 32
lastName blank: false, maxSize: 64
email blank: false, maxSize: 128, email:true
twitterHandle nullable:true, maxSize:64
}
}
grails create-app RestfulCrud
With an IDE...
Test Rest
$ curl -i -X GET http://localhost:8080/persons
[jfrosch@localhost demo]$ HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: text/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 07 Sep 2015 21:26:16 GMT
<?xml version="1.0" encoding="UTF-8"?><list />
$
Hmm... 200 OK good ... XML bad.
Building a Grails microservice federation
Download all the sources for this from: https://github.com/jfrosch/GrailsMicroFederation
Build a Eureka Server
Build a Eureka Server
Build a Zuul Server
/product-catalog acts as the root of the uri
Build a Zuul Server
Inspecting Zuul Routes
Microservice Setup
product-catalog is the name we map the Zuul route to
Microservice Setup
Can use @EnableDiscoveryClient if not intending to use Netflix implementation
@HystrixCommand
Notice we're using Zuul server at port 8765 and Zuul route, not Recommendation Engine app's endpoint directly
I've had some difficulty applying circuit-breakers in controllers, so do so only in services
Build Hystrix Dashboard
Note we're using Zuul server, not a microservice endpoint, to catch all data streams
All services are up
... After killing recommendation engine ...
... After restarting recommendation engine ...
Summary
- Adaptable
- Scalable
- Resilient
- Maintainable
- Monitorable
- Cloud ready
You'd be crazy to adopt a microservice architecture using Grails...
unless your application needs to be ...
(even if your company isn't ready for the cloud yet)
Resources
- http://projects.spring.io/spring-cloud/
- http://cloud.spring.io/spring-cloud-netflix/
- http://martinfowler.com/articles/microservices.html
-
Github repo for demo: https://github.com/jfrosch/GrailsMicroFederation
Want to learn more about Groovy and Grails? Come to our monthly Groovy / Grails Meetup:
https://www.meetup.com/st-louis-groovy-and-grails-meetup/
Questions?
STLJUG NFJS Building a Microservice Federation with Grails
By Jack Frosch
STLJUG NFJS Building a Microservice Federation with Grails
- 1,910