Ratpack and Grails 3 GR8Conf US 2014

25
Ratpack and Grails 3 Lari Hotari @lhotari Pivotal Software, Inc.

description

One of the goals of Grails 3 is to reach out of the servlet container. Grails 3 has a concept of application profiles for choosing a certain set of core plugins to use. In this talk Lari will present how Ratpack fits in Grails 3. He will also talk about how Grails 3 supports micro service architectures.

Transcript of Ratpack and Grails 3 GR8Conf US 2014

Page 1: Ratpack and Grails 3 GR8Conf US 2014

Ratpack and Grails 3

Lari Hotari @lhotariPivotal Software, Inc.

Page 2: Ratpack and Grails 3 GR8Conf US 2014

Agenda

• Grails 3 and Ratpack

• Why async?

• Modularity and micro service architectures

Page 3: Ratpack and Grails 3 GR8Conf US 2014

• Embrace Gradle

• Abstract packaging / deployment

• Reach outside the servlet container

• App profiles: Netty, Servlet, Batch, Hadoop

• Lightweight deployments, support micro services

Grails 3

Page 4: Ratpack and Grails 3 GR8Conf US 2014
Page 5: Ratpack and Grails 3 GR8Conf US 2014

Why Netty / Ratpack?

Why async?

Page 6: Ratpack and Grails 3 GR8Conf US 2014

Amdahl's law

Page 7: Ratpack and Grails 3 GR8Conf US 2014

Little's law

• What does Little's law tell us when we are using the thread-per-request model?

MeanNumberInSystem = MeanThroughput * MeanResponseTime

MeanThroughput = MeanNumberInSystem / MeanResponseTime

L = λW

Page 8: Ratpack and Grails 3 GR8Conf US 2014

Programming model• Declarative programming expresses the

logic of a computation without describing its control flow.

• It's programming without the call stack, the programmer doesn't decide execution details.

• Examples: functional and reactive programming, event / message based execution, distributed parallel computation algorithms like Map/Reduce

Page 9: Ratpack and Grails 3 GR8Conf US 2014

1 import org.pac4j.core.profile.UserProfile 2 import org.pac4j.http.client.FormClient 3 import org.pac4j.http.credentials.SimpleTestUsernamePasswordAuthenticator 4 import ratpack.codahale.metrics.CodaHaleMetricsModule 5 import ratpack.codahale.metrics.HealthCheckHandler 6 import ratpack.codahale.metrics.MetricsWebsocketBroadcastHandler 7 import ratpack.example.books.* 8 import ratpack.form.Form 9 import ratpack.groovy.sql.SqlModule 10 import ratpack.hikari.HikariModule 11 import ratpack.hystrix.HystrixRatpack 12 import ratpack.jackson.JacksonModule 13 import ratpack.pac4j.Pac4jModule 14 import ratpack.remote.RemoteControlModule 15 import ratpack.rx.RxRatpack 16 import ratpack.session.SessionModule 17 import ratpack.session.store.MapSessionsModule 18 import ratpack.session.store.SessionStorage 19 20 import static ratpack.groovy.Groovy.groovyTemplate 21 import static ratpack.groovy.Groovy.ratpack 22 import static ratpack.jackson.Jackson.json 23 import static ratpack.pac4j.internal.SessionConstants.USER_PROFILE 24 25 ratpack { 26 bindings { 27 bind DatabaseHealthCheck 28 add new CodaHaleMetricsModule().jvmMetrics().jmx().websocket(). 29 healthChecks() 30 add new HikariModule([URL: "jdbc:h2:mem:dev;INIT=CREATE SCHEMA IF NOT 31 EXISTS DEV"], "org.h2.jdbcx.JdbcDataSource") 32 add new SqlModule() 33 add new JacksonModule() 34 add new BookModule() 35 add new RemoteControlModule() 36 add new SessionModule() 37 add new MapSessionsModule(10, 5) 38 add new Pac4jModule<>(new FormClient("/login", new 39 SimpleTestUsernamePasswordAuthenticator()), new 40 AuthPathAuthorizer()) 41 42 init { BookService bookService -> 43 RxRatpack.initialize() 44 HystrixRatpack.initialize() 45 bookService.createTable() 46 } 47 } 48 49 handlers { BookService bookService -> 50 51 get { 52 bookService.all().toList().subscribe { List<Book> books -> 53 SessionStorage sessionStorage = request.get(SessionStorage) 54 UserProfile profile = sessionStorage.get(USER_PROFILE) 55 def username = profile?.getAttribute("username") 56 57 render groovyTemplate("listing.html", 58 username: username ?: "", 59 title: "Books", 60 books: books, 61 msg: request.queryParams.msg ?: "") 62 } 63 } 64 65 handler("create") { 66 byMethod { 67 get { 68 render groovyTemplate("create.html", title: "Create Book") 69 } 70 post { 71 Form form = parse(Form) 72 bookService.insert( 73 form.isbn, 74 form.get("quantity").asType(Long), 75 form.get("price").asType(BigDecimal) 76 ).single().subscribe { String isbn -> 77 redirect "/?msg=Book+$isbn+created" 78 } 79 } 80 } 81 } 82 83 handler("update/:isbn") { 84 def isbn = pathTokens["isbn"] 85 86 bookService.find(isbn).single().subscribe { Book book -> 87 if (book == null) { 88 clientError(404) 89 } else { 90 byMethod { 91 get { 92 render groovyTemplate("update.html", title: 93 "Update Book", book: book) 94 } 95 post { 96 Form form = parse(Form) 97 bookService.update( 98 isbn, 99 form.get("quantity").asType(Long),100 form.get("price").asType(BigDecimal)101 ) subscribe {102 redirect "/?msg=Book+$isbn+updated"103 }104 }105 }106 }107 }108 }109 110 post("delete/:isbn") {111 def isbn = pathTokens["isbn"]112 bookService.delete(isbn).subscribe {113 redirect "/?msg=Book+$isbn+deleted"114 }115 }116 117 prefix("api") {118 get("books") {119 bookService.all().toList().subscribe { List<Book> books ->120 render json(books)121 }122 }123 124 handler("book/:isbn?", registry.get(BookRestEndpoint))125 }126 127 prefix("admin") {128 get("health-check/:name?", new HealthCheckHandler())129 get("metrics-report", new MetricsWebsocketBroadcastHandler())130 131 get("metrics") {132 render groovyTemplate("metrics.html", title: "Metrics")133 }134 }135 136 handler("login") {137 render groovyTemplate("login.html", title: "Login", error: 138 request.queryParams.error ?: "")139 }140 141 assets "public"142 }143 144 }

