Arquitectura da Grid
A Grid está desenhada como um conjunto de componentes, em que cada tem o seu papel crucial em manter a Grid. Isto pode parecer um pouco complicado, mas esperamos que este documento ajude a esclarecer alguma confusão.
Os componentes chave
Os componentes principais da Grid são:
- Event Bus
- Usado para enviar mensagens que podem ser recebidas de forma assíncrona entre os outros componentes.
- New Session Queue
- Mantém uma lista de pedidos de sessão que serão assignadas a um Node pelo Distributor.
- Distributor
- Responsável por manter um modelo das localizações da Grid (slots) onde uma sessão pode ser lançada e também por aceitar novos pedidos de sessão e assignar a um slot livre.
- Node
- Executa uma sessão WebDriver. Cada sessão é assignada a um slot e cada Node tem um ou mais slots.
- Session Map
- Mantém um mapeamento entre um ID de sessão e o endereço do Node onde a sessão está a ser executada.
- Router
- Este é o ponto de entrada da Grid. É também a única parte da Grid que poderá estar exposta à Internet (embora nós não recomendemos). Este componente reencaminha novos pedidos para New Session Queue ou para o Node onde a sessão esteja a ser executada
Ao falar da Grid, há alguns conceitos úteis a ter em mente:
- Um slot é o sítio onde uma sessão pode ser executada
- Cada slot tem um stereotype. Isto é um conjunto mínimo de capacidades que um pedido de nova sessão terá que corresponder antes que o Distributor envie esse pedido ao Node que tenha esse slot
- O Grid Model é como o Distributor mantém o estado actual da Grid. Como o nome sugere, este modelo pode perder o sincronismo com a realidade. Este mecanismo é preferível do que estar a questionar cada Node, e desta forma, o Distributor rapidamente consegue alocar uma nova sessão a um slot.
Chamadas Síncronas e Assíncronas
Existem duas formas de comunicação dentro da Grid:
- Chamadas Síncronas “REST-ish” que usam JSON sobre pedidos HTTP.
- Eventos Assíncronos enviados para o Event Bus.
Como é que escolhemos que tipo de mecanismo de comunicação a usar? Afinal, podiamos ter escolhido usar apenas comunicação baseada em eventos e tudo iria funcionar sem problemas.
No entanto a resposta correcta é, se a acção em curso é síncrona, por exemplo a maioria das chamadas WebDriver, ou se perder uma resposta é problemático, a Grid usa chamadas síncronas. Se quisermos propagar informação que pode ter várias partes interessadas, ou se perder a mensagem não for crítico, a Grid usará o event bus.
Um facto interessante a notar é que as chamadas assíncronas estão menos “presas” aos processos que as executam do que todas as chamadas síncronas.
Sequência de início e dependencias entre componentes
Embora a Grid seja desenhada para permitir que os componentes possam iniciar em qualquer ordem, conceptualmente é esperado que a ordem de início seja:
- O Event Bus e o Session Map iniciam primeiro. Estes componentes não tem qualquer dependencia, nem mesmo entre eles e como tal, podem iniciar em paralelo.
- A Session Queue inicia de seguida
- O Distributor inicia. Irá periodicamente procurar novos pedidos de sessão na Session Queue, embora possa também receber um evento de um pedidos de sessão.
- O Router pode ser agora iniciado. Novos pedidos de sessão são direccionados para a Session Queue, o Distributor tentará encontrar um slot onde a sessão possa ser executada.
- O Node pode ser iniciado, veja mais abaixo os detalhes de como o Node se regista na Grid. Uma vez que o registo esteja concluído, a Grid estará pronta a receber pedidos.
Nesta tabela pode ser visualizada a dependencia ente os vários componentes. Um “✅” indica que a dependência é síncrona.
Event Bus | Distributor | Node | Router | Session Map | Session Queue | |
---|---|---|---|---|---|---|
Event Bus | X | |||||
Distributor | ✅ | X | ✅ | ✅ | ||
Node | ✅ | X | ||||
Router | ✅ | X | ✅ | |||
Session Map | X | |||||
Session Queue | ✅ | X |
Registo de Node
O processo de registar um Node na Grid é um processo “leve”.
- Quando um Node inicia, vai publicar um evento “heart beat” numa base regular. Este evento contém o estado do Node.
- O Distributor escuta os eventos “heart beat” e quando obtém um,
tenta um
GET
ao endpoint/status
do Node. A Grid é preparada com base nesta informação.
O Distributor irá usar regularmente o endpoint /status
para continuar
a obter o estado do Node. Por seu lado, o Node continua a publicar um
evento “heart beat” mesmo depois do registo ter sido concluído com
sucesso.
Isto é feito para que mesmo que um Distributor não tenha um estado
da Grid possa reiniciar e assim obter novamente uma visão do estado
da Grid e assim ficar actualizado.
Objecto Node Status
O Node Status é um blob JSON com os seguintes campos:
Nome | Tipo | Descrição |
---|---|---|
availability | string | Uma string com up , draining , ou down . A mais importante é draining , que indica que não devem ser enviados novos pedidos de sessão para o Node e assim que a última sessão termine, o Node irá reiniciar ou concluir. |
externalUrl | string | Uma URI que os outros componentes da Grid se devem ligar. |
lastSessionCreated | integer | Um timestamp da última sessão que foi criada neste Node. O Distributor irá tentar enviar novos pedidos de sessão para o Node que esteja parado há mais tempo. |
maxSessionCount | integer | Embora seja possível inferir o número máximo de sessões a partir da lista de slots disponíveis, este número é usado para determinar qual é o máximo de sessões que este Node pode executar em simultâneo antes que se considere que está “cheio”. |
nodeId | string | Um identificador UUID para esta instância do Node. |
osInfo | object | Um objecto contendo os campos arch , name , e version . Isto é usado pela Grid UI e pelas queries GraphQL. |
slots | array | Um array de objectos Slot (descritos na secção seguinte) |
version | string | A versão do Node (para Selenium, será igual à versão do Selenium) |
É recomendado que todos os campos tenham valores.
O Objecto Slot
O objecto Slot representa um slot dentro de um Node. Um “slot” é onde uma sessão consegue ser executada. É possível que um Node tenha mais do que um Slot capaz de executar ao mesmo tempo. Por exemplo, um Node pode ser capaz de executar até 10 sessões em simultâneo, mas podem ser uma qualquer combinação de Chrome, Firefox ou Edge e neste caso, o Node irá indicar 10 como o número máximo de sessões, indicando que podem ser 10 Chrome, 10 Firefox e 10 Edge.
Nome | Tipo | Descrição |
---|---|---|
id | string | Um identificador UUID para este slot |
lastStarted | string | timestamp no formato ISO-8601 contendo a data em que a última sessão iniciou |
stereotype | object | Conjunto mínimo de capacidades que fazem match com este slot. O exemplo mínimo será por exemplo {"browserName": "firefox"} |
session | object | O objecto Session (descrito na secção seguinte) |
O Objecto Session
Representa uma sessão em execução dentro de um Slot
Nome | Tipo | Descrição |
---|---|---|
capabilities | object | A lista de capacidades fornecidas pela sessão. Irá coincidir com o valor obtido pelo comando nova sessão |
startTime | string | timestamp no formato ISO-8601 contendo a data em que a última sessão iniciou |
stereotype | object | Conjunto mínimo de capacidades que fazem match com este slot. O exemplo mínimo será por exemplo {"browserName": "firefox"} |
uri | string | A URI usada pelo Node para comunicar com a sessão |