Developing modular, polyglot applications with Spring (SpringOne India 2012)

Post on 10-May-2015

2.493 views 4 download

Transcript of Developing modular, polyglot applications with Spring (SpringOne India 2012)

Developing modular, polyglot applications with Spring

Chris Richardson

Author of POJOs in Action, Founder of the original CloudFoundry.com

@crichardson chris.richardson@springsource.com

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

Thank you!

@crichardson chris.richardson@springsource.comhttp://plainoldobjects.com

www.cloudfoundry.com @cloudfoundry