martes, 30 de noviembre de 2021

Libros gratuitos

 

Download IT Guides!

 

JAXB Tutorial

The first specification of JAXB was done in March 2003 and the work process is tracked in the Java Specification Request 31. In this specification request you can find a lot of...

 
 

JavaFX Programming Cookbook

JavaFX 2.0 and later is implemented as a native Java library, and applications using JavaFX are written in native Java code. JavaFX Script has been scrapped by Oracle, but development is...

 
 

Java Servlet Tutorial

A servlet can almost be thought of as an applet that runs on the server side-without a face. Java servlets make many Web applications possible.

Java...

 
 

Groovy Programming Cookbook

Groovy 1.0 was released on January 2, 2007, and Groovy 2.0 in July, 2012. Since version 2, Groovy can also be compiled statically, offering type inference and performance very close to...

 

lunes, 29 de noviembre de 2021

Por qué están teniendo éxito las arquitecturas de microservicios?


La historia del desarrollo de software está plagada de historias de grandes proyectos de desarrollo que, después de una inversión de millones de dólares y cientos de miles de horas de desarrollador de software, y con muchas de las mejores y más brillantes mentes de la industria trabajando en ellos, de alguna manera nunca lograron para entregar algo de valor a sus clientes y literalmente colapsó bajo su propia complejidad y peso.

Estos proyectos gigantescos tendían a seguir metodologías de desarrollo en cascada grandes y tradicionales que insistían en que todos los requisitos y el diseño de la aplicación se definieran al comienzo del proyecto. Se puso tanto énfasis en conseguir que todas las especificaciones del software fueran “correctas” que hubo poco margen para cumplir con los nuevos requisitos comerciales, o refactorizar y aprender de los errores cometidos en las primeras etapas de desarrollo.

Sin embargo, la realidad es que el desarrollo de software no es un proceso lineal de definición y ejecución, sino más bien evolutivo en el que se necesitan varias iteraciones de comunicación, aprendizaje y entrega al cliente antes de que el equipo de desarrollo realmente comprenda el problema. 

Para agravar los desafíos del uso de metodologías tradicionales, es que muchas veces la granularidad de los artefactos de software que se entregan estos proyectos: 

  • Estrechamente acoplado: la invocación de la lógica empresarial se realiza a nivel del lenguaje de programación en lugar de a través de protocolos como SOAP and REST. Esto aumenta en gran medida la posibilidad de que incluso un pequeño cambio en un componente de la aplicación pueda romper otras partes de la aplicación e introducir nuevos errores.
  • Fugas: la mayoría de las aplicaciones de software grandes administran diferentes tipos de datos. Por ejemplo, una aplicación de gestión de relaciones con el cliente (CRM) puede gestionar información sobre clientes, ventas y productos. En un modelo tradicional, estos datos se mantienen en el mismo modelo de datos y dentro del mismo almacén de datos. Aunque existen límites obvios entre los datos, con demasiada frecuencia resulta tentador salir de un dominio para acceder directamente a los datos que pertenecen a otro equipo. Este fácil acceso a los datos crea dependencias ocultas y permite que los detalles de implementación de las estructuras de datos internas de un componente se filtren a través de toda la aplicación. Incluso pequeños cambios en una sola tabla de base de datos pueden requerir un número significativo de cambios de código y pruebas de regresión en toda la aplicación.
  • Monolítico: debido a que la mayoría de los componentes de una aplicación tradicional residen en una única base de código que se comparte entre varios equipos, cada vez que se realiza un cambio en el código, se debe volver a compilar la aplicación completa, volver a ejecutar durante un ciclo de prueba completo, y redistribuido. Incluso los pequeños cambios en el código base de la aplicación, ya sean nuevos requisitos del cliente o correcciones de errores, se vuelven costosos y requieren mucho tiempo, y los grandes cambios se vuelven casi imposible de hacer de manera oportuna.

Una arquitectura basada en microservicios adopta un enfoque diferente para brindar funcionalidad. Específicamente, las arquitecturas basadas en microservicios tienen estas características:

  • Restringido: los microservicios tienen un único conjunto de responsabilidades y su alcance es limitado. Los microservicios adoptan la filosofía UNIX de que una aplicación no es más que una colección de servicios donde cada servicio hace una cosa y hace esa cosa realmente bien.
  • Poco acoplado: una aplicación basada en microservicios es una colección de pequeños servicios que solo interactúan entre sí a través de una especificación y no de implementación. Cada microservicio tiene una interfaz bien definida, utilizando un protocolo de invocación no propietario (por ejemplo, HTTP y REST). Siempre que la interfaz del servicio no cambie, los propietarios del microservicio tienen más libertad para realizar modificaciones en el servicio que en una arquitectura de aplicación tradicional.
  • Abstración: los microservicios poseen por completo sus estructuras y fuentes de datos. Los datos que pertenecen a un microservicio solo pueden ser modificados por ese servicio. El control de acceso a la base de datos que contiene los datos del microservicio se puede bloquear para permitir que solo el servicio acceda a ella.
  • Independiente: cada microservicio en una aplicación de microservicio se puede compilar e implementar independientemente de los otros servicios utilizados en la aplicación. Esto significa que los cambios se pueden aislar y probar mucho más fácilmente que con una aplicación monolítica.

¿Por qué estos atributos de la arquitectura de microservicios son importantes para el desarrollo basado en la nube? Las aplicaciones basadas en la nube en general tienen :

  • Una base de usuarios grande y diversa: los diferentes clientes desean funciones diferentes y no quieren tener que esperar un ciclo de lanzamiento de aplicaciones prolongado antes de poder comenzar a usar estas funciones. Los microservicios permiten que las características se entreguen rápidamente, porque cada servicio tiene un alcance pequeño y se accede a él a través de una interfaz bien definida.
  • Requisitos de tiempo de actividad extremadamente altos: debido a la naturaleza descentralizada de los microservicios, las aplicaciones basadas en microservicios pueden aislar más fácilmente fallas y problemas apartes de una aplicación sin eliminar la aplicación completa. Esto reduce el tiempo de inactividad general de las aplicaciones y las hace más resistentes a los problemas.
  • Requisitos de volumen desiguales: las aplicaciones tradicionales implementadas dentro de las cuatro paredes de un centro de datos corporativo generalmente tienen patrones de uso consistentes que emergen con el tiempo. Esto simplifica la planificación de la capacidad para este tipo de aplicaciones. Pero en una aplicación basada en la nube, un simple tweet en Twitter o una publicación en Slashdot puede impulsar la demanda de una aplicación basada en la nube por las nubes.

