Slide 1

Slide 1 text

Mohit Sarveiya Micronaut with Kotlin Coroutines @heyitsmohit

Slide 2

Slide 2 text

Micronaut with Kotlin Coroutines ● Building REST API ● Coroutines ● Data Access ● Circuit breakers ● Load Balancing

Slide 3

Slide 3 text

What is Micronaut? ● JVM Framework for building microservices ● Created by Graham Rose ● Polygot - Java, Kotlin or Groovy

Slide 4

Slide 4 text

Benefits ● Fast Startup & Small Memory ● Meta programming ● Reactive ● Ahead-of-Time Compilation (AOT)

Slide 5

Slide 5 text

Building Micronaut Project ● CLI tool ● Intellij ● Micronaut Launch

Slide 6

Slide 6 text

Project Creation CLI Tool ● Download via SDKMAN ● Create scaffolding for app ● Create controllers, beans or clients

Slide 7

Slide 7 text

Create Hello World App mn create-app

Slide 8

Slide 8 text

Create Hello World App mn create-app • create-cli-app • create-grpc-app • create-messaging-app

Slide 9

Slide 9 text

Create Hello World App mn create-app -l kotlin • Kotlin • Java • Groovy

Slide 10

Slide 10 text

Create Hello World App mn create-app -l kotlin com.learn.micronautapp Package name

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Hello World REST API GET /greet/{name}

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Create Controller mn create-controller

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Testing @MicronautTest class GreetingControllerTest { 
 }

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Client("/") interface GreetingClient { @Get("/greet/{name}") fun greetings(name: String): String } }

Slide 25

Slide 25 text

Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { 
 @Inject lateinit var greetingClient: GreetingClient }

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Controller @Controller class GreetingController { 
 }

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

GET http: // localhost:8080/greet/micronaut

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Services HTTP/1.1 200 OK 
 
 content-type: text/plain 
 
 content-length: 19 
 
 connection: keep-alive 
 
 Greetings micronaut 
 GET http: // localhost:8080/greet/micronaut

Slide 36

Slide 36 text

Summary ● Create Micronaut Project ● Create controller 
 ● Unit testing API 


Slide 37

Slide 37 text

Coroutine Support ● Suspending functions ● Scopes ● Dispatchers 
 ● Flows 


Slide 38

Slide 38 text

Coroutine Support Micronaut Core RxJava Coroutines

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { }

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Coroutine Support ● Suspending functions ● Dispatchers ● Scope 
 ● Flows 


Slide 49

Slide 49 text

Using Dispatcher @Controller class GreetingController { 
 @Get(uri=“/greet/{name}“) fun greet(name: String): String { return "Greetings $name" } } Use a dispatcher

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { 
 val coroutineDispatcher: CoroutineDispatcher init { coroutineDispatcher = executor.asCoroutineDispatcher() } }

Slide 52

Slide 52 text

Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { 
 @Get(uri="/greet/{name}") suspend fun greetings(name: String) = 
 withContext(coroutineDispatcher) { } }

Slide 53

Slide 53 text

Coroutine Support ● Suspending functions ● Dispatchers ● Scope 
 ● Flows 


Slide 54

Slide 54 text

Creating Scope @Controller class GreetingController(val executor: ExecutorService) { 
 @Get(uri="/greet/{name}") suspend fun greetings(name: String) = coroutineScope { } }

Slide 55

Slide 55 text

Coroutine Support ● Suspending functions ● Dispatchers ● Scope 
 ● Flows 


Slide 56

Slide 56 text

Flows Server Client

Slide 57

Slide 57 text

Flows GET /stream

Slide 58

Slide 58 text

Flows @Controller class GreetingController { 
 @Get(uri=“/stream“) fun stream(): Flow }

Slide 59

Slide 59 text

Flows @Controller class GreetingController { 
 @Get(uri=“/stream“) fun stream(): Flow = flowOf(1,2,3) .onEach { delay(2000) } }

Slide 60

Slide 60 text

GET http: // localhost:8080/stream

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Kotlin / kotlinx.coroutines Code ! Issues Pull Requests kotlinx.coroutines Modules • Reactive • RxJava 2.x • RxJava 3.x

Slide 63

Slide 63 text

Coroutine Support Micronaut Core RxJava Coroutines

Slide 64

Slide 64 text

GET http: // localhost:8080/stream Services HTTP/1.1 200 OK transfer-encoding: chunked [ 1, 2, 3 ]

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

Coroutine Support ● Suspending functions ● Dispatchers ● Scope 
 ● Flows 


Slide 68

Slide 68 text

Micronaut with Kotlin Coroutines ● Building REST API ● Coroutines ● Data Access ● Circuit breakers ● Load Balancing

Slide 69

Slide 69 text

Task REST API GET /tasks/list

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

