Quantcast
Channel: Planeta Código
Viewing all 2715 articles
Browse latest View live

Variable not found: Implementando más fácilmente background tasks en ASP.NET Core 2.1 (bueno, y 2.0)

$
0
0
ASP.NET CoreHace algún tiempo hablamos por aquí sobre cómo implementar tareas en segundo plano en ASP.NET Core con IHostedService, una fórmula en principio bastante sencilla proporcionada por el framework para introducir en nuestras aplicaciones servicios, o procesos en segundo plano. Recordaréis que la magia consistía únicamente en implementar el interfaz IHostedService y registrar dicha implementación en el inyector de dependencias.

Sin embargo, aunque podía parecer lo contrario, la implementación correcta del interfaz IHostedService no era una tarea sencilla. De hecho, como se comenta en este issue de Github, IHostedServiceera un interfaz de muy bajo nivel y, al no quedar claro cómo debía utilizarse en escenarios reales, podría dar lugar a bastantes confusiones y problemas.

Entendiendo IHostedService

Lo mejor para entender cómo funcionan las cosas, sin duda, es abrir el capó. Buceando en las tripas de ASP.NET Core, se puede observar que el componente encargado de lanzar los servicios en segundo plano es HostedServiceExecutor. Este componente recibe en su constructor la colección de objetos de IHostedService registrados en el sistema y se encarga principalmente de dos cosas: arrancar y detener los servicios.
Este componente, a su vez, es utilizado por WebHost, que es la implementación por defecto de IWebHost, el host que se construye por defecto durante la inicialización de la aplicación en el archivo Program.cs. Es en sus métodos StartAsync() y StopAsync() donde se llaman respectivamente a los métodos con el mismo nombre de HostedServiceExecutor.
El método StartAsync() de HostedServiceExecutor, invocado durante el inicio del host, recorre todos los IHostedServices e invoca a su método StartAsync(). Más o menos el código de este proceso (algo simplificado) es el siguiente, donde _services es la colección de IHostedServices registrados en el inyector de dependencias:
...
foreach(var service in _services)
{
await service.StartAsync(cancellationToken)
}
El método StartAsync() de cada IHostedService es invocado con un await, lo que implica que el bucle quedará bloqueado en cada iteración hasta que acabe esta llamada. Por tanto, todas las tareas posteriores del inicio de la aplicación como llamar al StartAsync() de otros servicios o incluso lanzar Kestrel no podrán ser realizadas mientras el hilo esté bloqueado.

También en este contexto queda más claro que el token de cancelación que nos llegaba a StartAsync() no tiene como objetivo gestionar la cancelación de la tarea en segundo plano, sino cancelar la propia inicialización. Y lo mismo ocurre con el token recibido en StopAsync(), cuya misión es forzar la finalización de las tareas.

Por tanto, la forma correcta de implementar un servicio en segundo plano en ASP.NET Core 2.0 es algo menos trivial de lo que podría parecer en un principio. Por ejemplo, en el método StartAsync() debemos asegurarnos de iniciar la tarea y retornar rápidamente el control para que el arranque de la aplicación no quede bloqueado, gestionar correctamente los tokens de cancelación, la detención de la tarea, y otros detalles.

La forma correcta de hacerlo en ASP.NET Core 2.1 (y ASP.NET Core 2.0)

Consciente de estas dificultades, el equipo de ASP.NET Core ha introducido en la versión 2.1 la clase BackgroundService, que nos lo pone todo bastante más sencillo. De hecho, es la fórmula recomendada y que debemos utilizar para no tener problemas. Por si tenéis curiosidad por ver qué hace por dentro, ahí va el código actual (lo he limpiado un poco para hacerlo más legible):
publicabstractclass BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
privatereadonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

protectedabstract Task ExecuteAsync(CancellationToken stoppingToken);

publicvirtual Task StartAsync(CancellationToken cancellationToken)
{
_executingTask = ExecuteAsync(_stoppingCts.Token);
if (_executingTask.IsCompleted)
return _executingTask; // If the task is completed then return it

return Task.CompletedTask; // Otherwise it's running
}

publicvirtualasync Task StopAsync(CancellationToken cancellationToken)
{
if (_executingTask == null)
return; // Stop called without start

try
{
_stoppingCts.Cancel(); // Signal cancellation to the executing method
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}

publicvirtualvoidDispose()
{
_stoppingCts.Cancel();
}
}
Como podemos ver, esta clase abstracta realiza internamente todos los trabajos de fontanería necesarios para iniciar y parar la tarea correctamente sin bloquear el arranque y utilizando apropiadamente los tokens de cancelación. Nosotros simplemente deberemos sobrescribir su método ExecuteAsync(), donde podremos centrarnos en implementar la lógica propia de nuestra aplicación.

Así, la forma correcta de escribir un servicio utilizando esta clase sería la siguiente; veréis que es bastante más sencillo que haciendo lo propio con IHostedService:
publicclass MyBackgroundService : BackgroundService
{
privatereadonly ILogger<MyBackgroundService> _logger;

publicMyBackgroundService(ILogger<MyBackgroundService> logger)
{
_logger = logger;
}

protectedoverrideasync Task ExecuteAsync(CancellationToken stoppingToken)
{
for (var i = 1; !stoppingToken.IsCancellationRequested; i++)
{
_logger.LogInformation($"Loop #{i}");
await Task.Delay(1000, stoppingToken);
}
}
}
Por cierto, recordad que la llamada a Task.Delay() lanzará una excepción de tipo CancelledTaskException si es cancelada, por lo que si ponéis algún código tras el bucle es posible que no se ejecute. No olvidéis usar un try/catch en estos casos.
Por último, fijaos que aunque la clase BackgroundServices se ha introducido en ASP.NET Core 2.1, podéis usarla en vuestras aplicaciones ASP.NET Core 2.0 o 1.x si la incluís manualmente en vuestras aplicaciones, bien copiando y pegando el código que tenéis más arriba, o bien obteniéndolo desde Github.

Publicado en Variable not found.

Picando Código: NotPinkCon – conferencia de seguridad informática en Buenos Aires, Argentina

$
0
0

NotPinkCon es un evento de conferencias técnicas en seguridad informática, impartidas por mujeres y dirigidas a estudiantes, profesionales y entusiastas del tema (hombres y mujeres). Las charlas serán de nivel técnico variado, útiles tanto para quienes recién comienzan o aún no lo han hecho pero tienen interés en aprender, como para quienes tienen más experiencia.

Está abierto registro y call for papers en este enlace. El llamado a charlas cierra el 30 de junio, las charlas se anuncian el 10 de julio y el evento es el 24 de agosto.

Pueden seguir la conferencia en Twitter para mantenerse al tanto de las novedades: @NotPinkCon.

Fecha y hora
Viernes 24 de agosto de 2018
9:00 – 17:00 hora estándar de Argentina (Buenos Aires)

Ubicación
Mario Bravo 1050
1050 Mario Bravo
Palermo, CABA C1175
Argentina

La entrada es gratuita, y ya está abierto el registro.

NotPinkCon 2018

Blog Bitix: Dependencias sobre librerías de lado de cliente con Webjars en una aplicación web Java

$
0
0
Java
Webjars

Una aplicación web se compone de código de lado de servidor, en el caso de utilizar el lenguaje de programación Java de código Java normalmente utilizando algún de los muchos framework web, por otra parte se compone de código de lado de cliente con una gran variedad de librerías de JavaScript como jQuery, React, Underscore o Bootstrap para los estilos. En las aplicaciones Java las librerías de lado de servidor se gestionan como dependencias del proyecto y con herramientas como Gradle se puede automatizar el descargar la librería de repositorios como Maven Central y la versión que se necesite así como hacer sencillo actualizar a una nueva. En el caso de las librerías de lado del cliente con Webjars se consiguen los mismos beneficios.

Los webjars son librerías de extensión jar con los recursos de lado del cliente empaquetados en ellos que en el momento de ser requeridos pueden ser devueltos como un recurso estático por la aplicación, incluyen los archivos JavaScript sin minimizar y minimizados, los archivos map para depuración si minimizados están ofuscados, recursos de estilos CSS o imágenes. Se gestionan como cualquier otra dependencia del proyecto Java lo que proporciona las mismas ventajas de obtener las dependencias de forma automática y hace fácil actualizar a una nueva versión. Por si fuera poco es muy sencillo utilizar webjars, para los frameworks más populares se ofrece una pequeña guía de uso en la documentación.

Las librerías más populares de JavaScript o CSS están empaquetadas como webjars en las diferentes versiones y han sido publicadas de forma que es posible añadir la dependencia en la versión concreta que necesite la aplicación. Dado que los webjars se gestionan como una dependencia Java si estos a su vez tiene alguna dependencia sobre otra librería está se incluyen en el proyecto de forma transitiva. El contenido del webjar para jQuery es el siguiente.

En el caso del framework web Apache Tapestry basado en componentes para el desarrollo de aplicaciones web Java tan solo hay que incluir la dependencia en el proyecto y un poco de configuración en el módulo de la aplicación para el contenedor de dependencias como se indica en la guía de uso.

Inspeccionando el código fuente de la página devuelta se observa que en el caso de Tapestry la URL generada al solicitar en un webjar es del estilo https://localhost:8443/assets/webjars/z941c28a3/requirejs/2.3.5/require.js.

Apache Tapestry con Webjars

Los webjars muy útiles para gestionar las librerías de lado cliente que hacen innecesario descargar manualmente las dependencias, automatizan la descarga, hacen muy sencillo actualizar a nuevas versiones y es muy fácil de usar al no requerir mucha confiración ni ser invasiva. Además, al estar como una dependencia en el archivo de construcción del proyecto queda indicado de forma explícita que el proyecto utiliza y necesita esa librería. Para mi son una herramienta imprescindible.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


Jesús Perales: No me funciona el depurador de Firefox

$
0
0
No me funciona el depurador de Firefox

Al grano:

Se escribe en la barra navegación about:config , aceptamos el riesgo y después buscamos devtools.debugger.new-debugger-frontend , damos click derecho y cambiamos de true a false, ya podrias depurar javascript de nuevo.

Esto parece suceder debido a que se esta cambiando el depurador por uno nuevo y en Firefox edición para desarrolladores esta activado por defecto, por lo cual debemos desactivarlo y tendremos el viejo y confiable depurador, solo hasta que se encuentre mas estable, en teoría traerá muchas mejoras.

Fuentes

Blog Bitix: Actualizar las versiones de las librerías JavaScript built-in de Apache Tapestry

$
0
0
Apache Tapestry
Java

El frameworkApache Tapestry para el desarrollo de aplicaciones web Java basado en componentes aparte de ser un framework para el desarrollo de la capa de presentación del lado del servidor y lógica de negocio también ofrece soporte para el desarrollo de funcionalidad del lado del cliente. Incorpocopora de serie las librerías RequireJS para la gestión de módulos y dependencias de JavaScript, la popular jQuery para la manipulación de elementos del HTML y Underscore que añade algunas utilidades que no tiene el lenguaje JavaScript y Bootstrap para los estilos además de alguna otra librería JavaScript de menor relevancia que estas.

Sin embargo, las versiones de las librerías de lado del cliente que incorpora de serie son antiguas. En la versión 5.4.3 de RequireJS se incorpora la versión 2.1.17, de jQuery la versión 1.12.1 y de Underscore la versión 1.8.3 cuando en el momento de publicar este artículo sus versiones más nuevas son 2.3.5, 3.3.1 y 1.9.1 respectivamente. Dado que de Apache Tapestry no se publican versiones frecuentemente el framework no sigue el ritmo de actualizaciones más rápido de las librerías JavaScript. Pero pueden ser actualizadas sin mucho esfuerzo.

Apache Tapestry es un framework extremadamente personalizable, adaptable y extensible, prácticamente cualquier cosa interna de su funcionamiento puede ser modificada gracias a su propio gestor de dependencias o inversión de control. Las versiones de las librerías anteriores se definen en el archivo JavaScriptModule.java del código fuente de Tapestry y haciendo una contribución en el contenedor de dependencias a la configuración del servicio JavaScriptStack se pueden modificar.

Estas pocas líneas de código bastan para redefinir las versiones de las librerías.

Antes y después de actualizar las librerías JavaScript

Además, en este caso las nuevas versiones las he proporcionado gestionando las dependencias de lado del cliente con webjars que se incluyen como cualquier otra dependencia Java del proyecto. Esto permite saber qué dependencias de lado de cliente tiene el proyecto, obtener las dependencias de forma automática y actualizarlas de forma sencilla con la herramienta de construcción del proyecto como Gradle.

