Jobs y paralelismo
Introducción
Cuando un proceso tarda demasiado, solemos impacientarnos por obtener el resultado. Cuando es necesario ejecutar un segundo proceso, lo habitual es abrir una segunda consola de PowerShell. Pero si el segundo proceso está vinculado a todos los objetos y variables que se recuperaron o crearon justo antes de ejecutar el primero, ¿cómo proceder?
Es posible establecer este primer proceso como un trabajo en segundo plano, y luego pasar directamente al segundo. En PowerShell, esta función se denomina Jobs (trabajo). Esta función se añadió en la versión 2.0 de PowerShell y desde entonces ha incorporado múltiples mejoras funcionales.
El lanzamiento de jobs permite recuperar la consola directamente y seguir trabajando. La necesidad de utilizar jobs para realizar varias tareas de forma asíncrona se debe a que PowerShell, por defecto, solo utiliza un núcleo de procesador y un solo hilo de ejecución. La mayoría de los jobs se ejecutan en una nueva instancia de PowerShell con su propio proceso powershell.exe dedicado. Sin embargo, veremos que hay una excepción.
Ahora se pueden utilizar varios tipos de jobs:
-
BackgroundJob: trabajos en segundo plano. Se ejecutan desde el comando Start-Job.
-
ThreadJob: trabajos en segundo plano basados en múltiples threads (hilos), a diferencia de los backgroundJob, que utilizan múltiples procesos. Este tipo requiere la instalación...
Trabajo de fondo local (BackgroundJob)
El mecanismo que vamos a examinar aquí se refiere a los jobs locales. Es posible lanzar un proceso largo en segundo plano y recuperar el control de la consola de inmediato. Los resultados de los jobs se recuperan posteriormente.
El otro caso de uso es subdividir un trabajo global en varios más pequeños. Supongamos que se necesita exportar o sincronizar datos entre dos bases de datos o directorios. Si las diferencias son muy grandes, tardará varias horas o incluso varios días. Múltiples procesos asíncronos con programación pueden reducir el tiempo de ejecución.
Los jobs son procesos secundarios cuyo proceso principal es la consola desde la que se ejecutan. Si el proceso principal se detiene alguna vez, todos los procesos secundarios también se detienen. Esto también significa que los resultados de un job solo pueden ser recuperados desde la consola que lo lanzó. Los resultados no se escriben en disco (excepto para PSWorkflowJoby RemoteJob).
Hay varios comandos disponibles para interactuar con los jobs:
Comando |
Descripción |
Start-Job |
Este comando crea e inicia un job en segundo plano. |
Get-Job |
Este comando recupera todos los jobs presentes, independientemente de su estado. |
Receive-Job |
Este comando recupera los resultados de uno o más jobs. |
Remove-Job |
Este comando borra los jobs que han finalizado. Para que un job se borre sin problemas, primero debe haber recuperado el resultado con Receive-Job. |
Stop-Job |
Este comando se utiliza para detener una tarea en segundo plano. |
Wait-Job |
Este comando se utiliza para esperar a que finalicen uno o varios jobs antes de entregarlos al usuario en la consola. |
En las primeras versiones de PowerShell 2.0, los jobs locales se basaban en el mismo mecanismo que los jobs remotos. Utilizaban un flujo de interacción con WS-Management (o WSMan para Web Service Management). Debido a un comportamiento errático, este proceso se ha retirado.
1. Creación de un primer job
Ejemplo de creación de un job sencillo con el comando Start-Job y Get-HotFix:
PS > Start-Job -Scriptblock {Get-Hotfix}
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- ------- ...
Trabajo de fondo basado en threads (ThreadJob)
Los ThreadJobs, a diferencia de los BackgroundJobs, utilizan múltiples threads para ejecutar el job, mientras que los BackgroundJobs utilizan múltiples procesos.
Gracias a esta diferencia, los ThreadJobs ofrecen un rendimiento significativamente mejor. Los jobs se inician y finalizan en menos tiempo.
Los ThreadJobs no están disponibles de forma predeterminada en PowerShell. Es necesario instalar el módulo ThreadJob desde la Galería de PowerShell:
PS > Install-Module ThreadJob
1. Uso de los ThreadJobs
Una vez instalado el módulo, puede ver los comandos que contiene:
PS > Get-Command -Module ThreadJob
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Start-ThreadJob 2.0.3 Threadjob
El módulo tiene un único comando, Start-ThreadJob. Es el equivalente de Start-Job para BackgroundJob.
Esta es la única diferencia en el uso entre ambos tipos de job. Para el resto, se aplica la misma gestión que para BackgroundJob.
Volvamos al ejemplo anterior:
PS > Start-ThreadJob -ScriptBlock {
1..60 | ForEach-Object {
Write-Output...
Jobs remotos (RemoteJob)
Una de las principales capacidades de los jobs es que pueden lanzarse en sistemas remotos. Estos trabajos se crean utilizando el comando Invokey su parámetro -AsJob.
Caso especial con los comandos WMI: es posible ejecutarlos en sistemas remotos. Sin embargo, los trabajos conservarán el tipo WMIJob.
Para que el comando Invoke-Command funcione correctamente, es imprescindible que la conexión remota a través de WinRM esté correctamente configurada entre los sistemas.
Los RemoteJobs tienen una estructura padre/hijo. Cada hijo representa un host remoto y un proceso dedicado.
En cuanto a los errores, si uno de los trabajos hijo tiene un error, el trabajo padre también lo tiene. Sin embargo, esto no impide que los demás trabajos hijos continúen su ejecución.
1. Iniciar un job remoto
Para ello se utiliza el comando Invoke-Command. Puede ejecutarse utilizando un nombre de host o a través de una sesión existente. Por lo general, es preferible utilizar una sesión cuando es necesario llamar varios comandos Invoke-Command. Esto se debe a que este comando crea, utiliza y destruye una sesión remota de PowerShell cuando se utiliza con -ComputerName.
En este caso, solo se utiliza un comando:
PS > $SBRem = {Get-NetAdapter}
PS > Invoke-Command -ComputerName SRVDC01 -ScriptBlock $SBRem `
-AsJob
Id Name PSJobTypeName State HasMoreData Location Command ...
Trabajos programados (PSScheduledJob)
Imaginemos que un proceso se ejecuta durante varias horas, a horas concretas, una o dos veces al día. Podría verse tentado a configurar una tarea programada. El problema es que el scripting PowerShell no es nativo de la aplicación y requiere que se llame al ejecutable powershell.exe. Sin embargo, existe una alternativa. Desde la versión 3.0, es posible crear tareas programadas que admiten totalmente la ejecución de código PowerShell.
Este tipo de job puede gestionarse mediante los comandos contenidos en el módulo PSScheduledJob:
PS C:\Users\Nicol> Get-Command -module PSScheduledJob
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Add-JobTrigger 1.1.0.0 PSScheduledJob
Cmdlet Disable-JobTrigger 1.1.0.0 PSScheduledJob
Cmdlet Disable-ScheduledJob 1.1.0.0 PSScheduledJob
Cmdlet Enable-JobTrigger 1.1.0.0 PSScheduledJob
Cmdlet Enable-ScheduledJob 1.1.0.0 PSScheduledJob
Cmdlet Get-JobTrigger 1.1.0.0 PSScheduledJob
Cmdlet Get-ScheduledJob 1.1.0.0 PSScheduledJob
Cmdlet Get-ScheduledJobOption 1.1.0.0 PSScheduledJob ...
Workflows (PSWorkflowJob)
El workflow es un tipo especial de job. Permite ejecutar trabajos en segundo plano, ya sea de forma local o remota. Los mecanismos implicados se explican con más detalle en el capítulo siguiente.
Si tomamos el ejemplo de la creación de archivos y lo adaptamos a un workflow, obtenemos:
PS > workflow CompressNBALogs {
$LogPath = "$env:ProgramData\MyApp\Logs"
$ArchivePath = Join-Path $LogPath `
"LogArviches$(Get-Date 'yyyy-MM-dd-HH-mm')"
$LogFiles = Get-ChildItem -Path $LogPath -File `
-Filter '*.log'
Write-Output "Fichiers logs trouves : $LogFiles"
$LogFiles | Compress-Archive -DestinationPath $ArchivePath
Write-Output 'Fin de la compresión'
$LogFiles | Remove-Item -Force
Write-Output 'Fin del borrado'
}
Para ejecutar el flujo de trabajo, basta con llamarlo añadiendo el parámetro -AsJob:
PS > CompressNBAJob -AsJob
Id Name PSJobTypeName State HasMoreData Location Command ...
ForEach-Object
En la versión 7.2 de PowerShell Core, es posible el paralelismo en las acciones del comando ForEach-Object.
Esto facilita el paralelismo en tareas sencillas, como probar conexiones a varios puertos de una máquina:
PS > 80,5001,5000,445 | ForEach-Object -Parallel { Test-NetConnection
-ComputerName 192.168.2.100 -Port $_ }
Para esta prueba, puede utilizar el comando Measure-Command para ver los beneficios. En este caso, el tiempo de ejecución se divide por cuatro, porque simplemente hay cuatro puertos que probar.
PowerShell API
Hay un último elemento que permite el paralelismo en las acciones PowerShell. Esto está dirigido principalmente a los desarrolladores. Sin embargo, siempre es una buena idea tener varias cuerdas a su arco. Se trata simplemente de una introducción.
En términos generales, una aplicación utiliza una API (Application Programming Interface) para interactuar con una tecnología determinada. PowerShell tiene su propia API. Permite a aplicaciones de terceros utilizar el motor PowerShell para lanzar un comando o un bloque de script. Es fácil imaginar un sitio web desarrollado en ASP.NET haciendo llamadas a PowerShell en el sistema anfitrión.
Es posible llamar a esta API directamente desde la consola PowerShell y ejecutar procesos asíncronos. El acelerador [powershell] se utiliza para interactuar con la API. Se pueden realizar acciones creando un runspace dedicado al procesamiento.
PS > $RS = [powershell]::Create()
En primer lugar, instanciamos el objeto.
PS > $RS.AddComand("Get-NetAdapter")
PS > $RS.Invoke()
Caption :
Description : ...