1 import org.pac4j.core.profile.UserProfile 2 import org.pac4j.http.client.FormClient 3 import org.pac4j.http.credentials.SimpleTestUsernamePasswordAuthenticator 4 import ratpack.codahale.metrics.CodaHaleMetricsModule 5 import ratpack.codahale.metrics.HealthCheckHandler 6 import ratpack.codahale.metrics.MetricsWebsocketBroadcastHandler 7 import ratpack.example.books.* 8 import ratpack.form.Form 9 import ratpack.groovy.sql.SqlModule 10 import ratpack.hikari.HikariModule 11 import ratpack.hystrix.HystrixRatpack 12 import ratpack.jackson.JacksonModule 13 import ratpack.pac4j.Pac4jModule 14 import ratpack.remote.RemoteControlModule 15 import ratpack.rx.RxRatpack 16 import ratpack.session.SessionModule 17 import ratpack.session.store.MapSessionsModule 18 import ratpack.session.store.SessionStorage 19 20 import static ratpack.groovy.Groovy.groovyTemplate 21 import static ratpack.groovy.Groovy.ratpack 22 import static ratpack.jackson.Jackson.json 23 import static ratpack.pac4j.internal.SessionConstants.USER_PROFILE 24 25 ratpack { 26 bindings { 27 bind DatabaseHealthCheck 28 add new CodaHaleMetricsModule().jvmMetrics().jmx().websocket(). 29 healthChecks() 30 add new HikariModule([URL: "jdbc:h2:mem:dev;INIT=CREATE SCHEMA IF NOT 31 EXISTS DEV"], "org.h2.jdbcx.JdbcDataSource") 32 add new SqlModule() 33 add new JacksonModule() 34 add new BookModule() 35 add new RemoteControlModule() 36 add new SessionModule() 37 add new MapSessionsModule(10, 5) 38 add new Pac4jModule<>(new FormClient("/login", new 39 SimpleTestUsernamePasswordAuthenticator()), new 40 AuthPathAuthorizer()) 41 42 init { BookService bookService -> 43 RxRatpack.initialize() 44 HystrixRatpack.initialize() 45 bookService.createTable() 46 } 47 } 48 49 handlers { BookService bookService -> 50 51 get { 52 bookService.all().toList().subscribe { List<Book> books -> 53 SessionStorage sessionStorage = request.get(SessionStorage) 54 UserProfile profile = sessionStorage.get(USER_PROFILE) 55 def username = profile?.getAttribute("username") 56 57 render groovyTemplate("listing.html", 58 username: username ?: "", 59 title: "Books", 60 books: books, 61 msg: request.queryParams.msg ?: "") 62 } 63 } 64 65 handler("create") { 66 byMethod { 67 get { 68 render groovyTemplate("create.html", title: "Create Book") 69 } 70 post { 71 Form form = parse(Form) 72 bookService.insert( 73 form.isbn, 74 form.get("quantity").asType(Long), 75 form.get("price").asType(BigDecimal) 76 ).single().subscribe { String isbn -> 77 redirect "/?msg=Book+$isbn+created" 78 } 79 } 80 } 81 } 82 83 handler("update/:isbn") { 84 def isbn = pathTokens["isbn"] 85 86 bookService.find(isbn).single().subscribe { Book book -> 87 if (book == null) { 88 clientError(404) 89 } else { 90 byMethod { 91 get { 92 render groovyTemplate("update.html", title: 93 "Update Book", book: book) 94 } 95 post { 96 Form form = parse(Form) 97 bookService.update( 98 isbn, 99 form.get("quantity").asType(Long),100 form.get("price").asType(BigDecimal)101 ) subscribe {102 redirect "/?msg=Book+$isbn+updated"103 }104 }105 }106 }107 }108 }109 110 post("delete/:isbn") {111 def isbn = pathTokens["isbn"]112 bookService.delete(isbn).subscribe {113 redirect "/?msg=Book+$isbn+deleted"114 }115 }116 117 prefix("api") {118 get("books") {119 bookService.all().toList().subscribe { List<Book> books ->120 render json(books)121 }122 }123 124 handler("book/:isbn?", registry.get(BookRestEndpoint))125 }126 127 prefix("admin") {128 get("health-check/:name?", new HealthCheckHandler())129 get("metrics-report", new MetricsWebsocketBroadcastHandler())130 131 get("metrics") {132 render groovyTemplate("metrics.html", title: "Metrics")133 }134 }135 136 handler("login") {137 render groovyTemplate("login.html", title: "Login", error: 138 request.queryParams.error ?: "")139 }140 141 assets "public"142 }143 144 }

