Langage de stratégie de routage pour les équilibreurs de charge

Découvrez comment écrire des instructions de condition de stratégie de routage qui guident le comportement de routage d'un équilibreur de charge.

Vous devez créer des stratégies pour contrôler la façon dont les demandes entrantes sont acheminées vers des ressources telles que des serveurs Web. Ces stratégies prennent la forme générale suivante : "Si (condition quelconque), alors transmettre le trafic à un ensemble de back-ends." L'ensemble de back-ends doit avoir été créé au préalable.

Les stratégies de routage fonctionnent comme suit :

  • Chaque demande HTTP est évaluée par rapport aux règles.
  • Les règles sont exécutées dans l'ordre défini dans la stratégie.
  • Chaque règle comporte au moins une condition et un ensemble de back-ends.
  • Si la condition de demande HTTP correspond à une règle, la demande est transmise à l'ensemble de back-ends défini pour cette dernière. La demande n'est pas évaluée par rapport aux autres règles de la stratégie, qui sont ignorées.

Exemple : une règle reposant sur un chemin

Voici un exemple d'ensemble de règles de stratégie de routage contenant une seule règle reposant sur un chemin :

{
  "name": "BasicPathBasedPolicy",
  "conditionLanguageVersion": "V1",
  "rules": [
    {
      "name": "Documents_rule",
      "condition" : "any(http.request.url.path eq (i '/documents'))",
      "actions": [{
        "name": "FORWARD_TO_BACKENDSET",
        "backendSetName": "backendSetForDocuments"
      }]
    }
  ]
}

Cet exemple présente les éléments suivants :

  • L'ensemble de règles est placé entre accolades { }, et contient le nom de l'ensemble de règles, le numéro de version du langage et le nom de l'ensemble de règles.

  • Le nom de l'ensemble de règles dans l'exemple est "BasicPathBasedPolicy". Les règles de l'ensemble sont contenues entre crochets.

  • La seule règle de l'ensemble est nommée "Documents_rule".

  • La condition de la règle indique que si n'importe laquelle (any) des conditions est remplie, alors l'action qui figure dans "actions" doit être effectuée.

  • La condition compare le chemin d'URL de demande HTTP entrante à /documents. L'opérateur de comparaison est eq, ce qui signifie "égal", qui peut aussi être écrit sous la forme =.

  • Dans l'instruction de condition, (i '/documents') déclare que '/documents' ne distingue pas les majuscules des minuscules.

  • Lorsque la condition est remplie, l'action entreprise consiste à transmettre la demande à un ensemble de back-ends spécifique, en l'occurrence "backendSetForDocuments". Cet ensemble de back-ends doit exister pour que la règle soit valide.

La règle peut être paraphrasée comme suit : "Si le chemin d'URL demandé présente une correspondance exacte avec /documents, alors transmettre la demande à l'ensemble de back-ends backendSetForDocuments."

Exemple : deux règles de chemin simples

Voici un exemple d'ensemble de règles de stratégie de routage contenant deux règles simples reposant sur un chemin : En fonction du chemin d'URL de demande, les requêtes entrantes sont envoyées vers un ensemble de back-ends différent. La transmission se produit si la première condition ou la seconde est remplie. Les règles sont évaluées dans l'ordre dans lequel elles figurent dans la stratégie. Si une requête remplit les deux conditions, l'action est exécutée pour la première correspondance. La seconde est ignorée.

{
  "name": "PathBasedPolicy",
  "conditionLanguageVersion": "V1",
  "rules": [
    {
      "name": "Documents_rule",
      "condition" : "any(http.request.url.path eq (i '/documents'))",
      "actions": [{
        "name": "FORWARD_TO_BACKENDSET",
        "backendSetName": "backendSetForDocuments"
      }]
    },
    {
      "name": "Videos_rule",
      "condition" : "any(http.request.url.path eq (i '/videos'))",
      "actions": [{
        "name": "FORWARD_TO_BACKENDSET",
        "backendSetName": "backendSetForVideos"
      }]
    }
  ]
}

Exemple : une règle avec deux conditions

La stratégie suivante comporte une règle avec deux conditions (séparées par une virgule). La première condition examine les en-têtes de demande et la seconde la chaîne de requête de la demande :

      {
        "name": "Example_policy",
        "conditionLanguageVersion": "V1",
        "rules": [
          {
            "name": "HR_mobile_user_rule",
            "condition" : "all(http.request.headers[(i 'user-agent')] eq (i 'mobile'), http.request.url.query['department'] eq 'HR')",
            "actions": [{
              "name": "FORWARD_TO_BACKENDSET",
              "backendSetName": "backendSetForHRMobileUsers"
            }]
          }
        ]
      }