Al usar una versión más reciente de las librerías es importante asegurarse y revisar que todas las funcionalidades necesarias son compatibles hacia atrás. Al hacer en el caso de jQuery una actualización a una versión mayor hay que probar y leer las notas de publicación de las versiones por si hubiera un problema de compatibilidad en las funcionalidades que requiere de ella el framework Apache Tapestry.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


Variable not found: Enlaces interesantes 324

$
0
0
Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

Variable not found: Novedades de ASP.NET Core 2.1

$
0
0
ASP.NET CoreComo seguro sabréis, tras varias previews y release candidates, hace pocos días se lanzaron las actualizaciones de varios productos que ya llevábamos tiempo esperando:
Centrándonos en ASP.NET Core, la versión 2.1 incluye numerosas mejoras sobre su predecesora. Muchos componentes internos han sido reescritos y se han añadido características que, sin duda, continúan haciendo de ASP.NET Core un framework cada vez más completo y eficiente.

Pero eso sí, no rompe nada y todo lo que sabemos de ASP.NET 2.0 sigue siendo válido (de ahí que sea simplemente una revisión 2.x: no hay breaking changes).

En este post vamos a ver por encima las novedades que creo más destacadas de esta entrega.

Más rendimiento

Este ha sido uno de los grandes focos en ASP.NET 2.1, y vamos a notarlo en muchos aspectos.

Primero, la compilación de proyectos será bastante más rápida, algo que podrá apreciarse especialmente en proyectos grandes.

Pero más importante son las mejoras en tiempo de ejecución, muchas de ellas apoyadas en las novedades a este respecto introducidas en .NET Core 2.1. De hecho, ASP.NET Core está apareciendo ya en las posiciones más altas de los rankings de rendimiento.

En versiones anteriores de ASP.NET Core, libuv, una biblioteca de comunicaciones asíncronas, era el transporte utilizado por defecto por Kestrel para comunicarse con el exterior. En ASP.NET Core 2.1, este componente ha sido reemplazado por una implementación de sockets .NET; al evitar dependencias nativas, hace que el despliegue en nuevas plataformas sólo dependa de la disponibilidad de .NET Core.

Además, el equipo de ASP.NET Core ha puesto toda la carne en el asador con esta nueva implementación de sockets. Aprovechando nuevas capacidades del framework como Span<T> y afinando al máximo han conseguido un componente que es mucho más rápido que el que disfrutábamos hasta la versión 2.0.
Lamentablemente, a última hora se ha caído de esta revisión el esperado nuevo modo de funcionamiento in-process del módulo ASP.NET Core para IIS, que prometía un aumento del rendimiento de entre 4x y 6x eliminado la necesidad de conexiones en localhost entre IIS y Kestrel.
En definitiva, como podéis comprobar, mejorar el rendimiento ha sido un objetivo importante para esta revisión. Por haceros una idea, según benchmarks realizados por TechEmpower hace bien poco, ASP.NET Core es capaz de resolver más de siete millones de peticiones por segundo respondiendo texto plano:

Ranking de becnhmarks de Techempower

Hey, pero no penséis que migrando vuestra aplicación a ASP.NET Core 2.1 seréis capaces de responder peticiones a ese ritmo, ni mucho menos. Estos benchmarks se realizan en entornos muy controlados y afinando al máximo el código para conseguir este rendimiento. Por ejemplo, en este caso, la aplicación testada no usa MVC, ni siquiera routing o middlewares, lo cual hace que estas pruebas concretas sólo muestren el tope máximo al que podría llegarse en un escenario ideal (e irreal), y su posición respecto al resto de competidores en escenarios similares.
Podéis ver el código fuente de la aplicación que consigue este rendimiento en el repositorio de TechEmpower en GitHub.

Mejoras en MVC/Web APIs

El nuevo atributo [ApiController] permite identificar los controladores cuyas acciones son Web APIs para aplicarles de forma automática convenciones bastante razonables, como asumir que los objetos complejos serán bindeados desde el cuerpo de la petición, obligar a que se use rutado por atributos, o retornar un HTTP 400 cuando no se cumplan las reglas de validación establecidas.

También en esta revisión volvemos a encontrarnos con un viejo conocido: ActionResult… o más exactamente en este caso, ActionResult<T>. Una acción que retorne este tipo indica que ésta retornará o bien un IActionResult o bien un objeto de tipo T, lo cual facilita bastante su autodocumentación, sobre todo vistas a la alineación con OpenAPI, y hace su implementación más fácil de escribir y leer:
// Antes (<=2.0)
[Produces(typeof(Invoice))]
public IActionResult Get(int id)
{
var invoice = _invoiceServices.GetInvoice(id);
if (invoice == null)
return NotFound();

return Ok(invoice);
}

// Ahora (>=2.1)
public ActionResult<Invoice> Get(int id)
{
var invoice = _invoiceServices.GetInvoice(id);
if (invoice == null)
return NotFound();

return invoice;
}
Además de esto, se ha añadido soporte para el estándar Problem Details, que especifica una forma estructurada de retornar detalles de error en APIs HTTP. Para ello, se han introducido clases como ValidationProblemDetails y el método ValidationProblem() para retornar un error 400 con esta estructura de datos:
if (!ModelState.IsValid)
{
return ValidationProblem(ModelState);
}

Mejoras en Razor

Las vistas o páginas Razor pueden ser ahora compiladas y distribuidas en bibliotecas, lo que permitirá construir interfaces de usuario reutilizables. Cuando una aplicación MVC o Web Pages incorpora estas bibliotecas, las vistas que incluya serán detectadas automáticamente y utilizadas de forma normal, como si se encontraran en la carpeta /Views o /Pages. Sin embargo, pueden ser sobrescritas por las aplicaciones simplemente creando vistas con el mismo nombre en las ubicaciones habituales.

Esto, por ejemplo, se pone de manifiesto en la nueva forma de introducir en las aplicaciones creadas usando las plantillas existentes el interfaz de usuario relativo a la gestión de identidad (autenticación, perfil, etc.) usando Identity Framework. Todo el interfaz se distribuye en forma de biblioteca de clases Razor y puede ser incluido en nuestros proyectos usando los scaffolders disponibles a nivel de línea de comando o desde Visual Studio.

Por otra parte, las vistas parciales pueden incluirse ahora desde otra vista utilizando el tag helper<Partial>, que elimina problemas y dudas relativas al uso de helpers como Html.Partial(), Html.RenderPartial() y sus correspondientes versiones asíncronas.

De hecho, los problemas causados por el uso de las versiones síncronas son tan graves que, a partir de esta versión, las llamadas a Html.Partial() serán marcadas como warning, sugiriéndose que sean sustituidas por <partial> o await Html.PartialAsync():

Warning al usar Partial()

También Razor Pages ha sufrido algunas pequeñas mejoras, como el soporte para áreas, el uso de /Pages/Shared como fallback para la búsqueda de páginas, o la introducción de IPageFilter e IAsyncPageFilter para la implementación de funcionalidades transversales, tipo filtro de acción, en este tipo de componentes.

El nuevo paquete Microsoft.AspNetCore.App

Hasta ahora, el metapaquete referenciado habitualmente en los proyectos era Microsoft.AspNetCore.All, pero ahora se promoverá el uso de Microsoft.AspNetCore.App en los nuevos proyectos.

En este nuevo metapaquete han eliminado dependencias hacia proyectos de terceros como los conectores con Sqlite, Redis o algunos servicios de Azure. Por tanto, a partir de la versión 2.1, si queremos usar estos componentes tendremos que referenciar sus paquetes de forma directa.

Por cierto, el metapaquete Microsoft.AspNetCore.All seguirá existiendo durante todas las versiones 2.x, por lo que no es necesario migrarlo de momento. Pero en cualquier caso, si hubiera que hacerlo, tampoco sería demasiado complicado; podéis leer más en este issue de GitHub.

Testing end to end

Las versiones anteriores de ASP.NET Core disponían de una infraestructura muy básica para preparar tests de extremo a extremo, aunque era bastante complicado echarlo a andar con MVC.