Data Access Server DB

Slide 76

Slide 76 text

Data Access Server DB Hibernate

Slide 77

Slide 77 text

Data Access data class Task( val id: Long? = null, val description: String = "" )

Slide 78

Slide 78 text

Data Access @Entity data class Task( )

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

Data Access data class Task( @Id val id: Long? = null, )

Slide 81

Slide 81 text

Data Access data class Task( @Id @GeneratedValue(strategy = GenerationType.AUTO) val id: Long? = null, )

Slide 82

Slide 82 text

Data Access data class Task( @Column(name = "description", nullable = false, unique = true) val description: String = "" )

Slide 83

Slide 83 text

Data Access DB Task Repo

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

Data Access class TasksRepository { }

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long): Task { return entityManager.find(Task :: class.java, taskId) } }

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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" } }

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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() }

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

Data Access class TasksRepository(val entityManager: EntityManager) { fun delete(taskId: Long) { val task = findById(taskId) task ?. let { entityManager.remove(it) } } }

Slide 100

Slide 100 text

Data Access DB Task Repo Controller

Slide 101

Slide 101 text

Controller class TaskController { }

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Get("/{taskId}") fun getTask(taskId: Long): Task? { return taskRepository.findById(taskId) } }

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Data Access data class TaskSaveCommand( val description: String )

Slide 109

Slide 109 text

Data Access Controller Client

Slide 110

Slide 110 text

Data Access Controller Client “Task 1”

Slide 111

Slide 111 text

Data Access Controller Client “ ”

Slide 112

Slide 112 text

Data Access Controller Client Validation

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

HTTP Response Controller Client 201 OK

Slide 119

Slide 119 text

Micronaut HTTP ● Set status code ● Update headers ● Send errors

Slide 120

Slide 120 text

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) } }

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

HTTP Response Controller Client Delete taskID

Slide 123

Slide 123 text

HTTP Response Controller Client No content

Slide 124

Slide 124 text

Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId: Long): HttpResponse { taskRepository.delete(taskId) return HttpResponse.noContent() } }

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

Hibernate In-memory Server DB

Slide 127

Slide 127 text

@Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId: Long): HttpResponse { taskRepository.delete(taskId) return HttpResponse.noContent() } } Services org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.H2Dialect

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

Configure Database micronaut: application: name: complete datasources: default: url: {JDBC_URL:` ... `} username: ${JDBC_USER:sa} password: ${JDBC_PASSWORD:""} driverClassName: ${JDBC_DRIVER:org.h2.Driver}

Slide 130

Slide 130 text

POST /tasks { “description”: “task1” }

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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

Slide 133

Slide 133 text

Create Client Server Client

Slide 134

Slide 134 text

CLI Client ● Simple client to test APIs ● Create Runnable 


Slide 135

Slide 135 text

CLI App mn create-cli-app

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

interface TaskClient { 
 
 @Get("/task/list") fun getTasks(): List } CLI App

Slide 138

Slide 138 text

interface TaskClient { 
 
 @Get("/task/list") fun getTasks(): List @Post("/task") fun save(description: String): Task } CLI App

Slide 139

Slide 139 text

class TaskclientCommand : Runnable { @Inject lateinit var taskClient: TaskClient } CLI App

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

Summary ● Setup Controller for REST API ● HTTP Requests & Responses ● Data Access ● Create Client 


Slide 142

Slide 142 text

Micronaut with Kotlin Coroutines ● Building REST API ● Coroutines ● Data Access ● Circuit breakers ● Load Balancing

Slide 143

Slide 143 text

Controller @Controller("/tasks") class TaskController(val taskRepository: TaskRepository) { @Get(“/list") fun list(): List { } } Exception

Slide 144

Slide 144 text

Circuit Breaker ● # of Retries ● Delay 


Slide 145

Slide 145 text

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

Slide 146

Slide 146 text

Micronaut with Kotlin Coroutines ● Building REST API ● Coroutines ● Data Access ● Circuit breakers ● Load Balancing

Slide 147

Slide 147 text

Load Balancing Server Client Server Server

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

@Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId: Long): HttpResponse { taskRepository.delete(taskId) return HttpResponse.noContent() } } Services Registered service with Consul io.micronaut.runtime.Micronaut - Server Running: http: // localhost:44004

Slide 151

Slide 151 text

Load Balancing Server Client Server Server

Slide 152

Slide 152 text

Micronaut with Kotlin Coroutines ● Building REST API ● Coroutines ● Data Access ● Circuit breakers ● Load Balancing

Slide 153

Slide 153 text

Resources ● Introduction to Micronaut • Graeme Rocher 
 ● Talk on coroutines! 
 
 https: // codingwithmohit.com/talks/

Slide 154

Slide 154 text

Thank You! www.codingwithmohit.com @heyitsmohit