Exemple : RabbitMQ en AMQP 0-9-1
RabbitMQ en AMQP 0-9-1 : un canal lié à chaque personne, un flux public lu sans jeton.
ExempleExploitationDepuis la version 1.5.0
Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la fonctionnalité côté produit : guide produit ; l'appeler : guide développeur.
RabbitMQ en AMQP 0-9-1 : canal par personne, flux public
Chaque joueur publie et lit ses notifications - le canal est lié au sujet vérifié du jeton - ; le
tableau des scores est un flux public, lu sans jeton, repris sur 128 messages. Un producteur hors de la
passerelle y écrit en application/cloudevents+json : lu en mode structuré. La passerelle se connecte avec
son utilisateur, sur son hôte virtuel (/games, le chemin de l'URL).
# RabbitMQ in AMQP 0-9-1 behind the gateway's controls (1.5.0): each player reads its
# own notifications only (bound to the verified subject), anyone - anonymous included - reads the
# declared public feed. The gateway authenticates itself: its own user, its own virtual host.
version: 1
contracts:
openapi: ./openapi.yaml
asyncapi: ./asyncapi.yaml
upstreams:
bus:
protocol: amqp # AMQP 0-9-1 (protocol_version: 0-9-1, the default)
brokers: ["amqps://rabbitmq.internal:5671/games"] # the path is the virtual host; never a password in the URL
sasl: { mechanism: PLAIN, username: flexway, password: "file:/etc/flexway-secrets/rabbitmq/password" }
timeout: 2s # publisher confirm included
auth:
issuers:
- name: players
discovery: https://login.example.net/.well-known/openid-configuration
audience: games
tenant_claim: org# examples/async-amqp/asyncapi.yaml
asyncapi: 3.0.0
info: { title: Game events, version: 1.0.0 }
channels:
notifications:
# A topic exchange the broker already has: the gateway never declares one.
address: "players.{sub}.notifications"
parameters:
sub: { x-flexway-bind: subject } # the verified subject: a player reads its own channel only
bindings:
amqp: { is: routingKey, exchange: { name: notifications, type: topic } }
messages:
note:
contentType: application/json
payload: { type: object, required: [text], properties: { text: { type: string } } }
scoreboard:
address: scores.public # binds nothing, filters nothing: declared public below
bindings:
amqp: { is: routingKey, exchange: { name: scores, type: topic } }
messages:
# Written by a producer outside the gateway (an outbox) as application/cloudevents+json: read in
# structured mode; any other content type is read in binary mode.
score:
payload: { type: object, required: [match], properties: { match: { type: string }, score: { type: string } } }
operations:
publishNotification: { action: send, channel: { $ref: "#/channels/notifications" }, x-flexway-upstream: bus }
receiveNotifications: { action: receive, channel: { $ref: "#/channels/notifications" }, x-flexway-upstream: bus }
receiveScores:
action: receive
channel: { $ref: "#/channels/scoreboard" }
x-flexway-upstream: bus
x-flexway-public: true # read by anyone, anonymous included# examples/async-amqp/openapi.yaml
openapi: 3.1.0
info: { title: Game events, version: "1.0.0" }
components:
securitySchemes:
players: { type: openIdConnect, openIdConnectUrl: https://login.example.net/.well-known/openid-configuration }
paths:
/me/notifications:
post:
operationId: notifyMe
x-flexway-publish: { operation: publishNotification }
security: [ { players: [] } ]
requestBody:
required: true
content:
application/json:
schema: { type: object, required: [text], properties: { text: { type: string } } }
responses: { "202": { description: Confirmed by the broker } }
get:
operationId: streamMyNotifications
# WebSocket: at least once - the client acknowledges {"ack": "<id>"} within ack_timeout.
x-flexway-subscribe: { operation: receiveNotifications, transport: [sse, websocket], max_streams: 3, ack_timeout: 20s }
security: [ { players: [] } ]
responses: { "200": { description: The caller's own notifications } }
/scores:
get:
operationId: streamScores
# SSE only; one consumer per replica whatever the audience, 128 messages kept for Last-Event-ID.
x-flexway-subscribe: { operation: receiveScores, transport: [sse], max_streams: 2, max_anonymous_streams: 500, history: 128 }
security: [] # anonymous door: only toward a public operation
responses: { "200": { description: The public scoreboard } }| Cas | Ce que fait la passerelle |
|---|---|
POST /me/notifications |
Publié sur players.<sub>.notifications de l'échange notifications, 202 après la confirmation |
GET /me/notifications |
Le seul canal du sujet ; une valeur hors des lettres, chiffres, _ et - est refusée (403) |
GET /scores sans jeton |
Public déclaré : jusqu'à 500 anonymes ensemble, 2 flux par adresse ; un abonné trop lent fermé, les autres continuent |
| Webhook | Même déclaration qu'en Kafka : publier, diffuser, livrer : une file nommée partagée par les répliques |
Lancer
Enregistrer dans un répertoire le
flexway.yamlmontré et les contrats qu'il référence, à côté de lui (examples/async-amqp/dans les sources de la release).Valider hors ligne, sans courtier ni réseau - le validateur est dans l'image publiée :
docker run --rm -v "$PWD:/etc/flexway:ro" ghcr.io/flexwiz/flexway:<version> validate -vMonter les secrets que le fichier référence (
file:/etc/flexway-secrets/...), puis démarrer :docker run --rm -p 8080:8080 -p 9090:9090 \ -v "$PWD:/etc/flexway:ro" -v "$PWD/secrets:/etc/flexway-secrets:ro" \ ghcr.io/flexwiz/flexway:<version>
Vérifier
validate -vsort en0et liste chaque opération résolue ; un refus nomme le champ fautif et sort en1.- Une fois démarrée,
curl -s localhost:9090/readyzrépond200{"status":"ready","operations":<n>}; un secret absent fait refuser le démarrage, qui le nomme. - Chaque ligne du tableau des cas se rejoue avec le développeur : Publier et s'abonner, Recevoir des webhooks signés.