Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Du 22 au 24 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. PowerShell
  3. Jobs et parallélisation
Extrait - PowerShell Fonctionnalités avancées (2e édition)
Extraits du livre
PowerShell Fonctionnalités avancées (2e édition)
1 avis
Revenir à la page d'achat du livre

Jobs et parallélisation

Introduction

Lorsqu’un traitement prend beaucoup de temps, on est souvent impatient d’avoir le résultat. Quand un second traitement doit être fait, on ouvre généralement une seconde console PowerShell. Mais si le second traitement est lié à tous les objets et toutes les variables que l’on a récupérés et créés juste avant de lancer le premier, comment procède-t-on ?

Il est possible de mettre ce premier traitement en travail de fond, pour passer directement au second. Sous PowerShell, cette fonctionnalité s’appelle jobs (travaux). Elle a été ajoutée dans la version 2.0 de PowerShell et a subi depuis de nombreux ajouts fonctionnels.

Le lancement de jobs permet de récupérer directement la console et de continuer à travailler. L’obligation de passer par des jobs pour faire plusieurs tâches de manière asynchrone vient du fait que le processus PowerShell n’utilise par défaut qu’un seul cœur de processeur avec un seul thread. La plupart des jobs sont lancés dans un nouvel environnement PowerShell avec son propre processus powershell.exe dédié. Toutefois, nous verrons qu’il existe une exception.

Différents types de jobs sont aujourd’hui exploitables :

  • BackgroundJob : travaux en arrière-plan. Ils sont lancés depuis...

Travaux en arrière-plan locaux (BackgroundJob)

Le mécanisme qui va être vu ici concerne les jobs locaux. Il est possible de lancer un long travail en arrière-plan, afin de récupérer la main directement dans la console. Le résultat des jobs est récupéré dans un second temps.

L’autre cas d’usage est de subdiviser un travail global en plusieurs plus petits. Imaginons que l’on a un export ou une synchronisation à faire entre deux bases de données ou annuaires. Si les différences sont trop importantes, cela va prendre plusieurs heures, voire plusieurs jours. De multiples traitements asynchrones avec une planification peuvent réduire le temps d’exécution.

Les jobs sont des processus enfants dont le processus parent est celui de la console depuis laquelle ils sont lancés. Si jamais le processus parent est arrêté, l’ensemble des processus enfants l’est également. Cela implique aussi que les résultats d’un job ne peuvent être récupérés que dans la console ayant lancé le job. Les résultats ne sont pas inscrits sur le disque (sauf pour les PSWorkflowJob et RemoteJob).

Pour interagir avec les jobs, des commandes sont disponibles :

Commande

Description

Start-Job

Cette commande crée et démarre un job en arrière-plan.

Get-Job

Cette commande récupère l’ensemble des jobs présents, peu importe leur état.

Receive-Job

Cette commande récupère le résultat d’un ou de plusieurs jobs.

Remove-Job

Cette commande supprime les jobs qui sont terminés. Pour qu’un job se supprime sans problème, il est nécessaire d’avoir préalablement récupéré son résultat avec Receive-Job.

Stop-Job

Cette commande permet d’arrêter une tâche en arrière-plan.

Wait-Job

Cette commande permet d’attendre la fin d’un ou de plusieurs jobs avant de rendre la main à l’utilisateur sur la console.

Dans les premiers temps de développement de la version 2.0 de PowerShell, les jobs locaux se basaient sur le même mécanisme que les jobs distants. Ils utilisaient un flux d’interaction avec WS-Management (ou WSMan pour Web Service Management). En raison de comportements...

Travaux en arrière-plan basés sur les threads (ThreadJob)

Les ThreadJob contrairement aux BackgroundJob, se basent sur de multiples threads pour l’exécution du job, là où les BackgroundJob utilisent de multiples processus. 

Du fait de cette différence, on obtient une performance bien plus importante de la part des ThreadJob. En effet, l’initiation du job et sa fermeture nécessitent moins de temps.

Par ailleurs, les ThreadJob ne sont pas disponibles par défaut sous PowerShell. Il est nécessaire d’installer le module ThreadJob depuis la PowerShell Gallery :

PS > Install-Module ThreadJob 

1. Utilisation des ThreadJob

Une fois le module installé, il est possible de consulter les commandes qu’il contient :

PS > Get-Command -Module ThreadJob 
 
CommandType Name            Version Source 
----------- ----            ------- ------ 
Cmdlet      Start-ThreadJob 2.0.3   Threadjob 