ASP.NET Core 2.1 incluye el nuevo paquete Microsoft.AspNetCore.Mvc.Testing que añade infraestructura para la creación de pruebas end to end en memoria. Recordad que este tipo de pruebas, a diferencia de las unitarias, permiten verificar el funcionamiento del stack completo, pues consiste básicamente en simular peticiones y analizar las respuestas.
publicclass TestingMvcFunctionalTests : IClassFixture<WebApplicationTestFixture<Startup>>
{
public HttpClient Client { get; }

publicTestingMvcFunctionalTests(WebApplicationTestFixture<Startup> fixture)
{
Client = fixture.Client;
}


[Fact]
publicasync Task GetHomePage()
{
// Arrange & Act
var response = await Client.GetAsync("/");

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
Fijaos que no estamos hablando de hacer peticiones, sino de simularlas en memoria, por lo que todo será bastante más rápido que si tuviéramos que lanzar un servidor y realizar peticiones reales a través de red (aunque sea el loopback).

Seguridad y cumplimiento de GDPR

La seguridad es un área en continuo movimiento y, además, la llegada de GDPR ha sido uno de los grandes acontecimientos de este año. Obviamente la nueva versión de un framework moderno como ASP.NET Core no podía ignorar este hecho.
  • Los proyectos ASP.NET Core 2.1 utilizarán por defecto HTTPS (se lanzará en localhost:5001), y tendremos disponible un middleware específico para forzar la redirección HTTP->HTTPS:
    app.UseHttpsRedirection();
     
  • Asimismo, mediante otro middlewarepodremos utilizar HSTS (HTTP Strict Transport Security) para informar a los navegadores de nuestro uso de protocolos seguros.
     
  • Las plantillas de proyectos web incluyen un banner de aceptación de cookies que impedirá la emisión de galletitas no esenciales si no es aceptado.
     
  • La plantilla de proyecto que utiliza Identity Framework incluye de serie opciones en el perfil para descargar los datos personales y para eliminar el usuario.

¡SignalR Core!

Pues sí, amigos, por fin tenemos disponible la versión "core" del framework de servicios en tiempo real lista para utilizar en producción.

Aunque siguen existiendo hubs y otros conceptos que nos resultarán familiares, se trata de una reescritura desde cero, que no mantiene compatibilidad ningún tipo con SignalR 2.x para .NET framework. Aparte de aprovechar las ventajas de ASP.NET Core, En esta versión se han eliminado las dependencias con jQuery, se usan protocolos más eficientes, un modelo de escalado simplificado y muchas novedades más.

Podéis leer más sobre SignalR Core en la documentación oficial.

Mejoras en CLI

Aunque estrictamente hablando se trata de una novedad de .NET Core, vamos a comentar aquí también las mejoras en el interfaz de línea de comandos (CLI) porque seguro que afectará a la forma en que desarrollamos aplicaciones web.

En primer lugar, ahora vienen integrados de serie comandos que antes eran externos y debían ser instalados como paquetes NuGet en el proyecto:
  • dotnet watch, para habilitar la compilación automática ante cambios de código.
  • dotnet ef para realizar operaciones relacionadas con Entity Framework.
  • dotnet user-secrets para gestionar user secrets.
  • dotnet dev-certs para manejar certificados en la máquina local.
  • dotnet sql-cache para configurar servidores de caché distribuida.
Por otra parte, ¿habéis mirando alguna vez con envidia esos comandos basados en NodeJS que se instalan a través de npm? ¡Pues demos la bienvenida a las .NET Core Global tools!

Gracias a esta nueva característica podremos crear comandos con .NET Core y distribuirlos a través de NuGet mediante el comando dotnet tool install -g. Tras ello, lo tendremos disponibles como órdenes de línea de comandos, por ejemplo:
C:\>dotnet tool install -g dotnetsay
Puede invocar la herramienta con el comando siguiente: dotnetsay
La herramienta "dotnetsay" (versión '2.1.3') se instaló correctamente.

C:\>dotnetsay

Welcome to using a .NET Core global tool!
[...]

C:\>_
Sin duda, esto nos llevará a un interesante ecosistema de herramientas para ASP.NET Core basadas en el CLI.

Otras mejoras

Aunque no todas ellas están relacionadas directamente con ASP.NET Core, creo que es interesante al menos citar las siguientes novedades:
  • HttpClientFactory es un nuevo mecanismo para facilitar la creación y uso correcto de objetos HttpClient. A diferencia de las versiones anteriores, hace uso del inyector de dependencias para registrar y obtener clientes HTTP, implementa un nuevo concepto llamado outgoing middleware y gestiona el ciclo de vida de forma automática.
     
  • Mejoras en la forma de trabajar con objetos Option, como la posibilidad de configurarlos a través de servicios registrados en el sistema, o de implementar clases que configuren varios objetos de este tipo al mismo tiempo.
     
  • El UI de Identity Framework se puede incluir en los proyectos como biblioteca Razor UI, y modificar únicamente lo que necesitemos. Relacionado con esto, también se ha incluido un nuevo scaffolder en Visual Studio que permite añadir desde el IDE dicha biblioteca y los componentes a sobrescribir.
     
  • Se ha introducido una fórmula para especificar el nivel de compatibilidad de nuestro código con versiones de ASP.NET Core MVC. Mediante el método SetCompatibilityVersion() podemos indicar expresamente si queremos que el framework actúe conforme a una versión específica para proteger nuestra aplicación de posibles cambios futuros:
    publicvoidConfigureServices(IServiceCollection services)
    {
    services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
    Por defecto se asume que el nivel de compatibilidad de las aplicaciones que no lo indiquen es CompatibilityVersion.Version_2_0.
     
  • Se han actualizado las plantillas de proyecto web SPA para Angular, React y React con Redux.

Y esto es todo, o casi…

… o al menos lo que más me ha llamado la atención hasta el momento. Espero no haberme dejado muchas novedades interesantes por detrás, pero, si detectáis alguna que os parezca interesante comentar, no dudéis en decirlo ;)

Ah, por último, en la documentación oficial tenéis información sobre el proceso de migración desde ASP.NET Core 2.0, aunque que es bastante sencillito.

Publicado en Variable not found.

Poesía Binaria: Ejecutando tareas desde consola simultáneamente con GNU Parallel

$
0
0


Los ordenadores cada día son más potentes. Y una de las formas de poder ejecutar tareas de forma más rápida, es simultanearlas. Por un lado, es lógico que si una tarea está esperando un dato (ese dato puede venir del disco, del usuario por medio del teclado o el ratón, o descargándoselo de Internet), es normal que el ordenador decida ejecutar otras cosas para no perder el tiempo. Y de hecho se ha aprovechado esto para tener varios programas en ejecución, constantemente dando y quitando el acceso al procesador. Aunque, con la evolución en el mundo del hardware, ahora es común tener ordenadores que comprenden varios núcleos de procesador y son capaces de disponer de varios hilos de ejecución simultáneos. Lo que quiere decir, que pueden hacer realmente varias cosas a la vez, por lo que tiene más sentido incluso ejecutar tareas en paralelo.

Ejecución concurrente en la shell

Normalmente, si queremos ejecutar varias tareas al mismo tiempo, o concurrentes desde una terminal Unix. Basta con ejecutar un programa de la siguiente manera:

programa &
19238
… ejecuto más cosas …

Con un ampersand (&) al final. De esta forma, el terminal nos devolverá el control a nosotros como usuarios, permitiéndonos ejecutar otras cosas mientras la tarea que le hemos pedido que ejecute lo hace de fondo (en background). Es conveniente que la tarea no escriba muchas cosas en pantalla, porque si no va a ser un jaleo.
También podemos ejecutarla de forma normal, y en mitad de la ejecución pulsar Control+Z (Representado por ^Z). Control+Z enviará una señal SIGSTOP al programa en cuestión, por lo que la ejecución se detendrá, permitiéndonos ahora ejecutar otras cosas, y luego continuar la tarea anterior tanto en el frente (foreground), escribiendo fg o, de fondo (como hacíamos antes, en background), escribiendo bg.
programa
^Z
[1]+ Stopped   programa
… ejecuto más cosas …
fg
programa
… programa sigue ejecutándose …
^Z
[1]+ Stopped    programa
bg
[1]+ programa &
… puedo ejecutar lo que quiera …

Generalmente, si tenemos un ordenador con varios núcleos y capaz de ejecutar varias tareas al mismo tiempo. El núcleo del sistema operativo se encargará de repartir estas tareas entre los núcleos disponibles para que la ejecución global sea más rápida. Si tenemos 2 tareas que se ejecutan en un solo hilo y nuestro ordenador puede ejecutar dos hilos de forma simultánea, ¡vamos a ejecutar los dos al mismo tiempo y terminarán antes!
Concretamente, si tenemos dos programas:

  • pi : Calcula el número PI con 4000 decimales, y tarda 10 segundos.
  • color : Corrige el color de una imagen jpg enorme, y tarda 14 segundos.

Si ejecutamos los dos programas de forma secuencial (primero uno y luego otro), tardaremos 24 segundos en realizar las dos tareas. Pero si las ejecutamos a la vez, como nuestro procesador tiene dos núcleos y no está haciendo nada más, tardaremos en completar las dos tareas 14 segundos (lo que tarda la más lenta de las dos).

Cuando se nos queda corto…

Cuando hablamos de paralelizar tareas, podemos hacerlo en casi cualquier lenguaje o entorno. Es más, justo con la información de antes podemos aprovechar para lanzar varias tareas. Incluso con algo de programación, establecer algún que otro bloqueo para guardar el orden de las ejecuciones. O incluso para poder ejecutar las tareas de dos en dos, o de tres en tres… aunque tratándose de Bash puede ser un poco duro programar ciertas cosas, sobre todo cuando tenemos prisa.

GNU Parallel nos permite ejecutar tareas de forma simultánea, agrupar tareas, ordenar la salida, e incluso nos evita tener que pelearnos con aquellos conceptos de concurrencia que a más de uno nos han traído de cabeza. Como extras, nos permite jugar con los argumentos de entrada de los programas de forma muy divertida a la par que útil.

Instalación

GNU Parallel suele venir en los repositorios de las distribuciones de GNU/Linux más populares, así que podemos hacer:

sudo apt install parallel

En caso de Debian/Ubuntu y derivados

dnf install parallel

Si estamos en Fedora.

pacman -S parallel

El programa no tiene muchas dependencias y es totalmente multiplataforma, ya que está escrito en Perl. Podremos ejecutarlo en nuestro ordenador, un servidor, un móvil, una Raspberry PI o cualquier cacharro que corra GNU/Linux.

Es importante saber la versión que tenemos instalada. Podemos averiguarlo con:

parallel --version
GNU parallel 20161222

Aunque casi todos los ejemplos del post corren con versiones anteriores, para algunas cosas necesitaremos como mínimo la versión 20161222. Si tu distribución no la tiene por defecto, siempre podemos descargarlo de la página oficial.

Calcular el número PI

Un programa que suelo utilizar para este tipo de ejemplos es el cálculo del número Pi. Sobre todo porque es una tarea que tarda un rato y utiliza intensamente el procesador. Sólo necesitamos tener bc instalado. El script es muy sencillo y lo he llamado pi:

1
2
#!/bin/bash
echo"scale=4000; a(1)*4"|bc-l

Ahora debemos dar permiso de ejecución a dicho archivo:

chmod +x pi

Y podemos hacer una prueba sin concurrencia ni nada para ver lo que tarda en ejecutarse en nuestro ordenador:
time ./pi
3.141592653589793238462643383279502884197169399375105820974944592307\
….
66983895228684783123552658213144957685726243344189303968642624341077\
3226978028073189154411010446823252716201052652272111660396
real 0m10.800s
user 0m10.792s
sys 0m0.008s

Si quieres seguir esta guía y ves que la ejecución de pi tarda mucho, puedes bajar el número de dígitos (donde pone scale=4000, puedes bajar el número). El objetivo es no eternizarnos, pero darnos cuenta de cómo se está ejecutando todo.

Varias ejecuciones de un programa

GNU Parallel está diseñado para pasar argumentos de forma variable a cada una de las instancias que ejecutemos del programa. Siempre tendremos que pasar argumentos al programa a ejecutar aunque, en este caso, solo queremos ejecutar varias veces pi, sin argumentos. Una forma fácil es generar una secuencia de números del 1 al total de veces que queremos ejecutar el programa, lo podemos hacer con seq (por ejemplo con 10):

seq 10
1
2
3
4
5
6
7
8
9
10

Una vez que tenemos esta entrada, se la podemos pasar a GNU Parallel. En este caso, le diremos que a pi no le pase argumentos (-n0):
seq 10 | parallel -n0 ./pi

Esta ejecución tardará un tiempo. Eso sí, mientras se ejecuta, estaría bien que investigáramos un poco qué está haciendo el programa. En mi ordenador, puedo ejecutar hasta 8 hilos de forma simultánea. Eso quiere decir que, como pi es una tarea que tira mucho de CPU, ejecutar las 10 instancias del programa al mismo tiempo no haría que la ejecución global fuera más rápido que una ejecución secuencial puesto que dos procesos se pelearían por utilizar la CPU y el sistema operativo tendría que dar y quitar el paso a CPU de los procesos. La situación se agravaría si en lugar de 10 ejecuciones hiciéramos 100 o más, incluso podríamos tener otros problemas si intentamos ejecutar una tarea muy complicada demasiadas veces al mismo tiempo.

Podemos ver qué está haciendo la CPU en cada momento ejecutando top, un gestor de tareas o un monitor de CPU.
Uso de CPU
Lo que quiero que observemos es el número de procesos que estamos utilizando en cada momento. En este caso, la ejecución se hará de 8 en 8 porque GNU Parallel ha detectado que mi sistema puede correr 8 hilos simultáneamente. Podemos comprobarlo si mientras ejecutamos el comando anterior, en otro terminal ejecutamos:

pidof bc | wc -w
8

Veremos ese 8 todo el tiempo. Ya que parallel controla ese número de ejecuciones.

Alternativamente, podemos especificar nosotros cuántas ejecuciones simultáneas tendrá el programa con el argumento -j. De la siguiente manera:

seq 10 | parallel -j2 -n0 ./pi

En este caso, ejecutaremos las tareas de 2 en 2. Podemos especificar el número de tareas simultáneas que necesitemos. Incluso puede exceder el número de núcleos o hilos disponibles en nuestro sistema. El objetivo es que da igual lo que dure la ejecución de pi, mantendremos el número de ejecuciones que le digamos a parallel hasta que ya no haya más instancias por lanzar.

Cambiando los argumentos de entrada

La gracia de ejecutar tareas en paralelo es que podamos variar los argumentos de entrada de dicho programa. Es más, un programa podrá tener argumentos fijos y argumentos variables. Esto lo podemos poner en práctica ejecutando echo de forma concurrente con diferentes parámetros. Vale, ejecutar echo no es ninguna tarea intensiva, pero nos vale para ver cómo se están produciendo las ejecuciones y algunas cosas curiosas más.

Argumentos desde teclado (entrada estándar)

Una forma muy sencilla de pasar argumentos de entrada a parallel es de forma interactiva, escribiéndolos por teclado y pulsando Control+D al finalizar:

parallel echo
parallel: Warning: Input is read from the terminal. Only experts do this on purpose. Press CTRL-D to exit.
Jaén
Córdoba
Sevilla
Huelva
Cádiz
Málaga
Granada
Almería
Jaén
Córdoba
Sevilla
Huelva
Cádiz
Málaga
Granada
Almería

Argumentos desde pipe

Estos argumentos podemos pasarlos desde una pipe o desde un archivo, pero tiene que haber uno por línea. Podemos introducir las provincias en un archivo o hacer lo siguiente (con tr estamos convirtiendo los espacios en retornos de carro), además, estamos introduciendo un argumento fijo “Saludos desde”:

echo “Jaén Córdoba Sevilla Huelva Cádiz Málaga Granada Almería” | tr ‘ ‘ ‘\n’ | parallel echo “Saludos desde”
Saludos desde Jaén
Saludos desde Córdoba
Saludos desde Sevilla
Saludos desde Huelva
Saludos desde Cádiz
Saludos desde Málaga
Saludos desde Granada
Saludos desde Almería

Argumentos desde pipe en orden

Pero claro, no todos los programas aceptan los argumentos en el mismo lugar, imaginemos que queremos decir algo más tras el nombre de la provincia andaluza, para ello tendremos que introducir alguna cosa más:

echo “Jaén Córdoba Sevilla Huelva Cádiz Málaga Granada Almería” | tr ‘ ‘ ‘\n’ | parallel echo “Saludos desde” {}”, una bonita provincia andaluza.”
Saludos desde Jaén, una bonita provincia andaluza.
Saludos desde Córdoba, una bonita provincia andaluza.
Saludos desde Sevilla, una bonita provincia andaluza.
Saludos desde Huelva, una bonita provincia andaluza.
Saludos desde Cádiz, una bonita provincia andaluza.
Saludos desde Málaga, una bonita provincia andaluza.
Saludos desde Granada, una bonita provincia andaluza.
Saludos desde Almería, una bonita provincia andaluza.

Varios argumentos variables por ejecución

Ahora, vamos a crear un archivo, en el que introduciremos un número (el número de letras que tiene la provincia) y la provincia, de forma alternativa en cada línea. Lo llamaremos andalucia.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4
Jaén
7
Córdoba
7
Sevilla
6
Huelva
5
Cádiz
6
Málaga
7
Granada
7
Almería

Con este archivo podríamos hacer lo siguiente:

cat andalucia.txt | parallel -n 2 echo “Dame {1} letras para {2}”
Dame 4 letras para Jaén
Dame 7 letras para Córdoba
Dame 7 letras para Sevilla
Dame 6 letras para Huelva
Dame 5 letras para Cádiz
Dame 6 letras para Málaga
Dame 7 letras para Granada
Dame 7 letras para Almería

Con -n2 estamos indicando que echo llevará dos argumentos, por lo que cada ejecución de echo tomará dos líneas del fichero como argumentos. Con {1} y {2} indicamos el número de parámetro que estamos colocando. También podríamos hacer lo siguiente:
cat andalucia.txt | parallel -n 2 echo “{2} tiene {1} letras.”
Jaén tiene 4 letras.
Córdoba tiene 7 letras.
Sevilla tiene 7 letras.
Huelva tiene 6 letras.
Cádiz tiene 5 letras.
Málaga tiene 6 letras.
Granada tiene 7 letras.
Almería tiene 7 letras.

Argumentos de parallel como argumentos del programa

Otra forma de incluir argumentos variables es la siguiente:

parallel echo ::: Ávila Burgos León Palencia Salamanca Segovia Soria Valladolid Zamora
Ávila
Burgos
León
Palencia
Salamanca
Segovia
Soria
Valladolid
Zamora

Argumentos y combinaciones

Cuando tenemos que lanzar muchas tareas, a veces es útil que los argumentos hagan combinaciones de parámetros. De esta manera no tendremos que generar un fichero que reúna las combinaciones y luego pasárselo a Parallel. Por ejemplo, si queremos hacer una operación lógica AND, podemos hacer lo siguiente:

parallel ‘echo -n {1} \&\& {2} = ; (({1}&&{2})) && echo 1 || echo 0’ ::: 0 1 ::: 0 1
0 && 0 =0
0 && 1 =0
1 && 0 =0
1 && 1 =1

O, mucho más sencillo, si queremos todas las combinaciones que podemos hacer con 3 bits, podemos hacer lo siguiente:

parallel echo ::: 0 1 ::: 0 1 ::: 0 1
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

Un paso más, ahora queremos averiguar con bc su valor en decimal:

parallel echo -n {1}{2}{3} = \;’echo “ibase=2;{1}{2}{3}” | bc’ ::: 0 1 ::: 0 1 ::: 0 1
000 =0
001 =1
010 =2
011 =3
100 =4
101 =5
110 =6
111 =7

Datos desde un fichero separado por columnas

Podemos utilizar un archivo cuyo contenido pueda ser determinado por columnas, como un CSV (Comma Separated Values) o TSV (Tab Separated Values). Podríamos coger el archivo de provincias y hacer lo siguiente (andalucia2.csv):

1
2
3
4
5
6
7
8
4,Jaén,jiennense
7,Córdoba,cordobés
7,Sevilla,sevillano
6,Huelva,onubense
5,Cádiz,gaditano
6,Málaga,malagueño
7,Granada,granadino
7,Almería,almeriense

En este caso descartaremos el primer valor. No tenemos por qué utilizarlo si no queremos.

parallel --colsep ‘,’ echo “Un habitante de {2} se llama {3}” :::: andalucia2.csv
Un habitante de Jaén se llama jiennense
Un habitante de Córdoba se llama cordobés
Un habitante de Sevilla se llama sevillano
Un habitante de Huelva se llama onubense
Un habitante de Cádiz se llama gaditano
Un habitante de Málaga se llama malagueño
Un habitante de Granada se llama granadino
Un habitante de Almería se llama almeriense

El orden de ejecución

Como se lanzan varios procesos a la vez y no todos tienen por qué tardar lo mismo. El orden en el que vemos los resultados puede variar. Lo podemos comprobar de la siguiente forma:

parallel sleep {}\; echo {} ::: 4 8 2 6
2
4
6
8

De esta forma hemos ejecutado 4 procesos que generarán esperas de 4, 8, 2 y 6 segundos, eso sí, la salida se irá generando por el orden en el que los procesos lleguen al echo. Aunque el orden de salida no es el orden de los argumentos que le hemos pasado. Es más, si queremos, podemos hacerlo más enrevesado:
parallel -j2 sleep {}\; echo {} ::: 4 8 2 6
4
2
8
6

En este caso ejecutamos dos hilos de forma simultánea, por lo que se lanzan los procesos que tardan 4 y 8 segundos a la vez. El de 4 segundos termina, y se lanza el de 2 (ahora están trabajando el proceso que tarda 8 (que va por su cuarto segundo) y el que tarda 2. Lógicamente acaba primero el que tarda 2 segundos y se lanza el de 6, pero al de 8 le quedan 2 segundos, por lo que acaba antes que el de 6.
El caso es que puedo forzar a parallel para que muestre la salida en el mismo orden en el que le paso los argumentos de entrada con el argumento -k:
parallel -k sleep {}\; echo {} ::: 4 8 2 6
4
8
2
6

Da igual qué hilos esté lanzando y cómo termine, la salida generada me la mostrará parallel en el orden que yo le he dicho, lo cual es útil para nuestros scripts si necesitamos controlar la salida generada.

Ejecutando funciones de Bash

Como hemos visto parallel nos permite ejecutar echo, palabra reservada de Bash, pero también es posible crear nuestras propias funciones y ejecutarlas. De esta forma, no solo podemos ejecutar un programa, podremos crear funciones y ejecutarlas también. Solo tenemos que exportarlas:

1
2
3
4
5
6
7
8
function distancia(){
  localORIGEN="$(echo -n $1 | jq -s -R -r @uri)";
  localDESTINO="$(echo -n $2 | jq -s -R -r @uri)";
  curl -s "http://maps.googleapis.com/maps/api/directions/json?origin=${ORIGEN}&destination=${DESTINO}&sensor=false"2>/dev/null |tee"${ORIGEN}_${DESTINO}.txt"| jq '.routes[0].legs[0].distance.text';
}

export-f distancia
parallel echo-n"De {1} a {2} =\> " \; distancia {1}{2} ::: "La Coruna""Lugo""Orense""Pontevedra" ::: "Alicante""Castellon""Valencia"

Con este ejemplo sacaremos muchas distancias entre ciudades con la ayuda de la API de mapas de Google. Es un ejemplo donde podemos ver que cada petición tarda un tiempo diferente, los resultados salen en orden de llegada.

Reemplazos de argumentos

Parallel permite no solo pasar los argumentos a los programas que ejecuta tal cual, sino que podemos hacer alguna operación de reemplazo por si no queremos el parámetro tal cual nos lo pasan. Algunos parámetros comunes pueden ser nombres de archivo (a los que podremos quitarle rutas o extensiones), cadenas de caracteres (que podremos recortar), o incluso extraer información de las tareas. Incluso podremos cambiar las cadenas si nos resulta incómodo utilizar estos caracteres en nuestros scripts. Esto podría ser muy extenso, así que voy a extraer algunos ejemplos rápidos:

Convertir imágenes de JPG a PNG

Con esta orden podremos buscar todas las imágenes jpg que hay en el directorio actual y convertirlas a png. Eso sí, utilizando toda la potencia de nuestro ordenador, iniciando tantos procesos convert simultáneos como núcleos tenga nuestra CPU. Podemos ver que {} muestra el argumento tal cual, pero {/.} extrae la ruta y la extensión. Si queremos investigar, {/} solo extrae la ruta y {.} solo extrae la extensión. En este caso guardamos las imágenes convertidas en el directorio convertidas.

find -maxdepth 1 -name ‘*.jpg’ | parallel convert -verbose {} convertidas/{/.}.png

Slots y tareas

Podemos saber qué número de tarea estamos ejecutando en cada momento. Es muy útil si por ejemplo cada tarea tiene que repartirse un tamaño muy grande y estas deben saber qué trozo de tarea les corresponde. Pensad en una imagen, divididla en cuadros numerados secuencialmente y cada tarea que lancemos debe atacar a uno de los cuadros. En este caso cada una de las tareas debe saber a qué cuadro tienen que ir. Por otro lado, los slots son grupos, como si fueran “cajas” en las que se ejecutan las tareas. Es decir, si vamos a ejecutar 3 tareas simultáneamente, tendremos 3 cajas y una tarea en ejecución ocupará una caja. De tal forma que nunca tendremos dos trabajos corriendo al mismo tiempo en el mismo slot. Podemos ver cómo funciona esto de los slots así:

parallel -j2 sleep {} \; echo {%} {#} --- {} ::: 5 2 1 3 4
2 2 --- 2
2 3 --- 1
1 1 --- 5
2 4 --- 3
1 5 --- 4

Reemplazo con funciones

Parallel también soporta funciones (casi todas las funciones están disponibles a partir de la versión 20161222. Por ejemplo podemos decirle a una tarea el número total de tareas que vamos a ejecutar. Aunque {= ‘$_=total_jobs($_)’ =} podemos sustituirlo por {##}:

seq 100 | parallel echo Job {#} of {= ‘$_=total_jobs($_)’ =}

Aunque algo muy útil es la implementación de condiciones dentro de la propia ejecución, de la siguiente manera:

seq 50 | parallel echo Hola Mundo {%} ‘> {= $_= $job->seq() % 2 == 0 ? “/dev/tty” : “/dev/null” =}’

Con esto le decimos que la salida se produzca por /dev/tty si el número de la tarea (job->seq()) es par (si su división por dos da resto cero), y si no, mandamos la salida a /dev/null.

Hay muchos más reemplazos

Progreso

Como siempre, el usuario se desespera, y muchas veces necesita que le demos algo de información para que no se desespere. Una buena idea es mostrar un progreso para que sepa por dónde va la ejecución. Como parallel está pensado para ejecutar muchas tareas, puede medir el progreso mirando todas aquellas tareas que han finalizado y midiendo tiempos. Podemos utilizar el mismo ejemplo de antes para convertir fotos de la siguiente manera:

find -maxdepth 1 -name ‘*.jpg’ | parallel --progress convert {} convertidas/{/.}.png

O también podemos utilizar –bar. O incluso mejor, pasarle la salida del progreso a un programa como zenity, para ver la salida de forma bonita:
find -maxdepth 1 -name ‘*.jpg’ | parallel --bar convert {} convertidas/{/.}.png 2> >(zenity --progress --auto-kill)

También podemos utilizar –eta para que nos indique el tiempo estimado de finalización de la tarea.

Muchas más opciones

Las utilidades de este programa no han terminado. El propio parallel nos permite ejecutar tareas simultáneas en servidores desde nuestra máquina cliente. Además, junto con GNU Parallel encontramos utilidades para acceso a base de datos y para la realización de operaciones. Es un programa muy flexible que nos permite realizar multitud de operaciones muy útiles y muy interesantes.

Sobre GNU Parallel

El autor nos pide que le citemmos si utilizamos este programa para una publicación. Sólo hay que mencionar al autor, además, en el enlace encontramos un manual muy completo y actualizado:

Encontramos otras formas de mención muy útiles si estamos escribiendo en LaTeX:

parallel --bibtex

Foto principal: unsplash-logoJonathan Pendleton

The post Ejecutando tareas desde consola simultáneamente con GNU Parallel appeared first on Poesía Binaria.


Adrianistán: Bindings entre Rust y C/C++ con bindgen

$
0
0

Rust es un lenguaje con muchas posibilidades pero existe mucho código ya escrito en librerías de C y C++. Código que ha llevado mucho tiempo, que ha sido probado en miles de escenarios, software maduro y que no compensa reescribir. Afortunadamente podemos reutilizar ese código en nuestras aplicaciones Rust a través de bindings. Los bindings no son más que trozos de código que sirven de pegamento entre lenguajes. Esto es algo que se ha podido hacer desde siempre pero dependiendo de la librería podía llegar a ser muy tedioso. Afortunadamente tenemos bindgen, un programa que permite generar estos bindings de forma automática analizando el código de la librería de C o C++.

En este post veremos como usar SQLite desde Rust usando bindgen.

Instalando bindgen

En primer lugar necesitamos tener instalado Clang 3.9 o superior. En Ubuntu o Debian necesitamos estos paquetes:

sudo apt install llvm-3.9-dev libclang-3.9-dev clang-3.9

Para el resto de plataformas puedes descargar el binario desde la página de descargas de LLVM.

Bindgen permite dos modos de uso: línea de comandos o desde el código Rust. El más habitual es desde código Rust pero antes veremos el modo en línea de comandos.

Modo línea de comandos

Para bindings sencillos podemos usar el modo línea de comandos. Instalamos binden con Cargo:

cargo install bindgen

Su uso es muy sencillo:

bindgen /usr/include/sqlite3.h -o sqlite.rs

Simplemente indicamos el fichero de cabecera que queremos traducir y su correspondiente fichero de salida en Rust. Este fichero será el pegamento. Vamos a crear un programa que use este pegamento:

mod sqlite;

use sqlite::{sqlite3_open, sqlite3_exec, sqlite3_close, SQLITE_OK};
use std::ffi::CString;
use std::ptr::{null_mut,null};

fn main(){
    let mut db = null_mut();
    let database_name = CString::new("test.db").unwrap().into_raw();
    let sql = CString::new("
    CREATE TABLE contacts (name TEXT, tel TEXT);
    INSERT INTO contacts VALUES ('Adrian','555-555-555');").unwrap().into_raw(); 
    let mut error_msg = null_mut();
    unsafe{
        sqlite3_open(database_name,&mut db);
        let rc = sqlite3_exec(db,sql,None,null_mut(),&mut error_msg);
        if rc != SQLITE_OK as i32 {
            let error = CString::from_raw(error_msg);
            println!("ERROR: {}",error.into_string().unwrap());
        }
        sqlite3_close(db);
    }
}

Como se puede apreciar, las llamadas al módulo de pegamento de hacen desde un bloque unsafe ya que se van a usar punteros al estilo C, de forma insegura. Hace tiempo escribí sobre ello así que voy a saltarme esa parte.

Compilamos enlazando de forma manual libsqlite3 de la siguiente forma:

rustc main.rs -lsqlite3

Si todo va bien, compilará aunque con numerosos warnings. En principio no son importantes.

Ahora si ejecutamos el programa resultante debería crear una base de datos nueva con una tabla contacts y los datos insertados.

¡Hemos conseguido llamar a una librería de C desde Rust y no hemos escrito ningún binding!

Build.rs

El sistema anterior funciona, pero no es lo más práctico, además no usa Cargo que es el sistema estándar de construcción de programas y crates un Rust. Lo habitual es dejar este proceso automatizado en el fichero build.rs que se ejecuta con Cargo.

Lo primero es añadir la siguiente línea al fichero Cargo.toml:

[build-requires]
bindgen = "0.26.3"

El siguiente paso consiste en crear un archivo cabecera de C que a su vez haga referencia a todos los archivos de cabecera que necesitamos. En el caso de SQLite es bastante simple.

#include <sqlite3.h>

Y lo llamamos wrapper.h

Ahora viene lo interesante. Dentro de build.rs creamos un programa que gracias a la API de bindgen haga lo mismo que la línea de comandos.

extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // indicamos al linker que necesitamos sqlite3
    println!("cargo:rustc-link-lib=sqlite3");


    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .generate()
        .expect("Unable to generate bindings");

    // escribir los bindings en $OUT_DIR/bindings.rs
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

El archivo build.rs debe estar en la misma carpeta que Cargo.toml para que funcione.

Finalmente para hacer accesible nuestros bindings creamos un módulo llamado sqlite.rs con el siguiente contenido.

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

Lo único que hace es desactivar unos warnings molestos e incluir el texto de bindings.rs al completo.

Una vez hecho esto podemos usar desde el programa principal la librería de la misma forma que hemos visto antes.

Ahora podríamos usar estos bindings directamente en nuestro programa o rustizarlos (darles una capa segura alrededor e idiomática) y subirlo a Crates.io.

El código del post está en GitHub

La entrada Bindings entre Rust y C/C++ con bindgen se publicó primero en Adrianistán.

Blog Bitix: Soporte para lanzar eventos desde JavaScript con Ajax en un componente de Apache Tapestry

$
0
0
Apache Tapestry
Java

Para desarrollar aplicaciones web con el lenguaje de programación Java hay gran cantidad de frameworks que proponen un marco de trabajo para proporcionar la mayor parte de la funcionalidad de infraestructura necesaria para una aplicación. La mayoría basados en acciones y unos pocos basados en componentes. Una aplicación web no solo consta de la parte del servidor y desde hace tiempo la parte de cliente ha cobrado gran importancia.

Algunos frameworks proporcionan cierto soporte para JavaScript y recursos CSS en otros es muy escaso o inexistente. En el caso del frameworkApache Tapestry en la categoría de los basados en componentes proporciona un gran soporte no solo en la parte del servidor sino también para la parte cliente.

Una de estas funcionalidades que proporciona Tapestry es poder lanzar eventos desde el cliente mediante una petición Ajax para que sean procesados en el servidor y obtener la respuesta que se devuelva desde el servidor normalmente en formato Json. Hay que definir un manejador de evento en el servidor siguiendo la convención on[Event] y en caso de querer lanzar un evento desde el cliente anotándolo con @PublishEvent.

El uso del componente en una plantilla de una página.

En el código JavaScript asociado a una página o componente hay que hacer uso del módulo que ofrece el soporte para Ajax y los eventos desde el cliente, con RequireJS se obtiene una referencia a él. Solo es necesario indicar como parámetro el nombre del evento a lanzar, los parámetros si los hubiese y los manejadores de respuesta, tanto en el caso de ser correcta que recibirá los datos devueltos en el servidor como incorrecta. En el archivo ajax.coffee están documentados todos los parámetros que posee la función ajax del módulo t5/core/ajax.

En el primer elemento del HTML se añade un atributo data-componenent-events que contiene la URL necesaria para cada evento que haya sido declarado como lanzable. A partir del elemento indicado en la opción element se busca la URL en el atributo data-componenent-events siguiendo un orden empezando por el propio elemento, en los previos al mismo nivel jerárquicamente empezando por el más cercano desde abajo hacia arriba, en los padres y finalmente en el elemento body.

Petición Ajax y atributo con la URL del evento

Esta funcionalidad se incorporó en Apache Tapestry 5.2 donde hasta entonces era necesario construir la URL del evento en el servidor con ComponentResources.createEventLink() y enviarlo al componente haciendo uso de JavaScriptSupport como se muestra en el componente Ajax que no hace uso de esta funcionalidad de eventos.

Con este soporte es algo más fácil enviar eventos y realizar peticiones Ajax desde el cliente para obtener datos.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


Koalite: Microsoft, GitHub, unicornios y apuestas

$
0
0

OJO: Éste es un post de tertuliano de medio pelo de esos que salen en televisión hablando de cosas sin tener ni idea. No le hagas mucho caso.

La noticia de la semana pasada en el mundo tecnológico fue la compra de GitHub por parte de Microsoft. A estas alturas ya no es noticia, pero éste tampoco ha sido nunca un blog de actualidad, así que no tenía ninguna prisa por escribir sobre el tema.

Las reacciones a la noticia han sido variadas y, en muchas ocasiones, claramente sesgadas en función del amor/odio de quien las emitiera hacia Microsoft. O Micro$oft, que todavía hay quien lo ve así.

Las reticencias de siempre

Entre los que se llevan las manos a la cabeza por la adquisición, encontramos los que creen que Microsoft se cargará GitHub y lo llenará de características enterprise de dudosa utilidad para integrarlo con sus otros productos. Supongo que es un temor razonable viendo la evolución que ha tenido Skype. Por lo que se ha comunicado hasta ahora, no parece que eso vaya a ocurrir y GitHub seguirá operando de forma independiente, aunque sólo el tiempo dirá como acaba la cosa.

Otros hablan de la inseguridad que genera almacenar repositorios privados a los que puede tener acceso Microsoft y robarte el código fuente. Esto no hay por donde cogerlo, la verdad. Para empezar, el valor de la mayoría del código fuente es mucho menor del que pensamos, y en los casos en que realmente es tan importante, seguramente esté alojado on-premise y no en un servicio en la nube.

Además, si Microsoft estuvieran tan interesado en alguna tecnología, probablemente le resultaría más rentable comprar la empresa entera que andar robando su código fuente. Y por último, no olvidemos que uno de los principales competidores de GitHub está alojado en Azure, por lo que si Microsoft fuese ese ente maligno robacódigo podría también obtenerlo por ahí.

Suspicacias aparte, podemos también encontrar argumentos a favor de la compra. Puestos a confiar tus repositorios a alguien, parece mejor Microsoft, que tradicionalmente ha mantenido sus productos durante mucho tiempo, que una startup de incierto futuro (y no lo digo sólo por GitHub, sino por algunas alternativas que se han planteado los que huyen ahora de allí).

El modelo actual

Con todo lo exitoso que nos pudiera parecer GitHub a nosotros como desarrolladores, desde el punto de vista de empresa su única posibilidad de supervivencia era ser comprada.

Y puestos a ser comprada, Microsoft no parece la peor opción (imagínate que hubiera sido Oracle).

Lo malo es que se han pagado por él 7.500 millones de dólares y ¿cuántas empresas pueden pagar eso? Sólo las más grandes, lo que implica que cada vez se concentran más las herramientas de desarrollo en unas pocas grandes empresas. No tengo nada en contra del capitalismo y el libre mercado, pero como usuario no me gusta que todo quede en unas pocas manos porque acaba limitando mi libertad de elección.

Uno de los motivos que intuyo influyen en todo esto es la cantidad de capital riesgo que hay en circulación. Algunos hablan de burbuja (yo no sé tanto del tema como para afirmarlo), pero lo que está claro es que hay mucho dinero en circulación para financiar startups. Muchas de estas startups tienen un modelo de negocio más que cuestionable (si es que lo tienen), y su único objetivo es “ponerse guapas” y esperar a ser compradas por una empresa grande.

Como desarrollador, esto tiene varias implicaciones directas. Por una parte, al haber más dinero en circulación es más fácil conseguir buenos sueldos, y eso obviamente se agradece, pero son sueldos que no están mantenidos por el modelo productivo de la empresa, sino por financiación externa. Si te cortan el grifo, se acabó. A esto se le une que el objetivo de la empresa es ser apetecible para que te compren, por lo que es fácil ver empresas que queman rápido la inversión externa recibida para que las curvas de crecimiento de los KPIs de turno queden bien en un Powerpoint, pero el crecimiento no es todo lo sólido, ni técnica ni económicamente, que debería.

Se podría pensar que la existencia de estas empresas que dilapidan financiación sin conseguir triunfar (o sea, ser compradas) es algo terrible para los inversores, pero lo cierto es que es algo que se da por descontado. Este tipo de inversión funciona al modo de apuestas: meto dinero en 100 startups asumiendo que voy a perder lo invertido en muchas, recuperar lo de algunas, ganar con las menos y, si tengo suerte, dar el pelotazo con algún unicornio. Mientras se mantenga el ratio de éxito los inversores estarán contentos. Otra cosa son los compradores de estos unicornios.

Eso deja en una posición complicada a las empresas que quieren crecer sin financiación externa, de una forma orgánica y sostenible, manteniéndose con sus propios beneficios. Cuando las empresas que hay a tu alrededor reciben rondas de financiación que se gastan en dos años en aras de conseguir ese crecimiento explosivo que las haga apetecibles a sus compradores, captar y retener talento no es sencillo y seguir el ritmo del mercado a nivel de sueldos se hace francamente complicado.

Por mi trabajo en el Mundo Real™ tengo contacto con bastantes startups que desarrollan productos para nuestro sector y nos plantean llegar a alianzas o integraciones con nuestos productos. Son empresas financiadas por capital riesgo, que consiguen montar equipos de desarrollo mucho más grandes que el nuestro en poco tiempo y que (parecen) avanzar muy rápido, pero en la mayoría de los casos su plan de negocio no soporta el primer contacto con el mundo real.

En definitiva (y como ya decía al principio, este no es un tema que domine) me cuesta pensar que una situación así sea sostenible. Inflar los precios de la tecnología a base de financiación externa sin un modelo de negocio real (más allá de la propia inversión y compraventa de empresas) me parece una situación arriesgada para todo el sector y, como dice David Heinemeier Hansson el mundo necesita más empresas modestas de crecimiento lineal.

Posts relacionados:

  1. Open Source en un mundo Microsoft

Variable not found: Enlaces interesantes 325

$
0
0
Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

Poesía Binaria: Programación de tareas en segundo plano para nuestras aplicaciones web (Introducción. Parte 1 de 3)

$
0
0

Tareas programadas en segundo plano

Las aplicaciones web se van complicando cada vez más. Es un hecho y es algo bueno. Los ordenadores tienen más potencia, y los creadores cada vez más opciones e imaginación para destacar sobre la competencia. Una práctica interesante es la posibilidad de realizar tareas en segundo plano o en un momento concreto del tiempo sin que exista una mediación por parte del usuario.

Tenemos varias posibilidades y en estos posts vamos a centrarnos en cada una de ellas. Por otro lado, aunque este post es más general e independiente del lenguaje, quiero centrarme en PHP. Por un lado, es un lenguaje con el que he trabajado mucho, por otro, aunque es posible que diferentes partes de un proyecto estén realizadas en diferentes lenguajes, y PHP no sea un lenguaje pensado a priori para esto, en ocasiones, es más fácil y barato para una empresa pequeña recolocar a alguien en otro módulo de la apliación si todo está desarrollado en el mismo lenguaje. Como quiero hacer este post más o menos completo y relatar mi experiencia en cada uno de los puntos, se me ha alargado mucho, por eso he decidido dividirlo en tres partes, incluso puede que algún ejemplo dé para una cuarta.

¿Qué podemos hacer con estas tareas?

Bueno, ¿para qué vale todo esto? Es cierto que una web que tenga sus usuarios, envíe correos y sirva cierta información al usuario no tiene por qué ejecutar nada en segundo plano. Todo se servirá y se realizará al ritmo que los usuarios lo pidan. Eso está bien, en principio. Pero claro, imaginad que la web empieza a ganar visitantes. Lo primero que puede darnos problema es el envío de correo.

Puede que si se registran 10 usuarios por hora no pase nada pero, ¿10 usuarios por segundo? A los que debemos enviar confirmaciones de registro (no son muchos), pero luego enviaremos alertas para ciertos eventos, recordatorios de contraseña, avisos de que están intentando entrar con esa cuenta de correo… en definitiva, nuestra aplicación deberá establecer varias conexiones por segundo con el sistema de correo, sistema que muchas veces es externo y seguro que se cabrea (y nos expulsa por establecer tantas conexiones). Seguro que estaría bien coger cada 10, 15 o 20 segundos, y ejecutar una tarea que envíe todos los mensajes que tenga pendiente, lo que se llama un spool de correo, incluso enviar todos los correos pendientes aprovechando una misma conexión al servidor de correo, de manera mucho más eficiente y ordenada.

Supongamos que queremos tener un control de la llegada de dichos mensajes, o al menos comprobar que el e-mail sigue siendo válido para determinar si nuestros usuarios son legítimos o tenemos algún problema con sus datos. En este caso, cuando el servidor de correo de destino está recibiendo el mail normalmente contestará. Puede que el correo se entregue bien, o puede que estemos en una lista negra, el servidor de destino puede aplicar listas grises, nuestro correo puede ser tachado como Spam, o incluso que el servidor de destino no funcione ahora mismo. En caso de fallo, nuestro servidor de correo nos enviará un mensaje con el motivo por el que no se ha podido entregar el correo. Dichos mensaje podemos leerlos periódicamente y asignar un código de estado a los mensajes que hemos enviado a modo de diagnóstico, o para hacer un reintento pasados unos minutos, desactivar usuarios, etc.

Una web actual, normalmente también descarga contenidos de otros lados, y los procesa. Puede que descarguemos tweets, una captura de una web, extraigamos una imagen o una descripción de un post, la cotización del euro o de alguna criptomoneda. Esto son tareas que no dependen de nosotros. Así que no podemos controlar que el tiempo que tardamos en ellas sea fijo, o que nos pueda echar por tierra una página. También tenemos que pensar en que si cada vez que un visitante requiere alguno de estos datos desde nuestro sitio tenemos que acceder a otro lado para sacarlo, tal vez el otro lado se harte de nosotros, o que no responda en algún momento debido a una caída. Lo que podemos hacer es acceder periódicamente a esos sitios sin que exista mediación por parte de nuestros usuarios y guardar esos contenidos en una memoria o base de datos, de modo que nosotros podamos acudir a esos datos de forma muy rápida. Nuestro dato tal vez no sea el más actualizado, en el caso de las cotizaciones, tal vez tengamos un cierto desfase (aunque podemos aplicar otras técnicas), pero si se trata de tweets, posts y demás, esos contenidos no van a variar con el tiempo.

Otro uso muy útil es la importación, exportación y cálculo de datos. En sistemas de estadística, puede que nuestra aplicación deba importar gran cantidad de datos procedente de diversas fuentes (webs externas, ficheros de usuario, APIs, etc). Esta tarea puede ser muy rápida o llegar a ser muy lenta en su ejecución. ¿Por qué no hacerla en segundo plano y avisar al usuario cuando terminemos? Por un lado, evitamos que el usuario esté eternamente esperando una respuesta (y que se estrese poniéndose a pedir los mismos datos una y otra vez), por otro, si la tarea hace un uso muy intensivo de CPU, podemos limitarlo (la tarea puede realizarse de forma más lenta, pero al menos no nos tira el servicio). Tanto si estamos introduciendo datos, generando un fichero de reporte, realizando cálculos, que también puede ser convirtiendo un vídeo u obteniendo una miniatura de un vídeo, son tareas que pueden estresar al usuario y no deberían influir en la carga de nuestras páginas.

También puede que nos interese eliminar sesiones de usuarios inactivos, invalidar o generar cachés de contenidos complicados. El objetivo es que el usuario final de nuestra página tenga que esperar lo menos posible para la realización de las tareas antes de ver un resultado en la página web. Por supuesto, luego podemos consultar por Ajax si una tarea ha finalizado para avisar al usuario o, como es el caso del envío de correos, es algo que al usuario no le importa, pero a nosotros sí.

Escalabilidad

Algo que tenemos que tener en cuenta cuando estamos inmersos en un proyecto es su escalabilidad. En un primer momento, y si no nos queda otra, cuando empezamos a tener visitas a nuestros servicios podremos optar por una escalabilidad vertical. Es decir, utilizar máquinas más potentes para servir nuestros contenidos. Aunque pronto empiezan a surgir más problemas si hacemos de esta práctica algo común. Los costes suelen dispararse, es más barato comprarse 4 ordenadores pequeños que uno cuya potencia englobe a los cuatro. Vale, no siempre es así, pero si nos ponemos a mirar presupuestos de ordenadores llega un momento en el que sí se cumple. Por otro lado, si tenemos nuestro servicio alojado únicamente en una máquina, sólo hace falta que se rompa una máquina para echar todo al traste. Parece una frase tonta pero es así, mientras que si tenemos nuestro servicio replicado en cuatro máquinas, si se rompe una nos fastidia, pero no nos pillamos los dedos. Bueno, tras todo esto tendríamos que pensar qué parte debemos escalar y cómo, porque puede que escalar toda una aplicación mastodóntica no sea la solución, debemos analizar los cuellos de botella y optar por una solución inteligente.

El caso es que muchas veces este tipo de tareas pueden pasarse a otra máquina. Es decir, nuestra máquina principal (o máquinas), como punto de entrada servirán webs y punto. Pero para las tareas programadas, que puede que algunas requieran mucha CPU vamos a utilizar otra máquina, que incluso será privada y funcionará de forma independiente, incluso si vemos que las tareas son muy exigentes podemos dividirlas en varias máquinas a nuestro gusto. De esta manera podremos hacer crecer nuestra aplicación.

Informes de errores

Aunque muchos programadores dejan esto para el final, en mi opinión es lo primero que deberíamos implementar. Cada vez que ejecutamos una de estas tareas deberíamos escribir en algún lado (base de datos, fichero log, servicio de logs externo, etc) que estamos empezando a realizar la tarea. Deberíamos escribir los errores que nos encontramos y los pasos que damos a la hora de ejecutar tareas, así como su finalización. Con esto tendremos un seguimiento que nos puede ayudar a corregir errores o saber si alguna tarea se ha quedado bloqueada. Más tarde podremos desactivar mensajes para que solo muestre inicio de tarea, fin de tarea y errores, así sabemos que nuestro programa hace su trabajo.

Tareas tras la carga de la web

Una técnica que se ha utilizado durante mucho tiempo es que tras la carga completa de la web se ejecute cierto código de mantenimiento. Es cierto que durante mucho tiempo, una vez se ha cargado la web, se comprueba si se deben realizar tareas y se realizan en caso afirmativo. Muchos sitios lo hacen cuando carga una web, otros sitios hacen una petición Ajax para ejecutar tareas… el problema es que en cualquier caso, estamos haciendo que el usuario intervenga en el disparo de dichas tareas. Eso puede causar que la página cargue más lenta, que haya demasiadas peticiones innecesarias al sistema de tareas, que si la tarea tarda mucho en ejecutarse se cancele debido a los timeouts de PHP o del servidor web, o que, si ningún usuario entra a la web en un tiempo, no se lance ninguna tarea. En cualquier caso, yo soy de la idea de que “son cosas que al usuario no le interesan“, incluso en algunos sistemas, podemos ver tras la petición (sobre todo las Ajax), si se ha ejecutado la tarea y cómo ha ido dicha ejecución, cosa que interesa mucho menos al usuario.

El origen de todo esto es porque muchos servidores (sobre todo los compartidos), históricamente no nos dejaban hacer llamadas a otras aplicaciones de gestión de tareas o incluso la creación de tareas programadas (cron jobs). Actualmente, hasta los hospedajes compartidos nos dejan hacer cosas así, de todas formas, yo te recomiendo montarte por lo menos un VPN para tener más libertar con estas cosas. Aún así, las necesidades de ejecución de tareas en nuestras aplicaciones están creciendo.

Cron jobs

Reloj
Son las tareas programadas de toda la vida, de esta forma le podemos decir al sistema ejecuta este programa cada x días, h horas y m minutos. Normalmente un servidor ejecuta muchas tareas en segundo plano como pueden ser mantenimiento de logs, sincronización del reloj, comprobación de actualizaciones, envío de informes de salud, generación de estadísticas y algunas más. Así que, ¿por qué no introducir nuestras propias tareas?

Por un lado podríamos utilizar un script para cada tarea, por ejemplo invalidación de cachés o limpieza de base de datos, consulta de webs externas y envío de e-mails incluso introducir diferente periodicidad para cada una de ellas. Pero en muchas ocasiones podemos incluso centralizarlo todo en una única llamada. Si hablamos de PHP, podremos ejecutar un único archivo PHP o Python, o Java y que éste se encargue de revisar las tareas pendientes y ejecutar las que crea pertinentes. Habrá ejemplos en PHP en el siguiente post. Incluso si lo hacemos de forma centralizada, podremos introducir tareas que solo deban lanzarse una vez en el juego. Por ejemplo cuando exista una tarea que no tiene por qué ser periódica, como puede ser la obtención de los últimos posts de un blog que acaban de introducir en nuestra web (no es periódico, se realiza cuando un usuario da de alta su blog). Luego este cron job lo podemos ejecutar cada minuto o cada dos minutos, como lo creamos necesario. Basta con ejecutar:

crontab -e

Y luego añadir
1
2
# m h  dom mon dow   command
*/2 * * * * php /ruta/del/archivo/archivo.php

Con esto ejectaremos la tarea cada dos minutos. Seguro que buscando por la red encontramos mucha información sobre los cron jobs.

En nuestro script podremos hacer una consulta a base de datos para determinar las tareas que hay pendientes (periódicas o no) y ejecutar la que toque, incluso cada tarea podrá realizar llamadas a otros procesos, y todo sin que el usuario tenga que intervenir y sin costarle tiempo a él.

Lo malo es que los cron jobs de Unix no pueden alcanzar una resolución de segundos por lo que si queremos lanzar una tarea cada segundo, cada dos, o cada quince segundos tenemos que andar ejecutando tareas con sleep delante, aunque podríamos tener problemas si una tarea tarda más de lo normal en ejecutarse.

Programador de tareas en ejecución

Otra opción es crear nosotros un programador de tareas en el lenguaje en el que estemos trabajando. Por un lado, si utilizamos una base de datos para el control de nuestras tareas no tendremos que iniciar conexiones cada vez que ejecutamos la tarea, lo que gasta tiempo y recursos, sino que podremos tener una conexión abierta todo el tiempo. Y, por otro lado, podemos mirar si tenemos una tarea pendiente cada menos tiempo, por ejemplo cada dos segundos. En este caso, si, en el caso de una tarea de envío de correo electrónico, comprobamos si tenemos que enviar e-mails cada minuto, en el peor de los casos, el mensaje tardará un minuto (más o menos en salir). Mientras que si comprobamos cada dos segundos, al usuario prácticamente le da tiempo a cambiar de ventana, acceder a su correo y comprobar si tiene algo nuevo cuando ya ha recibido el mensaje. Parece que no, pero da una mejor sensación a los usuarios.

Por otro lado, si utilizamos un programador de tareas de este estilo podremos tener varias instancias del programador corriendo en una misma máquina. En caso de que no sean tareas muy intensas computacionalmente, como son enviar e-mails, descargar información de webs externas, etc podríamos tener varias tareas en ejecución a la vez. Eso sí, deberíamos controlar muy bien que no se lance la misma tarea por dos programadores a la vez, lo que puede ser un problema. Así como determinar qué tareas pueden ejecutarse en varios sitios a la vez y cuáles no.

Como el artículo me ha quedado muy largo, he decidido dividirlo en tres. Así que para el próximo incluiré ejemplos de código fuente. La segunda parte estará disponible el día 18 de Julio.

Foto principal: unsplash-logoEstée Janssens

Foto (reloj): unsplash-logoÁlvaro Bernal

The post Programación de tareas en segundo plano para nuestras aplicaciones web (Introducción. Parte 1 de 3) appeared first on Poesía Binaria.

Picando Código: Lanzamiento ATuNombre.uy 2.0 👩👧👱👵

$
0
0

DATA presenta una versión renovada de ATuNombre: la aplicación que señala el problema de la desigualdad de género en el nomenclator urbano:

ATuNombre.uy

Hoy les escribimos para contarles del re-lanzamiento, en una versión completamente renovada, de ATuNombre.uy. Un proyecto que toma los nombres de las calles de Montevideo, para hablar sobre la desigualdad de género.

El objetivo de este mapa es dar visibilidad a los aportes que hacen las mujeres a nuestra sociedad, promover su visibilidad y reconocimiento público a través de los nombres de calles de Montevideo. Queremos ver en qué medida estos aportes son valorados, reconocidos y qué hacían esas mujeres para que “pasen a la historia” otorgándoles el nombre de una calle, pasaje o avenida.
¿Cómo lo hicimos?

Este proyecto nace de una propuesta de DATA Uruguay, que invitó a la comunidad a desarrollar esta idea a través de nuestros encuentros regulares llamados Café de DATA (reuniones para trabajar en Datos Abiertos y herramientas sociales).

La primera versión de este sitio se comenzó a trabajar junto a un equipo voluntario (¡gracias!) el 21 de febrero de 2015 (Día Mundial de los Datos Abiertos) y en 2017, empezamos a trabajar nuevamente en renovar la propuesta a lo largo de varios encuentros y con trabajo remoto. Conocé al equipo que hizo esto posible y tené en cuenta que este proyecto va a seguir desarrollándose y mejorando.

Adrianistán: ¿Cómo funcionan los sistemas de archivos basados en FAT?

$
0
0

Voy a dedicar unas entradas en el blog a hablar del funcionamiento de los sistemas de archivos, un componente fundamental en la gran mayoría de sistemas informáticos. Voy a empezar con los basados en FAT sin centrarme en ninguno en concreto (FAT16, FAT32, exFAT,…). Intentaré escribir también sobre inodos y mecanismos más raros y avanzados.

En esencia un sistema de archivos es un método ordenado que permite guardar datos sobre un soporte físico para luego poder acceder a ellos. Históricamente ha habido muchos enfoques a este problema: los sistemas más usados usan archivos, directorios y enlaces.

Bloques y sectores: la división del disco

Esta parte es común a muchos sistemas de archivos, tanto FAT como inodos, como otros. A nivel físico los dipositivos están divididos. En el caso del disco duro, el dispositivo de almacenamiento más común, los discos se dividen en sectores de N bytes, según parámetros de la fabricación. Estos sectores cuentan con código de control de errores incorporado y todo ello es manejado por la controladora de disco que opera ajena al sistema operativo. Los sistemas de archivos dividen a su vez el disco en bloques. Es importante dejar claro que bloque no es igual a sector. El bloque es una división que hace el sistema de archivos y los sectores los hace la controladora de disco. Usualmente un bloque equivale a varios sectores, aunque la equivalencia real depende del sistema de archivos en concreto.

Algunos bloques especiales: boot y superbloque y raíz

Antes de entrar en el mecanismo específico de FAT es necesario comentar que en un disco existirán dos bloques de vital importancia ajenos a FAT. El primero es el bloque de boot o arranque (también llamado MBR). Normalmente situado en el sector 0 del disco duro, contiene código para iniciar el sistema operativo. El superbloque suele ser el bloque siguiente, contiene metadatos del sistema de archivos (por ejemplo, puede decir que usamos FAT con bloques de 32KiB, etc). Además en FAT es necesario que exista un fichero siempre, el fichero del directorio raíz.

Directorios

Los directorios o carpetas son archivos como otros cualquiera, solamente que en sus metadatos se indica que es un directorio y no un fichero corriente. Los ficheros de directorios son los encargados de almacenar los metadatos de los ficheros (paras saber si son ficheros, otros directorios, su fecha de modificación, propietario y tamaño entre otros) que se encuentran en el directorio así como una referencia al bloque.

Un fichero en FAT

Vamos al asunto. Supongamos que en un determinado bloque N tenemos un fichero. Este bloque es de 64 KiB. Si un fichero ocupa menos de 64 KiB, perfecto, todos los datos entran en el bloque. Simplemente ajustamos los metadatos de tamaño con el valor correcto y el resto del espacio que queda en el bloque queda inutilizado.

Este espacio perdido se denomina fragmentación interna y dependiendo de los datos que se almacenen en un disco duro, el porcentaje de pérdida puede ser mayor o menor. Evidentemente si tenemos bloques muy grandes y ficheros muy pequeños perderemos mucho espacio debido a la fragmentación interna. Tener bloques muy pequeños y ficheros muy grandes también es problemático pero por otros motivos.

Tipos de fragmentación

En un sistema de archivos existen 3 tipos de fragmentación: interna, externa y de datos. La interna se refiere al espacio perdido en bloques asignados a ficheros que no están llenos por completo. La externa se refiere al espacio que perdemos por no tener un espacio libre contiguo lo suficientemente grande como para guardar el fichero allí. Ningún sistema FAT o de inodos tiene fragmentación externa al usar todos bloques de tamaño predefinido. Por último la fragmentación de datos, o fragmentación a secas, se refiere a que los bloques asignados estén contiguos o no. Esto tiene implicaciones a nivel de rendimiento pero no al número de bytes que se vuelven inútiles como los otros dos tipos de fragmentación.

¿Pero qué pasa si el contenido de nuestro fichero no puede entrar en el tamaño de bloque? Aquí viene la gracia de FAT, la File-Allocation-Table. La FAT es una tabla compuesta por entradas que indican el siguiente bloque del archivo. La tabla está indexada por bloque y además de indicar cuál es el siguiente bloque del archivo también indica si el archivo acaba ahí o si ese bloque está libre y puede usarse para un archivo nuevo.

En la foto el archivo /home/user/hola.txt tiene una longitud menor al tamaño de bloque. Así que miramos en la FAT la entrada 150 y efectivamente comprobamos que no hay bloque siguiente ya que es un End-of-File.

Pongamos un ejemplo con un archivo largo. Cada celda de la tabla correspondiente al índice del bloque actual indica el siguiente bloque a leer. Estos bloques pueden estar en cualquier parte. Si en un disco duro muchos ficheros tienen los bloques muy dispersos, se dice que necesita ser desfragmentado.

Conclusiones

FAT es muy rápido si la tabla FAT consigue ser cargada en memoria, ya que obtener los datos necesarios de ella será muy rápido. Desgraciadamente, dependiendo del tamaño del disco duro y del tamaño de los bloques, esta tabla puede ocupar mucho y ser impráctico.

El mecanismo FAT como vemos es simple pero efectivo. Dispone de una fragmentación interna limitada y carece de fragmentación externa. Un inconveniente es que FAT cuando busca un archivo necesita empezar siempre desde el directorio raíz. Esto implica accesos cada vez más lentos ha medida que vayamos haciendo carpetas, aunque con sistemas de caché se puede reducir.

El esquema FAT tampoco impone restricciones propiamente dichas al número archivos de un sistema ni a su tamaño. Sin embargo en la práctica suelen existir límites.

Edito: He explicado mejor que significa la fragmentación y sus tipos

La entrada ¿Cómo funcionan los sistemas de archivos basados en FAT? se publicó primero en Adrianistán.


Variable not found: Stack traces asíncronas legibles, de serie en .NET Core 2.1 (¡y también en otros frameworks!)

$
0
0
ASP.NET CoreAparte de las novedades que comentamos hace una semana, ASP.NET Core 2.1 incluye algunas sorpresas adicionales que, sin ser revolucionarias, nos harán más sencillo el día a día. En esta ocasión vamos a comentar una característica introducida en .NET Core 2.1 que aporta mejoras de legibilidad en las trazas de excepciones producidas en llamadas asíncronas, algo que obtendremos for free, por el mero hecho de utilizar la última revisión del framework.

Veamos muy rápidamente en qué consiste.

Un ejemplo de traza de excepción ilegible

Imaginemos el código de una aplicación de consola tan simple como la siguiente:
class Program
{
staticasync Task Main(string[] args)
{
try
{
await DoSomethingAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

staticasync Task DoSomethingAsync()
{
await DoAnotherThingAsync();
}

privatestatic Task DoAnotherThingAsync()
{
thrownew Exception();
}
}
Si compilamos esta aplicación en .NET Core 2.0 o anteriores, o incluso en .NET Framework, y la ejecutamos, la salida por consola será más o menos la siguiente:
System.Exception: Exception of type 'System.Exception' was thrown.
at ConsoleApp_20.Program.DoAnotherThingAsync() in C:\ConsoleApp-20\Program.cs:line29
at ConsoleApp_20.Program.<DoSomethingAsync>d__1.MoveNext() in C:\ConsoleApp-20\Program.cs:line24
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ConsoleApp_20.Program.<Main>d__0.MoveNext() in C:\ConsoleApp-20\Program.cs:line12
El problema con esta traza es el ruido y la cantidad de información que muestra, que hace que no sea fácil de leer e interpretar. Aunque la línea donde se ha producido el error se puede ver con relativa claridad (línea 29, en DoAnotherThingAsync()), a partir de ahí no es sencillo ver por dónde ha ido pasando el flujo de ejecución.

Si esto es así en una aplicación de consola de pocas líneas, seguro que ya habréis sufrido lo complicado que es leerla cuando se trata de una aplicación de mayor calado, además montada sobre un framework complejo y puramente asíncrono como ASP.NET Core.

Trazas de excepciones legibles en .NET Core 2.1

Probemos ahora exactamente el mismo código, pero esta vez compilándolo y ejecutándolo sobre .NET Core 2.1. Sin hacer nada más, en esta ocasión la traza que obtenemos en pantalla es la siguiente:
System.Exception: Exception oftype'System.Exception' was thrown.
at ConsoleApp_21.Program.DoAnotherThingAsync() in C:\ConsoleApp-21\Program.cs:line 29
at ConsoleApp_21.Program.DoSomethingAsync() in C:\ConsoleApp-21\Program.cs:line 24
at ConsoleApp_21.Program.Main(String[] args) in C:\ConsoleApp-21\Program.cs:line 12
Genial, ¿eh? Ahora sí que queda perfectamente claro dónde explotó la aplicación, y la traza completa de ejecución :)
Si estáis interesados, podéis leer más sobre esta feature en el post Stacktrace improvements in .NET Core 2.1 de uno de sus desarrolladores.

Hey, ¿y no podría conseguir lo mismo en .NET Framework o versiones anteriores de .NET Core?

Pues sí :) Afortunadamente, Ben Adams, Microsoft MVP y uno de los principales contributors de ASP.NET Core, además de trabajar en varios pull requests que implementaban esta característica en .NET Core 2.1, creó hace unos meses el proyecto Ben.Demystifier, que permite “desmitificar” los stack traces ilegibles dejándolos limpios de información no esencial, como en el ejemplo que vimos antes.

Para utilizarlo, basta con instalar el paquete desde Nuget, e invocar el método Demystify() para generar una excepción con las trazas retocadas:
try
{
await DoSomethingAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Demystify());
}
Publicado en Variable not found.

Adrianistán: ¿Cómo funcionan los sistemas basados en inodos?

$
0
0

Después de ver como funcionan de forma genérica los sistemas FAT, saltamos a los sistemas de inodos. Estos se han usado tradicionalmente en sistemas UNIX (UFS, ext2), así que tradicionalmente ha existido una cierta rivalidad  en las redes entre FAT e inodos similar a la de Windows/Linux. Lo cierto es que a nivel técnico cada uno tiene fortalezas y debilidades.

Partición

Tomando la base de FAT, una partición de un sistema basado en inodos también contiene un sector de arranque y un superbloque con metadatos. También es necesario un bloque dedicado al directorio raíz presente en el disco. Además es necesario espacio para almacenar todos los inodos y un mapa de bits de espacio libre que en FAT no hacía falta, ya que la propia tabla nos indicaba que bloques del disco estaban libres.

Los inodos

¿Qué es inodo te podrás preguntar? Es una estructura de datos, su nombre proviene de index node y es que los inodos no son más que índices, que almacenan los números de bloque de las diferentes partes del archivo. Además, contienen metadatos como permisos, propietario, tamaño, fecha de modificación, referencias, tipo de fichero (directorio, archivo, enlace duro, enlace blando,…) salvo el nombre del propio archivo, que en ningún sitio del inodo aparece.

Este sistema tiene una ventaja de rendimiento respecto a FAT en cuanto al acceso aleatorio a los archivos, ya que es mucho más rápido de esta forma que con FAT. En FAT para hacer lo mismo tenemos que ir recorriendo la tabla arriba y abajo siguiendo los números de bloque hasta encontrar el bloque deseado.

Normalmente un inodo tiene un tamaño fijo, lo que implica que el índice no se puede alargar hasta el infinito. Esto hace que haya un número limitado de bloques que puede usar un archivo y por ende, que haya un tamaño máximo de archivo que no es muy elevado. Para solventar este problema hay varias soluciones. El enfoque de UFS y de ext2/3/4 consiste en múltiples niveles de indexado progresivo.

Esto significa que los primeros bloques son números de bloque directos pero los siguientes son números de bloque que llevan a tablas de inodo secundarias que ya sí, hacen referencia al archivo real. Más adelante los números de bloque hacen referencias a tablas de inodo secundarias que a su vez llaman a tablas de inodos terciarias.

Esto provoca algo que en principio puede parecer paradójico y es que es más lento leer una zona final de un archivo que una zona del principio, aunque en una lectura secuencial no se nota demasiado.

Otra solución a esto es enlazar los inodos a modo de lista enlazada añadiendo al final de cada inodo un número de inodo al que continuar la lectura de índices.

Localización de los inodos

Los inodos se crean cuando se formatea el disco y permanecen en un estado libre. No se pueden añadir más inodos ni quitar inodos y no puede haber más archivos y directorios que inodos por esta misma razón. Esto es una desventaja respecto a FAT, ya que en FAT puede haber tantos archivos como bloques haya/entradas tenga la tabla. En sistemas de inodos como ext2/3/4 puede ocurrir que no queden inodos libres pero haya todavía bloques libres, dejando todo ese espacio inutilizado (aunque en realidad lo podrían usar los archivos existentes si creciesen).

Los inodos se pueden ubicar de dos formas distintas. Un enfoque consiste en ponerlos al principio del disco todos juntos. Otro enfoque, el que sigue ext3, consiste en dividir el disco en 4 zonas y ubicar inodos en cada inicio de zona. La idea es que los inodos de esa zona usen bloques de esa zona y de esta forma reducir los desplazamientos de las cabezas lectoras del disco (en unidades SSD esto da completamente igual como podréis suponer).

Gestión del espacio libre

Una de las grandes ventajas de FAT era que la tabla podía mantener a la vez un listado de bloques libres, listos para ser usados. Con inodos no tenemos esa facilidad y tenemos que recurrir a otros tipos de estructura. Aquí hay muchos planteamientos siendo el más común el mapa de bits. El mapa de bits es una estructura que se compone de un listado de bits. Cada bit corresponde a un bloque y dependiendo de si el bit es 1 o 0 podemos saber si el bloque está libre u ocupado.

Como veis, los sistemas basados en inodos tienen que mantener varias estructuras de datos de forma paralela. Esto aumenta las probabilidades de inconsistencias en el sistema. Por este motivo, muchos sistemas más avanzados como ext4 mantienen un journal o diario.

Fragmentación

Veamos como se comportan los sistemas de inodos ante los tres tipos de fragmentación. Respecto a la fragmentación interna, que es aquella que sucede cuando asignamos espacio de más a archivos que no necesitan tanto, tiene los mismos valores que FAT, ya que los bloques siguen pudiendo pertenecer únicamente a un archivo. Realmente, para mejorar la fragmentación interna tenemos que saltar a sistemas como XFS o JFS.

La fragmentación externa de los sistemas basados en inodos es la misma que la de FAT, 0, ya que todo se asigna mediante bloques del mismo tamaño.

Respecto a la fragmentación de datos, históricamente las implementaciones como UFS o ext2/3 se han comportado relativamente bien, aunque a nivel teórico nada impide que se comporten igual de mal que FAT, no existen mecanismos preventivos. Ext4 por contra, sí que tiene mecanismos de prevención (delayed allocation).

Directorios y enlaces duros

En los sistemas basados en inodos los directorios son más sencillos que en FAT, ya que no tienen que almacenar los metadatos del archivo en cuestión. En un fichero de directorio simplemente se almacena el nombre y el número de inodo correspondiente.

Aquí surge un concepto muy interesante, el de enlaces duros. Un enlace duro no es más que otra entrada de directorio que apunta a un mismo inodo. Realmente desde el punto de vista del sistema de archivos no existe un fichero original y su enlace. Simplemente dos entradas diferentes apuntando al mismo inodo. ¿Y cómo reacciona esto a los borrados? Imagina la siguiente situación: tengo un fichero y creo un enlace duro. Borro el fichero original y su inodo se libera. Ahora creo otro archivo y reutiliza ese mismo inodo que estaba libre. ¡Ahora el enlace duro apunta a un contenido totalmente distinto sin darse cuenta! Para prevenir esto, los inodos no se liberan hasta que no quede ninguna entrada de directorio apuntando a ellos. Para eso sirve el campo referencias dentro del inodo, para llevar la cuenta de cuántas veces se hace referencia al inodo en el sistema de archivos. Cuando el valor de referencias llega a cero, se puede liberar sin problema.

Conclusiones

Los sistemas basados en inodos son conceptualmente algo más complejos que los basados en FAT. Comparten además limitaciones comunes, usan más espacio de disco, aunque suelen ser más rápidos.

Actualmente existen sistemas basados en inodos mucho más avanzados y complejos que UFS y ext2/3/4 y que mejoran este sistema de forma sustancial. Por ejemplo en XFS los inodos tienen una estructura totalmente distinta donde se indica un bloque de inicio y una longitud en número de bloques contiguos que pertenecen al archivo. Muchos sistemas además usan estructuras de árbol B+ como pueden ser ZFS o Btrfs.

La entrada ¿Cómo funcionan los sistemas basados en inodos? se publicó primero en Adrianistán.

Blog Bitix: Java Magazine, la publicación gratuita sobre Java

$
0
0
Java

La revista Java Magazine es una publicación bimensual gratuita que contiene artículos técnicos sobre temas muy variados relacionados con Java. Las revistas pueden consultarse directamente en la web o de forma más cómoda descargándola a nuestro ordenador en formato de archivo PDF. También es posible suscribirse a una lista de distribución para recibir mediante un correo electrónico una notificación cuando se publique un nuevo número de la revista o estar atento y seguir al twitter de la revista @Oraclejavamag. Su longitud ronda algo menos de 100 páginas por número.

Estas son las portadas de la revista de los tres últimos años y medio, desde el 2015 hasta junio del 2018, en las que se incluye los principales temas tratados en cada revista. Durante este tiempo publicando por supuesto algún número dedicado a Java 9 y Java 10, Java EE 8, JSF, JavaMail, JAX-RS, Spring Boot, JVM, JUnit, IoT, HTTP/2, JavaFX, Gradle, Lombok, JCommander, Byte Buddy, Jsoup, WebSockets, JPA, CDI, Servlet 4.0, testing, miroservicios y contenedores y muchos más temas.

Java Magazine 2018
Java Magazine 2017
Java Magazine 2016
Java Magazine 2015

La mayoría de artículos que incluyen suelen ser muy interesantes y de muy buena calidad de los que casi siempre se aprende algo, algunos artículos están escritos por personas con muy altos conocimientos sobre Java, algunos han sido galardonados como Java Champion, y en algunos otros casos son los desarrolladores principales de algunas de la librerías más populares en el ecosistema Java.

Si estás interesado en Java seguir esta revista y leer sus artículos es una buena recomendación.

Variable not found: Enlaces interesantes 326

$
0
0
Enlaces interesantes
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

Variable not found: El nuevo tipo de retorno ActionResult en ASP.NET Core 2.1

$
0
0
ASP.NET Core MVC Como sabéis, por defecto las acciones MVC y Web API en ASP.NET Core retornan un IActionResult (o un Task<IActionResult> en caso de ser asíncronas). Este interfaz, cuya única misión es definir el método de ejecución asíncrona del resultado, ExecuteResultAsync(), es implementado por la gran variedad de tipos de resultado ofrecidos de serie por el framework, como ViewResult, RedirectResult o FileResult.

Sin embargo, no es esta la única opción disponible a la hora de especificar el resultado de una acción, como veremos a continuación.

¿Qué más puede retornar una acción en ASP.NET Core?

Pues además de una instancia de IActionResult, ASP.NET Core MVC ofrece otros tipos de resultado a la hora de implementar acciones.

Por ejemplo, el framework también incluye la clase abstracta ActionResult, definida como vemos más abajo:
publicabstractclass ActionResult : IActionResult
{
publicvirtual Task ExecuteResultAsync(ActionContext context)
{
ExecuteResult(context);
return Task.CompletedTask;
}

publicvirtualvoidExecuteResult(ActionContext context) { }
}
Esta clase, que es la base de prácticamente todos los tipos de resultados que vienen de serie con el framework, establece un puente sencillo entre la ejecución asíncrona del resultado y su implementación síncrona, facilitando la construcción de IActionResult personalizados.

También existe la posibilidad de retornar directamente objetos desde las acciones, una solución bastante útil cuando implementamos una API como la siguiente:
[HttpGet("users/{id}")]
public User Get(int id)
{
var user = _userRepository.GetById(id);
return user;
}
Observa que la gran ventaja de este último enfoque es que la acción muestra claramente el tipo de objeto retornado, por lo que, además de aumentar su legibilidad, herramientas como Swagger podrían generar documentación y clientes muy específicos.

Y por último, hace poco hablábamos de que la llegada de ASP.NET Core 2.1 venía acompañada de un nuevo tipo de resultado para las acciones MVC: la clase ActionResult<T>. Básicamente, una acción definida con un retorno ActionResult<T> devolverá o bien una instancia de ActionResult o bien una instancia de T. Esto es muy interesante para solucionar escenarios como el siguiente, que fallarían en compilación:
// Ojo, no compila!
[HttpGet("users/{id}")]
public User Get(int id)
{
var user = _userRepository.GetById(id);

if (user == null)
return NotFound();
return user;
}
Obviamente, el código anterior podríamos solucionarlo muy fácilmente como se muestra a continuación, pero entonces no bastaría con observar la firma de la acción para saber qué tipo de objeto es el que podemos esperar de ella:
// Compila, pero la acción no da pistas sobre el tipo de objetos que retorna
[HttpGet("users/{id}")]
public ActionResult Get(int id)
{
var user = _userRepository.GetById(id);
if (user == null)
return NotFound();
return Ok(user);
}
Y ahí es donde entra en juego el nuevo ActionResult<T>. Utilizando esta clase, la cosa quedaría bastante mejor pues tenemos la posibilidad de retornar cualquier ActionResult, pero manteniendo la claridad y capacidad de autodocumentación del método de acción:
// Compila y la acción muestra el tipo de objetos que retorna
[HttpGet("users/{id}")]
public ActionResult<User> Get(int id)
{
var user = _userRepository.GetById(id);

if (user == null)
return NotFound();
return user;
}

¿Y cómo es posible que compile? ¿Es esto magia negra? 👹

Pues aunque a primera vista pudiera parecerlo, no lo es ;) El truco está en la utilización ingeniosa del operador implicit de C# para conseguir asimilar los tipos ActionResult y T con ActionResult<T> mediante conversores implícitos.

Para los curiosos, aquí os dejo el código fuente de ActionResult<T>, así os evito ir a Github a verlo:
publicsealedclass ActionResult<TValue> : IConvertToActionResult
{
publicActionResult(TValue value)
{
Value = value;
}

publicActionResult(ActionResult result)
{
Result = result ?? thrownew ArgumentNullException(nameof(result));
}

public ActionResult Result { get; }
public TValue Value { get; }

publicstaticimplicitoperator ActionResult<TValue>(TValue value)
{
returnnew ActionResult<TValue>(value);
}

publicstaticimplicitoperator ActionResult<TValue>(ActionResult result)
{
returnnew ActionResult<TValue>(result);
}

IActionResult IConvertToActionResult.Convert()
{
return Result ?? new ObjectResult(Value)
{
DeclaredType = typeof(TValue),
};
}
}
Qué bueno, ¿eh? ;)

Publicado en Variable not found.
Viewing all 2715 articles
Browse latest View live