Share
Embed
Microservices on the JVM - a practical overview
Slides for @eigenbrodtm's and my session at Devoxx 2014
https://www.innoq.com/en/talks/2014/11/microservices-on-the-jvm-devoxx-2014/
Alexander Heusingfeld
November 12, 2014
Transcript
107 slides
-
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM MicroServices on the JVM A practical
overview Alexander Heusingfeld & Martin Eigenbrodt -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM #MicroServices #buzz • Defined by LOC?
• UI or not UI? • SOA in new clothes? -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM “Micro” Services? Monolith A A A
A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A C A A B D Y K -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM “Micro” Services? Monolith A A A
A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A C A A B D Y K -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM “Micro” Services? Monolith System A System
B System C System D A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A C A A B D Y K -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM “Micro” Services? Monolith System A System
B System C System D A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A C A A B D Y K -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Our approach • build around business
capabilities • separate runtime processes -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Our approach • build around business
capabilities • separate runtime processes • self-contained -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Our approach • build around business
capabilities • separate runtime processes • self-contained • loosely coupled -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Our approach • build around business
capabilities • separate runtime processes • self-contained • loosely coupled • independent life cycles -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Our approach • build around business
capabilities • separate runtime processes • self-contained • loosely coupled • independent life cycles • decentralised management -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Our approach • build around business
capabilities • separate runtime processes • self-contained • loosely coupled • independent life cycles • decentralised management Self-Contained System -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM We’re not talking about… • micro
architecture • slicing howtos -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM We’re not talking about… • micro
architecture • slicing howtos • communication protocol -
System A System B Persistence Logic UI System C Logic
UI Persistence Logic -
System A System B Persistence Logic UI System C HTTP
HTTP Logic UI Persistence Logic -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Challenges • distributed configuration • service
registration & discovery -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Challenges • distributed configuration • service
registration & discovery • resilience -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Challenges • distributed configuration • service
registration & discovery • resilience • fast, automated deployment -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Challenges • distributed configuration • service
registration & discovery • resilience • fast, automated deployment • metrics -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Dropwizard • Glue Code for well
known libraries • Java -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Dropwizard libraries • Jetty • Jersey
• Metrics • Jackson • Guava • Logback • Hibernate Validator • Apache Http Client • JDBI • Liquibase • Freemarker & Mustache • Joda -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot • convention over configuration
approach • Java, Groovy or Scala -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot • convention over configuration
approach • Java, Groovy or Scala • self-contained jar or war -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot • convention over configuration
approach • Java, Groovy or Scala • self-contained jar or war • tackles dependency-hell via pre-packaging -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Cloud • new umbrella project
for cloud connectors -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Cloud • new umbrella project
for cloud connectors • On top of Spring Boot -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Cloud • new umbrella project
for cloud connectors • On top of Spring Boot • config server for distributed configuration -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Cloud • new umbrella project
for cloud connectors • On top of Spring Boot • config server for distributed configuration • Eureka annotations for service-discovery -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Cloud • new umbrella project
for cloud connectors • On top of Spring Boot • config server for distributed configuration • Eureka annotations for service-discovery • Hystrix annotations for resilience enhancements -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Play 2 • Java or Scala
• based on Akka • strong async support -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @Path ("/cart") public class ShoppingCartResource {
@GET @Path("{id}") @Timed @Produces(MediaType.TEXT_HTML) public ShoppingCartView shoppingCart(@PathParam("id") String cartId, @Context UriInfo uriInfo) { ImmutableList<String> urls = dao.findItemsForCartId(cartId); ImmutableList<Product> products = catalog.resolveProducts(urls); return new ShoppingCartView(products, uriInfo.getAbsolutePath().toString() ); } ... } Dropwizard -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @Controller @RequestMapping(value = ORDERS_RESOURCE) public class
OrderController { private @Autowired CounterService counterService; @RequestMapping(method = RequestMethod.POST) public String checkoutCart(@RequestParam(value = "products") List<String> productUris) { counterService.increment("checkouts.withproducts." + productUris.size()); // save products as an order return "redirect:" ORDERS_RESOURCE + order.getId(); // like “/order/123” } } Spring Boot -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM # Routes # List of all
books GET / controllers.BooksController.books # A specific Book GET /books/:id controllers.BooksController.book(id:String) Play -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM object BooksController extends Controller { def
books = Action.async { implicit request => val bestsellerFuture = Bestseller.getBestseller val booksFuture = Books.books for { bestseller <- bestsellerFuture books <- booksFuture } yield { Ok(views.html.books(books.values.toList, bestseller)) } } ... } Play -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot @ComponentScan @EnableAutoConfiguration public class
OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot @ComponentScan @EnableAutoConfiguration public class
OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot • opinionated preset @ComponentScan
@EnableAutoConfiguration public class OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot • opinionated preset •
HTTP resource “/env” shows all properties @ComponentScan @EnableAutoConfiguration public class OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot • opinionated preset •
HTTP resource “/env” shows all properties • overwrite via application.properties or CLI parameter @ComponentScan @EnableAutoConfiguration public class OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Play - Typesafe Config • Config
Library used by akka, play and other -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Play - Typesafe Config • Config
Library used by akka, play and other • HOCON - JSON Data Model + syntactic sugar -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Play - Typesafe Config • Config
Library used by akka, play and other • HOCON - JSON Data Model + syntactic sugar • override via system property -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Play - Typesafe Config • Config
Library used by akka, play and other • HOCON - JSON Data Model + syntactic sugar • override via system property • rich merge and include possibilities -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM public Product resolveProduct(String url) { Product
product = client.resource(url) .accept(MediaType.APPLICATION_JSON).get(Product.class); return product; } Dropwizard -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM public Product resolveProduct(String url) { final
RestTemplate restTemplate = new RestTemplate(); return restTemplate.getForEntity(url, Product.class); } Spring Boot -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @ComponentScan @EnableAutoConfiguration @EnableEurekaServer public class EurekaServerApp
{ public static void main(String[] args) { SpringApplication.run(EurekaServerApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @ComponentScan @EnableAutoConfiguration @EnableEurekaServer public class EurekaServerApp
{ public static void main(String[] args) { SpringApplication.run(EurekaServerApp.class, args); } } @ComponentScan @EnableAutoConfiguration @EnableEurekaClient public class OrdersApp { public static void main(String[] args) { SpringApplication.run(OrdersApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @ComponentScan @EnableAutoConfiguration @EnableEurekaServer public class EurekaServerApp
{ public static void main(String[] args) { SpringApplication.run(EurekaServerApp.class, args); } } @ComponentScan @EnableAutoConfiguration @EnableEurekaClient public class OrdersApp { public static void main(String[] args) { SpringApplication.run(OrdersApp.class, args); } } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Resilience • isolate Failure • apply
graceful degradation • be responsive in case of failure -
> Provides Command-oriented Integration of Services > Introduces Circuit Breaker,
Bulkheads and Isolation > Decouples from Service-dependencies > Provides metrics-facility to protect from failures -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM public class CommandInDropwizard extends TenacityCommand<Product> {
@Override protected Product run() throws Exception { Product product = client.resource(url) .accept(MediaType.APPLICATION_JSON).get(Product.class); return product; } protected Product getFallback() { return FALLBACK_PRODUCT } } Hystrix & Dropwizard -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM ResolveProductCommand command = new ResolveProductCommand(client, url);
Product product = command.execute(); Hystrix & Dropwizard -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @HystrixCommand(fallbackMethod = “fallbackProduct") private Pair<String, ResponseEntity<Product>>
resolveProduct(String productUri) { final RestTemplate restTemplate = new RestTemplate(); return new Pair(productUri, restTemplate.getForEntity(productUri, Product.class)); } private Pair<String, ResponseEntity<Product>> fallbackProduct(String productUri) { final Product product = new Product(productUri, null, BigDecimal.ZERO); final ResponseEntity<Product> response = new ResponseEntity<Product>(product, PARTIAL_CONTENT); return new Pair(productUri, response); } Spring Cloud Hystrix -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @HystrixCommand(fallbackMethod = “fallbackProduct") private Pair<String, ResponseEntity<Product>>
resolveProduct(String productUri) { final RestTemplate restTemplate = new RestTemplate(); return new Pair(productUri, restTemplate.getForEntity(productUri, Product.class)); } private Pair<String, ResponseEntity<Product>> fallbackProduct(String productUri) { final Product product = new Product(productUri, null, BigDecimal.ZERO); final ResponseEntity<Product> response = new ResponseEntity<Product>(product, PARTIAL_CONTENT); return new Pair(productUri, response); } Spring Cloud Hystrix wrapped in command -> async! -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @HystrixCommand(fallbackMethod = “fallbackProduct") private Pair<String, ResponseEntity<Product>>
resolveProduct(String productUri) { final RestTemplate restTemplate = new RestTemplate(); return new Pair(productUri, restTemplate.getForEntity(productUri, Product.class)); } private Pair<String, ResponseEntity<Product>> fallbackProduct(String productUri) { final Product product = new Product(productUri, null, BigDecimal.ZERO); final ResponseEntity<Product> response = new ResponseEntity<Product>(product, PARTIAL_CONTENT); return new Pair(productUri, response); } Spring Cloud Hystrix -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @HystrixCommand(fallbackMethod = “fallbackProduct") private Pair<String, ResponseEntity<Product>>
resolveProduct(String productUri) { final RestTemplate restTemplate = new RestTemplate(); return new Pair(productUri, restTemplate.getForEntity(productUri, Product.class)); } private Pair<String, ResponseEntity<Product>> fallbackProduct(String productUri) { final Product product = new Product(productUri, null, BigDecimal.ZERO); final ResponseEntity<Product> response = new ResponseEntity<Product>(product, PARTIAL_CONTENT); return new Pair(productUri, response); } Spring Cloud Hystrix -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM @HystrixCommand(fallbackMethod = “fallbackProduct") private Pair<String, ResponseEntity<Product>>
resolveProduct(String productUri) { final RestTemplate restTemplate = new RestTemplate(); return new Pair(productUri, restTemplate.getForEntity(productUri, Product.class)); } private Pair<String, ResponseEntity<Product>> fallbackProduct(String productUri) { final Product product = new Product(productUri, null, BigDecimal.ZERO); final ResponseEntity<Product> response = new ResponseEntity<Product>(product, PARTIAL_CONTENT); return new Pair(productUri, response); } Spring Cloud Hystrix method reference? :( -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM val apiUrl = "..." val breaker
= CircuitBreaker(Akka.system().scheduler, maxFailures = 5, callTimeout = 2.seconds, resetTimeout = 1.minute) def getBestseller : Future[List[Bestseller]] = { breaker.withCircuitBreaker( WS.url(apiUrl).get.map { response => response.json.as[List[Bestseller]] }).recover { case e => List() } } Play - Circuit Breaker -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot - Packaging ./gradlew distZip
./gradlew build ZIP + shell-script -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot - Packaging ./gradlew distZip
./gradlew build ZIP + shell-script executable JAR -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Play - Packaging sbt dist sbt
debian:packageBin sbt rpm:packageBin -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Dropwizard • “Metrics” Integrated with Dropwizard
• @Timed on Resources • HTTP Client is already instrumented • JVM Data -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM "cart.resources.ShoppingCartResource.shoppingCart": { "count": 22, "max": 0.136162,
"mean": 0.01208109090909091, "min": 0.00093, "p50": 0.008174500000000001, "p75": 0.011782250000000001, "p95": 0.11783499999999976, "p98": 0.136162, "p99": 0.136162, "p999": 0.136162, "stddev": 0.02813530239821426, "m15_rate": 1.8524577712890011, "m1_rate": 0.18057796798879996, "m5_rate": 1.315746847992022, "mean_rate": 0.133050618509084, "duration_units": "seconds", "rate_units": "calls/second" } "org.apache.http.client.HttpClient.cart.get-requests": { "count": 11, "max": 0.062107, "mean": 0.013355909090909092, "min": 0.005750000000000001, "p50": 0.009454, "p75": 0.010427, "p95": 0.062107, "p98": 0.062107, "p99": 0.062107, "p999": 0.062107, "stddev": 0.016285873488729705, "m15_rate": 0, "m1_rate": 0, "m5_rate": 0, "mean_rate": 2.9714422786532126, "duration_units": "seconds", "rate_units": "calls/second" } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Dropwizard Metrics • Exposed over HTTP
(as Json) • Exposed as jmx • Others available: stdout, csv, slf4j, ganglia, graphite -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Spring Boot Metrics • Prepackaged Spring
Boot starter module • enables HTTP resources for metrics • configurable via application.properties http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#production-ready -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Using a counter metric in your
Java code… …will display it in the /metrics JSON counterService.increment("checkouts.withproducts." + productUris.size()); GET /metrics HTTP/1.1 { "counter.checkouts.withproducts.3": 4, ... } -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Take Aways • MicroServices aren’t micro!
• Only “micro” regarding business scope -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Take Aways • MicroServices aren’t micro!
• Only “micro” regarding business scope • micro services = distributed systems deep dive -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Take Aways • MicroServices aren’t micro!
• Only “micro” regarding business scope • micro services = distributed systems deep dive • no framework is a silver bullet -
@goldstift @eigenbrodtm #DV14 #MicroservicesJVM Thanks for your attention • Questions?
Martin Eigenbrodt, @eigenbrodtm Alexander Heusingfeld, @goldstift https://www.innoq.com/en/timeline/