Dans le cas présent, la règle exige que les deux conditions soient remplies pour transmettre une demande à un ensemble de back-ends, car elle commence par le mot-clé all. Les conditions de la règle peuvent être paraphrasées comme suit : "Si la valeur user-agent demandée dans l'en-tête est définie sur mobile et que la valeur department dans l'en-tête est HR, alors transmettre à l'ensemble de back-ends spécifié."

Exemple : deux règles

Le dernier exemple présente deux règles. Chaque règle est associée à une action différente et comporte deux conditions. Il est important de noter que la seconde règle commence par le mot-clé any, ce qui signifie qu'une seule des deux conditions doit être remplie pour déclencher l'action. Si plus de deux conditions étaient spécifiées, il suffirait que l'une d'elles soit remplie pour que l'action soit déclenchée.

      {
        "name": "Example_policy",
        "conditionLanguageVersion": "V1",
        "rules": [
          {
            "name": "HR_mobile_user_rule",
            "condition" : "all(http.request.headers[(i 'user-agent')] eq (i 'mobile'), http.request.url.query['department'] eq 'HR')",
            "actions": [{
              "name": "FORWARD_TO_BACKENDSET",
              "backendSetName": "backendSetForHRMobileUsers"
            }]
          },
          {
            "name": "Documents_rule",
            "condition" : "any(http.request.url.path eq (i '/documents'), http.request.headers[(i 'host')] eq 'doc.myapp.com')",
            "actions": [{
              "name": "FORWARD_TO_BACKENDSET"
              "backendSetName": "backendSetForDocuments"
            }]
          }
        ]
      }

Conditions de règle

Les conditions de règle sont écrites sous forme de prédicats. Les combinateurs permettent d'utiliser plusieurs prédicats dans une même condition. Les deux combinateurs : any() et all() se comportent comme des opérateurs logiques OR ou AND. Pour inverser un combinateur, vous pouvez placer le mot-clé not devant lui. Un prédicat simple peut être exprimé comme suit :

left value matcher right value

La condition d'une règle qui doit avoir la correspondance si le chemin d'URL de demande HTTP commence par /foo/bar est la suivante :

http.request.url.path sw '/foo/bar'

Pour plus de détails sur les opérateurs de correspondance disponibles, reportez-vous à Opérateurs de correspondance.

Pour plus de détails sur les variables disponibles, reportez-vous à Variables.

Syntaxe en cas de prédicats multiples

not? any|all(<condition>,<condition>,...)

Par exemple :

all(http.request.url.path sw '/foo', 'bar' in (http.request.url.query))

Exemples de condition

Voici d'autres exemples d'utilisation des conditions. Vous trouverez davantage de détails sur la syntaxe et les fonctionnalités exactes.

  • Pour mettre en correspondance une demande HTTP si son chemin d'URL commence par /category/element :
    http.request.url.path sw '/category/element'
  • Pour mettre en correspondance une demande HTTP si son chemin d'URL commence par /category ou se termine par /id :
    any(http.request.url.path sw '/category', http.request.url.path ew '/id')
  • Pour mettre en correspondance une demande HTTP si l'en-tête User-Agent de demande est présent :
    (i 'User-Agent') in (http.request.headers)
  • Pour mettre en correspondance une demande HTTP si l'en-tête User-Agent a la valeur Some User Agent, procédez comme suit :
    http.request.headers[(i 'User-Agent')] eq 'Some User Agent'
  • Pour mettre en correspondance une demande HTTP si la chaîne d'URL comporte une clé search sensible à la casse, comme dans l'URL https://www.example.com/category/?search=item+foo%20bar&page=1 :
    'search' in (http.request.url.query)
  • Pour mettre en correspondance une demande HTTP si la chaîne d'URL comporte une clé "search" search sensible à La casse avec une valeur non sensible à La casse item+foo%20bar, comme dans l'URL https://www.domain.com/category/?search=item+foo%20bar&page=1
    http.request.url.query['search'] = (i 'item foo bar')

    La mise en correspondance de la requête d'URL (clés et valeurs) doit être effectuée à l'aide de versions des valeurs sans échappement d'URL.

  • Pour faire correspondre (sans respect de la casse) une demande HTTP pour un cookie nommé tastycookie :
    (i 'tastycookie') in (http.request.cookies)
  • Afin de mettre en correspondance (sans respecter la casse) une demande HTTP pour un cookie nommé tastycookie contenant la valeur sensible à La casse strawberry, procédez comme suit :
    http.request.cookies[(i 'tastycookie')] = 'strawberry'