Le module dispose d’une seule commande, Start-ThreadJob. Cette dernière est l’équivalent de Start-Job pour les BackgroundJob.

Il s’agit de la seule différence d’utilisation entre les deux types de job. Pour le reste, la même gestion que pour les BackgroundJob s’applique....

Travaux en arrière-plan distants (RemoteJob)

L’une des capacités majeures qu’ont les jobs, c’est la possibilité d’être lancés sur des systèmes distants. Ces jobs sont créés avec l’utilisation de la commande Invoke-Command et de son paramètre -AsJob.

Cas particulier avec les commandes WMI : il est possible de les lancer sur des systèmes distants. Pour autant, les jobs garderont le type WMIJob.

Pour que la commande Invoke-Command fonctionne correctement, il est primordial que la connexion à distance via WinRM soit correctement configurée entre les systèmes. 

Les RemoteJob ont une structure de parent/enfants. Chaque enfant représente un hôte distant et un processus dédié.

Pour ce qui est des erreurs, si un des jobs enfants est en erreur, le job parent l’est également. Mais cela n’empêche en rien les autres jobs enfants de continuer leur traitement.

1. Lancement d’un job distant

Pour ce faire, la commande Invoke-Command est utilisée. Elle peut être exécutée à l’aide d’un nom d’hôte, ou bien à travers une session déjà existante. Il est généralement préférable d’utiliser une session quand plusieurs commandes Invoke-Command doivent être appelées. En effet, cette commande crée, utilise et détruit une session PowerShell distante lors de son utilisation avec -ComputerName.

Dans ce cas, une seule commande est utilisée :

PS > $SBRem = {Get-NetAdapter}...

Travaux planifiés (PSScheduledJob)

Imaginons qu’un traitement s’exécute sur plusieurs heures, à des heures précises, une à deux fois par jour. On serait tenté de mettre en place une tâche planifiée. Seulement voilà, la prise en compte d’un script PowerShell n’est pas native et nécessite l’appel de l’exécutable powershell.exe. Néanmoins, il existe une alternative. Depuis la version 3.0, il est possible de créer des jobs planifiés supportant pleinement les exécutions de code PowerShell.

Ce type de job est gérable grâce aux commandes contenues dans le module 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)

Le workflow est un type de job particulier. Il permet d’exécuter des travaux d’arrière-plan, de manière locale ou distante. Les mécanismes sont approfondis dans le chapitre suivant.

Si l’on reprend l’exemple de création d’archives pour l’adapter à un workflow, on obtient :

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 compression' 
    $LogFiles | Remove-Item -Force 
    Write-Output 'Fin Suppression' 
} 

Il suffit ensuite d’appeler le workflow pour l’exécution en y ajoutant le paramètre -AsJob :

PS > CompressNBAJob -AsJob 
Id Name PSJobTypeName  State   HasMoreData Location  Command ...

ForEach-Object

Dans la version 7.2 de PowerShell Core, il est possible de paralléliser les actions de la commande ForEach-Object.

On peut ainsi, de manière plus abordable, paralléliser une tâche simple comme le test de connexion à plusieurs ports d’une machine :

PS > 80,5001,5000,445 | ForEach-Object -Parallel { Test-NetConnection 
-ComputerName 192.168.2.100 -Port $_ } 

Pour ce test, on peut utiliser la commande Measure-Command pour constater des bénéfices. Dans le cas présent, on divise le temps d’exécution par quatre, car il y a tout simplement quatre ports à tester.

PowerShell API

Il existe un dernier élément permettant de paralléliser les actions PowerShell. Cela s’adresse avant tout au développeur. Pour autant, il est toujours bon d’avoir plusieurs cordes à son arc. Il s’agit ici d’une présentation.

De manière générale dans une application, on utilise une API (Application Programming Interface) pour interagir sur une technologie donnée. PowerShell dispose de sa propre API. Elle permet à des applications tierces d’utiliser le moteur PowerShell pour lancer une commande ou un bloc de script. On peut tout à fait imaginer un site web développé en ASP.NET réalisant des appels PowerShell sur le système hôte.

Il est possible d’appeler cette API directement sous la console PowerShell et de lancer des traitements asynchrones. C’est l’accélérateur [powershell] qui permet d’interagir avec l’API. On peut ainsi réaliser des actions à travers la création d’un runspace dédié au traitement.

PS > $RS = [powershell]::Create() 

On instancie en premier lieu l’objet.

PS > $RS.AddComand("Get-NetAdapter") 
PS > $RS.Invoke() 
 
Caption                                          : 
Description                                      : ...