Me llego este mail y queria compartirlo :
|
Me llego este mail y queria compartirlo :
|
Para nuestros propósitos, vamos a ver tres bibliotecas de clientes de Spring/Netflix diferentes en las que un consumidor de servicios puede interactuar con Ribbon. Estas bibliotecas pasarán del nivel más bajo de abstracción para interactuar con Ribbon al más alto.
Las bibliotecas que exploraremos incluyen
Spring Discovery Client ofrece el nivel más bajo de acceso a Ribbon y los servicios registrados en él. Usando Discovery Client, puede consultar todos los servicios registrados con el cliente y sus URL correspondientes.
Veamos un ejemplo simple del uso de DiscoveryClient para recuperar una de las direcciones URL de un servicio y luego llamaremos al servicio mediante una clase RestTemplate estándar. Para comenzar a usar DiscoveryClient, primero debe anotar la clase Application.java con la anotación @EnableDiscoveryClient:
@SpringBootApplication
@EnableDiscoveryClient
Anteriormente insinuamos que todas las instancias de type class son valores implícitos. Esto fue una simplificación. De hecho, podemos definir instancias de dos maneras:
¿Por qué construiríamos instancias a partir de otras instancias? Como ejemplo motivacional, considere definir un JsonWriter para Option. necesitaríamos un JsonWriter[Option[A]] para cada A que nos interesa en nuestra aplicación. Podríamos intentar resolver el problema por fuerza bruta creando una biblioteca de valores implícitos:
implicit val optionIntWriter: JsonWriter[Option[Int]] =
???
implicit val optionPersonWriter: JsonWriter[Option[Person]] =
???
// and so on...
Afortunadamente, podemos abstraer el código para manejar la Opción[A] en un constructor común basado en la instancia de A:
• si la opción es Some(aValue), escriba aValue usando el escritor para A;
• si la opción es None, devuelve JsNull.
Aquí está el mismo código escrito como una definición implícita:
implicit def optionWriter[A] (implicit writer: JsonWriter[A]): JsonWriter[Option[A]] =
new JsonWriter[Option[A]] {
def write(option: Option[A]): Json =
option match {
case Some(aValue) => writer.write(aValue)
case None => JsNull
}
}
Json.toJson(Option("A string"))
Implícitos de empaquetado : En una peculiaridad curiosa del lenguaje, cualquier definición marcada como implícita en Scala debe colocarse dentro de un objeto o trait en lugar de en el nivel superior. Colocar instancias en un objeto complementario a la clase de tipo tiene un significado especial en Scala porque juega con algo llamado alcance implícito.
Ámbito implícito : El compilador busca instancias de clase de tipo candidato por tipo. Los lugares donde el compilador busca instancias candidatas se conocen como ámbito implícito. El alcance implícito se aplica en el sitio de la llamada; ese es el punto donde llamamos a un método con un parámetro implícito. El alcance implícito que consta aproximadamente de:
• definiciones locales o heredadas;
• definiciones importadas;
• definiciones en el objeto complementario de la clase de tipo o el tipo de parámetro (en este caso, JsonWriter o String).
Las definiciones solo se incluyen en el ámbito implícito si están etiquetadas con la palabra clave implícita. Además, si el compilador ve múltiples definiciones candidatas, falla con un error de valores implícitos ambiguos:
implicit val writer1: JsonWriter[String] = JsonWriterInstances.stringWriter
implicit val writer2: JsonWriter[String] = JsonWriterInstances.stringWriter
Json.toJson("A string")
// error: ambiguous implicit values:
// both value writer1 in object App0 of type => repl.Session.App0. JsonWriter[String]
// and value writer2 in object App0 of type => repl.Session.App0. JsonWriter[String]
// match expected type repl.Session.App0.JsonWriter[String]
// Json.toJson("A string")
// ^^^^^^^^^^^^^^^^^^^^^^^
1. colocándolos en un objeto;
2. colocándolos en un trait;
3. colocándolos en el objeto compañero de la clase de tipos;
4. colocándolos en el objeto complementario del tipo de parámetro.
Con la opción 1 traemos las instancias al alcance importándolas. Con la opción 2 los traemos al alcance con la herencia. Con las opciones 3 y 4, las instancias siempre están en un alcance implícito, independientemente de dónde intentemos usarlas.
Es convencional colocar instancias de clase de tipo en un objeto complementario (opción 3 y 4 anteriores) si solo hay una implementación sensata, o al menos una implementación ampliamente aceptada como predeterminada. Esto hace que las instancias de clase de tipo sean más fáciles de usar, ya que no se requiere importarlas para incluirlas en el ámbito implícito.
Un uso de clase de tipo es cualquier funcionalidad que requiere una instancia de clase de tipo para funcionar. En Scala esto significa cualquier método que acepte instancias de la clase de tipo como parámetros implícitos.
La forma más sencilla de crear una interfaz que utilice una clase de tipo es colocar métodos en un objeto único:
object Json {
def toJson[A](value: A)(implicit w: JsonWriter[A]): Json =
w.write(value)
}
import JsonWriterInstances._
import JsonSyntax._
Person("Dave", "dave@example.com").toJson
// res3: Json = JsObject(
// Map("name" -> JsString("Dave"), "email" -> JsString("dave@example.com"))
// )
Spock es un framework de test y especificación para aplicaciones Java y Groovy. Lo que lo hace destacar entre la multitud de framework de test es su hermoso y altamente expresivo lenguaje de especificaciones. Gracias a que corre sobre JUnit, Spock es compatible con la mayoría de los IDE, herramientas de compilación y servidores de integración continua. Spock está inspirado en JUnit, jMock, RSpec, Groovy, Scala, Vulcans y otros frameworks y lenguajes.
La idea de spock es que hay diferentes bloques donde uno puede definir el setup del test, el estimulo y la respuesta esperada :
Bueno, nada mejor que un ejemplo para mostrar la propuesta de este framework:
def "two plus two should equal four"() {
given:
int left = 2
int right = 2
when:
int result = left + right
then:
result == 4
}
def "Should be able to remove from list"() {
given:
def list = [1, 2, 3, 4]
when:
list.remove(0)
then:
list == [2, 3, 4]
}
Dejo link: https://spockframework.org/
Si empezas con typescript un libro super recomendado es el handbook que se puede acceder de forma gratuita.
En la introducción nos da una perspectiva del libro para diferentes desarrolladores:
También se puede bajar como Pdf : https://www.typescriptlang.org/assets/typescript-handbook.pdf
Dejo link: https://www.typescriptlang.org/docs/handbook/intro.html
Type class es una especie de interfaz que define algún tipo de comportamiento. Si un tipo es miembro de una clase de tipos, significa que ese tipo soporta e implementa el comportamiento que define la clase de tipos. La gente que viene de lenguajes orientados a objetos es propensa a confundir las clases de tipos porque piensan que son como las clases en los lenguajes orientados a objetos. Bien, pues no lo son. Una aproximación más adecuada sería pensar que son como las interfaces de Java, o los protocolos de Objective-C, pero mejor.
Veamos un ejemplo:
ghci> :t (==)
(==) :: (Eq a) => a -> a -> Bool
Interesante. Aquí vemos algo nuevo, el símbolo =>. Cualquier cosa antes del símbolo => es una restricción de clase. Podemos leer la declaración de tipo anterior como: la función de igualdad toma dos parámetros que son del mismo tipo y devuelve un Bool. El tipo de estos dos parámetros debe ser miembro de la clase Eq (esto es la restricción de clase).
El tipo de clase Eq proporciona una interfaz para las comparaciones de igualdad. Cualquier tipo que tenga sentido comparar dos valores de ese tipo por igualdad debe ser miembro de la clase Eq. Todos los tipos estándar de Haskell excepto el tipo IO (un tipo para manejar la entrada/salida) y las funciones forman parte de la clase Eq.
Hay tres componentes importantes para implementar este patrón en Scala. Type class en Scala se implementan usando valores y parámetros implícitos y, opcionalmente, usando clases implícitas. Las construcciones del lenguaje Scala corresponden a los componentes de los type class de la siguiente manera:
En el post anterior levantamos un servidor Eureka, ahora vamos registrar un servicio.
Lo primero que debe hacer es agregar la dependencia Spring Eureka al archivo pom.xml del servicio que queremos registrar :
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
El proyecto comenzó como una implementación adecuada de ReactiveX, con influencias de programación funcional más fuertes y diseñado desde cero para la contrapresión y creado para interactuar limpiamente con la biblioteca estándar de Scala, compatible de forma inmediata con el protocolo Reactive Streams. Luego se expandió para incluir abstracciones para suspender los efectos secundarios y para el manejo de recursos, siendo uno de los padres e implementadores de Cats Effect.
Dejo link: https://monix.io/
Una vez más, el proyecto Spring Cloud hace que este tipo de configuración sea trivial de realizar. Vamos a utilizar Spring Cloud y el motor de descubrimiento de servicios Eureka de Netflix para implementar el patrón de descubrimiento de servicios. Como balanceador de carga del lado del cliente, utilizaremos Spring Cloud y las bibliotecas Ribbon de Netflix.
Vamos a tener que seguir los siguentes pasos:
Cualquier nueva instancia de servicios será visible, mientras que cualquier instancia que no esté en buen estado se eliminará de la memoria caché local.
A continuación, implementará este diseño configurando su servicio Spring Cloud Eureka.
Para empezar vamos a agregar la dependencia a Eureka, si usamos maven tenemos que agregar :
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
Me tope con el libro : Scala with Cats 2 que es gratuito, por lo que les dejo una reseña y a bajarlo y estudiar:
El objetivo principal de este libro es enseñar arquitectura y diseño de sistemas utilizando las técnicas de la programación funcional moderna. Esto significa diseñar sistemas como pequeñas unidades componibles, expresando restricciones e interacciones a través del sistema de tipos y utilizando la composición para guiar la construcción de grandes sistemas de una manera que mantenga la visión arquitectónica original.
El libro también sirve como una introducción a la biblioteca Cats. Usando abstracciones de Cats y explicando la estructura de Cats para que pueda usarla. Las ideas generales no son específicas de Cats, pero Cats proporciona una excelente implementación que es beneficiosa para aprender.
Dejo link: https://www.scalawithcats.com/
(defn create-session []
(let [snippets (repeatedly promise)
translations (delay (map translate
(strings->sentences (map deref snippets))))]
(new-session {:snippets snippets :translations translations})))
Seguimos usando una secuencia perezosa infinita de promesas para representar los fragmentos entrantes y un mapa sobre esa secuencia para representar las traducciones, pero ahora ambos están almacenados en una sesión.
A continuación, debemos modificar accept-snippet y get-translation para buscar :snippets o :translations dentro de una sesión:
(defn accept-snippet [session n text]
(deliver (nth (:snippets session) n) text))
(defn get-translation [session n]
@(nth @(:translations session) n))
(def last-session-id (atom 0))
(defn next-session-id []
(swap! last-session-id inc))
(def sessions (atom {}))
(defn new-session [initial]
(let [session-id (next-session-id)]
(swap! sessions assoc session-id initial)
session-id))
(defn get-session [id]
(@sessions id))
Creamos una nueva sesión pasando un valor inicial a new-session, que obtiene una nueva ID de sesión y la agrega a las sesiones llamando a swap!. Recuper una sesión con get-session es una simple cuestión de buscarlo por su ID.
Si no vamos a aumentar continuamente la cantidad de memoria que usamos, necesitaremos alguna forma de eliminar las sesiones cuando ya no estén en uso. Nosotros podría hacer esto explícitamente (quizás con una función de eliminación de sesión), pero dado que estamos escribiendo un servicio web en el que no necesariamente podemos confiar en que los clientes limpien correctamente, vamos a implementar la caducidad de la sesión (expiry) en su lugar. Esto requiere un pequeño cambio en el código anterior:
(def sessions (atom {}))
(defn now []
(System/currentTimeMillis))
(defn new-session [initial]
(let [session-id (next-session-id)
session (assoc initial :last-referenced (atom (now)))]
(swap! sessions assoc session-id session)
session-id))
(defn get-session [id]
(let [session (@sessions id)]
(reset! (:last-referenced session) (now))
session))
Ahora que cada sesión tiene una entrada :last-referenced, podemos caducar sesiones verificando periódicamente si alguna no ha sido referenciada por más de un cierto período de tiempo:
(defn session-expiry-time []
(- (now) (* 10 60 1000)))
(defn expired? [session]
(< @(:last-referenced session) (session-expiry-time)))
(defn sweep-sessions []
(swap! sessions #(remove-vals % expired?)))
(def session-sweeper
(schedule {:min (range 0 60 5)} sweep-sessions))