Developing polyglot applications on Cloud Foundry
Chris Richardson
Author of POJOs in Action
Founder of the original CloudFoundry.com
@crichardson
http://plainoldobjects.com/
Presentation goal
Modular, polyglot applications:
what, why and how?
How Cloud Foundry simplifies
their development and deployment
About Chris
(About Chris)
About Chris()
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
• Deploying modular, polyglot applications
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
Real-time web ≅ NodeJS
Limitations of relational databases
• Scalability
• Distribution
• Schema updates
• O/R impedance mismatch
• Handling semi-structured data
Monolithic architecture=
Problems
Intimidates developers
• 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
• Decomposing applications into services
• Using NoSQL
• Presentation layer design
• Deploying modular, polyglot applications
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
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
• Less for each developer to learn
• Doesn’t overload IDE or container
• Improves fault isolation
• Eliminates long-term commitment to a single technology stack
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 stackPick the best tool for the jobRapidly evolving - regularly rewrite
Modular, polyglot, applications
Inter-service communication options
• Transports: Synchronous HTTP ⇔ asynchronous AMQP
• Formats: JSON, XML, Protocol Buffers, Thrift, ...
Asynchronous is preferred but REST is fine
JSON is fashionable but binary format is more efficient
StoreFrontUI
storefront web application
AccountingService
accounting application
InventoryService
inventory application
ShippingService
shipping application
MySQL
RabbitMQ(Message Broker)
Asynchronous message-based communication
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Decomposing applications into services
• Using NoSQL
• Presentation layer design
• Deploying modular, polyglot applications
Solution: Use 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
RabbitMQ(Message Broker)
Polyglot persistence architecture
Redis
Mongo
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Decomposing applications into services
• Using NoSQL
• Presentation layer design
• Deploying modular, polyglot applications
Modular application
Choice of presentation layer technology
NodeJS is the fashionable technology
Why NodeJS?• Familiar JavaScript
• High-performance, scalable event-driven, non-blocking I/O model
• 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
A modern web application
BrowserService 1
Service 2
...
HTML 5ApplicationSocket.io
client
Events
REST
Server ApplicationSocket.io
server
Node JS
NodeJS as a mediator
Node JS
Service
RabbitMQ Service
REST
AMQP AMQP
REST
Events
socket.io
Socket.io - server-sidevar express = require('express') , http = require('http') , amqp = require(‘amqp’) ....;
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(“myQueue”, {}, function(queue) { queue.bind(“myExchange”, “myBindingKey”); queue.subscribe(amqpMessageHandler); });});
Handle socket.io connection
Subscribe to AMQP queue
Republish as socket.io event
Socket.io - client-side
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
Inventory Service Shipping
NodeJS
Modular, polyglot architecture
RabbitMQ
NodeJS
Inventory Service Shipping Service
Billing Service Redis Inventory Database
Mongo Order Database
Standalone“headless” Spring/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 dev
elop,
test
and d
eplo
y th
is?
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Deploying modular, polyglot applications
• Overview of Cloud Foundry
• Using Cloud Foundry
• Consuming Cloud Foundry services
Simple, Open, Flexible,
Scalable
The Open Platform as a Service
Deploy and scale polyglot applications in seconds, without
locking yourself into a single cloud
Applica'on Service Interface
OSS community
Private Clouds
PublicClouds
MicroClouds
Data Services
Other Services
Msg Services
vFabric Postgres
vFabric RabbitMQTM
Additional partners services …
CloudFoundry.COM - Multi-tenant PaaS operated by VMware
Runtimes & FrameworksServices
vCenter / vSphere
CloudFoundry.COM (beta)
Infrastructure
Micro Cloud FoundryTM – Industry first downloadable PaaS
Open source Platform as a Service project
App Instances Services
A cloud packaged as a VMware Virtual Machine
CloudFoundry.ORG - Vibrant Apache 2 licensed open-source project
CloudFoundry.ORG
DownloadCode
Setup Environment
Deploy Behind FirewallBOSH
Your Infrastructure
Cloud Foundry: you can trade-off effort vs flexibility
Public PaaS
Private PaaS
Custom Private PaaS
Less
More
Less
More
.COM....
....
git clone git://github.com/cloudfoundry/vcap.git
Effort Flexibility
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Deploying modular, polyglot applications
• Overview of Cloud Foundry
• Using Cloud Foundry
• Consuming Cloud Foundry services
Using Cloud Foundry
$ vmc target <any cloud>
$ vmc login <credentials> $ vmc push <my-app>
> bind services? Yes
$ vmc update <my-app>
$ vmc instances <my-app> +100
Example vmc commands
Sinatra + Redisrequire 'sinatra'require 'redis'
configure do $r = Redis.new(:host => '127.0.0.1', :port => '6379') if !$r end
get '/' do "Hello World! " + $r.incr("hitcounter").to_send
http://sgce2012.cloudfoundry.com/
Connect to Redis
Increment hit counter
One step deployment--- applications: ./storefront: name: nodestore framework: name: node info: mem: 64M description: Node.js Application exec: url: ${name}.${target-base} mem: 64M instances: 1 services: si-redis: type: redis si-rabbit: type: rabbitmq
Path to application
Required platform services
applications: standalone-inventory/target/appassembler: name: standalone-shipping framework: name: standalone info: description: Standalone Application exec: runtime: java command: bin/standalone-shipping url: mem: 512M instances: 1 services: si-rabbit: type: rabbitmq si-mongo: type: mongodb
One step deployment
Path to application
Required platform services
One step deployment$ vmc push
Would you like to deploy from the current directory? [Yn]:
Pushing application 'inventory'...
Creating Application: OK
Creating Service [si-rabbit]: OK
Binding Service [si-rabbit]: OK
Creating Service [si-mongo]: OK
Binding Service [si-mongo]: OK
Creating Service [si-redis]: OK
Binding Service [si-redis]: OK
Uploading Application:
Checking for available resources: OK
Processing resources: OK
Packing application: OK
Uploading (12K): OK
Push Status: OK
Staging Application 'inventory': OK
Starting Application 'inventory': OK
Pushing application 'store'...
vmc push:•Reads the manifest file•Creates the required platform services•Deploys all the applications
Micro Cloud Foundry = your sandbox
Open source Platform as a Service project
App Instances Services
10.04
A PaaS packaged as a VMware Virtual Machine
Use as a developer sandbox• Use the services from Junit integration tests
• Deploy your application for functional testing
• Remote debugging from STS
Agenda
• The (sometimes evil) monolith
• Refactoring to a modular, polyglot architecture
• Deploying modular, polyglot applications
• Overview of Cloud Foundry
• Using Cloud Foundry
• Consuming Cloud Foundry services
Cloud Foundry services
Creating a service instance$ vmc create-service mysql mysql1Creating Service: OK
$ vmc services......
=========== Provisioned Services ============+-------------+---------+| Name | Service |+-------------+---------+| mysql1 | mysql |+-------------+---------+
$
Binding a service to an application
$ vmc push cer-spring --path web/target/Application Deployed URL [cer-spring.cloudfoundry.com]: Detected a Java SpringSource Spring Application, is this correct? [Yn]:
Memory Reservation (64M, 128M, 256M, 512M, 1G) [512M]:
Creating Application: OKWould you like to bind any services to 'cer-spring'? [yN]: y
Would you like to use an existing provisioned service? [yN]: yThe following provisioned services are available
1: mysql1
2: mysql-135e0Please select one you wish to use: 1
Binding Service [mysql1]: OKUploading Application:
Checking for available resources: OK
Processing resources: OK Packing application: OK
Uploading (12K): OK
Would you like to bind any services to 'cer-spring'? [yN]: yWould you like to use an existing provisioned service? [yN]: yThe following provisioned services are available1: mysql12: mysql-135e0Please select one you wish to use: 1Binding Service [mysql1]: OK
Bindings exposed through VCAP_SERVICES environment variable
{ "mysql-5.1": [{
"name": "mysql1", "label": "mysql-5.1",
"plan": "free", "tags": ["mysql", "mysql-5.1", "relational"],
"credentials": {
"name": "da81b57c25cca4c65929a223f0ed068a0",
"host": "172.99.99.99",
"port": 3306,
"username": "secretusername",
"password": "secretuserpassword"
....
}
}] }
Accessing bound services:low-level
var services = JSON.parse(process.env.VCAP_SERVICES);var creds = services['mysql-5.1'][0].credentials
configure do services = JSON.parse(ENV['VCAP_SERVICES']) mysql_key = services.keys.select { |svc| svc =~ /mysql/i }.first mysql = services[mysql_key].first['credentials'] mysql_conf = {:host => mysql['hostname'], :port => mysql['port'], :username => mysql['user'], :password => mysql['password']} @@client = Mysql2::Client.new mysql_confend
Accessing bound services:high-level, Spring/Java
<bean id="dataSource" class="…"> … DataSource for MySQL instance … </bean>
reconfigured automatically
CloudEnvironment environment = new CloudEnvironment();RdbmsServiceInfo mysqlSvc = environment.getServiceInfo("mysqlService", RdbmsServiceInfo.class);RdbmsServiceCreator dataSourceCreator = new RdbmsServiceCreator();DataSource dataSource = dataSourceCreator.createService(mysqlSvc);
<cloud:data-source id="dataSource" service-name="mysql1"> <cloud:pool pool-size="1-5"/> <cloud:connection properties="charset=utf-8"/></cloud:data-source>...
Accessing bound services:high-level, Ruby
RedisHost = "127.0.0.1"RedisPort = 10000$r = Redis.new(:host => RedisHost, :port => RedisPort) if !$r
reconfigured automatically
production: adapter: sqlite3 database: db/production.sqlite3 pool: 5 timeout: 5000
rewritten automatically
The human body as a system
50 to 70 billion of your cells die each day
Yet you (the system) remain you
Can we build software systems with these characteristics?
http://dreamsongs.com/Files/WhitherSoftware.pdf
http://dreamsongs.com/Files/DesignBeyondHumanAbilitiesSimp.pdf
Questions?
@crichardson [email protected]://plainoldobjects.com/presentations/
www.cloudfoundry.com @cloudfoundry