Opérateurs de correspondance

Plusieurs opérateurs de correspondance peuvent être utilisés dans les conditions.

Opérateurs de correspondance de chaîne

Le tableau suivant répertorie les opérateurs de correspondance utilisés avec les valeurs de chaîne. Certains opérateurs de correspondance ont des variantes utilisables de manière interchangeable.

Les exemples donnés pour chaque opérateur de correspondance correspondent tous au chemin http.request.url.path contenant /category/element/id :

Nom Variantes Description Exemple
eq =, ==, equal, equals Etablit une correspondance si les valeurs à gauche et à droite de l'opérateur de correspondance sont identiques. http.request.url.path eq "/category/element/id"
ew Etablit une correspondance si la valeur à gauche se termine par celle à droite. http.request.url.path ew '/id'
sw Etablit une correspondance si la valeur à gauche commence par celle à droite. http.request.url.path sw '/category'
not eq !=, not equal, not equals Etablit une correspondance si les valeurs à gauche et à droite de l'opérateur de correspondance ne sont pas identiques. http.request.url.path neq '/some/other/path'
not ew Etablit une correspondance si la valeur à gauche ne se termine pas par celle à droite. http.request.url.path not ew '/not_id'
not sw Etablit une correspondance si la valeur à gauche ne commence pas par celle à droite. http.request.url.path not sw '/not_category'

Opérateurs de correspondance partiels

Certaines des variables utilisées dans les règles contiennent des correspondances de données clé-valeur arbitraires lors de l'exécution des règles. Par exemple, http.request.headers contient les en-têtes des demandes HTTP. Pour plus d'informations sur les correspondances disponibles, reportez-vous à Variables.

Les opérateurs de correspondance in et not in peuvent être utilisés pour vérifier si une variable d'association contient une clé spécifique. Ce que la clé représente réellement dépend de la variable.

Syntaxe permettant de vérifier si une variable de correspondance contient une clé spécifique :

<key> in (<map variable>)
  • <key> doit être une chaîne sensibles à la casse ou non.
  • La valeur à droite doit être entre parenthèses.

Par exemple, cette condition établit la correspondance si la demande HTTP comporte un cookie portant le nom Foo :

'Foo' in (http.request.cookies)

Valeurs

Les valeurs utilisées dans les prédicats peuvent être des valeurs constantes ou des variables évaluées lors de l'exécution.

Constantes

Les règles prennent en charge les constantes de chaîne écrites entre guillemets simples.

Exemple :

http.request.url.path sw '/foo'

Respect de la casse des chaînes

Par défaut, la mise en correspondance de chaînes utilise des comparaisons sensibles à la casse.

Par exemple, si le chemin d'URL d'une demande HTTP est /foo, le prédicat suivant n'établit pas de correspondance pour cette demande, car la comparaison de chaînes sensible à la casse est utilisée :

http.request.url.path eq '/FOO'

Pour effectuer une mise en correspondance non sensible à la casse, au moins une des valeurs comparées doit être une chaîne non sensible à la casse. La syntaxe d'une chaîne non sensible à la casse est la suivante :

(i '<string content>')

Par exemple, les chaînes suivantes ne sont pas sensibles à la casse et sont donc équivalentes lorsqu'elles sont utilisées dans des prédicats :

(i 'foo')
(i 'Foo')
(i 'FOO')

Par rapport à l'exemple d'origine, ce prédicat établit une correspondance car il utilise une comparaison non sensible à la casse :

http.request.url.path eq (i '/FOO')

Respect de la casse pour les chaînes

Par défaut, la mise en correspondance de chaînes utilise des comparaisons sensibles à la casse.

Par exemple, si le chemin d'URL d'une demande HTTP est /foo, le prédicat suivant n'établit pas la correspondance pour cette demande, car une comparaison de chaînes sensibles à la casse est utilisée :

http.request.url.path eq '/FOO'

La mise en correspondance n'est pas sensible aux majuscules/minuscules si au moins l'une des valeurs comparées est une chaîne non sensible aux minuscules. La syntaxe d'une chaîne non sensible à la casse est la suivante :

(i '<string content>')

Par exemple, les chaînes suivantes sont toutes sensibles à la casse et sont donc équivalents lorsqu'elles sont utilisées dans des prédicats :

(i 'foo')
(i 'Foo')
(i 'FOO')

Par rapport à l'exemple d'origine, ce prédicat établit la correspondance car il utilise une comparaison non sensible à la casse :

