Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Reactive microservices with Micronaut - Greach 2018

8,619 views

Published on

Micronaut is a modern, JVM-based, full stack microservices framework designed for building modular, easily testable microservice applications. Micronaut is developed by the creators of Grails and takes inspiration from lessons learnt over the years building real-world applications from monoliths to microservices using Spring, Spring Boot and Grails.

This session covers the current features of Particle for building microservices, such as:

– Dependency Injection and Inversion of Control (IoC).
– Configuration system.
– HTTP services.
– Cloud and serverless deployments.
– Management & Monitoring.

Published in: Software
  • Nice !! Download 100 % Free Ebooks, PPts, Study Notes, Novels, etc @ https://www.thesisscientist.com/top-30-sites-for-download-free-books-2018
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Reactive microservices with Micronaut - Greach 2018

  1. 1. Reactive microservices with Micronaut Álvaro Sánchez-Mariscal @alvaro_sanchez
  2. 2. About me — Developer since 2001. — Founded Salenda in 2007. — Grails user since v0.4. — Working @ OCI since 2015. — Father since 2017! ! @alvaro_sanchez
  3. 3. DISCLAIMER ! Work in progress
  4. 4. Introducing
  5. 5.  Introducing Micronaut The productivity of Grails with the performance of a compile-time, non-blocking framework. — Designed from the ground up with microservices and the cloud in mind. — Ultra-lightweight and Reactive (based on Netty) — Integrated AOP and Compile-Time DI for Java and Groovy. — HTTP Client & Server. @alvaro_sanchez
  6. 6. Reactive microservices — Any Reactive Streams implementation: — RxJava 2.x. — Reactor 3.x. — Akka. — Plain old java.util.concurrent.CompletableFuture. @alvaro_sanchez
  7. 7. Micronaut is small and fast! — JAR files: 8Mb (Java) / 12Mb (Groovy). — Spring+Groovy: 36MB / Grails: 27Mb. — Heap size: 7Mb (Java) / 19Mb (Groovy). — Spring+Groovy: 33Mb / Grails: 49Mb. — Startup time: ~1 second. — Spring / Grails: ~3-4 seconds. @alvaro_sanchez
  8. 8. Features
  9. 9. Inversion of Control Dependency Injection
  10. 10. Inversion of Control / Dependency Injection — Inspired from Spring. — Compile-time: no reflection, no runtime proxies. — JSR-330 (javax.inject) or Spring-like annotations. — Implemented via annotation processors (Java) and AST transformations (Groovy) that use ASM to generate bytecode. @alvaro_sanchez
  11. 11. interface Engine { String start() } @Singleton class V8Engine implements Engine { String start() { "Starting V8" } } @Singleton class Vehicle { final Engine engine @Inject Vehicle(Engine engine) { this.engine = engine } String start() { engine.start() // "Starting V8" } } @alvaro_sanchez
  12. 12. Implicit injection via constructor @Singleton class WeatherService {} @Controller('/weather') class WeatherController { final WeatherService weatherService WeatherController(WeatherService weatherService) { this.weatherService = weatherService } } @alvaro_sanchez
  13. 13. Injectable types — An Optional of a bean. If the bean doesn’t exist empty() is injected. Alternatively, use @Nullable. — An Iterable or subtype of Iterable (eg List, Collection etc). — A lazy Stream of beans. — A native array of beans of a given type (Engine[]). — A javax.inject.Provider if a circular dependency requires it. @alvaro_sanchez
  14. 14. Bean qualifiers @Singleton class V6Engine implements Engine { ... } @Singleton class V8Engine implements Engine { ... } //Vehicle constructor @Inject Vehicle(@Named('v8') Engine engine) { this.engine = engine } @alvaro_sanchez
  15. 15. Bean qualifiers @Qualifier @Retention(RUNTIME) @interface V8 { } //Vehicle constructor @Inject Vehicle(@V8 Engine engine) { this.engine = engine } @alvaro_sanchez
  16. 16. Refreshable beans @Refreshable class WeatherService { String forecast @PostConstruct void init() { forecast = "Scattered Clouds ${new Date().format('dd/MMM/yy HH:ss.SSS')}" } String latestForecast() { return forecast } } @alvaro_sanchez
  17. 17. Refreshable beans @Controller('/weather') class WeatherController { @Inject WeatherService weatherService ... } — Refreshable via: — The /refresh endpoint. — applicationContext.publishEvent(new RefreshEvent()) @alvaro_sanchez
  18. 18. Refreshing upon config changes @Bean(preDestroy = "close") @Refreshable('mongodb') MongoClient mongoClient(ReactiveMongoConfiguration rmc) { return MongoClients.create(rmc.buildSettings()) } @alvaro_sanchez
  19. 19.  Bean factories @Singleton class CrankShaft {} @Factory class EngineFactory { @Bean @Singleton Engine v8Engine(CrankShaft crankShaft) { new V8Engine(crankShaft) } } @alvaro_sanchez
  20. 20.  Conditional beans @Singleton @Requires(beans = DataSource) @Requires(property = "datasource.url") class JdbcBookService implements BookService { DataSource dataSource public JdbcBookService(DataSource dataSource) { this.dataSource = dataSource } } @alvaro_sanchez
  21. 21.  Conditional beans @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PACKAGE, ElementType.TYPE}) @Requires(beans = DataSource) @Requires(property = "datasource.url") @interface RequiresJdbc { } @RequiresJdbc class JdbcBookService implements BookService { ... } @alvaro_sanchez
  22. 22.  Supported conditions — @Requires(classes=javax.servlet.Servlet) — @Requires(beans=javax.sql.DataSource) — @Requires(env='TRAVIS') — @Requires(configuration='foo.bar') — @Requires(sdk=Sdk.JAVA, value="1.8") — @Requires(property='consul.enabled', value="true", defaultValue="false") @alvaro_sanchez
  23. 23.  Bean configurations — Grouping of multiple bean definitions within a package: //file: src/main/groovy/com/example/json/package-info.groovy @Configuration @Requires(classes = com.fasterxml.jackson.databind.ObjectMapper) package com.example.json — All the beans from the package can be required with @Requires(configuration='com.example.json') @alvaro_sanchez
  24. 24. Life-cycle methods and events — javax.annotation.PostConstruct is supported. — For events: @Singleton class EngineInitializer implements BeanInitializedEventListener<EngineFactory> { @Override EngineFactory onInitialized(BeanInitializingEvent<EngineFactory> event) { EngineFactory engineFactory = event.bean engineFactory.rodLength = 6.6 return event.bean } } @alvaro_sanchez
  25. 25. Application configuration
  26. 26. PropertySources — Command line arguments. — Properties from SPRING_APPLICATION_JSON (for Spring compatibility). — Properties from MICRONAUT_APPLICATION_JSON. — Java System Properties. — OS environment variables. — Enviroment-specific properties from application-{environment}. {extension}. — (Either .properties, .json, .yml or .groovy formats supported) — Application-specific properties from application.{extension}. @alvaro_sanchez
  27. 27.  Injecting configuration values @Singleton class EngineImpl implements Engine { @Value('${my.engine.cylinders:6}') int cylinders } @alvaro_sanchez
  28. 28.  @EachProperty @EachProperty("myapp.datasources") public class DataSourceConfiguration { final String name final URI url } //src/main/resources/application.properties myapp.datasources.one = "jdbc:mysql://localhost/one" myapp.datasources.two = "jdbc:mysql://localhost/two" @alvaro_sanchez
  29. 29.  @EachBean @Factory class DataSourceFactory { @EachBean(DataSourceConfiguration) DataSource dataSource(DataSourceConfiguration cfg) { URI url = cfg.url return new DataSource(url) } } @alvaro_sanchez
  30. 30.  Type-safe @ConfigurationProperties @ConfigurationProperties('my.engine') class EngineConfig { @NotBlank String manufacturer = "Ford" @Min(1L) int cylinders CrankShaft crankShaft = new CrankShaft() @ConfigurationProperties('crank-shaft') static class CrankShaft { Optional<Double> rodLength = Optional.empty() } } @alvaro_sanchez
  31. 31.  Type-safe @ConfigurationProperties #src/main/resources/application.yml my: engine: manufacturer: Subaru cylinders: 4 crank-shaft: rodLength: 4 @alvaro_sanchez
  32. 32.  Type-safe @ConfigurationProperties — Simple injection: @Singleton class EngineImpl implements Engine { final EngineConfig config EngineImpl(EngineConfig config) { this.config = config } } @alvaro_sanchez
  33. 33. AOP
  34. 34. Micronaut's AOP — Around Advice - decorates a method or class. — Trace logging. — Hystrix support. — Cache abstraction. — Circuit breaker pattern with retry support. — Validation. — Introduction Advice - introduces new behaviour to a class. — HTTP client. @alvaro_sanchez
  35. 35. Cache abstraction @Singleton @CacheConfig("pets") class PetService { @Cacheable Pet findById(Long id) { ... } @CachePut void save(Pet pet) { ... } @CacheInvalidate void delete(Pet pet) { ... } } @alvaro_sanchez
  36. 36. HTTP Server
  37. 37. Hello World //src/main/groovy/example/Application.groovy package example import io.micronaut.runtime.Micronaut Micronaut.run(getClass()) @alvaro_sanchez
  38. 38. Hello World //src/main/groovy/example/HelloController.groovy @Controller("/") class HelloController { @Get("/hello/{name}") String hello(String name) { return "Hello $name!" } } @alvaro_sanchez
  39. 39. Hello World class HelloControllerSpec extends Specification { @Shared @AutoCleanup EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer) @Shared @AutoCleanup HttpClient client = HttpClient.create(embeddedServer.URL) void "test hello world response"() { expect: client.toBlocking() .retrieve(HttpRequest.GET('/hello/Greach')) == "Hello Greach!" } } @alvaro_sanchez
  40. 40. JUnit with Java test public class HelloWorldTest { private static EmbeddedServer server; private static HttpClient client; @BeforeClass public static void setupServer() { server = ApplicationContext.run(EmbeddedServer.class); client = server.getApplicationContext().createBean(HttpClient.class, server.getURL()); } @AfterClass public static void stopServer() { if(server != null) { server.stop(); } if(client != null) { client.stop(); } } @Test public void testHelloWorkd() throws Exception { String body = client.toBlocking().retrieve("/hello/Greach"); assertEquals(body, "Hello Greach!"); } } @alvaro_sanchez
  41. 41. Declarative routing @Get("/hello") String hello() { return "Hello World" } @Get String hello() { return "Hello World" } @alvaro_sanchez
  42. 42. Programmatic routing @Singleton class MyRoutes extends GroovyRouteBuilder { MyRoutes(ApplicationContext beanContext) { super(beanContext) } @Inject void bookResources(BookController bookController, AuthorController authorController) { GET(bookController) { POST("/hello{/message}", bookController.&hello) } GET(bookController, ID) { GET(authorController) } } } @alvaro_sanchez
  43. 43. Request binding — Body: String hello(@Body String body) — Cookies: String hello(@CookieValue String myCookie) — Headers: String hello(@Header String contentType) — Parameters: String hello(@Parameter String myParam) — Special cases: — String hello(@Header Optional<String> contentType) — String hello(@Parameter ZonedDateTime date) @alvaro_sanchez
  44. 44. Direct request/response manipulation @Get("/hello") HttpResponse<String> hello(HttpRequest<?> request) { String name = request.getParameters() .getFirst("name") .orElse("Nobody"); return HttpResponse.ok("Hello " + name + "!!") .header("X-My-Header", "Foo"); } @alvaro_sanchez
  45. 45. Reactive request processing @Post(consumes = MediaType.TEXT_PLAIN) Single<MutableHttpResponse<String>> echoFlow(@Body Flowable<String> text) { return text.collect(StringBuffer::new, StringBuffer::append) .map(buffer -> HttpResponse.ok(buffer.toString()) ); } @alvaro_sanchez
  46. 46. Reactive responses — Any type that implements the org.reactivestreams.Publisher: Flowable<String> hello() {} — A Java's CompletableFuture instance: CompletableFuture<String> hello() {} — An io.micronaut.http.HttpResponse and optional response body: HttpResponse<Flowable<String>> hello() {} @alvaro_sanchez
  47. 47. Non-reactive responses — Any implementation of CharSequence: String hello() — Any simple POJO type Book show() @alvaro_sanchez
  48. 48. Threading model The response type determines the thread pool used to execute the request: — Non-blocking requests will be executed in the Netty event loop thread pool. — Blocking requests will be executed on the I/O thread pool. @alvaro_sanchez
  49. 49. Reactive JSON parsing with Jackson @Post public Single<HttpResponse<Person>> save(@Body Single<Person> person) { return person.map(p -> { //do something blocking, and then: return HttpResponse.created(p); } ); } @alvaro_sanchez
  50. 50. Non-reactive JSON parsing If your method does not do any blocking I/O: @Post public HttpResponse<Person> save(@Body Person person) { //do something, and then: return HttpResponse.created(person); } @alvaro_sanchez
  51. 51. HTTP Client
  52. 52.  Basics //src/main/groovy/example/HelloController.groovy @Controller("/") class HelloController { @Get("/hello/{name}") String hello(String name) { return "Hello $name!" } } //src/test/groovy/example/HelloClient.groovy @Client('/') interface HelloClient { @Get("/hello/{name}") String hello(String name) } @alvaro_sanchez
  53. 53.  Testing it class HelloControllerSpec extends Specification { @Shared @AutoCleanup EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer) void "test hello world"() { given: HelloClient client = embeddedServer.applicationContext.getBean(HelloClient) expect: client.hello("Fred") == "Hello Fred!" } } @alvaro_sanchez
  54. 54.  Features — Service discovery aware: Consul and Eureka support. — Load balancing: round robin, Netflix's Ribbon. — Reactive: based on the return types. — Fault tolerant: retry, fallback, circuit breaker. @alvaro_sanchez
  55. 55. Fallback support @Validated interface PetOperations<T extends Pet> { @Get("/") Single<List<T>> list() } @Client(id = "pets", path = "/v1/pets") interface PetClient extends PetOperations<Pet> { @Get("/") Single<List<Pet>> findAll() } @Fallback class PetClientFallback implements PetOperations<Pet> { Single<List<Pet>> list() { Single.just([] as List<Pet>) } } @alvaro_sanchez
  56. 56. Retry / circuit breaker support @Client("/dodgy-api") @Retryable(attempts = '5', delay = '5ms') interface ApiClient { ... } @Singleton @CircuitBreaker(attempts = '5', delay = '5ms', reset = '300ms') class MyService { ... } @alvaro_sanchez
  57. 57. Retryable beans @Singleton class Neo4jDriverBuilder { @Retryable(ServiceUnavailableException) Driver buildDriver() { //build driver } } @alvaro_sanchez
  58. 58. Serverless
  59. 59. Simple functions as Groovy scripts @Field @Inject Twitter twitterClient @CompileStatic UpdateResult updateStatus(Message status) { Status s = twitterClient.updateStatus(status.text) URL url= new URL("https://twitter.com/$s.user.screenName/status/${s.id}") return new UpdateResult(url, s.createdAt.time) } @alvaro_sanchez
  60. 60. Function beans import java.util.function.Function @FunctionBean('book') class BookFunction implements Function<Book, Book> { @Override Book apply(Book book) { book.title = book.title.toUpperCase() return book } } @alvaro_sanchez
  61. 61. Function clients @FunctionClient interface TweetClient { Single<Result> updateStatus(String text) static class Result { URL url } } @alvaro_sanchez
  62. 62. Testing: directly void "run function directly"() { expect: new HelloWorldFunction() .hello(new Person(name: "Fred")) .text == "Hello Fred!" } @alvaro_sanchez
  63. 63. Testing: REST void "run function as REST service"() { given: EmbeddedServer server = ApplicationContext.run(EmbeddedServer) HelloClient client = server.getApplicationContext().getBean(HelloClient) when: Message message = client.hello("Fred").blockingGet() then: message.text == "Hello Fred!" } @alvaro_sanchez
  64. 64.  Testing: AWS Lambda void "run execute function as lambda"() { given: ApplicationContext applicationContext = ApplicationContext.run( 'aws.lambda.functions.hello.functionName':'hello-world', 'aws.lambda.region':'us-east-1' ) HelloClient client = applicationContext.getBean(HelloClient) when: Message message = client.hello("Fred").blockingGet() then: message.text == "Hello Fred!" } @alvaro_sanchez
  65. 65. Micronaut Petstore
  66. 66. Petstore architecture @alvaro_sanchez
  67. 67. Q & A Álvaro Sánchez-Mariscal @alvaro_sanchez

×