Debido a que las aplicaciones de microservicio se dividen en pequeños componentes que se pueden implementar de forma independiente entre sí, es mucho más fácil enfocarse en los componentes que están bajo carga y escalen esos componentes horizontalmente en varios servidores en una nube.

Aunque se necesita más que personal técnico para entregar una aplicación completa, creo que la base para el desarrollo exitoso de microservicios comienza con las perspectivas de tres roles críticos:

  • El arquitecto: el trabajo del arquitecto es ver el panorama general y comprender cómo se puede descomponer una aplicación en microservicios individuales y cómo los microservicios interactuarán para ofrecer una solución.
  • El desarrollador de software: el desarrollador de software escribe el código y comprende en detalle cómo funcionarán el lenguaje y los marcos de desarrollo del lenguaje.
  • El ingeniero de DevOps: el ingeniero de DevOps aporta inteligencia sobre cómo se implementan y administran los servicios no solo en la producción, sino también en todos los los entornos de no producción. Las palabras clave del ingeniero de DevOps son coherencia y repetibilidad en todos los entornos.


viernes, 26 de noviembre de 2021

Primeros pasos con Apache Kafka parte 21

Seguimos con Kafka

Hasta ahora nos hemos centrado en aprender la API del consumidor, pero solo hemos analizado algunas de las propiedades de configuración, solo los obligatorios bootstrap.servers, group.id, key.deserializer y value.deserializer. Toda la configuración del consumidor está documentada en la documentación de Apache Kafka. La mayoría de los parámetros tienen una razón pueden tener valores predeterminados y no requieren modificación, pero algunos tienen implicaciones en el rendimiento y la disponibilidad de los consumidores. Echemos un vistazo a algunas de las propiedades más importantes.

fetch.min.bytes

Esta propiedad permite que un consumidor especifique la cantidad mínima de datos que desea recibir del corredor al buscar registros. Si un corredor recibe una solicitud de registros de un consumidor, pero los nuevos registros suman menos bytes que min.fetch.bytes, el corredor esperará hasta que haya más mensajes disponibles antes de devolver los registros al consumidor. Esto reduce la carga tanto para el consumidor como para el corredor, ya que tienen que manejar menos mensajes de ida y vuelta en los casos en que los temas no tienen mucha actividad nueva (o para horas de menor actividad del día). Deberá establecer este parámetro más alto que el predeterminado si el consumidor está usando demasiada CPU cuando no hay muchos datos disponibles, o reducir la carga de los intermediarios cuando tiene una gran cantidad de consumidores.

fetch.max.wait.ms

Al establecer fetch.min.bytes, le indica a Kafka que espere hasta que tenga suficientes datos para enviar antes de responder al consumidor. fetch.max.wait.ms te permite controlar cuánto tiempo esperar. De forma predeterminada, Kafka esperará hasta 500 ms. Esto da como resultado hasta 500 ms de latencia adicional en caso de que no haya suficientes datos fluyendo al tema de Kafka para satisfacer la cantidad mínima de datos para devolver. Si desea limitar la latencia potencial (generalmente debido a que los SLA controlan la latencia máxima de la aplicación), puede establecer fetch.max.wait.ms en un valor más bajo. Si establece fetch.max.wait.ms en 100 ms y fetch.min.bytes en 1 MB, Kafka recibirá una solicitud de recuperación del consumidor y responderá con datos cuando tenga 1 MB de datos para devolver o después de 100 ms, lo que ocurra primero.

max.partition.fetch.bytes

Esta propiedad controla el número máximo de bytes que el servidor devolverá por partición. El valor predeterminado es 1 MB, lo que significa que cuando KafkaConsumer.poll () devuelve ConsumerRecords, el objeto de registro utilizará como máximo un máximo de partición.fetch.bytes por partición asignada al consumidor. Entonces, si un tema tiene 20 particiones y tiene 5 consumidores, cada consumidor deberá tener 4 MB de memoria disponible para Consumer Records. En la práctica, querrá asignar más memoria ya que cada consumidor necesitará manejar más particiones si otros consumidores del grupo fallan. máx. partition.fetch.bytes debe ser más grande que el mensaje más grande que aceptará un corredor (determinado por la propiedad max.message.size en la configuración del corredor), o el corredor puede tener mensajes que el consumidor no podrá consumir, en los caso que el consumidor se cuelgue tratando de leerlos. Otra consideración importante al establecer max.partition.fetch.bytes es la cantidad de tiempo que le toma al consumidor procesar los datos. Como recordará, el consumidor debe llamar a poll () con la frecuencia suficiente para evitar el tiempo de espera de la sesión y el reequilibrio posterior. Si la cantidad de datos que devuelve un solo poll () es muy grande, el consumidor puede tardar más en procesar, lo que significa que no llegará a la siguiente iteración del ciclo de sondeo a tiempo para evitar un tiempo de espera de sesión. Si esto ocurre, las dos opciones son reducir el máx. partición.fetch.bytes o para aumentar el tiempo de espera de la sesión.

session.timeout.ms

La cantidad de tiempo que un consumidor puede estar fuera de contacto con los corredores mientras aún se considera vivo es de 3 segundos. Si pasa más de session.timeout.ms sin que el consumidor envíe un latido al coordinador del grupo, se considera muerto y el coordinador del grupo activará un reequilibrio del grupo de consumidores para asignar particiones del consumidor muerto a los otros consumidores del grupo. . Esta propiedad está estrechamente relacionada con heartbeat.interval.ms. heartbeat.interval.ms controla la frecuencia con la que el método poll () de KafkaConsumer enviará un latido al coordinador del grupo, mientras que session.timeout.ms controla cuánto tiempo puede pasar un consumidor sin enviar un latido. Por lo tanto, esas dos propiedades generalmente se modifican juntas: heatbeat.interval.ms debe ser menor que session.timeout.ms, y generalmente se establece en un tercio del valor de tiempo de espera. Entonces, si session.timeout.ms es de 3 segundos, heartbeat.interval.ms debería ser de 1 segundo. Establecer session.timeout.ms por debajo del valor predeterminado permitirá a los grupos de consumidores detectar y recuperarse de fallas antes, pero también puede causar reequilibrios no deseados como resultado de que los consumidores tarden más en completar el ciclo de sondeo o la recolección de basura. Configurar session.timeout.ms más alto reducirá la posibilidad de un reequilibrio accidental, pero también significa que tomará más tiempo detectar una falla real.

