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
220
Building Shared UIs across Platforms with Compose
heyitsmohit
1
620
Building Multiplatform Apps with Compose
heyitsmohit
2
500
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.8k
Building Android Testing Infrastructure
heyitsmohit
1
480
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
780
Using Square Workflow for Android & iOS
heyitsmohit
1
420
Building Android Infrastructure Teams at Scale
heyitsmohit
3
320
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
550
Other Decks in Programming
See All in Programming
ニーリーにおけるプロダクトエンジニア
nealle
0
790
Kotlin エンジニアへ送る:Swift 案件に参加させられる日に備えて~似てるけど色々違う Swift の仕様 / from Kotlin to Swift
lovee
1
260
スタートアップの急成長を支えるプラットフォームエンジニアリングと組織戦略
sutochin26
1
4.7k
XP, Testing and ninja testing
m_seki
3
240
VS Code Update for GitHub Copilot
74th
2
630
A full stack side project webapp all in Kotlin (KotlinConf 2025)
dankim
0
110
チームのテスト力を総合的に鍛えて品質、スピード、レジリエンスを共立させる/Testing approach that improves quality, speed, and resilience
goyoki
4
730
PHPで始める振る舞い駆動開発(Behaviour-Driven Development)
ohmori_yusuke
2
350
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
2
650
Deep Dive into ~/.claude/projects
hiragram
13
2.5k
AIと”コードの評価関数”を共有する / Share the "code evaluation function" with AI
euglena1215
1
150
10 Costly Database Performance Mistakes (And How To Fix Them)
andyatkinson
0
230
Featured
See All Featured
Scaling GitHub
holman
459
140k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
161
15k
The Cost Of JavaScript in 2023
addyosmani
51
8.5k
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.5k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
Optimizing for Happiness
mojombo
379
70k
Six Lessons from altMBA
skipperchong
28
3.9k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
138
34k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
The Language of Interfaces
destraynor
158
25k
The Straight Up "How To Draw Better" Workshop
denniskardys
234
140k
Docker and Python
trallard
44
3.5k
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