Decompose That WAR! Architecting for Adaptability, Scalability, and Deployability (jmaghreb, jmaghreb2013)
 

Decompose That WAR! Architecting for Adaptability, Scalability, and Deployability (jmaghreb, jmaghreb2013)

on

  • 1,229 views

It’s no longer acceptable to develop large, monolithic, single-language, single-framework Web applications. In this session, you will learn how to use the scale cube to decompose your monolithic Web ...

It’s no longer acceptable to develop large, monolithic, single-language, single-framework Web applications. In this session, you will learn how to use the scale cube to decompose your monolithic Web application into a set of narrowly focused, independently deployable services. The presentation discusses how a modular architecture makes it easy to adopt newer and better languages and technologies. You will learn about the various communication mechanisms—synchronous and asynchronous—that these services can use.

Statistics

Views

Total Views
1,229
Views on SlideShare
1,228
Embed Views
1

Actions

Likes
5
Downloads
47
Comments
0

1 Embed 1

http://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Decompose That WAR! Architecting for Adaptability, Scalability, and Deployability (jmaghreb, jmaghreb2013) Decompose That WAR! Architecting for Adaptability, Scalability, and Deployability (jmaghreb, jmaghreb2013) Presentation Transcript

  • Decompose That WAR! Architecting for Adaptability, Scalability, and Deployability Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris@chrisrichardson.net http://plainoldobjects.com @crichardson
  • Presentation goal How decomposing applications improves deployability and scalability and simplifies the adoption of new technologies @crichardson
  • (About Chris) @crichardson
  • About Chris() @crichardson
  • About Chris http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/ @crichardson
  • About Chris @crichardson
  • Agenda The (sometimes evil) monolith Decomposing applications into services API gateway design Refactoring the monolith @crichardson
  • Let’s imagine you are building an online store @crichardson
  • Traditional application architecture WAR/EAR StoreFrontUI Browser Apache/ Load balancer Spring MVC Product Info Service Recommendation Service Spring Hibernate MySQL Database Review Service Simple to develop test deploy scale Order Service Tomcat @crichardson
  • But there are problems with a monolithic architecture @crichardson
  • Users expect a rich, dynamic and interactive experience @crichardson
  • Presentation layer evolution.... WAR Browser Old HTML / HTTPre tu e tyl s Ia U h ug o StoreFrontUI en d oo ’t g Controller isn View c ite rch Model @crichardson
  • ...Presentation layer evolution Browser - desktop and mobile View Web application Controller Model Static content JSON-REST HTML 5 - JavaScript Native mobile client IoS or Android Events RESTful Endpoints Event publisher No elaborate, server-side web framework required @crichardson
  • Intimidates developers @crichardson
  • Obstacle to frequent deployments Need to redeploy everything to change one component Interrupts long running background (e.g. Quartz) jobs Increases risk of failure Eggs in one basket Fear of change Updates will happen less often - really long QA cycles e.g. Makes A/B testing UI really difficult @crichardson
  • Overloads your IDE and container Slows down development @crichardson
  • Obstacle to scaling development Accounting Engineering E-commerce application Shipping team @crichardson
  • Obstacle to scaling development WAR UI team StoreFrontUI Product Info team Product Info service Recommendations team Recommendation service Reviews team Review service Orders team Order service @crichardson
  • Obstacle to scaling development I want to update the UI But the backend is not working yet! Lots of coordination and communication required @crichardson
  • Requires long-term commitment to a technology stack @crichardson
  • Agenda The (sometimes evil) monolith Decomposing applications into services API gateway design Refactoring the monolith @crichardson
  • @crichardson
  • The scale cube X axis - horizontal duplication Sc Z ax is Scale by splitting different things ale - d by ata sp pa th litt rtit in in ion gs g sim ing ila r Y axis functional decomposition @crichardson
  • Y-axis scaling - application level WAR StoreFrontUI Product Info Service Recommendation Service Review Service Order Service @crichardson
  • Y-axis scaling - application level product info application Product Info Service recommendations application Store front application StoreFrontUI Recommendation Service reviews application Review Service orders application Order Service Apply X axis cloning and/or Z axis partitioning to each service @crichardson
  • Partitioning strategies... Partition by noun, e.g. product info service Partition by verb, e.g. shipping service Single Responsibility Principle Unix utilities - do one focussed thing well @crichardson
  • Partitioning strategies Too few Drawbacks of the monolithic architecture Too many - a.k.a. Nano-service anti-pattern Runtime overhead Potential risk of excessive network hops Potentially difficult to understand system Something of an art @crichardson
  • Inter-service communication options Synchronous HTTP asynchronous AMQP Formats: JSON, XML, Protocol Buffers, Thrift, ... Asynchronous is preferred but REST is popular too JSON is fashionable but binary format is more efficient @crichardson
  • Example micro-service class RegistrationSmsServlet extends RegistrationSmsScalatraStack { } post("/") { val phoneNumber = request.getParameter("From") val registrationUrl = System.getenv("REGISTRATION_URL") + "?phoneNumber=" + encodeForUrl(phoneNumber) <Response> <Sms>To complete registration please go to {registrationUrl} </Sms> </Response> } For more on micro-services see http:/ /oredev.org/2012/sessions/microservice-architecture @crichardson
  • Real world examples http://techblog.netflix.com/ http://highscalability.com/amazon-architecture http://www.addsimplicity.com/downloads/ eBaySDForum2006-11-29.pdf http://queue.acm.org/detail.cfm?id=1394128 @crichardson
  • There are drawbacks @crichardson
  • Complexity See Steve Yegge’s Google Platforms Rant re Amazon.com @crichardson
  • Multiple databases & Transaction management @crichardson
  • Implementing features that span multiple services @crichardson
  • When to use it? In the beginning: •You don’t need it •It will slow you down Later on: •You need it •Refactoring is painful @crichardson
  • But there are many benefits @crichardson
  • Smaller, simpler apps Easier to understand and develop Reduced startup time - important for GAE Less jar/classpath hell Who needs OSGI? @crichardson
  • Scales development: develop, deploy and scale each service independently @crichardson
  • Improves fault isolation @crichardson
  • Eliminates long-term commitment to a single technology stack Modular, polyglot, multiframework applications @crichardson
  • Two levels of architecture System-level Services Inter-service glue: interfaces and communication mechanisms Slow changing Service-level Internal architecture of each service Each service could use a different technology stack Pick the best tool for the job Rapidly evolving @crichardson
  • Easily try other technologies ... and fail safely @crichardson
  • The human body as a system @crichardson
  • 50 to 70 billion of your cells die each day @crichardson
  • Yet you (the system) remain you @crichardson
  • Can we build software systems with these characteristics? http://dreamsongs.com/Files/ DesignBeyondHumanAbilitiesSimp.pdf http://dreamsongs.com/Files/WhitherSoftware.pdf @crichardson
  • Agenda The (sometimes evil) monolith Decomposing applications into services API gateway design Refactoring the monolith @crichardson
  • Directly connecting the front-end to the backend Chatty API View REST Product Info service REST Recommendation Service AMQP Controller Review service Model Traditional web application View Controller Model Browser/Native App Web unfriendly protocols @crichardson
  • Use an API gateway Single entry point View REST Product Info service REST Recommendation Service AMQP Controller Review service Model Traditional web application View API Gateway Controller Model Browser/Native App Client specific APIs Protocol translation @crichardson
  • Optimized client-specific APIs Desktop Browser getProductInfo() getRecomm...() getReviews() NodeJS API Gateway REST proxy Mobile App getProductDetails() Event publishing REST Product Info service REST Recommendation Service AMQP Review service @crichardson
  • Netflix API Gateway Device specific end points http://techblog.netflix.com/2013/01/optimizing-netflix-api.html @crichardson
  • Developing an API gateway Java EE web technologies Netty-based technology stack Non-Java options: e.g. Node.JS @crichardson
  • API gateway design issues @crichardson
  • The need for parallelism Product Info getProductDetails() get Product Details Product Details API getRecomendations() Recommendations Call in parallel getReviews() Reviews @crichardson
  • Futures are a great concurrency abstraction An object that will contain the result of a concurrent computation http://en.wikipedia.org/wiki/Futures_and_promises Future<Integer> result = executorService.submit(new Callable<Integer>() {... }); Java has basic futures. We can do much better.... @crichardson
  • Better: Futures with callbacks val f : Future[Int] = Future { ... } f onSuccess { case x : Int => println(x) } f onFailure { case e : Exception => println("exception thrown") } Guava ListenableFutures, Java 8 CompletableFuture, Scala Futures @crichardson
  • Even better: Composable Futures val f1 = Future { val f2 = Future { ... ; 1 } ... ; 2 } Transforms Future val f4 = f2.map(_ * 2) assertEquals(4, Await.result(f4, 1 second)) Combines two futures val fzip = f1 zip f2 assertEquals((1, 2), Await.result(fzip, 1 second)) def asyncOp(x : Int) = Future { x * x} val f = Future.sequence((1 to 5).map { x => asyncOp(x) }) assertEquals(List(1, 4, 9, 16, 25), Await.result(f, 1 second)) Transforms list of futures to a future containing a list Scala Futures @crichardson
  • Composing concurrent requests using Scala Futures class ProductDetailsService @Autowired()(....) { def getProductDetails(productId: Long) = { val productInfoFuture = productInfoService.getProductInfo(productId) val recommendationsFuture = recommendationService.getRecommendations(productId) val reviewsFuture = reviewService.getReviews(productId) for (((productInfo, recommendations), reviews) <productInfoFuture zip recommendationsFuture zip reviewsFuture) yield ProductDetails(productInfo, recommendations, reviews) } } Asynchronously creates a Future containing result @crichardson
  • Handling partial failures Product Info getProductDetails() get Product Details Product Details Controller getRecomendations() getReviews() X Recommendations Reviews @crichardson
  • About Netflix > 1B API calls/day 1 API call average 6 service calls Fault tolerance is essential http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html @crichardson
  • How to run out of threads Thread 1 X HTTP Request Thread 2 X X Thread 3 API Gateway Code X Recommendation Service Thread n X Execute thread pool Tomcat Eventually all threads will be blocked If service is down then thread will be blocked @crichardson
  • Their approach Network timeouts Invoke remote services via a bounded thread pool Use the Circuit Breaker pattern On failure: return default/cached data return error to caller https://github.com/Netflix/Hystrix @crichardson
  • Agenda The (sometimes evil) monolith Decomposing applications into services API gateway design Refactoring the monolith @crichardson
  • How do you decompose your big, scary monolithic application? @crichardson
  • Strategy #1: Stop digging @crichardson
  • New functionality = service Pristine Monolith Anti-corruption layer Service Glue code @crichardson
  • Sounds simple but... Dependencies between monolith and service e.g. Common entities Building the anti-corruption layer can be challenging Must replicate data between systems ... @crichardson
  • Strategy #2: Split front-end & backend @crichardson
  • Split front-end & backend Independently deployable Monolith Presentation layer Front-end Backend Backend @crichardson
  • Strategy #3: Decompose front-end @crichardson
  • Decompose front-end Front-end Catalog /catalog Catalog Checkout /checkout Checkout Account /account Account Orders /orders Orders @crichardson
  • Issues and challenges Server-side Session state: Need to use a separate session state server ... @crichardson
  • Strategy #4: extract services @crichardson
  • Module service ... WAR Module @crichardson
  • ... Module WAR service Anti-corruption layer Service @crichardson
  • What to extract? Have the ideal partitioned architecture in mind: Partitioned by verb or noun Start with troublesome modules: Frequently updated Stateful components that prevent clustering Conflicting resource requirements @crichardson
  • Untangling dependencies Monolith Service API A API C API B Trouble! Domain model 1 X Domain model 2 C A Y B Z @crichardson
  • Useful idea: Bounded context Different services have a different view of a domain object, e.g. User Management = complex view of user Rest of application: User = PK + ACL + Name Different services can have a different domain model Services exchange messages to synchronize data @crichardson
  • Untangling orders and customers Customer Service Order Service availableCredit() updateCustomer() placeOrder() Order management Order Customer management belongs to has orders creditLimit ... total Invariant: sum(order.total) <= creditLimit Customer Trouble! available credit= creditLimit sum(order.total) @crichardson
  • Replicating the credit limit Order Service sum(order.total) <= creditLimit placeOrder() Order management updateCustomer() Customer management Customer Order Simplified Customer Service creditLimit ... total CreditLimitChangedEvent Customer’ creditLimit @crichardson
  • Maintaining the openOrderTotal Customer Service Order Service placeOrder() = creditLimit - openOrderTotal Order management availableCredit() Customer management Customer Order creditLimit openOrderTotal ... customerId total OpenOrderTotalUpdated @crichardson
  • Refactoring a monolith is not easy BUT the alternative is far worse @crichardson
  • Summary @crichardson
  • Monolithic applications are simple to develop and deploy BUT have significant drawbacks @crichardson
  • Apply the scale cube Modular, polyglot, and scalable applications Services developed, deployed and scaled independently @crichardson
  • Use a modular, polyglot architecture View REST Controller Product Info service REST Recommendation Service AMQP Review service Model Traditional web application View Controller API Gateway Model @crichardson
  • @crichardson chris@chrisrichardson.net Questions? http://plainoldobjects.com @crichardson