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.1k
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
160
Building Shared UIs across Platforms with Compose
heyitsmohit
1
550
Building Multiplatform Apps with Compose
heyitsmohit
2
420
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.7k
Building Android Testing Infrastructure
heyitsmohit
1
390
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
690
Using Square Workflow for Android & iOS
heyitsmohit
1
380
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
テストケースの名前はどうつけるべきか?
orgachem
PRO
0
140
今年一番支援させていただいたのは認証系サービスでした
satoshi256kbyte
1
260
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
280
これでLambdaが不要に?!Step FunctionsのJSONata対応について
iwatatomoya
2
3.7k
競技プログラミングへのお誘い@阪大BOOSTセミナー
kotamanegi
0
360
Fibonacci Function Gallery - Part 1
philipschwarz
PRO
0
220
情報漏洩させないための設計
kubotak
2
280
CQRS+ES の力を使って効果を感じる / Feel the effects of using the power of CQRS+ES
seike460
PRO
0
140
[JAWS-UG横浜 #76] イケてるアップデートを宇宙いち早く紹介するよ!
maroon1st
0
460
fs2-io を試してたらバグを見つけて直した話
chencmd
0
240
コンテナをたくさん詰め込んだシステムとランタイムの変化
makihiro
1
130
ドメインイベント増えすぎ問題
h0r15h0
2
340
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2k
The Language of Interfaces
destraynor
154
24k
Testing 201, or: Great Expectations
jmmastey
40
7.1k
Typedesign – Prime Four
hannesfritz
40
2.4k
How to train your dragon (web standard)
notwaldorf
88
5.7k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
169
50k
YesSQL, Process and Tooling at Scale
rocio
169
14k
How to Think Like a Performance Engineer
csswizardry
22
1.2k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
Visualization
eitanlees
146
15k
Optimizing for Happiness
mojombo
376
70k
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