developing modular, polyglot applications with spring (springone india 2012)
TRANSCRIPT
Developing modular, polyglot applications with Spring
Chris Richardson
Author of POJOs in Action, Founder of the original CloudFoundry.com
@crichardson [email protected]
http://plainoldobjects.com/
Presentation goal
Modular, polyglot applications:
what, why and how?
Why Spring is the ideal technology
for building these applications
About Chris
(About Chris)
About Chris - 1999
WebLogic Server
CORBA
Enterprise JavaBeans
About Chris - 2004
Spring programming model
POJO
Dependency Injection
Aspect-Oriented
Programming
Portable Service
Abstractions
Transformed how I developed software!
About Chris()
About Chris - 2006
Cloud Computing
Oakland JUG
Transformed how I deployed software!
About Chris
About Chris
http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/
vmc push About-Chris
Signup at http://cloudfoundry.com
Developer Advocate for CloudFoundry.com
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Presentation layer design
• Using NoSQL databases
• Inter-service communication options
Let’s imagine you are building an e-commerce application
Traditional web application architecture
Tomcat
Browser
WAR
MySQL Database
ShippingService
AccountingService
InventoryService
StoreFrontUI
developtestdeploy
Simple to
Apache
scale
But there are problems
Users expect a rich, dynamic and interactive experience
Java Web ApplicationBrowser
HTTP Request
HTML/Javascript
Old style UI architecture isn’t good enough
HTML5/JavaScript browser applicationsReal-time web ≅ NodeJS
Limitations of relational databases
• Scalability
•Distribution
• Schema updates
•O/R impedance mismatch
• Handling semi-structured data
Intimidates developers
Scalability issues
•Different components have different scalability needs ⇔ but
you can only scale the entire system
• Some components might not be scalable ⇒ can’t scale system
• Need to redeploy everything to change one component
• Increases risk of failure, e.g. interrupts background jobs
Fear of change
• Extensive test cycle
• Updates will happen less often, e.g. Makes A/B testing UI really difficult
Obstacle to frequent deployments
Overloads your IDE and container
Slows down development
Lots of coordination and communication required
Obstacle to scaling developmentI want to update
the UI
But the backend is not working yet!
Requires long-term commitment to a technology stack
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Presentation layer design
• Using NoSQL databases
• Inter-service communication options
The scale cube
X axis - horizontal duplication
Z axis
- data
parti
tionin
g
Y axis - functionaldecomposition
Scale
by sp
litting
similar
thing
s
Scale by splitting different things
Y-axis scaling - application level
WAR
ShippingService
AccountingService
InventoryService
StoreFrontUI
Y-axis scaling - application level
Store front web application
shipping application
inventory application
Apply X axis cloning and/or Z axis partitioning to each service
ShippingService
AccountingService
InventoryServiceStoreFrontUI
accounting application
Real world examples
http://highscalability.com/amazon-architecture
Between 100-150 services are accessed to build a page.
http://techblog.netflix.com/
http://www.addsimplicity.com/downloads/eBaySDForum2006-11-29.pdf
http://queue.acm.org/detail.cfm?id=1394128
Drawbacks of Y-axis splits
• Complexity
• Partitioned databases and transaction management
•Deciding when to use this approach
But there are many benefits
• Scales development: develop, deploy and scale each service independently
• Enables frequent deployments
• Less for each developer to learn
• Doesn’t overload IDE or container
• Improves fault isolation
• Easily adopt new technologies
Two levels of architecture
System-level
ServicesInter-service glue: interfaces and communication mechanismsSlow changing
Service-level
Internal architecture of each serviceEach service can use a different technology stackRapidly evolving - regularly rewritePick the best tool for the job
Modular, polyglot, applications
Lots of languages and frameworks to choose from
And many more...
But there are lots of awesome, and mature Spring projects
• Spring Framework
• Spring Security
• Spring Mobile
• Spring Data
• Spring Web Services
• Spring Batch
• Spring Integration
• Spring AMQP
• Spring Social
• Spring Shell
• ...
Spring works with Scala@Controllerclass TwilioController {
@Autowired var surveyManagementService: SurveyManagementService = _
@RequestMapping(value = Array("/begincall.html")) @ResponseBody def beginCall(@RequestParam("From") callerId: String) = { surveyManagementService.findSurveyByCallerId(callerId) match { case None => <Response> <Say>Sorry don't recognize your number</Say> <Hangup/> </Response> case Some(survey) => <Response> <Say>{ survey.prompt }</Say> <Gather action="handleresponse.html" method="POST" numDigits="1"> { for ((choice, index) <- survey.choices zipWithIndex) yield <Say>Press { index } for { choice }</Say> } </Gather> <Say>We are sorry you could not decide</Say> <Hangup/> </Response> } }
The Spring Scala project
• Scala-style “setters/getters”
• Bean configuration DSL
• Scala-style template classes
https://github.com/SpringSource/spring-scala
class PersonConfiguration extends FunctionalConfiguration { val jack = bean() { new Person("Jack", "Doe") }
val jane = bean() { new Person("Jane", "Doe") }
val john = bean() { val john = new Person("John", "Doe") john.father = jack() john.mother = jane() john }}
Coming in 2013Java 8 closures = concise code!
http://cr.openjdk.java.net/~briangoetz/lambda/sotc3.html
List<Album> albums = ...List<Album> sortedFavorites = albums.stream() .filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4))) .sorted(comparing(a -> a.name)) .into(new ArrayList<>());
Predicate
int sum = shapes.parallel() .filter(s -> s.getColor() == BLUE) .map(s -> s.getWeight()) .sum();
Parallelism
Modularity
Productivity
Portability
Testability
Key benefits of Spring are still
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Presentation layer design
• Using NoSQL databases
• Inter-service communication options
Browser
WAR
StoreFrontUI
Model
View Controller
Presentation layer evolution....
HTML / HTTP
Browser Web application
RESTfulEndpoints
Model
View Controller
...Presentation layer evolution
JSON-REST
HTML 5 - JavaScript
No elaborate, server-side web framework required
Event publisher
Events
Static content
How to publish events to browser?
NodeJS is the fashionable technology
Why NodeJS?• Familiar JavaScript
• High-performance, scalable event-driven, non-blocking I/O model
• Compact runtime, e.g. 64M
•Over 13,000 17,000 modules developed by the community
•Many JavaScript client frameworks have a NodeJS counterpart, e.g. socket.io and SockJS
Why not NodeJS?
a.k.a. callback hell
Dealing with JavaScript
“...Still the consequence of this is that we must take javascript
seriously as a first-class language and concentrate on how to limit the
damage its flaws cause....”
http://martinfowler.com/bliki/gotoAarhus2012.html
NodeJS as a mediator...
BrowserService 1
Service 2
...
HTML 5ApplicationSocket.io
client
Events
REST
Server app
Socket.ioserver
Node JS
Static content
...NodeJS as a mediator
Node JS
Service
RabbitMQ Service
REST
AMQP AMQP
REST
Events
socket.io
Example NodeJS applicationvar express = require('express') , http = require('http') , amqp = require(‘amqp’) , server = http.createServer(app) , io = require('socket.io').listen(server) ....;
server.listen(8081);...var amqpCon = amqp.createConnection(...);
io.sockets.on('connection', function (socket) { function amqpMessageHandler(message, headers, deliveryInfo) { var m = JSON.parse(message.data.toString()); socket.emit(‘tick’, m); }; amqpCon.queue(“”, {}, function(queue) { queue.bind(“myExchange”, “myBindingKey”); queue.subscribe(amqpMessageHandler); });});
Handle socket.io connection
Subscribe to AMQP queue
Republish as socket.io event
Example browser application
var socket = io.connect(location.hostname);
function ClockModel() { self.ticker = ko.observable(1); socket.on('tick', function (data) { self.ticker(data); });};
ko.applyBindings(new ClockModel());
<html><body>
The event is <span data-bind="text: ticker"></span>
<script src="/socket.io/socket.io.js"></script><script src="/knockout-2.0.0.js"></script><script src="/clock.js"></script>
</body></html>
clock.js
Connect to socket.io server
Subscribe to tick event
Bind to model
Update model
About Cujojs & s2js
JavaScript
Dependency Injection
Aspect-Oriented
Programming
Portable Service
Abstractions
www.cujojs.com github.com/s2js
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Presentation layer design
• Using NoSQL databases
• Inter-service communication options
Why NoSQL?
Benefits
• Higher performance
• Higher scalability
• Richer data-model
• Schema-less
Drawbacks
• Limited transactions
• Limited querying
• Relaxed consistency
• Unconstrained data
Example NoSQL Databases
Database Key features
Cassandra Extensible column store, very scalable, distributed
Neo4j Graph database
MongoDB Document-oriented, fast, scalable
Redis Key-value store, very fast
http://nosql-database.org/ lists 122+ NoSQL databases
NoSQL and the scale cube
MongoDB replica setsCassandra replication
Mongo
DB sha
rding
Cassan
dra
parti
tionin
g
The future is polyglot
IEEE Software Sept/October 2010 - Debasish Ghosh / Twitter @debasishg
StoreFrontUI
storefront web application
AccountingService
accounting application
InventoryService
inventory application
ShippingService
shipping application
MySQL
Polyglot persistence architecture
Redis
Mongo
Spring Data = simplifying data access
http://www.springsource.org/spring-data
•Redis
•MongoDB
•Neo4j
• JPA
• REST
• Apache Hadoop
• Gemfire
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Presentation layer design
• Using NoSQL databases
• Inter-service communication options
Inter-service communication options
• Synchronous HTTP ⇔ asynchronous AMQP
• Formats: JSON, XML, Protocol Buffers, Thrift, ...
• JSON is fashionable and easier to debug
• Binary format is more efficient
StoreFrontUI
wgrus-store.war
AccountingService
wgrus-billing.war
InventoryService
wgrus-inventory.war
ShippingService
wgrus-shipping.war
MySQL
RabbitMQ(Message Broker)
Asynchronous message-based communication
Benefits
•Decouples caller from server
• Caller unaware of server’s coordinates (URL)
•Message broker buffers message when server is down/slow
• Supports a variety of communication patterns
Drawbacks
• Additional complexity of message broker
• Request/reply using messaging is more complex
• Firewall unfriendly
Spring AMQP
• Encapsulates low-level details
• Simplifies sending and receiving of messages
Producer
Spring AMQP
AMQP
AmqpTemplate
Consumer
ListenerContainer
Spring Integration• Provides the building blocks for a pipes and
filters architecture
• Enables development of application components that are
• loosely coupled
• insulated from messaging infrastructure
• Messaging defined declaratively
Order Service
Messaging Gateway
Channel Service Activator
Shipping service
Development time: same JVM@Servicepublic class OrderServiceImpl {
@Autowiredprivate ShippingService shippingService;
public void placeOrder() { String orderId = generateOrderId(); … shippingService.shipOrder(orderId);}
}
@Servicepublic class ShippingServiceImpl {
public void shipOrder(String orderId) { ....}
}
shipOrder()
Order Service
Messaging Gateway
Channel Service Activator
Shipping service
AMQP
RabbitMQ
AMQP Channel
Test and production: distributed
Application code is unchanged
Synchronous REST
ShippingService
StoreFrontUI
wgrus-store.war
AccountingService
wgrus-billing.war
wgrus-shipping.war
InventoryService
wgrus-inventory.war
MySQL
REST
...
Pros and cons of REST
• Pros
• Simple and familiar
• Request/reply is easy
• Firewall friendly
•No intermediate broker
• Cons
•Only supports request/reply
• Server must be available
• Client needs to know URL(s) of server(s)
Spring MVC makes REST easy@Controllerpublic class AccountController {
@Autowired private MoneyTransferService moneyTransferService; @RequestMapping(value = "/accounts/{accountId}", method = RequestMethod.GET) @ResponseBody public AccountInfo getAccount(@PathVariable String accountId) { Account account = moneyTransferService.findAccountByid(accountId); return makeAccountInfo(account); }
@RequestMapping(value = "/accounts", method = RequestMethod.POST) @ResponseStatus( HttpStatus.CREATED ) public void createAccount(@RequestBody AccountInfo accountInfo, UriComponentsBuilder builder, HttpServletResponse response) { ... }
URL matching &
destructuringobject ⇒XML/JSON
XML/JSON ⇒
object
Not all APIs are RESTful
From http://martinfowler.com/articles/richardsonMaturityModel.html
About Hypertext As The Engine Of Application State
• Single well known URL
• Entity representation has typed relationship links
• Client discovers what it can do to an entity via those links
See http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
About HATEOAS
$ curl http://cf-auto-scaler.cloudfoundry.com{"links": [ {"rel":"autoscaledapps", "href":"http://cf-auto-scaler.cloudfoundry.com/autoscaledapps"}]}
The well known URL
Linklink type
$ curl http://cf-auto-scaler.cloudfoundry.com/autoscaledapps{"content":[ {"name":"vertx-clock", "links":[ {"rel":"self","href":"http://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock"}, {"rel":"rules","href":"http://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock/rules"} ] }],...}
Links to act on this app
Spring HATEOAS@Controller@RequestMapping(value = "/autoscaledapps")public class AutoscaledAppController {
@RequestMapping(value = "/{appName}", method = RequestMethod.GET) public HttpEntity<AutoscaledAppResource> get(@PathVariable String appName) { AutoscaledAppResource ar = new AutoscaledAppResource(appName); ar.add(linkTo(AutoscaledAppController.class)
.slash(appName).withSelfRel()); ar.add(linkTo(AutoscaledAppController.class)
.slash(appName).slash("rules").withRel("rules")); return new HttpEntity<AutoscaledAppResource>(ar); } ...}
https://github.com/SpringSource/spring-hateoas
public class AutoscaledAppResource extends ResourceSupport {
private String name;
Consuming RESTful WS
RestTemplate restTemplate = new RestTemplate();
AccountInfo accountInfo = new AccountInfo(...);URI accountUrl = restTemplate.postForLocation("http://localhost/accounts", accountInfo);
ResponseEntity<AccountInfo> accountInfoResponse = restTemplate.getForEntity(accountUrl, AccountInfo.class);
Assert.assertEquals(HttpStatus.SC_OK, accountInfoResponse.getStatusCode());AccountInfo accountInfo2 = accountInfoResponse.getBody();...
The Spring REST shell$ rest-shellhttp://localhost:8080:> baseUri http://cf-auto-scaler.cloudfoundry.comhttp://cf-auto-scaler.cloudfoundry.com:> discoverrel href =======================================================================autoscaledapps http://cf-auto-scaler.cloudfoundry.com/autoscaledapps
http://cf-auto-scaler.cloudfoundry.com:> follow --rel autoscaledapps
http://cf-auto-scaler.cloudfoundry.com/autoscaledapps:> post --from src/test/resources/examplejson/createapp1.json --follow true
1 files uploaded to the server using POSThttp://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock:> discoverrel href ================================================================================self http://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock rules http://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock/ruleshttp://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock:> follow --rel rules http://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock/rules:> post
--from src/test/resources/examplejson/createrule1.json --follow true1 files uploaded to the server using POSThttp://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock/rules/idle:> uphttp://cf-auto-scaler.cloudfoundry.com/autoscaledapps/vertx-clock/rules:> up
Original architecture
Tomcat
Browser
WAR
MySQL Database
ShippingService
AccountingService
InventoryService
StoreFrontUI
developtestdeploy
Simple to
Apache
scale
Inventory Service Shipping
NodeJS
Modular, polyglot architecture
RabbitMQ
NodeJS
Inventory Service Shipping Service
Billing Service Redis Inventory Database
Mongo Order Database
Standalone“headless”
Spring Integration/Java
applications
Spring/Scala web application
MySQL Customer Database
Desktop Browser Native Mobile application HTML5 mobile application
StoreUI StoreUI StoreUI
StoreUIJavascriptAsynchronous,
scalable communication
How
do w
e dep
loy
this!
?!
Applica'on Service Interface
OSS community
Private Clouds
PublicClouds
MicroClouds
Data Services
Other Services
Msg Services
vFabric Postgres
vFabric RabbitMQTM
Additional partners services …
Cloud Foundry simplifies the deployment of modular, polyglot
applications
Spring makes it easy to consume Cloud Foundry services
Many Spring applications run on Cloud Foundry unchanged
Summary
•Modern applications have a modular, polyglot architecture
•Spring is the ideal technology for building these applications
•Cloud Foundry is the ideal way to deploy these applications