http.request.url.path eq (i '/FOO')

Variables

Les variables sont utilisées dans les conditions pour établir une correspondance avec une valeur particulière de la demande HTTP. Les valeurs réelles de chaque variable sont définies lors de l'exécution des règles, c'est-à-dire lors de chaque demande HTTP.

Variables de type correspondance

Certaines variables contiennent une correspondance clé-valeur arbitraires des données de demande, par exemple des en -têtes de demande ou des cookies. Il peut exister plusieurs valeurs pour chaque clé. Par exemple, plusieurs en-têtes de demande peuvent porter le même nom.

En règle générale, les variables d'association peuvent être utilisées dans les règles pour :

  • vérifier si une correspondance comporte une clé spécifique,
  • vérifier si une correspondance comporte une clé spécifique avec une valeur spécifique.

Vérification de la présence d'une clé spécifique dans une correspondance :

La vérification de l'existence d'une variable correspondance dans une clé spécifique est effectuée avec le programme de correspondance in. Pour plus de détails, reportez-vous à Langage de stratégie de routage pour les équilibreurs de charge.

Par exemple :

'Foo' in (http.request.cookies)

Cette condition établit la correspondance si la demande HTTP comporte un cookie portant le nom Foo.

Vérification de la présence d'une clé spécifique avec une valeur spécifique dans une correspondance :

La vérification de l'existence d'une clé spécifique avec une valeur spécifique dans une correspondance est effectuée à l'aide de la notation entre crochets [] qui permet d'obtenir les valeurs au niveau d'une clé spécifique. Voici la syntaxe pour utiliser la notation entre crochets :

<variable>[<key>]

La valeur <key> doit être indiquée sous la forme d'une chaîne sensibles à la casse ou non.

La vérification réelle de l'existence d'une valeur spécifique est effectuée à l'aide du programme de correspondance eq qui vérifie si l'une (any) des valeurs de la clé sont identiques à cette valeur spécifique. Le prédicat établit une correspondance si au moins l'une des valeurs de la clé correspond à la valeur considérée.

Exemples :

  • Pour établir une correspondance si l'une des valeur de l'en-tête header-name est égale à header-value :
    http.request.headers[(i 'header-name')] eq 'header-value'

    Le nom name de l'en-tête est comparé sans respect de la casse, mais la valeur value de l'en-tête est comparée avec respect de La casse.

  • Pour établir la correspondance si une des valeurs du cookie "cookie-name" est identique à cookie-value, procédez comme suit :
    http.request.cookies['cookie-name'] eq 'cookie-value'

L'opérateur de correspondance not eq peut être utilisé pour vérifier que none des valeurs de la clé n'est identique à une valeur spécifique.

Exemples :

  • Pour établir la correspondance si aucune valeur de l'en-tête header-name n'est identique à header-value :
    http.request.headers[(i 'header-name')] not eq 'header-value'

    Le nom d'en-tête est comparé sans respect de la casse. La valeur d'en-tête est comparée avec respect de la casse.

  • Pour établir la correspondance si aucune valeur du cookie "cookie-name" n'est égale à cookie-value :
    http.request.cookies['cookie-name'] not eq 'cookie-value'

Toutes les variables disponibles

Les variables pouvant être utilisées dans les conditions sont les suivantes :

Nom Description Exemple
http.request.headers Correspondance contenant des en-têtes de demande HTTP.

