jueves, 29 de septiembre de 2022

[O'Reilly eBook] Container Networking: From Docker to Kubernetes

 

EBOOK

Ebook: Container Networking | From Docker to Kubernetes

Companies that begin working with containerized applications often aren’t prepared for the challenge of container networking. In this new eBook, Michael Hausenblas, a member of Red Hat’s OpenShift team, provides a detailed look at the many challenges of container networking, container orchestration, and service discovery, and shares several available solutions. Along the way, you’ll learn the capabilities of many open source tools, including Kubernetes.

Download this eBook to:

  • Get an introduction to container networking by exploring Docker networking modes
  • Maintain a map of running containers and their locations with service discovery tools such as ZooKeeper and etcd
  • Configure network interfaces in Linux containers by using plugins with the Container Network Interface (CNI)
  • Learn how the Kubernetes orchestration system approaches container networking

Concurnas, un lenguaje pensado para la concurrencia para la jvm


Concurnas es un lenguaje de programación de la JVM de open source, diseñado para construir sistemas concurrente, distribuidos y en paralelos, confiables, escalables y de alto rendimiento.

El uso de Concurnas ayuda a las organizaciones a aprovechar al máximo el hardware moderno de GPU y CPU multinúcleo al facilitar la programación concurrente y en paralela.

Concurnas es interoperable con Java (y otros lenguajes JVM): las organizaciones pueden aprovechar el software Java existente en un entorno concurrente.

En comparación con otros lenguajes, con Concurnas los desarrolladores necesitan escribir menos líneas de código. Ese código es más seguro y más fácil de probar, lo que aumenta la calidad y aumenta la productividad de los desarrolladores.

Entre las caracteristicas de Corcunas podemos nombrar: 

Fácil de aprender: Inspirado en lenguajes como Python y Java, Concurnas es un lenguaje de tipado estatico que utiliza la inferencia de tipos con una sintaxis fácil de aprender.

Alto rendimiento: Concurnas es un lenguaje compilado que se ejecuta en la máquina virtual de Java y, como tal, tiene acceso al increíble rendimiento que ofrece la JVM.

Fácil de escalar: Concurnas hace posible usar el mismo código desde un pequeño prototipo de investigación hasta una solución de producción distribuida a escala planetaria

Open Source: Concurnas es una tecnología de código abierto, libre de usar y modificar. Con desarrollo continuo y soporte comercial proporcionado por Concurnas Ltd.

La verdad que quiero poner un ejemplo pero hay tantos en la pagina y tan interesantes que sería incompleto, igual tiro uno para que tengan idea: 

//factorial

def factorial(i int) int {

    match(i){

        0 => 1

        n => n * factorial(n-1)

    }

}


Como se ve, es un lenguaje completo que le viene bien cualquier cosa, si queres podes programar imperativo, orientado a objeto, funcional, lo que pinte. Eso esta bueno pero es un problema si no entendes todos los conceptos. Un lenguaje de programación bien escrito te debería hacer fácil hacer las cosas bien y difícil hacer las cosas mal. Tanta libertad puede ser una desventaja. Esto ultimo es mi opinión. 

Dejo link: https://concurnas.com/

martes, 27 de septiembre de 2022

Por que usar Akka?


Las transmisiones reactivas fluyen los datos desde los productores hasta los consumidores. Pero todo va más o menos en una dirección. Los actores te dan más libertad que eso. Porque pueden colaborar y comunicarse como lo hacemos los humanos.

En particular, hablaremos sobre cómo los actores pueden crear otros actores. Cómo cambian su comportamiento con el tiempo, cómo intercambian mensajes. 

Antes de sumergirnos en lo que son los Actores, primero echemos un vistazo a por qué queremos investigarlos y de dónde provienen.

El formalismo Actor fue publicado por primera vez por Hewitt, Bishop y Steiger en 1973. Y lo que querían lograr es crear un modelo en el que puedan formular los programas para su investigación de inteligencia artificial.

Uno de los estudiantes de Hewitt publicó su tesis doctoral en 1986. Gul Agha formuló lenguajes de actores, cómo escribir programas de actores, cómo razonar sobre ellos. Describió los patrones de comunicación entre los Actores y cómo usarlos para resolver problemas reales.

En el mismo año, Ericsson comenzó a desarrollar un lenguaje de programación llamado Erlang. Este es un lenguaje de programación puramente funcional, cuyo modelo de concurrencia se basa en Actors. Y que luego se utilizó posteriormente en productos comerciales.

En 1995, Ericsson presentó su nueva plataforma de telecomunicaciones. El cual tuvo un gran éxito. Hubo alrededor de 30 milisegundos de tiempo de inactividad por año. Esta solidez y resiliencia fue posible gracias al modelo Actor.

Inspirado por el éxito de Erlang de Ericsson, Philipp Haller agregó Actors a la biblioteca estándar de Scala en 2006.

Luego, Jonas Bonér fue influenciado por Erlang, así como por Scala Actors para crear Akka en 2009. Akka es un framework de la JVM con APIs para Java y Scala que hace que el modelo Actor esté disponible para una amplia gama de desarrolladores.

Los actores son aplicables en una amplia gama de problemas. En el pasado, los programas se volvieron más rápidos al usar la próxima generación de CPU. Esto se debió a una frecuencia central cada vez mayor. Pero esto se detuvo alrededor del año 2005. Desde entonces, las CPU no son cada vez más rápidas, sino más anchas. Esto significa que, en lugar de hacer que un núcleo de ejecución sea más potente, se incorporan múltiples núcleos de este tipo dentro de un chip, accediendo a la memoria compartida.

Y en algunos de estos, los núcleos incluso están virtualizados, de modo que un núcleo de ejecución física puede albergar múltiples subprocesos de ejecución lógica. Hay diferentes formas de beneficiarse de estas CPU más amplias.

La primera es que puede ejecutar varios programas en paralelo en la misma computadora. Esto se llama multitarea y se ha hecho desde las primeras versiones de Unix. Pero si tiene un solo programa que necesita ejecutar más rápido y no tiene más remedio que ejecutar partes del mismo programa en paralelo. Y esto se llama subprocesos múltiples.

Para lograr subprocesos múltiples, su programa debe estar escrito de una manera diferente a la forma secuencial tradicional.

La diferencia entre ejecutar programas separados en paralelo y usar subprocesos del mismo programa en paralelo es que estos subprocesos colaboran en una tarea común.

Y si piensas en un grupo de personas haciendo algo juntas, necesitarán sincronizar sus acciones. De lo contrario, se pisan. Lo mismo puede suceder en un programa de subprocesos múltiples. Hay una nueva clase de errores y problemas que te esperan allí. Y esta es la razón por la cual los programas deben formularse de manera diferente para estar listos para subprocesos múltiples. Para entender lo que eso significa, echemos un vistazo a una cuenta bancaria:

class BankAccount {

private val balance = 0

def deposit(amount: Int): Unit = if (amount > 0) balance = balance + amount

def withdraw(amount: Int): Int = 

    if (amount  > 0 && amount <  balance) {

        balance = balance - amount 

        balance

    } else throw new Error("Fondo insuficiente")

}


Contiene un campo para el saldo y tiene dos métodos para depositar una cantidad y retirar una cantidad.

Ahora veamos el método de retiro en detalle. Entonces, echemos un vistazo a lo que sucede si dos subprocesos ejecutan este código al mismo tiempo.

Digamos que este es el subproceso 1 y este es el subproceso 2. Son ejecutados por diferentes CPU, por lo que pueden ejecutarse en paralelo.

Entran al método con una cantidad. Digamos que el hilo 1 quiere retirar 50 francos suizos, por ejemplo. Y el hilo 2 quiere retirar 40. Lo primero que harán ambos será leer el saldo actual. En ambos casos, digamos el saldo ahora mismo es 80, por lo que ambos verán 80. Luego, ambos ingresarán la declaración if, del chequeo. ¿La cantidad es positiva? Sí, lo es. ¿Y hay realmente suficientes fondos en la cuenta? Sí hay. Así ambos continuarán. Ellos calcularán el nuevo saldo.

El nuevo saldo en el primer subproceso será 30, y en el segundo será 40. Lo siguiente que harán los subprocesos será escribir el nuevo saldo en el campo de saldo de la cuenta bancaria, objeto.

El primer subproceso escribirá 30 y el segundo escribirá 40. Esto está claramente en conflicto, porque solo uno de los escritores puede ganar al final. El que viene en último lugar, sobrescribirá al que vino antes.

Este es el primer problema que vemos aquí. Es que en realidad se pierde una de las actualizaciones del saldo.

El otro problema es que se viola el invariante de la cuenta bancaria. En eso hemos retirado 50 y 40 francos suizos que son 90 en total, y el saldo era solo 80, lo que no debería haber sido posible. Uno de los hilos debería haber fallado. Y eso, eso no sucedió es el otro problema con este código.

Ahora, ¿qué podemos hacer para solucionar este problema? Necesitamos agregar sincronización. Cuando varios subprocesos trabajan con los mismos datos, necesitan sincronizar sus acciones.

Porque de lo contrario, serían propensos a pisarse. Lo que debemos hacer es asegurarnos de que cuando un subproceso esté trabajando con los datos, los demás se mantengan al margen. Como si pusieras un cartel de no molestar en la puerta de tu hotel.

Entonces, digamos que en este ejemplo estamos viendo el saldo, como los datos que se protegerán. Y lo que tenemos que hacer es poner una valla alrededor, de modo que cuando un subproceso esté trabajando con los datos, digamos el subproceso 1. Que este tenga acceso exclusivo a él. Lo que significa que la transacción 2, si intenta acceder a los datos, en realidad se le negará el acceso en este momento. Y tiene que esperar hasta que la transacción 1 termine con ella. De esta manera, el saldo estará protegido. Y todas las modificaciones realizadas en él se realizan de manera consistente, una tras otra. También decimos serializado.

Las herramientas principales para lograr este tipo de sincronización son lock o mutex. Que es básicamente el mismo concepto que se mostró anteriormente. O un semáforo donde la diferencia es que múltiples pero solo un número definido de subprocesos pueden ingresar a esta región.

En Scala, cada objeto tiene un candado asociado. Al que puede acceder llamando al método sincronizado en él. Y acepta un bloqueo de código que se ejecutará en esta región protegida.

¿Cómo aplicamos esto a la cuenta bancaria para sincronizarla?


def withdraw(amount: Int): Int = this.synchronized {

        if (amount  > 0 && amount <  balance) {

            balance = balance - amount 

            balance

        } else throw new Error("Fondo insuficiente")

}


Bueno, aquí tenemos el método de retiro. Y si lo ponemos todo dentro de un bloque sincronizado, entonces leemos el balance aquí. Realizar el cheque y volver a escribirlo, todo se hará como una acción atómica. Que no puede ser perturbado por otro hilo que ejecuta retirar, al mismo tiempo. Ese otro hilo tendrá que esperar.

Pero, ¿debemos sincronizar también aquí en el método de depósito? El método de depósito también modifica el saldo. Y si no esta sincronizado, entonces podría modificarlo sin protección. Y una vez que el retiro devuelve el saldo aquí, anularía la anulación de la actualización realizada por depósito al mismo tiempo.

Esto es para ilustrar que todos los accesos al saldo deben estar sincronizados, y no solo el que hemos demostrado que es problemático.

Ahora, intentemos transferir algo de dinero de una cuenta bancaria a otra.

Lo que debemos hacer es sincronizar ambos objetos, de modo que estén en un estado consistente. Porque de lo contrario, alguien leyendo el saldo de las cuentas podría encontrar el dinero en fuga. Retiramos de una cuenta. Depositamos después en otro.

Durante este tiempo, el dinero básicamente no está en ninguna parte. Y si la invariante que debe cumplirse es que la suma de from y to debe ser la misma, esto se violará. Por lo tanto, necesitamos sincronizar.

Entonces, primero tomamos el bloqueo en la cuenta de origen. Luego tomamos el bloqueo en la cuenta. Ahora estamos seguros de que ningún otro hilo puede modificar estas dos cuentas, pero nosotros podemos. Una propiedad de los bloqueos en Scala es que son reentrantes, lo que significa que el mismo subproceso puede tomarlo dos veces o cualquier cantidad de veces.

Hay un problema con este código, ya que introduce la posibilidad de un interbloqueo.

Digamos que un hilo quiere transferir de la cuenta A a la cuenta B en un hilo.

Y otro hilo intenta transferir en la dirección opuesta. Si ambos comienzan al mismo tiempo, toman el primer candado, este en la cuenta A, este en la cuenta B.

Luego van a tomar la otra cerradura. Este no logrará tomar el bloqueo, porque ya fue tomado por el otro subproceso para la cuenta B. Lo mismo es cierto para la cuenta A en el otro subproceso.

Esto significa que ninguno de los subprocesos puede progresar, ambos se atascarán para siempre. Porque no hay posibilidad de que ninguno de los dos ceda el candado que ya tienen. Esto se llama interbloqueo o deadlock

Hay soluciones. Por ejemplo, llevar siempre las transacciones en el mismo orden. Debe definir un orden para las cuentas, etc. y entonces podrías potencialmente resolver esto.

Descubrirá que la mayoría de las veces hay soluciones de ese tipo, pero se agregarán y harán que su código sea mucho más complicado con el tiempo. Y en este caso es sencillo, porque ambas son cuentas bancarias. Pero, ¿qué sucede si desea que colaboren objetos que no provienen de la misma base de código, por ejemplo, en los que no puede modificar?

En ese sentido, sería mucho mejor que nuestros objetos no requirieran sincronización o bloqueo. Porque el bloqueo es lo que realmente hace que ocurra el interbloqueo.

El otro problema con el bloqueo es que es malo para la utilización de la CPU. Si hay otros subprocesos para ejecutar, el sistema operativo los ejecutará. Pero de lo contrario, la CPU estará inactiva. Y despertarlo o hacer que un subproceso vuelva a ejecutarse cuando otro subproceso lo ha interrumpido lleva mucho tiempo. Por lo tanto, su programa se ejecutará más lentamente si usa el bloqueo.

Otro problema con el bloqueo de objetos es que la comunicación sincrónica une al emisor y al receptor con bastante fuerza. Porque el remitente debe esperar hasta que el receptor esté listo. Entonces, si llamo a una cuenta bancaria que está sincronizada, esa cuenta bancaria me bloqueará hasta que esté lista. Los objetos que no bloquean son exactamente lo que son los Actores.

Los actores representan objetos y el modelo de actor describe cómo estos objetos interactúan, y todo esto está inspirado en cómo nos organizamos los humanos y respeta especialmente las leyes de la física. ¿Qué quiero decir con esto? Digamos que una persona sin reloj que quiere saber la hora actual y hay otra persona con un reloj. Ahora la primera persona se hará la pregunta, ¿qué hora es?  Y la segunda persona podría responder, son las 12:43. Este es un intercambio simple entre dos humanos y todos hemos hecho algo así antes, así que espero que todos puedan identificarse con este ejemplo. Pero hay más de lo que es visible en la superficie. En primer lugar, tomamos nota de que transmitimos información hablando y escuchando las palabras. Eso significa que la primera persona envía un mensaje a la segunda persona. La segunda persona luego piensa un poco, mira el reloj y responde con otro mensaje, viajando hacia atrás, en ondas de sonido. Las dos cualidades fundamentales aquí son, en primer lugar, que se basa únicamente en mensajes y, en segundo lugar, los mensajes tardan en viajar de un objeto a otro. Es útil pensar en los actores no como estos objetos abstractos a los que llamas métodos. Pero en lugar de visualizarlos como personas que hablan entre sí. Una cosa también a tener en cuenta es que cuando los humanos hablamos, no nos metemos en el cerebro de los demás, por ejemplo, leyendo la mente. nos basamos únicamente en los mensajes, y eso es una parte muy fundamental de los actores. Más formalmente, un actor, tal como lo definen Hewitt, Bishop y Steiger, es un objeto con identidad. Y también tiene un comportamiento, que ya hemos encontrado, y solo interactúa mediante el paso de mensajes. Lo que sigue siendo compatible con la definición de objeto normal, lo especial de los actores es que su paso de mensajes siempre es asíncrono. 

Tenemos un actor, llamémoslo Actor A. Y este quiere enviar un mensaje al actor B. El actor A enviará el mensaje y luego puede continuar haciendo lo que quiera después de eso, sin tener que esperar por el  mensaje, este viajará a B y será procesado por B, especialmente el procesamiento ocurre en un contexto diferente, en un momento posterior diferente según lo determine el sistema en el que se ejecutan los actores. 

Esta es la propiedad de las más importante de los actores. Para esto, usamos los trait de actor en Akka. El tipo de actor define un método abstracto que se llama recibir y este recibir devuelve algo del tipo Receive. Recibir es una función parcial de Any a Unit y describe la respuesta del actor a un mensaje.

Cualquier mensaje podría entrar, por lo tanto es Any, y el actor puede hacer muchas cosas, pero al final, no devuelve nada, debido al paso asincrónico del mensaje.

Por ahora vamos a ver solo estos conseptos, y luego los profundizaremos en post posteriores. 

Dejo link : https://akka.io/

domingo, 25 de septiembre de 2022

Sealed classes de Kotlin: definición de jerarquías de clases restringidas


Veamos un ejemplo para entender mejor para que utilizar seald classess. La superclase Expr tiene dos subclases: Num, que representa un número; y Sum, que representa la suma de dos expresiones. Es conveniente manejar todas las subclases posibles en una expresión when. Pero tenemos que escribir el  else para especificar qué debería suceder si no tenemos coincidencias:


interface Expr

    class Num(val value: Int) : Expr

    class Sum(val left: Expr, val right: Expr) : Expr


fun eval(e: Expr): Int =

    when (e) {

        is Num -> e.value

        is Sum -> eval(e.right) + eval(e.left)

        else ->

            throw IllegalArgumentException("Unknown expression")

}


Cuando evalúas una expresión usando la construcción when, el compilador de Kotlin te obliga a especificar todas las opciones, pero nosotros sabemos que no es posible que haya otra opción. 

Tener que agregar siempre el else no es conveniente. Además, si agrega una nueva subclase, el compilador no detectará que algo ha cambiado y va a ir por el else. 

Kotlin proporciona una solución a este problema: Sealed classes  o clases selladas. Marcas una superclase con el modificador sellado, y eso restringe la posibilidad de crear subclases. Todas las subclases directas deben anidarse en la superclase:


sealed class Expr {

    class Num(val value: Int) : Expr()

    class Sum(val left: Expr, val right: Expr) : Expr()

}


fun eval(e: Expr): Int =

    when (e) {

        is Expr.Num -> e.value

        is Expr.Sum -> eval(e.right) + eval(e.left)

    }


Y listo!


El modificador sealed implica que la clase es abierta por lo tanto, no necesitamos agregar modificador open de forma explícita.

Otra cosa importante es que no puede declarar una interfaz sellada. ¿Por qué? Si pudiera, el compilador de Kotlin no podría garantizar que alguien no pueda implementar esta interfaz en el código Java.


sábado, 24 de septiembre de 2022

Recursos para aprender scala

 


  • Scala By Example, para ver más ejemplos que ilustran los conceptos tratados en las conferencias.

  • Scala Cheatsheet, para una referencia rápida que cubre la sintaxis de scala.


Libros
:

martes, 20 de septiembre de 2022

Spring boot con jetty




Tomcat y Jetty son los servidores web más populares en el mundo Java, pero Spring boot nos brinda la funcionalidad de servidor embebido con tomcat. Por que? ni idea, pero podemos cambiar esto y utilizar jetty de la siguiente manera. 

Generamos nuestro proyecto en : https://start.spring.io/ o como quieran pero tienen que elegir spring web como dependencia. 

Si ya funciona el entorno, vamos a la dependencia de spring web y excluimos tomcat y luego agregamos jetty : 


implementation ('org.springframework.boot:spring-boot-starter-web') {

exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'

}


implementation 'org.springframework.boot:spring-boot-starter-jetty'


Y listo, cuando inicie spring lo va hacer con un jetty embebido. Van a poder ver la siguiente linea en el log : 


2022-09-20 08:06:28.788  INFO 1304813 --- [           main] o.s.b.web.embedded.jetty.JettyWebServer


lunes, 19 de septiembre de 2022

Clases internas y anidadas en Kotlin


Al igual que en Java, en Kotlin puedes declarar una clase en otra clase. Hacerlo puede ser útil para encapsular una clase auxiliar o colocar el código más cerca de donde se usa. La diferencia es que las clases anidadas de Kotlin no tienen acceso a la instancia de la clase externa, a menos que lo solicite específicamente. Veamos un ejemplo que muestra por qué esto es importante.

Imagine que desea definir un elemento View, cuyo estado se puede serializar. Puede que no sea fácil serializar una vista, pero puede copiar todos los datos necesarios en otra clase auxiliar. Declaras la interfaz State que implementa Serializable. La interfaz View declara los métodos getCurrentState y restoreState que se pueden usar para guardar el estado de una vista.

interface State: Serializable

interface View {

    fun getCurrentState(): State

    fun restoreState(state: State) {}

}

Es útil definir una clase que guarde el estado de un botón en la clase Botón. Veamos cómo se puede hacer en Java (el código Kotlin similar se mostrará en un momento).

/* Java */

public class Button implements View {

    @Override

    public State getCurrentState() {

        return new ButtonState();

    }

    @Override

    public void restoreState(State state) { /*...*/ }

    public class ButtonState implements State { /*...*/ }

}

La clase ButtonState que implementa la interfaz State y contiene información específica para Button. En el método getCurrentState, crea una nueva instancia de esta clase. En un caso real, inicializaría ButtonState con todos los datos necesarios.

¿Qué tiene de malo este código? ¿Por qué se obtiene un java.io.NotSerializableException? Eso puede parecer extraño al principio: ButtonState guarda una referencia a Button que no es Serializable.

Todo queda claro cuando recuerdas que en Java, cuando declaras una clase en otra clase, se convierte en una clase interna por defecto. La clase ButtonState del ejemplo almacena implícitamente una referencia a su clase Button externa. Eso explica por qué ButtonState no se puede serializar: Button no se puede serializar y la referencia a él interrumpe la serialización de ButtonState.

Para solucionar este problema, debe declarar la clase ButtonState como estática. Declarar una clase anidada como estática elimina la referencia implícita de esa clase a su clase adjunta. En Kotlin, el comportamiento predeterminado de las clases internas es lo opuesto a lo que acabamos de describir.

class Button : View {

override fun getCurrentState(): State = ButtonState()

override fun restoreState(state: State) { /*...*/ }

class ButtonState : State { /*...*/ }

}

Una clase anidada en Kotlin sin modificadores explícitos es lo mismo que una clase anidada estática en Java. Para convertirlo en una clase interna para que contenga una referencia a una clase externa se puede usar el modificador inner. 

La sintaxis para hacer referencia a una instancia de una clase externa en Kotlin también difiere de Java. Se escribe this@Outer para acceder a la clase Outer desde la clase Inner:

class Outer {

    inner class Inner {

        fun getOuterReference(): Outer = this@Outer

    }

}



viernes, 16 de septiembre de 2022

Microservices Up and Running

 

EBOOK

[Full eBook] Microservices Up and Running

Hi Emanuel,

Microservices architectures offer faster functional updates, better scalability, and cleaner, evolvable system designs. But implementing your first microservices architecture is difficult. This eBook, compliments of NGINX and O'Reilly Media, provides step-by-step guidance for building an effective microservices architecture.

In this eBook you will learn:

  • How to design an effective and explicit microservices system end-to-end
  • About forming teams, assigning responsibilities, and working together
  • How to establish the right functional boundaries between the microservices that make up an application
  • About building a simple yet powerful CI/CD pipeline for infrastructure changes