Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Micronaut with Kotlin Coroutines

Mohit S
February 03, 2021

Micronaut with Kotlin Coroutines

Learn how to build microservices with Micronaut.

Mohit S

February 03, 2021
Tweet

More Decks by Mohit S

Other Decks in Programming

Transcript

  1. Mohit Sarveiya Micronaut with Kotlin Coroutines @heyitsmohit

  2. Micronaut with Kotlin Coroutines • Building REST API • Coroutines

    • Data Access • Circuit breakers • Load Balancing
  3. What is Micronaut? • JVM Framework for building microservices •

    Created by Graham Rose • Polygot - Java, Kotlin or Groovy
  4. Benefits • Fast Startup & Small Memory • Meta programming

    • Reactive • Ahead-of-Time Compilation (AOT)
  5. Building Micronaut Project • CLI tool • Intellij • Micronaut

    Launch
  6. Project Creation CLI Tool • Download via SDKMAN • Create

    scaffolding for app • Create controllers, beans or clients
  7. Create Hello World App mn create-app

  8. Create Hello World App mn create-app • create-cli-app • create-grpc-app

    • create-messaging-app
  9. Create Hello World App mn create-app -l kotlin • Kotlin

    • Java • Groovy
  10. Create Hello World App mn create-app -l kotlin com.learn.micronautapp Package

    name
  11. Create Hello World App micronautapp src build.gradle Gradle project D

    Dockerfile
  12. Create Hello World App micronautapp src build.gradle D Dockerfile Application.kt

  13. Start App fun main(args: Array<String>) { build() .args(*args) .packages("com.learn") .start()

    }
  14. Start App fun main(args: Array<String>) { build() .args(*args) .packages("com.learn") .start()

    }
  15. Hello World REST API GET /greet/{name}

  16. Hello World REST API GET /greet/{name} Hello {name}

  17. Create Controller mn create-controller

  18. Create Controller mn create-controller com.learn.micronautapp.Greeting

  19. Create Controller mn create-controller com.learn.micronautapp.Greeting | Rendered controller to src/

    ... /GreetingController.kt | Rendered test to src/…/GreetingControllerTest.kt
  20. Create Controller mn create-controller com.learn.micronautapp.Greeting | Rendered controller to src/

    .
  21. Testing @MicronautTest class GreetingControllerTest { 
 }

  22. Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 }

  23. Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Client("/") interface

    GreetingClient { } }
  24. Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Client("/") interface

    GreetingClient { @Get("/greet/{name}") fun greetings(name: String): String } }
  25. Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Inject lateinit

    var greetingClient: GreetingClient }
  26. Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Test fun

    `should get greeting`() { val greeting = greetingClient.greetings("Micronaut") } }
  27. Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Test fun

    `should get greeting`() { val greeting = greetingClient.greetings("Micronaut") greeting shouldEqual "Greetings Micronaut" } }
  28. Controller @Controller class GreetingController { 
 }

  29. Controller @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) }

  30. Controller @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) }

  31. Controller @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) fun greet(name: String):

    String { } }
  32. Controller @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) fun greet(name: String):

    String { return "Greetings $name" } }
  33. GET http: // localhost:8080/greet/micronaut

  34. GET http: // localhost:8080/greet/micronaut Port

  35. Services HTTP/1.1 200 OK 
 
 content-type: text/plain 
 


    content-length: 19 
 
 connection: keep-alive 
 
 Greetings micronaut 
 GET http: // localhost:8080/greet/micronaut
  36. Summary • Create Micronaut Project • Create controller 
 •

    Unit testing API 

  37. Coroutine Support • Suspending functions • Scopes • Dispatchers 


    • Flows 

  38. Coroutine Support Micronaut Core RxJava Coroutines

  39. Using Coroutines @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) fun greet(name:

    String): String { return "Greetings $name" } }
  40. Using Coroutines @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) suspend fun

    greet(name: String): String { return "Greetings $name" } }
  41. Coroutines Support class Router { 
 Flowable.defer(() -> { if

    (isKotlinSuspendingFunction) { return executeKotlinSuspendingFunction( ... ); } else { ... } }) }
  42. Coroutines Support class Router { 
 Flowable.defer(() -> { if

    (isKotlinSuspendingFunction) { return executeKotlinSuspendingFunction( ... ); } else { ... } }) }
  43. Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { }

  44. Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {

    } }
  45. Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {

    if (isKotlinFunctionReturnTypeUnit) { } else { } } }
  46. Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {

    if (isKotlinFunctionReturnTypeUnit) { return Completable.fromPublisher( ... ).toFlowable(); } else { } } }
  47. Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {

    if (isKotlinFunctionReturnTypeUnit) { return Completable.fromPublisher( ... ).toFlowable(); } else { return Publishers.fromCompletableFuture(…); } } }
  48. Coroutine Support • Suspending functions • Dispatchers • Scope 


    • Flows 

  49. Using Dispatcher @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) fun greet(name:

    String): String { return "Greetings $name" } } Use a dispatcher
  50. Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { 
 }

  51. Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { 
 val

    coroutineDispatcher: CoroutineDispatcher init { coroutineDispatcher = executor.asCoroutineDispatcher() } }
  52. Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { 
 @Get(uri="/greet/{name}")

    suspend fun greetings(name: String) = 
 withContext(coroutineDispatcher) { } }
  53. Coroutine Support • Suspending functions • Dispatchers • Scope 


    • Flows 

  54. Creating Scope @Controller class GreetingController(val executor: ExecutorService) { 
 @Get(uri="/greet/{name}")

    suspend fun greetings(name: String) = coroutineScope { } }
  55. Coroutine Support • Suspending functions • Dispatchers • Scope 


    • Flows 

  56. Flows Server Client

  57. Flows GET /stream

  58. Flows @Controller class GreetingController { 
 @Get(uri=“/stream“) fun stream(): Flow<Int>

    }
  59. Flows @Controller class GreetingController { 
 @Get(uri=“/stream“) fun stream(): Flow<Int>

    = flowOf(1,2,3) .onEach { delay(2000) } }
  60. GET http: // localhost:8080/stream

  61. GET http: // localhost:8080/stream Services [ {} ]

  62. Kotlin / kotlinx.coroutines Code ! Issues Pull Requests kotlinx.coroutines Modules

    • Reactive • RxJava 2.x • RxJava 3.x
  63. Coroutine Support Micronaut Core RxJava Coroutines

  64. GET http: // localhost:8080/stream Services HTTP/1.1 200 OK transfer-encoding: chunked

    [ 1, 2, 3 ]
  65. Coroutine Support class FlowConverterRegistrar { @Override public void register( ...

    ) { addConverter( ... , flow -> Flowable.fromPublisher(ReactiveFlowKt.asPublisher(flow)) ); } }
  66. Coroutine Support class FlowConverterRegistrar { @Override public void register( ...

    ) { addConverter( ... , flow -> Flowable.fromPublisher(ReactiveFlowKt.asPublisher(flow)) ); } }
  67. Coroutine Support • Suspending functions • Dispatchers • Scope 


    • Flows 

  68. Micronaut with Kotlin Coroutines • Building REST API • Coroutines

    • Data Access • Circuit breakers • Load Balancing
  69. Task REST API GET /tasks/list

  70. Task REST API GET /tasks/list GET /tasks/{taskId}

  71. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks

  72. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks {

    “id”: taskId “description”: “ ” }
  73. Task REST API GET /tasks/list GET /tasks/{taskId} PUT /tasks {

    “id”: taskId “description”: “ ” } POST /tasks
  74. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  75. Data Access Server DB

  76. Data Access Server DB Hibernate

  77. Data Access data class Task( val id: Long? = null,

    val description: String = "" )
  78. Data Access @Entity data class Task( )

  79. Data Access @Entity @Table(name = "task") data class Task( )

  80. Data Access data class Task( @Id val id: Long? =

    null, )
  81. Data Access data class Task( @Id @GeneratedValue(strategy = GenerationType.AUTO) val

    id: Long? = null, )
  82. Data Access data class Task( @Column(name = "description", nullable =

    false, unique = true) val description: String = "" )
  83. Data Access DB Task Repo

  84. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  85. Data Access class TasksRepository { }

  86. Data Access class TasksRepository(val entityManager: EntityManager) { }

  87. Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long):

    Task { } }
  88. Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long):

    Task { return entityManager.find(Task :: class.java, taskId) } }
  89. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  90. Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):

    Task { } }
  91. Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):

    Task { val task = Task(description = description) } }
  92. Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):

    Task { val task = Task(description = description) entityManager.persist(task) return task } }
  93. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  94. Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,

    description: String): Int { } }
  95. Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,

    description: String): Int { val query = "UPDATE Task t SET description = :description where taskId = :taskId" } }
  96. Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,

    description: String): Int { return entityManager.createQuery(sql) }
  97. Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,

    description: String): Int { return entityManager.createQuery(sql) .setParameter(”taskId", taskId) .setParameter(“description", description) .executeUpdate() }
  98. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  99. Data Access class TasksRepository(val entityManager: EntityManager) { fun delete(taskId: Long)

    { val task = findById(taskId) task ?. let { entityManager.remove(it) } } }
  100. Data Access DB Task Repo Controller

  101. Controller class TaskController { }

  102. Controller class TaskController(private val taskRepository: TaskRepository) { }

  103. Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { }

  104. Controller @ExecuteOn(TaskExecutors.IO) @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { }

  105. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  106. Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Get("/{taskId}") fun

    getTask(taskId: Long): Task? { return taskRepository.findById(taskId) } }
  107. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  108. Data Access data class TaskSaveCommand( val description: String )

  109. Data Access Controller Client

  110. Data Access Controller Client “Task 1”

  111. Data Access Controller Client “ ”

  112. Data Access Controller Client Validation

  113. Data Access data class TaskSaveCommand( @field:NotBlank val description: String )

  114. Data Access @Introspected data class TaskSaveCommand( @field:NotBlank val description: String

    )
  115. Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post

    suspend fun save(@Body command: TaskSaveCommand) { } }
  116. Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post

    suspend fun save(@Body @Valid command: TaskSaveCommand) { } }
  117. Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post

    suspend fun save(@Body @Valid command: TaskSaveCommand) { val task = taskRepository.save(command.description) } }
  118. HTTP Response Controller Client 201 OK

  119. Micronaut HTTP • Set status code • Update headers •

    Send errors
  120. Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post

    suspend fun save(@Body @Valid command: TaskSaveCommand) { val task = taskRepository.save(command.description) return HttpResponse.created(task) } }
  121. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  122. HTTP Response Controller Client Delete taskID

  123. HTTP Response Controller Client No content

  124. Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}")

    fun delete(taskId: Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } }
  125. Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT

    /tasks DELETE /tasks/taskId
  126. Hibernate In-memory Server DB

  127. @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId:

    Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } } Services org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.H2Dialect
  128. Tasks Project taskapp src build.gradle Configure app D Dockerfile application.yml

  129. Configure Database micronaut: application: name: complete datasources: default: url: {JDBC_URL:`

    ... `} username: ${JDBC_USER:sa} password: ${JDBC_PASSWORD:""} driverClassName: ${JDBC_DRIVER:org.h2.Driver}
  130. POST /tasks { “description”: “task1” }

  131. HTTP/1.1 201 OK { "id": 1, "description": "task1" } POST

    /tasks
  132. GET /tasks/list [ { "id": 1, "description": "task1" } ]

  133. Create Client Server Client

  134. CLI Client • Simple client to test APIs • Create

    Runnable 

  135. CLI App mn create-cli-app

  136. CLI App mn create-cli-app -l kotlin com.learn.cliapp

  137. interface TaskClient { 
 
 @Get("/task/list") fun getTasks(): List<Task> }

    CLI App
  138. interface TaskClient { 
 
 @Get("/task/list") fun getTasks(): List<Task> @Post("/task")

    fun save(description: String): Task } CLI App
  139. class TaskclientCommand : Runnable { @Inject lateinit var taskClient: TaskClient

    } CLI App
  140. class TaskclientCommand : Runnable { fun run() { taskClient.getTasks() .forEach

    { println(it) } } CLI App
  141. Summary • Setup Controller for REST API • HTTP Requests

    & Responses • Data Access • Create Client 

  142. Micronaut with Kotlin Coroutines • Building REST API • Coroutines

    • Data Access • Circuit breakers • Load Balancing
  143. Controller @Controller("/tasks") class TaskController(val taskRepository: TaskRepository) { @Get(“/list") fun list():

    List<Task> { } } Exception
  144. Circuit Breaker • # of Retries • Delay 


  145. @CircuitBreaker(delay="1s", attempts = "5") interface TaskClient { 
 } CLI

    App
  146. Micronaut with Kotlin Coroutines • Building REST API • Coroutines

    • Data Access • Circuit breakers • Load Balancing
  147. Load Balancing Server Client Server Server

  148. Tasks Project taskapp src build.gradle Configure app D Dockerfile application.yml

  149. Tasks Project consul: client: registration: enabled: true default-zone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"

  150. @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId:

    Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } } Services Registered service with Consul io.micronaut.runtime.Micronaut - Server Running: http: // localhost:44004
  151. Load Balancing Server Client Server Server

  152. Micronaut with Kotlin Coroutines • Building REST API • Coroutines

    • Data Access • Circuit breakers • Load Balancing
  153. Resources • Introduction to Micronaut • Graeme Rocher 
 •

    Talk on coroutines! 
 
 https: // codingwithmohit.com/talks/
  154. Thank You! www.codingwithmohit.com @heyitsmohit