Ir al contenido principal

Destacado

Spring Cloud Sleuth y StackDriver Trace

Siguiendo con nuestra saga de proyectos de Spring GCP, ahora, nos toca ver Spring Cloud Sleuth.
En proyectos de Arquitectura de Micro servicios es muy importante la información y observabilidad de todo. Si se están realizando llamadas entre servicios A, B y C; es importante entender si estas fueron exitosas o existe latencia entre dichas llamadas.

Nosotros podemos agregar esta dependencia de Sleuth a nuestro proyecto Spring Boot para darle ese "super poder" de tracing distribuido y luego exponer dicha información en un Zipkin o Jaegger por ejemplo.

GCP tiene Stackdriver trace, que es un servicio que nos permite guardar esta data de tracing sin tener que administrar nosotros mismos nuestro Zipking o storage. Stackdriver puede productir reportes de latencia y/o detectar anamalías en la performance.

Existen dos formas de usar Stackdriver Trace en nuestra aplicación Spring Boot:

1. Usas un Stackdriver Trace Zipkin proxy y configurar el Spring Cloud Sleuth para que use este prox…

Primeros pasos con Spring y Kotlin

Ahora ya podemos crear nuestros proyectos Spring Boot y escoger como lenguaje Kotlin.

Con Maven



Las dependencias seran:



Se puede especificar la ubicación de tu código fuente y tus tests:


  ${project.basedir}/src/main/kotlin
  ${project.basedir}/src/test/kotlin

Para compilar módulos kotlin, código fuente se usa un plugin de maven para kotlin:



La clase DemoApplication es la que inicia todo, tiene la anotación @SpringBootApplication. Por defecto en Kotlin todas las clases son públicas y se puede declarar sin {}.


@SpringBootApplication
class DemoApplication

fun main(args: Array) {
    SpringApplication.run(DemoApplication::class.java, *args)
}


Como se puede observar, se omite el public porque es dado por defecto, si no hay tipo de retorno se omite el void, asimismo, como se declara la función fuera del cuerpo de la clase por defecto es static.

Si ejecutamos la aplicación dentro del IDE o con mvn spring-boot:run la aplicación iniciará en el puerto 8080.


El HelloController, lucirá así:


@RestController
class HelloController (val helloService: HelloService) {

    @GetMapping("/hello")
    fun helloKotlin(): String {
        return "hello world"
    }

    @GetMapping("/hello-service")
    fun helloKotlinService(): String {
        return helloService.getHello()
    }

    @GetMapping("/hello-dto")
    fun helloDto(): HelloDto {
        return HelloDto("Hello from the dto")
    }
}

Como se ve se tiene un constructor donde se dará la inyección sin usar @Autowired y con val para que sea inmutable. Tenemos 3 casos. El primero devuelve un String, el segunDo también pero usando un Service y el tercer caso devuelve un DTO. El service sería de esta manera:


@Service
class HelloService{

    fun getHello(): String {
        return "hello service"
    }
}

El DTO sería un data class:



data class HelloDto(val greeting: String)

Este tipo de data class automáticamente crea equals/hashcode, toString, y una función copy. Pero, no nos da un constructor por defecto, el cual es requerido por librerías como Jackson. Si se quiere soportar esto se puede usar esta nueva dependencia en tu pom.


Finalmente el testing sería de la siguiente manera:


@RunWith(SpringRunner::class)
@SpringBootTest(classes = arrayOf(DemoApplication::class),
  webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTests {

 @Autowired
 lateinit var testRestTemplate: TestRestTemplate

 @Test
 fun whenCalled_shouldReturnHello() {
  val result = testRestTemplate
    // ...
    .getForEntity("/hello", String::class.java)

  assertNotNull(result)
  assertEquals(result?.statusCode, HttpStatus.OK)
  assertEquals(result?.body, "hello world")
 }

 @Test
 fun whenCalled_shouldReturnHelloService() {
  var result = testRestTemplate
    // ...
    .getForEntity("/hello-service", String::class.java)

  assertNotNull(result)
  assertEquals(result?.statusCode, HttpStatus.OK)
  assertEquals(result?.body, "hello service")
 }


 @Test
 fun whenCalled_shoudlReturnJSON() {
  val result = testRestTemplate
    // ...
    ?.getForEntity("/hello-dto", HelloDto::class.java)

  assertNotNull(result)
  assertEquals(result?.statusCode, HttpStatus.OK)
  assertEquals(result?.body, HelloDto("Hello from the dto"))
 }
}


De esto último se puede rescatar el null safety "?". Cuando una variable puede ser null debe ser declarada usando '?'.

Con Gradle

 Escogemos para esta otra versión la opción con Gradle.






A continuación veamos el build.gradle, los plugins y dependencias a usar.


buildscript {
 ext {
  kotlinVersion = '1.1.3-2'
  springBootVersion = '1.5.6.RELEASE'
 }
 repositories {
  mavenCentral()
 }
 dependencies {
  classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
  classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
 }
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
 kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
 kotlinOptions.jvmTarget = "1.8"
}

repositories {
 mavenCentral()
}


dependencies {

 compile('org.springframework.boot:spring-boot-starter-web')
 compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}")
 compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
 testCompile('org.springframework.boot:spring-boot-starter-test')
}



Luego todo lo explicado anteriormente aplica. Así que para tener una pequeña demo creamos esta clase Greeting y luego su Controller. Greeting.kt


package com.example.demogradle

/**
 * Created by josediaz on 7/27/17.
 */


data class Greeting(val id: Long, val content: String)



Y su controller:


@RestController
class GreetingController {

    val counter = AtomicLong()

    @GetMapping("/greeting")
    fun greeting(@RequestParam(value = "name", defaultValue = "World") name: String) =
            Greeting(counter.incrementAndGet(), "Hello, $name")

}


El resultado final es:
Enjoy! y los animo a empezar a utilizar kotlin en sus proyectos.

SOURCE CODE

Comentarios