Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Micronaut with Kotlin Coroutines
Search
Mohit S
February 03, 2021
Programming
4
2.2k
Micronaut with Kotlin Coroutines
Learn how to build microservices with Micronaut.
Mohit S
February 03, 2021
Tweet
Share
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
180
Building Shared UIs across Platforms with Compose
heyitsmohit
1
560
Building Multiplatform Apps with Compose
heyitsmohit
2
430
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.7k
Building Android Testing Infrastructure
heyitsmohit
1
400
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
710
Using Square Workflow for Android & iOS
heyitsmohit
1
390
Building Android Infrastructure Teams at Scale
heyitsmohit
3
290
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
510
Other Decks in Programming
See All in Programming
Оптимизируем производительность блока Казначейство
lamodatech
0
950
はてなにおけるfujiwara-wareの活用やecspressoのCI/CD構成 / Fujiwara Tech Conference 2025
cohalz
3
2.8k
2025.01.17_Sansan × DMM.swift
riofujimon
2
560
React 19でお手軽にCSS-in-JSを自作する
yukukotani
5
570
BEエンジニアがFEの業務をできるようになるまでにやったこと
yoshida_ryushin
0
200
毎日13時間もかかるバッチ処理をたった3日で60%短縮するためにやったこと
sho_ssk_
1
550
Асинхронность неизбежна: как мы проектировали сервис уведомлений
lamodatech
0
1.3k
どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか
okashoi
3
870
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
6
1.4k
ATDDで素早く安定した デリバリを実現しよう!
tonnsama
1
1.9k
ISUCON14感想戦で85万点まで頑張ってみた
ponyo877
1
590
Jaspr Dart Web Framework 박제창 @Devfest 2024
itsmedreamwalker
0
150
Featured
See All Featured
Designing on Purpose - Digital PM Summit 2013
jponch
116
7.1k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Facilitating Awesome Meetings
lara
51
6.2k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.8k
Mobile First: as difficult as doing things right
swwweet
222
9k
How GitHub (no longer) Works
holman
312
140k
The Power of CSS Pseudo Elements
geoffreycrofte
74
5.4k
The World Runs on Bad Software
bkeepers
PRO
66
11k
Statistics for Hackers
jakevdp
797
220k
A Tale of Four Properties
chriscoyier
157
23k
Designing for humans not robots
tammielis
250
25k
Transcript
Mohit Sarveiya Micronaut with Kotlin Coroutines @heyitsmohit
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
What is Micronaut? • JVM Framework for building microservices •
Created by Graham Rose • Polygot - Java, Kotlin or Groovy
Benefits • Fast Startup & Small Memory • Meta programming
• Reactive • Ahead-of-Time Compilation (AOT)
Building Micronaut Project • CLI tool • Intellij • Micronaut
Launch
Project Creation CLI Tool • Download via SDKMAN • Create
scaffolding for app • Create controllers, beans or clients
Create Hello World App mn create-app
Create Hello World App mn create-app • create-cli-app • create-grpc-app
• create-messaging-app
Create Hello World App mn create-app -l kotlin • Kotlin
• Java • Groovy
Create Hello World App mn create-app -l kotlin com.learn.micronautapp Package
name
Create Hello World App micronautapp src build.gradle Gradle project D
Dockerfile
Create Hello World App micronautapp src build.gradle D Dockerfile Application.kt
Start App fun main(args: Array<String>) { build() .args(*args) .packages("com.learn") .start()
}
Start App fun main(args: Array<String>) { build() .args(*args) .packages("com.learn") .start()
}
Hello World REST API GET /greet/{name}
Hello World REST API GET /greet/{name} Hello {name}
Create Controller mn create-controller
Create Controller mn create-controller com.learn.micronautapp.Greeting
Create Controller mn create-controller com.learn.micronautapp.Greeting | Rendered controller to src/
... /GreetingController.kt | Rendered test to src/…/GreetingControllerTest.kt
Create Controller mn create-controller com.learn.micronautapp.Greeting | Rendered controller to src/
.
Testing @MicronautTest class GreetingControllerTest { }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Client("/") interface
GreetingClient { } }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Client("/") interface
GreetingClient { @Get("/greet/{name}") fun greetings(name: String): String } }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Inject lateinit
var greetingClient: GreetingClient }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Test fun
`should get greeting`() { val greeting = greetingClient.greetings("Micronaut") } }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Test fun
`should get greeting`() { val greeting = greetingClient.greetings("Micronaut") greeting shouldEqual "Greetings Micronaut" } }
Controller @Controller class GreetingController { }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name: String):
String { } }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name: String):
String { return "Greetings $name" } }
GET http: // localhost:8080/greet/micronaut
GET http: // localhost:8080/greet/micronaut Port
Services HTTP/1.1 200 OK content-type: text/plain
content-length: 19 connection: keep-alive Greetings micronaut GET http: // localhost:8080/greet/micronaut
Summary • Create Micronaut Project • Create controller •
Unit testing API
Coroutine Support • Suspending functions • Scopes • Dispatchers
• Flows
Coroutine Support Micronaut Core RxJava Coroutines
Using Coroutines @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name:
String): String { return "Greetings $name" } }
Using Coroutines @Controller class GreetingController { @Get(uri=“/greet/{name}“) suspend fun
greet(name: String): String { return "Greetings $name" } }
Coroutines Support class Router { Flowable.defer(() -> { if
(isKotlinSuspendingFunction) { return executeKotlinSuspendingFunction( ... ); } else { ... } }) }
Coroutines Support class Router { Flowable.defer(() -> { if
(isKotlinSuspendingFunction) { return executeKotlinSuspendingFunction( ... ); } else { ... } }) }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
} }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
if (isKotlinFunctionReturnTypeUnit) { } else { } } }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
if (isKotlinFunctionReturnTypeUnit) { return Completable.fromPublisher( ... ).toFlowable(); } else { } } }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
if (isKotlinFunctionReturnTypeUnit) { return Completable.fromPublisher( ... ).toFlowable(); } else { return Publishers.fromCompletableFuture(…); } } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Using Dispatcher @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name:
String): String { return "Greetings $name" } } Use a dispatcher
Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { }
Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { val
coroutineDispatcher: CoroutineDispatcher init { coroutineDispatcher = executor.asCoroutineDispatcher() } }
Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { @Get(uri="/greet/{name}")
suspend fun greetings(name: String) = withContext(coroutineDispatcher) { } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Creating Scope @Controller class GreetingController(val executor: ExecutorService) { @Get(uri="/greet/{name}")
suspend fun greetings(name: String) = coroutineScope { } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Flows Server Client
Flows GET /stream
Flows @Controller class GreetingController { @Get(uri=“/stream“) fun stream(): Flow<Int>
}
Flows @Controller class GreetingController { @Get(uri=“/stream“) fun stream(): Flow<Int>
= flowOf(1,2,3) .onEach { delay(2000) } }
GET http: // localhost:8080/stream
GET http: // localhost:8080/stream Services [ {} ]
Kotlin / kotlinx.coroutines Code ! Issues Pull Requests kotlinx.coroutines Modules
• Reactive • RxJava 2.x • RxJava 3.x
Coroutine Support Micronaut Core RxJava Coroutines
GET http: // localhost:8080/stream Services HTTP/1.1 200 OK transfer-encoding: chunked
[ 1, 2, 3 ]
Coroutine Support class FlowConverterRegistrar { @Override public void register( ...
) { addConverter( ... , flow -> Flowable.fromPublisher(ReactiveFlowKt.asPublisher(flow)) ); } }
Coroutine Support class FlowConverterRegistrar { @Override public void register( ...
) { addConverter( ... , flow -> Flowable.fromPublisher(ReactiveFlowKt.asPublisher(flow)) ); } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Task REST API GET /tasks/list
Task REST API GET /tasks/list GET /tasks/{taskId}
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks {
“id”: taskId “description”: “ ” }
Task REST API GET /tasks/list GET /tasks/{taskId} PUT /tasks {
“id”: taskId “description”: “ ” } POST /tasks
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access Server DB
Data Access Server DB Hibernate
Data Access data class Task( val id: Long? = null,
val description: String = "" )
Data Access @Entity data class Task( )
Data Access @Entity @Table(name = "task") data class Task( )
Data Access data class Task( @Id val id: Long? =
null, )
Data Access data class Task( @Id @GeneratedValue(strategy = GenerationType.AUTO) val
id: Long? = null, )
Data Access data class Task( @Column(name = "description", nullable =
false, unique = true) val description: String = "" )
Data Access DB Task Repo
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository { }
Data Access class TasksRepository(val entityManager: EntityManager) { }
Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long):
Task { } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long):
Task { return entityManager.find(Task :: class.java, taskId) } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):
Task { } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):
Task { val task = Task(description = description) } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):
Task { val task = Task(description = description) entityManager.persist(task) return task } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,
description: String): Int { } }
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" } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,
description: String): Int { return entityManager.createQuery(sql) }
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() }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository(val entityManager: EntityManager) { fun delete(taskId: Long)
{ val task = findById(taskId) task ?. let { entityManager.remove(it) } } }
Data Access DB Task Repo Controller
Controller class TaskController { }
Controller class TaskController(private val taskRepository: TaskRepository) { }
Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { }
Controller @ExecuteOn(TaskExecutors.IO) @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Get("/{taskId}") fun
getTask(taskId: Long): Task? { return taskRepository.findById(taskId) } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access data class TaskSaveCommand( val description: String )
Data Access Controller Client
Data Access Controller Client “Task 1”
Data Access Controller Client “ ”
Data Access Controller Client Validation
Data Access data class TaskSaveCommand( @field:NotBlank val description: String )
Data Access @Introspected data class TaskSaveCommand( @field:NotBlank val description: String
)
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body command: TaskSaveCommand) { } }
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body @Valid command: TaskSaveCommand) { } }
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body @Valid command: TaskSaveCommand) { val task = taskRepository.save(command.description) } }
HTTP Response Controller Client 201 OK
Micronaut HTTP • Set status code • Update headers •
Send errors
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) } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
HTTP Response Controller Client Delete taskID
HTTP Response Controller Client No content
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}")
fun delete(taskId: Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Hibernate In-memory Server DB
@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
Tasks Project taskapp src build.gradle Configure app D Dockerfile application.yml
Configure Database micronaut: application: name: complete datasources: default: url: {JDBC_URL:`
... `} username: ${JDBC_USER:sa} password: ${JDBC_PASSWORD:""} driverClassName: ${JDBC_DRIVER:org.h2.Driver}
POST /tasks { “description”: “task1” }
HTTP/1.1 201 OK { "id": 1, "description": "task1" } POST
/tasks
GET /tasks/list [ { "id": 1, "description": "task1" } ]
Create Client Server Client
CLI Client • Simple client to test APIs • Create
Runnable
CLI App mn create-cli-app
CLI App mn create-cli-app -l kotlin com.learn.cliapp
interface TaskClient { @Get("/task/list") fun getTasks(): List<Task> }
CLI App
interface TaskClient { @Get("/task/list") fun getTasks(): List<Task> @Post("/task")
fun save(description: String): Task } CLI App
class TaskclientCommand : Runnable { @Inject lateinit var taskClient: TaskClient
} CLI App
class TaskclientCommand : Runnable { fun run() { taskClient.getTasks() .forEach
{ println(it) } } CLI App
Summary • Setup Controller for REST API • HTTP Requests
& Responses • Data Access • Create Client
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Controller @Controller("/tasks") class TaskController(val taskRepository: TaskRepository) { @Get(“/list") fun list():
List<Task> { } } Exception
Circuit Breaker • # of Retries • Delay
@CircuitBreaker(delay="1s", attempts = "5") interface TaskClient { } CLI
App
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Load Balancing Server Client Server Server
Tasks Project taskapp src build.gradle Configure app D Dockerfile application.yml
Tasks Project consul: client: registration: enabled: true default-zone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"
@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
Load Balancing Server Client Server Server
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Resources • Introduction to Micronaut • Graeme Rocher •
Talk on coroutines! https: // codingwithmohit.com/talks/
Thank You! www.codingwithmohit.com @heyitsmohit