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
190
Building Shared UIs across Platforms with Compose
heyitsmohit
1
570
Building Multiplatform Apps with Compose
heyitsmohit
2
440
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.8k
Building Android Testing Infrastructure
heyitsmohit
1
410
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
730
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
520
Other Decks in Programming
See All in Programming
Introduction to kotlinx.rpc
arawn
0
770
Datadog DBMでなにができる? JDDUG Meetup#7
nealle
0
150
15分で学ぶDuckDBの可愛い使い方 DuckDBの最近の更新
notrogue
3
750
SwiftUI移行のためのインプレッショントラッキング基盤の構築
kokihirokawa
0
160
Visual StudioのGitHub Copilotでいろいろやってみる
tomokusaba
1
210
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the benefits are huge)
lmammino
1
160
PHPカンファレンス名古屋2025 タスク分解の試行錯誤〜レビュー負荷を下げるために〜
soichi
1
730
How mixi2 Uses TiDB for SNS Scalability and Performance
kanmo
41
16k
1年目の私に伝えたい!テストコードを怖がらなくなるためのヒント/Tips for not being afraid of test code
push_gawa
1
630
2025.2.14_Developers Summit 2025_登壇資料
0101unite
0
220
自力でTTSモデルを作った話
zgock999
0
110
PEPCは何を変えようとしていたのか
ken7253
3
290
Featured
See All Featured
Docker and Python
trallard
44
3.3k
Why Our Code Smells
bkeepers
PRO
336
57k
GitHub's CSS Performance
jonrohan
1030
460k
How to Ace a Technical Interview
jacobian
276
23k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
49
2.3k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
13
1k
4 Signs Your Business is Dying
shpigford
183
22k
Being A Developer After 40
akosma
89
590k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
11
1.3k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.5k
Building Your Own Lightsaber
phodgson
104
6.2k
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