source: https://github.com/ratpack/example-books/blob/master/src/ratpack/Ratpack.groovy

Ratpack application consists of

functional handler chains

Page 10: Ratpack and Grails 3 GR8Conf US 2014

Ratpack applications• Ratpacks comes with Guice for dependency

injection

• Guice modules are also used as the plugin system for Ratpack

• Examples of Ratpack module contributions:

• Integrations to RxJava and Reactor. Can be used for async composition and preventing "callback hell".

• Integration to Netflix Hystrix for adding error resilience functionality . f.e., Circuit-breaker pattern impl.

Page 11: Ratpack and Grails 3 GR8Conf US 2014

Demo Ratpack and Grails (GORM) used

together

• https://github.com/lhotari/ratpack-gorm-example

• Spring Boot embedded in Ratpack, running GORM

Page 12: Ratpack and Grails 3 GR8Conf US 2014
Page 13: Ratpack and Grails 3 GR8Conf US 2014

Modularity

• logical partitioning of the "software design"

• allows complex software to be manageable for the purpose of implementation and maintenance

Page 14: Ratpack and Grails 3 GR8Conf US 2014

Coupling and Cohesion

• Coupling and cohesion are measures for describing how easy it will be to change the behaviour of some element in a system

• Modules are coupled if a change in one forces a change in a the other

• A module's cohesion is a measure of whether it's responsibilities form a meaningful unit

source: GOOS book

Page 15: Ratpack and Grails 3 GR8Conf US 2014

• Low coupling between modules ⟹ easier to change

• High cohesion within module ⟹ single responsibility

Page 16: Ratpack and Grails 3 GR8Conf US 2014

Microservice definitionby James Lewis

• Each application only does one thing

• Small enough to fit in your head

• Small enough that you can throw them away

• Embedded web container

• Packaged as a single executable jar

• Use HTTP and HATEOAS to decouple services

• Each app exposes metrics about itself

Page 17: Ratpack and Grails 3 GR8Conf US 2014

–Arnon Rotem-Gal-Oz, Practical SOA

“Nanoservice is an Anti-pattern where a service is too fine grained. Nanoservice

is a service whose overhead (communications, maintenance etc.)

out-weights its utility.”

Page 18: Ratpack and Grails 3 GR8Conf US 2014

Polyglot persistence• Common principle is that each service owns it's

data - there is no shared database across multiple services.

• If this principle is followed, it usually means switching to Hexagonal architecture, where persistence is an integration and not part of the core.

• "Start with the events and behaviour instead of the database."

• Data consistency models in distributed systems

Page 19: Ratpack and Grails 3 GR8Conf US 2014

Brooks: "No silver bullet"

• Essential complexity

• complexity that you cannot escape

• Accidental complexity

• we could be adding complexity by bad design

Page 20: Ratpack and Grails 3 GR8Conf US 2014

- Google's "Solve for X"

“You don't spend your time being bothered that you can't teleport from here to Japan,

because there's a part of you that thinks it's impossible.

Moonshot thinking is choosing to be bothered by that.”

Page 21: Ratpack and Grails 3 GR8Conf US 2014

Modular monoliths• Modular monoliths are composed of loosely

coupled modules of single responsibility

• Enabling the 3rd way (after monoliths and microservices) for building applications on the JVM across different libraries and frameworks

• Modules can be turned into true micro services when needed - instead of introducing accidental complexity to projects that don't really require micro services in the beginning, but could benefit of them later

Page 22: Ratpack and Grails 3 GR8Conf US 2014

The monoliths in the micro services architecture

Single Page App in

Browser

API Gatewayservice

µservice A

SAAS Service A

SAAS Service B

µservice B

µservice C

µservice D

µservice E

µservice F

Page 23: Ratpack and Grails 3 GR8Conf US 2014

"If you built it..."

Page 24: Ratpack and Grails 3 GR8Conf US 2014

 pretotyping.org

“Make sure you are building the right it before you build it

right."

"Fail fast ... and Often"

Page 25: Ratpack and Grails 3 GR8Conf US 2014

Thanks!

Lari Hotari @lhotariPivotal Software, Inc.