auto.offset.reset

Esta propiedad controla el comportamiento del consumidor cuando comienza a leer una partición para la cual no tiene una compensación comprometida o si la compensación comprometida que tiene no es válida (generalmente porque el consumidor estuvo inactivo durante tanto tiempo que el registro con esa compensación ya estaba obsoleto en el corredor). El valor predeterminado es "más reciente", lo que significa que, sin una compensación válida, el consumidor comenzará a leer los registros más recientes (registros que se escribieron después de que el consumidor comenzó a ejecutar). La alternativa es "la más temprana", lo que significa que, sin un desplazamiento válido, el consumidor leerá todos los datos de la partición, comenzando desde el principio.

enable.auto.commit

Este parámetro controla si el consumidor confirmará las compensaciones automáticamente y su valor predeterminado es verdadero. Configúrelo en falso si prefiere controlar cuándo se confirman las compensaciones, lo cual es necesario para minimizar los duplicados y evitar la pérdida de datos. Si establece enable.auto.commit en true, es posible que también desee controlar la frecuencia con la que se confirmarán las compensaciones mediante auto.commit.interval.ms.

division.assignment.strategy

Aprendimos que las particiones se asignan a los consumidores en un grupo de consumidores. Un PartitionAssignor es una clase que, dados los consumidores y los temas a los que se suscribieron, decide qué particiones se asignarán a qué consumidor. De forma predeterminada, Kafka tiene dos estrategias de asignación:

  • Range: Asigna a cada consumidor un subconjunto consecutivo de particiones de cada tema al que se suscribe. Entonces, si los consumidores C1 y C2 están suscritos a dos temas, T1 y T2, y cada uno de los temas tiene tres particiones, luego a C1 se le asignarán las particiones 0 y 1 de los temas T1 y T2, mientras que a C2 se le asignará la partición 2 de esos temas. Debido a que cada tema tiene un número impar de particiones y la asignación se realiza para cada tema de forma independiente, el primer consumidor termina con más particiones que el segundo. Esto sucede siempre que se utiliza la asignación de rango y el número de consumidores no divide el número de particiones en cada tema de forma ordenada.
  • RoundRobin: Toma todas las particiones de todos los temas suscritos y las asigna a los consumidores de forma secuencial, una por una. Si C1 y C2 se describieron anteriormente, usaron RoundRobin asignación, C1 tendría las particiones 0 y 2 del tema T1 y la partición 1 del tema T2. C2 tendría la partición 1 del tema T1 y las particiones 0 y 2 del tema T2. En general, si todos los consumidores están suscritos a los mismos temas (un escenario muy común), la asignación de RoundRobin terminará con todos los consumidores con el mismo número de particiones (o como máximo una diferencia de partición). 
division.assignment.strategy permite elegir una estrategia de asignación de partición. El valor predeterminado es org.apache.kafka.clients.consumer.RangeAssignor, que implementa la estrategia de rango descrita anteriormente. Puede reemplazarlo con org.apache.kafka.clients.consumer.RoundRobinAssignor. Una opción más avanzada es implementar su propia estrategia de asignación, en cuyo caso partition.assignment.strategy debe apuntar al nombre de su clase.

client.id

Esta puede ser cualquier cadena y será utilizada por los intermediarios para identificar los mensajes enviados desde el cliente. Se utiliza en registros y métricas, y para cuotas.

registros.max.poll.

Esto controla el número máximo de registros que devolverá una sola llamada a poll (). Esto es útil para ayudar a controlar la cantidad de datos que su aplicación necesitará procesar en el ciclo de sondeo. 

receive.buffer.bytes y send.buffer.bytes

Estos son los tamaños de los búferes de envío y recepción de TCP que utilizan los sockets al escribir y leer datos. Si se establecen en -1, se utilizarán los valores predeterminados del sistema operativo. Puede ser una buena idea aumentarlos cuando los productores o consumidores se comunican con intermediarios en un centro de datos diferente, porque esos enlaces de red suelen tener mayor latencia y menor ancho de banda.

sábado, 20 de noviembre de 2021

Ejemplo de microservicio con Spring boot y Spring Cloud


Vamos hacer un hola mundo con microservicios y Spring boot. Ojo usamos algunas tecnologías, no todo porque es un hola mundo: 

//Removed other imports for conciseness

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;

import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;


@SpringBootApplication

@RestController

@RequestMapping(value="hello")

@EnableCircuitBreaker //Enables the service to use the Hystrix and Ribbon libraries

@EnableEurekaClient //Service discovery 

public class Application {

public static void main(String[] args) {

    SpringApplication.run(Application.class, args);

    }


    @HystrixCommand(threadPoolKey = "helloThreadPool")

    public String helloRemoteServiceCall(String firstName, String lastName){

        ResponseEntity<String> restExchange = restTemplate.exchange( 

            "http://logical-service-id/name/[ca]{firstName}/{lastName}",

             HttpMethod.GET,

             null, String.class, firstName, lastName);

        return restExchange.getBody();

}


    @RequestMapping(value="/{firstName}/{lastName}", method = RequestMethod.GET)

    public String hello( @PathVariable("firstName") String firstName,

        @PathVariable("lastName") String lastName) {

         return helloRemoteServiceCall(firstName, lastName)

    }

}

La anotación @EnableCircuitBreaker le dice que el microservicio Spring va a utilizar las bibliotecas de Netflix Hystrix en su aplicación. La anotación @EnableEurekaClient dice que el  microservicio se registre con un agente de Eureka Service Discovery y que utilizará el descubrimiento de servicios para buscar puntos finales de servicios REST remotos en su código. La configuración se lleva a cabo en un archivo de propiedades que le indicará al servicio simple la ubicación y el número de puerto de un servidor Eureka para contactar. Usamos Hystrix cuando declaramos el método de saludo:

@HystrixCommand(threadPoolKey = "helloThreadPool")
public String helloRemoteServiceCall(String firstName, String lastName)