Cette correspondance présente un comportement particulier : les clés (noms d'en-tête) doivent être une chaîne non sensible à la casse. L'utilisation de chaînes sensibles à La casse pour les clés http.request.headers dans les prédicats est interdite.

  • Syntaxe correcte :
    (i 'User-Agent') in http.request.headers
    http.request.headers[(i 'User-Agent')] = 'Mobile'
  • Syntaxe incorrecte :
    'User-Agent' in http.request.headers
    http.request.headers['User-Agent'] = 'Foo'
http.request.url.path Chemin d'URL de demande HTTP. Il s'agit de l'URL de demande sans le protocole, le domaine, le port et la chaîne de requête.
http.request.url.query Correspondance contenant des éléments de requête d'URL de demande HTTP. Si l'URL de demande n'a pas de requête (le caractère ? n'est pas absent) ou si la requête est vide (le caractère ? est le dernier de l'URL), la correspondance est vide.

La requête est analysée pour produire la correspondance http.request.url.query, en prenant la partie de requête de l'URL (qui est la partie après le premier caractère ?) et en le divisant en paires clé-valeur, les caractères suivants étant comme spéciaux :

  • Le caractère & est le séparateur entre différentes paires clé-valeur.
  • Le caractère = est le séparateur entre la clé et sa valeur (dans une paire clé-valeur).

Le premier caractère ? présent dans l'URL marque le début de la chaînes de requête. Si d'autres caractères ? apparaissent après le premier, ils seront traités comme des caractères standard.

Dans une paire clé-valeur, le premier caractère = présent sépare la clé de la valeur. Si d'autres caractères = apparaissent, ils ont été traités comme faisant partie de la valeur.

Les clés et les valeurs ne sont pas échappées conformément aux règles d'échappement d'URL.

URL :https://www.domain.com/path?key=value&key=%61&another%20key=another+value

Les données http.request.url.query d'une demande avec cette URL ressembleraient à ce qui est au format de JSON :

{
  "key": [
    "value",
    "a"
  ],
  "another key": [
    "another value"
  ],
}

Dans cet exemple, la clé et la valeur sont mises en correspondance avec respect de la casse. Ainsi, si au lieu de key=value, l'URL contenait KEY=value ou key=VALUE, la condition n'établirait pas la correspondance.

Cependant, aucune paire clé-valeur de la chaîne http.request.url.query de requête d'URL n'est ajoutée à la correspondance dans les cas suivants :

  • Si aucun caractère = ne figure dans une paire clé-valeur

    Exemple :

    URL : https://www.example.com/path?no_key"

    Dans ce cas, l'élément de chaîne de requête no_key n'est pas présent dans la correspondance http.request.url.query.

  • Si rien n'est indiqué à gauche du caractère = (la clé n'est pas spécifiée)

    Exemple : URL : https://www.domain.com/path?=no_value

    Dans ce cas, l'élément de chaîne de requête no_value n'est pas présent dans la correspondance http.request.url.query.

Si la partie droite du caractère = est vide, la valeur de cette paire clé-valeur est une chaîne vide : ''.

http.request.cookies

Correspondance contenant les cookies de demande HTTP, analysés à partir de l'en-tête d'une demande "Cookie", comme indiqué dans RFC-6265, où la clé est le nom d'un cookie et la valeur, la valeur du cookie correspondante. Si l'en-tête de demande "Cookie" n'est pas présent dans la demande, la correspondance est vide.

Exemples

Une demande HTTP/1.1 entrante se présente comme suit (ligne de demande et en-têtes) :

GET /category/some_category?action=search&query=search+terms&filters[]=5&features[]=12 HTTP/1.1
Accept-Encoding: gzip, deflate, br
Cookie: cookie_a=1; cookie_b=foo
Host: www.domain.com
User-Agent: Browser Foo/1.0
X-Forwarded-For: 1.2.3.4, 5.6.7.8
X-Forwarded-For: 9.10.11.12

Les variables disponibles pour les règles sont alors remplies avec les données de la demande comme suit (les données des variables structurées sont affichées au format JSON) :

http.request.url.path: "/category/some_category"
 
http.request.url.query: {
  "action": ["search"],
  "query": ["search terms"],
  "filters[]": ["5", "12"]
}
 
http.request.headers: {
  "Accept-Encoding": ["gzip, deflate, br"],
  "Cookie": ["some_cookie=1; another_cookie=foo"],
  "Host": ["www.domain.com"],
  "User-Agent": ["Browser Foo/1.0"],
  "X-Forwarded-For": ["1.2.3.4, 5.6.7.8", "9.10.11.12"]
}
 
http.request.cookies: {
  "cookie_a": ["1"],
  "cookie_b": ["foo"]
}

Voici quelques exemples de mise en correspondance de cette demande :

  • Pour mettre en correspondance les demandes et les chemins d'URL qui commencent par /category/ avec le domaine www.domain.com, nous utiliserions une condition telle que La suivante :
    all(http.request.headers[(i 'Host')] eq 'www.domain.com', http.request.url.path sw '/category')
  • Pour mettre les demandes dont le chemin d'URL est exactement /category/some_category ou l'élément de requête de demande action=search, procédez comme suit :
    any(http.request.url.path eq '/category/some_category', http.request.url.query['action'] eq 'search')
  • Pour mettre En correspondance les demandes comportant un élément de chaîne de requête nommé query avec la valeur search terms (après annulation de l'échappement d'URL) :
    http.request.url.query['query'] eq 'search terms'
  • Pour mettre En correspondance les demandes comportant un cookie cookie_a, mais pas le cookie cookie_c :
    all('cookie_a' in (http.request.cookies), 'cookie_c' not in (http.request.cookies))