La anotación @HystrixCommand hace dos cosas. Primero, cada vez que se llama al método helloRemoteServiceCall, no se invocará directamente. En cambio, el método se delegará a un grupo de subprocesos administrado por Hystrix. Si la llamada tarda demasiado (el valor predeterminado es un segundo), Hystrix interviene e interrumpe la llamada. Esta es la implementación del patrón de circuit breaker . Lo segundo que hace esta anotación es crear un grupo de subprocesos llamado helloThreadPool que es administrado por Hystrix. Todas las llamadas al método helloRemoteServiceCall solo se producirán en este grupo de subprocesos y se aislarán de cualquier otra llamada de servicio remoto que se realice.

Lo último a tener en cuenta es lo que ocurre dentro del método helloRemoteServiceCall. La presencia de @EnableEurekaClient le ha dicho a Spring Boot que va a usar una clase RestTemplate modificada (no es así como la Spring RestTemplate estándar funcionaría de fábrica) cada vez que realiza una llamada de servicio REST. Esta clase RestTemplate le permitirá pasar un ID de servicio lógico para el servicio que está intentando invocar:

ResponseEntity<String> restExchange = restTemplate.exchange(http://logical-service-id/name/{firstName}/{lastName}

La clase RestTemplate se pondrá en contacto con el servicio de Eureka y buscará la ubicación física de una o más de las instancias del servicio "nombre". Como consumidor del servicio, su código nunca tiene que saber dónde se encuentra ese servicio.

Además, la clase RestTemplate está usando la biblioteca Ribbon de Netflix. Ribbon recuperará una lista de todos los puntos finales físicos asociados con un servicio. Cada vez que el cliente llama al servicio, realiza un "round-robins" de la llamada a las diferentes instancias del servicio en el cliente sin tener que pasar por un equilibrador de carga centralizado. Al eliminar un equilibrador de carga centralizado y trasladarlo al cliente, elimina otro punto de falla (el equilibrador de carga baja) en la infraestructura de su aplicación.



viernes, 19 de noviembre de 2021

Apache Pulsar



Apache Pulsar es una plataforma de transmisión y mensajería distribuida cloud-native creada originalmente por Yahoo! y ahora un proyecto de top-level de Apache Software Foundation

Las características clave de Pulsar son :

  • Soporte nativo para múltiples clústeres en una instancia de Pulsar, con replicación geográfica perfecta de mensajes entre clústeres.
  • Latencia de publicación y de extremo a extremo muy baja.
  • Escalabilidad perfecta a más de un millón de temas o topicos.
  • Una API de cliente simple y que se puede implementar en Java, Go, Python y C ++.
  • Múltiples tipos de suscripción (exclusiva, compartida y conmutación por error) para temas.
  • Entrega de mensajes garantizada con almacenamiento de mensajes persistente proporcionado por Apache BookKeeper.
  • Pulsar Functions, un framework liviano sin servidor, ofrece la capacidad de procesamiento de flujar de datos.
  • Pulsar IO, es un framework de conexión que se basa en Pulsar Functions, hace que sea más fácil mover datos dentro y fuera de Apache Pulsar.
  • El almacenamiento por niveles.

Si queremos levantar Pulsar con Docker es tan fácil como hacer : 

$ docker run -it \
  -p 6650:6650 \
  -p 8080:8080 \
  --mount source=pulsardata,target=/pulsar/data \
  --mount source=pulsarconf,target=/pulsar/conf \
  apachepulsar/pulsar:2.8.1 \
  bin/pulsar standalone

Veamos un ejemplo en Python, primero debemos instalar el cliente de pulsar : 

$ pip install pulsar-client

Y luego podemos hacer un cliente productor : 

import pulsar

client = pulsar.Client('pulsar://localhost:6650')
producer = client.create_producer('my-topic')

for i in range(10):
    producer.send(('hello-pulsar-%d' % i).encode('utf-8'))

client.close()

Y luego un consumidor : 

import pulsar

client = pulsar.Client('pulsar://localhost:6650')
consumer = client.subscribe('my-topic',
                            subscription_name='my-sub')

while True:
    msg = consumer.receive()
    print("Received message: '%s'" % msg.data())
    consumer.acknowledge(msg)

client.close()

Y listo!!

Dejo link: https://pulsar.apache.org/

Haciendo microservicios con Spring Cloud

 

El equipo de Spring ha integrado una gran cantidad de proyectos open source usados en el mercado en un subproyecto de Spring conocido colectivamente como Spring Cloud. 

Spring Cloud envuelve el trabajo de empresas de código abierto como Pivotal, HashiCorp y Netflix en la implementaciones de patrones para trabajar con microservicios orientados a la nube. Spring Cloud simplifica la instalación y configuración de estos proyectos en aplicaciones Spring para que el desarrollador pueda concentrarse en escribir código, sin quedar enterrado en los detalles de la configuración de toda la infraestructura necesaria para construir e implementar una aplicación de microservicio.

Veamos un gráfico que muestra las implementaciones a patrones de microservicios con spring cloud : 



Empecemos por el principio, todo estos framework se apoyan en spring boot. Spring Boot es la tecnología central utilizada en la implementación de microservicios. Spring Boot simplifica enormemente el desarrollo de microservicios al simplificar las tareas principales de crear microservicios basados en REST. Spring Boot también simplifica enormemente la asignación de verbos de estilo HTTP (GET, PUT, POST y DELETE) a URL y la serialización del protocolo JSON hacia y desde objetos Java, así como la asignación de excepciones de Java a códigos de error HTTP estándar.

Spring Cloud Config maneja la administración de los datos de configuración de la aplicación a través de un servicio centralizado para que los datos de configuración de la aplicación (particularmente los datos de configuración específicos de su entorno) estén claramente separados de su microservicio implementado. Esto asegura que no importa cuántas instancias de microservicio aparezcan, siempre tendrán la misma configuración. Spring Cloud Config tiene su propio repositorio de administración de propiedades, pero también se integra con proyectos de código abierto como los siguientes:

  • Git: Git (https://git-scm.com/) es un sistema de control de versiones de código abierto que le permite administrar y realizar un seguimiento de los cambios en cualquier tipo de archivo de texto. Spring Cloud Config puede integrarse con un repositorio respaldado por Git y leer los datos de configuración de la aplicación fuera del repositorio.
  • Consul — Consul (https://www.consul.io/) es una herramienta de descubrimiento de servicios de código abierto que permite que las instancias de servicio se registren en el servicio. Los clientes del servicio pueden preguntarle a Consul dónde se encuentran las instancias del servicio. Consul también incluye una base de datos basada en el almacén de valores clave que Spring Cloud Config puede utilizar para almacenar datos de configuración de la aplicación.
  • Eureka — Eureka (https://github.com/Netflix/eureka) es un proyecto de Netflix de código abierto que, como Consul, ofrece capacidades de descubrimiento de servicios similares. Eureka también tiene una base de datos de valores clave que se puede usar con Spring Cloud Config.

Con el descubrimiento del servicio Spring Cloud, puede abstraer la ubicación física (IP y / o nombre del servidor) de donde se implementan sus servidores de los clientes que consumen el servicio. Los consumidores de servicios invocan la lógica empresarial de los servidores a través de un nombre lógico en lugar de una ubicación física. El descubrimiento de servicios de Spring Cloud también maneja el registro y la cancelación del registro de las instancias de servicios a medida que se inician y apagan. El descubrimiento de servicios de Spring Cloud se puede implementar utilizando Consul (https://www.consul.io/) y Eureka (https://github.com/Netflix/eureka) como su motor de descubrimiento de servicios.

Spring Cloud se integra en gran medida con los proyectos de código abierto de Netflix. Para los patrones de resiliencia del cliente de microservicio, Spring Cloud envuelve las bibliotecas de Netflix Hystrix (https://github.com/Netflix/Hystrix) y el proyecto Ribbon (https://github.com/Netflix/Ribbon) y hace que sean fácil de usar. 

Con las bibliotecas de Netflix Hystrix, puede implementar rápidamente patrones de resistencia del cliente de servicio, como los patrones Circuit breakers y Bulkhead.

Si bien el proyecto Netflix Ribbon simplifica la integración con agentes de descubrimiento de servicios como Eureka, también proporciona equilibrio de carga del lado del cliente de las llamadas de servicio de un consumidor de servicios. Esto hace posible que un cliente continúe realizando llamadas de servicio incluso si el agente de descubrimiento de servicios no está disponible temporalmente.

Spring Cloud utiliza el proyecto Netflix Zuul (https://github.com/Netflix/zuul) para proporcionar capacidades de enrutamiento de servicios para su aplicación de microservicio. Zuul es una puerta de enlace de servicios que procesa las solicitudes de servicio y se asegura de que todas las llamadas a sus microservicios pasen por una única "puerta de entrada" antes de que se invoque el servicio de destino. Con esta centralización de llamadas de servicio, puede aplicar políticas de servicio estándar, como autenticación de autorización de seguridad, filtrado de contenido y reglas de enrutamiento.

Spring Cloud Stream (https://cloud.spring.io/spring-cloud-stream/) es una tecnología habilitadora que le permite integrar fácilmente el procesamiento de mensajes livianos en su microservicio. Con Spring Cloud Stream, puede crear microservicios inteligentes que pueden usar eventos asincrónicos a medida que ocurren en su aplicación. Con Spring Cloud Stream, puede integrar rápidamente sus microservicios con agentes de mensajes como RabbitMQ (https://www.rabbitmq.com/) y Kafka (http://kafka.apache.org/).

Spring Cloud Sleuth (https://cloud.spring.io/spring-cloud-sleuth/) le permite integrar identificadores de seguimiento únicos en las llamadas HTTP y los canales de mensajes (RabbitMQ, Apache Kafka) que se utilizan dentro de su aplicación. Estos números de seguimiento, a veces denominados identificadores de correlación o seguimiento, le permiten realizar un seguimiento de una transacción a medida que fluye a través de los diferentes servicios de su aplicación. Con Spring Cloud Sleuth, estos ID de seguimiento se agregan automáticamente a cualquier declaración de registro que realice en su microservicio.

La verdadera belleza de Spring Cloud Sleuth se ve cuando se combina con herramientas de tecnología de agregación de registros como Papertrail (http://papertrailapp.com) y herramientas de rastreo como Zipkin (http://zipkin.io). Papertail es una plataforma de registro basada en la nube que se utiliza para agregar registros en tiempo real de diferentes microservicios en una base de datos consultable. Open Zipkin toma datos producidos por Spring Cloud Sleuth y le permite visualizar el flujo de sus llamadas de servicio involucradas para una sola transacción.

Spring Cloud Security (https://cloud.spring.io/spring-cloud-security/) es un marco de autenticación y autorización que puede controlar quién puede acceder a sus servicios y qué pueden hacer con sus servicios. Spring Cloud Security se basa en tokens y permite que los servicios se comuniquen entre sí a través de un token emitido por un servidor de autenticación. Cada servicio que recibe una llamada puede verificar el token proporcionado en la llamada HTTP para validar la identidad del usuario y sus derechos de acceso al servicio. Además, Spring Cloud Security es compatible con JavaScript Web Token (https://jwt.io). JavaScript Web Token (JWT) estandariza el formato de cómo se crea un token OAuth2 y proporciona estándares para firmar digitalmente un token creado.

Dejo link : http://projects.spring.io/spring-cloud/

viernes, 12 de noviembre de 2021

Microservice build/deployment patterns


Una de las partes centrales de una arquitectura de microservicio es que cada instancia de un microservicio debe ser idéntica a todas sus demás instancias. No se puede permitir que ocurra un "desvio de la configuración" (algo cambia en un servidor después de que se ha implementado), porque esto puede introducir inestabilidad en las aplicaciones que lo consumen.

Para que esto no suceda debemos integrar la configuración con su infraestructura en el proceso de deployment para que ya no implemente artefactos de software como Java WAR o EAR sino en imagen del servidor virtual. Luego, cuando se deploya el microservicio, se deploya toda la imagen de la máquina virtual. Los patrones que nos ayudan a trabajar de esta manera son : 

Build and deployment pipeline : ¿cómo se crea un proceso de implementación y compilación repetible que enfatice las compilaciones y la implementación con un solo botón en cualquier entorno de su organización?

Infraestructura como código: ¿cómo trata el aprovisionamiento de sus servicios como código que se puede ejecutar y administrar bajo el control de versiones?

Servidores inmutables: una vez que se crea una imagen de microservicio, ¿cómo se asegura de que nunca cambie después de que se haya implementado?

Servidores Phoenix: cuanto más tiempo esté funcionando un servidor, mayor será la posibilidad de que la configuración se desvíe. ¿Cómo se asegura de que los servidores que ejecutan microservicios se eliminen de forma regular y se vuelvan a crear a partir de una imagen inmutable?

Nuestro objetivo con estos patrones y temas es exponer y eliminar despiadadamente los cambios de configuración lo más rápido posible antes de que escalen y se descontrole todo. 


Microservice logging and tracing patterns


La belleza de la arquitectura de microservicios es que una aplicación monolítica se divide en pequeñas piezas de funcionalidad que se pueden implementar de forma independiente entre sí. La desventaja de una arquitectura de microservicio es que es mucho más difícil depurar y rastrear qué diablos está sucediendo dentro de sus servicios.

Por este motivo, hay tres patrones básicos de registro y seguimiento:

  • Correlación de registros: ¿cómo se vinculan todos los registros producidos entre servicios para una sola transacción de usuario? Con este patrón se implementa un ID de correlación, que es un identificador único que se llevará a través de todas las llamadas de servicio en una transacción y se puede utilizar para unir las entradas de registro producidas por cada servicio.
  • Agregación de registros: con este patrón, se puede reunir todos los registros producidos por sus microservicios (y sus instancias individuales) en una sola base de datos consultable. También veremos cómo usar los ID de correlación para ayudarlo a buscar sus registros agregados.
  • Seguimiento de microservicios: este patron permite visualizar el flujo de una transacción de cliente en todos los servicios involucrados y comprender las características de rendimiento de los servicios involucrados en la transacción.


miércoles, 10 de noviembre de 2021

Microservice security patterns


Los patrones de seguridad de microservicios son:

  • Autenticación: ¿cómo se determina que el cliente de servicio que llama al servicio es quien dice ser?
  • Autorización: ¿cómo se determina si el cliente de servicio que llama a un microservicio puede realizar la acción que intenta realizar?
  • Administración y propagación de credenciales: ¿cómo se evita que un cliente de servicio tenga que presentar constantemente sus credenciales para las llamadas de servicio involucradas en una transacción? 


Microservice client resiliency patterns


Debido a que las arquitecturas de microservicio están altamente distribuidas, debe ser extremadamente sensible en cómo evitar que un problema en un solo servicio (o instancia de servicio) se propague en cascada hacia los consumidores del servicio. Con este fin, cubriremos cuatro patrones de resiliencia del cliente:

  • Equilibrio de carga del lado del cliente: ¿cómo se almacena en caché la ubicación de las instancias de servicio en el cliente de servicio para que las llamadas a varias instancias de un microservicio tengan un equilibrio de carga en todas las instancias de estado de ese microservicio?
  • Circuit breakers pattern : ¿cómo se evita que un cliente continúe llamando a un servicio que está fallando o sufre problemas de rendimiento? Cuando un servicio se ejecuta lentamente, consume recursos en el cliente que lo llama. Desea que las llamadas de microservicio fallidas fallen rápidamente para que el cliente que realiza la llamada pueda responder rápidamente y tomar una acción apropiada.
  • Fallback pattern : cuando falla una llamada de servicio, ¿cómo se proporciona un mecanismo de "complemento" que permitirá que el cliente del servicio intente realizar su trabajo a través de medios alternativos distintos del microservicio al que se llama?
  • Bulkhead pattern: las aplicaciones de microservicio utilizan varios recursos distribuidos para realizar su trabajo. ¿Cómo compartimenta estas llamadas para que el mal comportamiento de una llamada de servicio no afecte negativamente al resto de la aplicación?

Primeros pasos con Apache Kafka parte 20



Seguimos con Kafka

En el corazón de la API del consumidor hay un bucle simple para sondear el servidor en busca de datos.

Una vez que el consumidor se suscribe a los temas, el ciclo de sondeo maneja todos los detalles de coordinación, reequilibrio de particiones, pings y obtención de datos, dejando al desarrollador con una API limpia que simplemente que devuelve los datos disponibles de las particiones asignadas. Veamos un ejemplo :

try {

    while (true) {

        ConsumerRecords<String, String> records = consumer.poll(100);

        for (ConsumerRecord<String, String> record : records) {

            log.debug("topic = %s, partition = %s, offset = %d, customer = %s, country = %s\n",

                record.topic(), record.partition(), record.offset(), record.key(), record.value());

            int updatedCount = 1;

            if (custCountryMap.countainsValue(record.value())) {

                updatedCount = custCountryMap.get(record.value()) + 1;

            }

            custCountryMap.put(record.value(), updatedCount)

            JSONObject json = new JSONObject(custCountryMap);

            System.out.println(json.toString(4))

        }

    }

} finally {

    consumer.close();

}

Este es un bucle infinito. Los consumidores suelen ser aplicaciones de larga duración que sondean continuamente a Kafka en busca de más datos. 

ConsumerRecords<String, String> records = consumer.poll(100);  en esta linea se puede ver que los consumidores deben seguir sondeando a Kafka o serán considerados muertos y las particiones que están consumiendo serán entregadas a otro consumidor del grupo para que continúe consumiendo. El parámetro que pasamos, poll (), es un intervalo de tiempo de espera y controla cuánto tiempo se bloqueará poll () si los datos no están disponibles en el búfer del consumidor. Si se establece en 0, poll () regresará inmediatamente; de lo contrario, esperará el número especificado de milisegundos para que lleguen los datos del intermediario.

poll () devuelve una lista de registros. Cada registro contiene el tema y la partición de la que proviene el registro, el desplazamiento del registro dentro de la partición y, por supuesto, la clave y el valor del registro. Por lo general, queremos iterar sobre la lista y procesar los registros individualmente. El método poll () toma un parámetro de tiempo de espera. Esto especifica cuánto tiempo tardará la encuesta en regresar, con o sin datos. El valor suele depender de las necesidades de la aplicación para obtener respuestas rápidas: ¿qué tan rápido desea devolver el control al subproceso que realiza el sondeo?

El procesamiento generalmente termina escribiendo un resultado en un almacén de datos o actualizando un registro almacenado. Aquí, el objetivo es mantener un recuento continuo de clientes de cada condado, por lo que actualizamos una tabla hash e imprimimos el resultado como JSON. Un ejemplo más realista almacenaría el resultado de las actualizaciones en un almacén de datos.

Siempre se debe cerrar la conexión del consumidor antes de salir. Esto cerrará las conexiones de red y los slots. También activará un reequilibrio de inmediato en lugar de esperar a que el coordinador del grupo descubra que el consumidor dejó de enviar pings y es probable que esté muerto, lo que llevará más tiempo y, por lo tanto, dará como resultado un período de tiempo más largo en el que los consumidores no pueden consumir mensajes de un subconjunto de las particiones.

El ciclo de la encuesta hace mucho más que simplemente obtener datos. La primera vez que llama a poll () con un nuevo consumidor, es responsable de encontrar el GroupCoordinator, unirse al grupo de consumidores y recibir una asignación de partición. Si se activa un reequilibrio, también se manejará dentro del ciclo de sondeo. Y, por supuesto, los latidos que mantienen vivos a los consumidores se envían desde dentro del ciclo de la encuesta. Por esta razón, intentamos asegurarnos de que cualquier procesamiento que hagamos entre iteraciones sea rápido y eficiente.

Primeros pasos con Apache Kafka parte 19



Seguimos con Kafka

Una vez que creamos un consumidor, el siguiente paso es suscribirse a uno o más temas. El método subcribe () toma una lista de temas como parámetro, por lo que es bastante simple de usar:

consumer.subscribe(Collections.singletonList("customerCountries"));

También es posible llamar a subscribe con una expresión regular. La expresión puede coincidir con varios nombres de temas, y si alguien crea un nuevo tema con un nombre que coincida, se producirá un reequilibrio casi de inmediato y los consumidores comenzarán a consumir desde el nuevo tema. Esto es útil para aplicaciones que necesitan consumir de múltiples temas y pueden manejar los diferentes tipos de datos que contendrán los temas.

La suscripción a varios temas mediante una expresión regular se usa con mayor frecuencia en aplicaciones que replican datos entre Kafka y otro sistema.

Para suscribirse a todos los temas de prueba, podemos llamar a:

consumer.subscribe("test.*");


Essential Programming Books


 No conocía este sitio y esta rebueno. Un montón de libros gratuitos.  La lista es : 

Book
Essential Go
Essential C++
Essential Javascript
Essential CSS
Essential HTML
Essential HTML5 Canvas
Essential Java
Essential Kotlin
Essential C#
Essential Python
Essential PostgreSQL
Essential MySQL
Essential iOS
Essential Android
Essential Bash
Essential PowerShell
Essential Batch
Essential Git
Essential PHP
Essential Ruby
Essential .NET framework
Essential Node
Essential Dart
Essential TypeScript
Essential Swift
Essential Algorithms
Essential C
Essential Objective-C
Essential React
Essential React Native
Essential Ruby On Rails
Essential SQL

martes, 9 de noviembre de 2021

GraalVM 21.3 soporta Java 17


Oracle Labs lanzó GraalVM 21.3, que ofrece soporte para Java 17 y JDK Flight Recorder (JFR) para la generación de perfiles de producción de bajo costo de ejecutables nativos de Java. En comparación con la versión 21.1 de abril de 2021, esta versión crea ejecutables nativos un 14% más pequeños para Spring PetClinic JDBC con un 20% menos de tiempo de compilación. La versión ejecuta aplicaciones más rápido y comienza a admitir Java Platform Module System.

GraalVM también inicia aplicaciones Java no nativas interpretadas más rápido ahora. Esto ayuda durante el desarrollo donde la ejecución de ejecutables nativos de Java no es práctica debido a los largos tiempos de compilación.

GraalVM viene en dos ediciones: Community Edition y Enterprise Edition. La licencia GPL de Community Edition con "Classpath" Exception significa que es "de uso gratuito para cualquier propósito y viene sin condiciones, pero tampoco garantías ni soporte". Sus lanzamientos reciben actualizaciones durante un año. Enterprise Edition requiere la suscripción a Oracle Java SE para uso en producción, pagada por usuario o por procesador.

Enterprise Edition también incluye soporte de Oracle y tiene características exclusivas de rendimiento y seguridad, como optimizaciones guiadas por perfiles y el recolector de basura G1. Es por eso que sus ejecutables nativos alcanzan el 98% del rendimiento de OpenJDK en una combinación de referencia frente al 43% con la Community Edition, todo mientras usan menos memoria y se inician más rápido que las aplicaciones OpenJDK.

Oracle afirma que los ejecutables nativos de Enterprise Edition son incluso hasta un 40% más rápidos que OpenJDK en algunos escenarios de aprendizaje automático. Constant Blinding es un ejemplo de una función de seguridad en Enterprise Edition. Defiende contra ataques de pulverización JIT y se puede habilitar con -Dgraal.BlindConstants = true.

Una nueva opción de configuración en tiempo de compilación para ejecutables nativos permite deshabilitar funciones como la reflexión, JNI, objetos de proxy dinámico y excluir los recursos de classpath si no se puede acceder a una clase. Eso puede llevar a ejecutables nativos más pequeños.

La nueva proftool (Linux solo por ahora) recopila métricas de rendimiento en tiempo de ejecución de aplicaciones Java de JVM, la herramienta perf de Linux y HotSpot LogCompilation. Esto ayuda con el análisis de rendimiento JIT.

GraalVM Polyglot Runtime ejecuta varios lenguajes además de Java. El tiempo de ejecución de JavaScript se actualizó a Node.js 14.17.6 y mejoró su compatibilidad con WebAssembly. El tiempo de ejecución de Python ahora puede ejecutar paquetes binarios de HPy sin volver a compilarlos. También admite el módulo de multiprocesamiento para orquestar tareas en múltiples contextos de Python en una máquina de múltiples núcleos y tiene un módulo de socket escrito en código nativo.

Ruby en GraalVM ahora usa TRegex, un motor de expresión regular más eficiente, y es hasta 9 veces más rápido en algunos puntos de referencia. El tiempo de ejecución de R actualizado de PCRE a PCRE2 versión 10.37. La cadena de herramientas LLVM para compilar C / C ++ se actualizó a la versión 12.0.1. Y WebAssembly se ejecuta más rápido en algunos escenarios en GraalVM Enterprise Edition.

Las herramientas de GraalVM mejoradas en Visual Studio Code: las herramientas de GraalVM para Java se pueden adjuntar a ejecutables nativos para la depuración. Y las herramientas GraalVM para Micronaut pueden implementar, ejecutar y depurar aplicaciones Micronaut en un clúster de Kubernetes.

Spring Native, una nueva iniciativa de Spring, crea ejecutables nativos para aplicaciones Spring. La versión 0.11, cuyo lanzamiento está programado para el 19 de noviembre de 2021, se basará en GraalVM 21.3 y el próximo lanzamiento de Spring Boot 2.6. La pila Java nativa de Kubernetes Quarkus utilizará GraalVM 21.3 como la versión recomendada en la próxima versión 2.5. El marco de pila completa Micronaut admitirá oficialmente GraalVM 21.3 con el próximo lanzamiento de la versión 3.2.0 programado para fines de noviembre o diciembre de 2021.

Dejo link: https://medium.com/graalvm/graalvm-21-3-is-here-java-17-native-image-performance-updates-and-more-ac4cbafcfc05

domingo, 7 de noviembre de 2021

Microservice routing patterns


Los patrones de enrutamiento de microservicios se ocupan de cómo una aplicación cliente desea consumir un microservicio, como descubre la ubicación del servicio y se enruta a él.

En una aplicación basada en la nube, es posible que tenga cientos de instancias de microservicio en ejecución. Deberá abstraer la dirección IP física de estos servicios y tener un único punto de entrada para las llamadas de servicio, de modo que pueda hacer cumplir de manera constante las políticas de seguridad y contenido para todas las llamadas de servicio.

El descubrimiento y el enrutamiento de servicios responden a la pregunta: "¿Cómo obtengo la solicitud de un servicio de mi cliente a una instancia específica de un servicio?"

Descubrimiento de servicios: ¿cómo puede hacer que su microservicio sea reconocible para que las aplicaciones cliente puedan encontrarlos sin tener la ubicación del servicio codificada en la aplicación? ¿Cómo se asegura de que las instancias de microservicio que funciona mal se eliminen del grupo de instancias de servicio disponibles? 

Enrutamiento de servicios: ¿cómo proporciona un único punto de entrada para todos sus servicios de modo que las políticas de seguridad y las reglas de enrutamiento se apliquen de manera uniforme a múltiples servicios e instancias de servicio en sus aplicaciones de microservicio? ¿Cómo se asegura de que cada desarrollador de su equipo no tenga que idear sus propias soluciones para proporcionar enrutamiento a sus servicios? 

El descubrimiento de servicios y el enrutamiento de servicios parecen tener una secuencia de eventos codificada de forma rígida entre ellos (primero viene el enrutamiento de servicios y el descubrimiento de servicios). Sin embargo, los dos patrones no dependen el uno del otro. Por ejemplo, podemos implementar el descubrimiento de servicios sin enrutamiento de servicios. Puede implementar el enrutamiento de servicios sin descubrimiento de servicios (aunque su implementación es más difícil).

viernes, 5 de noviembre de 2021

Core development patterns


Los patrones de desarrollo core de microservicios abordan los conceptos básicos de construcción un microservicio. 

Granularidad del servicio: ¿cómo aborda la descomposición de un dominio empresarial en microservicios para que cada microservicio tenga el nivel adecuado de responsabilidad? Hacer que un servicio sea demasiado grueso con responsabilidades que se superponen en diferentes dominios de problemas comerciales hace que el servicio sea difícil de mantener y cambiar con el tiempo. Hacer que el servicio sea demasiado detallado aumenta la complejidad general de la aplicación y convierte el servicio en una capa de abstracción de datos “tonta” sin lógica.

Protocolos de comunicación: ¿cómo se comunicarán los desarrolladores con su servicio? ¿Utiliza XML (Extensible Markup Language), JSON (JavaScript Object Notation) o un protocolo binario como Thrift para enviar datos de un lado a otro de sus microservicios? 

Diseño de interfaz: ¿cuál es la mejor manera de diseñar las interfaces de servicio reales que los desarrolladores usarán para llamar a su servicio? ¿Cómo estructura las URL de sus servicios para comunicar la intención del servicio? ¿Qué pasa con el control de versiones de sus servicios? 

Gestión de la configuración del servicio: ¿cómo gestiona la configuración de su microservicio para que, a medida que se mueva entre diferentes entornos en la nube, nunca tenga que cambiar el código o la configuración de la aplicación principal?

Procesamiento de eventos entre servicios: ¿cómo desacopla su microservicio mediante eventos para minimizar las dependencias codificadas entre sus servicios y aumentar la resistencia de su aplicación? 

Los microservicios son más que escribir el código


Si bien los conceptos relacionados con la creación de microservicios individuales son fáciles de entender, ejecutar y admitir una aplicación de microservicio robusta (especialmente cuando se ejecuta en la nube) implica más que escribir el código para el servicio. Escribir un servicio sólido incluye considerar varios temas. 

Tamaño adecuado: ¿cómo nos aseguramos de que los microservicios tengan el tamaño adecuado para que no tenga un microservicio que asuma demasiada responsabilidad? Un servicio con el tamaño adecuado nos permite realizar cambios rápidamente en una aplicación y reduce el riesgo general de una interrupción en toda la aplicación.

Transparencia en la ubicación: ¿cómo gestionamos los detalles físicos de la invocación del servicio cuando, en una aplicación de microservicio, se pueden iniciar y cerrar varias instancias de servicio rápidamente?

Resiliente: ¿cómo protege a sus consumidores de microservicios y la integridad general de su aplicación al enrutar los servicios que fallan y asegurarse de adoptar un enfoque "fail-fast"?

Repetible: ¿cómo se asegura de que se garantice que cada nueva instancia de su servicio que aparece tenga la misma configuración y base de código que todas las demás instancias de servicio en producción?

Escalable: ¿cómo se utilizan los eventos y el procesamiento asincrónico para minimizar las dependencias directas entre sus servicios y asegurarse de poder escalar correctamente sus microservicios?

Con un enfoque basado en patrones, tenemos soluciones comunes que se pueden utilizar en diferentes implementaciones de tecnología para estos problemas. Entre los patrones existe seis categorías de patrones de microservicio:

  •  Core development patterns
  •  Routing patterns
  •  Client resiliency patterns
  •  Security patterns
  •  Logging and tracing patterns
  •  Build and deployment patterns

Repasemos estos patrones con más detalle en próximos posts. 

martes, 2 de noviembre de 2021

Oracle JDK 17 es gratuita nuevamente para uso comercial

 

Oracle JDK 17 es gratuita nuevamente para uso comercial, bajo la nueva licencia "Términos y condiciones sin cargo de Oracle" (NFTC). Este movimiento revierte la decisión de 2018 de cobrar por el uso en producción de Oracle JDK y no afecta la distribución de OpenJDK de Oracle. La NFTC se aplica a la versión 17 recientemente lanzada de Oracle JDK y versiones futuras.

En conclusión oracle revierte una mala decisión. 

Dejo link: https://www.oracle.com/downloads/licenses/no-fee-license.html