ID da região
O REGION_ID
é um código abreviado que a Google atribui com base na região que seleciona quando cria a sua app. O código não corresponde a um país ou uma província, embora alguns IDs de regiões possam parecer semelhantes aos códigos de países e províncias usados frequentemente. Para apps criadas após
fevereiro de 2020, REGION_ID.r
está incluído nos
URLs do App Engine. Para apps existentes criadas antes desta data, o
ID da região é opcional no URL.
Saiba mais acerca dos IDs de regiões.
Normalmente, os microsserviços no App Engine chamam-se uns aos outros através de APIs RESTful baseadas em HTTP. Também é possível invocar microsserviços em segundo plano através de filas de tarefas, e os princípios de design de APIs descritos aqui aplicam-se. É importante seguir determinados padrões para garantir que a sua aplicação baseada em microsserviços é estável, segura e tem um bom desempenho.
Usar contratos fortes
Um dos aspetos mais importantes das aplicações baseadas em microserviços é a capacidade de implementar microserviços completamente independentes uns dos outros. Para alcançar esta independência, cada microsserviço tem de fornecer um contrato com versões e bem definido aos respetivos clientes, que são outros microsserviços. Cada serviço não deve violar estes contratos com versões até se saber que nenhum outro microsserviço depende de um contrato específico com versões. Tenha em atenção que outros microsserviços podem ter de reverter para uma versão de código anterior que requer um contrato anterior. Por isso, é importante ter em conta este facto nas suas políticas de descontinuação e desativação.
Uma cultura em torno de contratos fortes e com versões é provavelmente o aspeto organizacional mais desafiante de uma aplicação estável baseada em microsserviços. As equipas de desenvolvimento têm de compreender a diferença entre uma alteração disruptiva e uma alteração não disruptiva. Têm de saber quando é necessário um novo lançamento principal. Têm de compreender como e quando um contrato antigo pode ser desativado. As equipas têm de usar técnicas de comunicação adequadas, incluindo avisos de descontinuação e desativação, para garantir que estão cientes das alterações aos contratos de microsserviços. Embora possa parecer uma tarefa complicada, incorporar estas práticas na sua cultura de desenvolvimento vai gerar grandes melhorias na velocidade e na qualidade ao longo do tempo.
Abordar os microserviços
Os serviços e as versões de código podem ser abordados diretamente. Como resultado, pode implementar novas versões de código lado a lado com as versões de código existentes e testar novo código antes de o tornar na versão de publicação predefinida.
Cada projeto do App Engine tem um serviço predefinido e cada serviço tem uma versão de código predefinida. Para aceder ao serviço predefinido da versão predefinida de um projeto,
use o seguinte URL:
https://PROJECT_ID.REGION_ID.r.appspot.com
Se implementar um serviço denominado user-service
, pode aceder à versão de publicação predefinida desse serviço através do seguinte URL:
https://user-service-dot-my-app.REGION_ID.r.appspot.com
Se implementar uma segunda versão do código não predefinida denominada banana
no serviço user-service
, pode aceder diretamente a essa versão do código
através do seguinte URL:
https://banana-dot-user-service-dot-my-app.REGION_ID.r.appspot.com
Tenha em atenção que, se implementar uma segunda versão do código não predefinida denominada cherry
no serviço default
, pode aceder a essa versão do código através do seguinte URL:
https://cherry-dot-my-app.REGION_ID.r.appspot.com
O App Engine aplica a regra de que os nomes das versões de código no serviço predefinido não podem entrar em conflito com os nomes dos serviços.
O endereçamento direto de versões de código específicas só deve ser usado para testes rápidos e para facilitar os testes A/B, a implementação progressiva e a reversão. Em alternativa, o código do cliente deve abordar apenas a versão de publicação predefinida do serviço predefinido ou de um serviço específico:
https://PROJECT_ID.REGION_ID.r.appspot.com
https://SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com
Este estilo de endereçamento permite que os microsserviços implementem novas versões dos respetivos serviços, incluindo correções de erros, sem exigir alterações aos clientes.
Usar versões da API
Cada API de microsserviço deve ter uma versão principal da API no URL, como:
/user-service/v1/
Esta versão principal da API identifica claramente nos registos a versão da API do microsserviço que está a ser chamada. Mais importante ainda, a versão principal da API gera URLs diferentes, para que as novas versões principais da API possam ser apresentadas lado a lado com as versões principais da API antigas:
/user-service/v1/ /user-service/v2/
Não é necessário incluir a versão secundária da API no URL porque, por definição, as versões secundárias da API não introduzem alterações significativas. Na verdade, a inclusão da versão da API secundária no URL resultaria numa proliferação de URLs e causaria incerteza quanto à capacidade de um cliente mudar para uma nova versão da API secundária.
Tenha em atenção que este artigo pressupõe um ambiente de integração e implementação contínuas em que o ramo principal está sempre a ser implementado no App Engine. Neste artigo, existem dois conceitos distintos de versão:
Versão do código, que é mapeada diretamente para uma versão do serviço do App Engine e representa uma etiqueta de confirmação específica do ramo principal.
Versão da API, que é mapeada diretamente para um URL da API e representa a forma dos argumentos do pedido, a forma do documento de resposta e o comportamento da API.
Este artigo também pressupõe que uma única implementação de código vai implementar as versões da API antigas e novas de uma API numa versão de código comum. Por exemplo, a ramificação principal implementada pode implementar /user-service/v1/
e /user-service/v2/
. Quando implementa novas versões secundárias e de patch, esta abordagem permite-lhe dividir o tráfego entre duas versões de código independentemente das versões da API que o código implementa realmente.
A sua organização pode optar por desenvolver o /user-service/v1/
e o /user-service/v2/
em diferentes ramificações de código. Ou seja, nenhuma implementação de código vai implementar ambos ao mesmo tempo. Este modelo também é possível no App Engine, mas para dividir o tráfego, teria de mover a versão principal da API para o próprio nome do serviço.
Por exemplo, os seus clientes usariam os seguintes URLs:
http://user-service-v1.my-app.REGION_ID.r.appspot.com/user-service/v1/ http://user-service-v2.my-app.REGION_IDappspot.com/user-service/v2/
A versão principal da API passa para o próprio nome do serviço, como
user-service-v1
e user-service-v2
.
(As partes /v1/
e /v2/
do caminho são redundantes neste modelo e podem ser removidas, embora ainda possam ser úteis na análise de registos.)
Este modelo requer um pouco mais de trabalho porque é provável que exija atualizações aos seus scripts de implementação para implementar novos serviços em alterações importantes da versão da API. Além disso, tenha em atenção o
número máximo de serviços permitidos
por aplicação do App Engine.
Alterações interruptivas versus não interruptivas
É importante compreender a diferença entre uma alteração interruptiva e uma alteração não interruptiva. As alterações interruptivas são frequentemente subtrativas, o que significa que retiram alguma parte do documento de pedido ou resposta. Alterar a forma do documento ou alterar o nome das chaves pode introduzir uma alteração interruptiva. Os novos argumentos obrigatórios são sempre alterações significativas. Também podem ocorrer alterações significativas se o comportamento do microsserviço mudar.
As alterações não destrutivas tendem a ser cumulativas. Um novo argumento de pedido opcional ou uma nova secção adicional no documento de resposta são alterações não destrutivas. Para alcançar alterações não destrutivas, a escolha da serialização em trânsito é essencial. Muitas serializações são compatíveis com alterações não destrutivas: JSON, buffers de protocolo ou Thrift. Quando são desserializadas, estas serializações ignoram silenciosamente informações adicionais inesperadas. Nos idiomas dinâmicos, as informações adicionais aparecem simplesmente no objeto desserializado.
Considere a seguinte definição de JSON para o serviço /user-service/v1/
:
{
"userId": "UID-123",
"firstName": "Jake",
"lastName": "Cole",
"username": "jcole@example.com"
}
A seguinte alteração interruptiva requer uma nova versão do serviço como
/user-service/v2/
:
{
"userId": "UID-123",
"name": "Jake Cole", # combined fields
"email": "jcole@example.com" # key change
}
No entanto, a seguinte alteração não destrutiva não requer uma nova versão:
{
"userId": "UID-123",
"firstName": "Jake",
"lastName": "Cole",
"username": "jcole@example.com",
"company": "Acme Corp." # new key
}
Implementação de novas versões secundárias da API não destrutivas
Quando implementa uma nova versão secundária da API, o App Engine permite que a nova versão do código seja lançada em paralelo com a versão do código antiga. No App Engine, embora possa direcionar diretamente qualquer uma das versões implementadas, apenas uma versão é a versão de publicação predefinida. Tenha em atenção que existe uma versão de publicação predefinida para cada serviço. Neste exemplo, temos a nossa versão do código antiga, denominada apple
, que é a versão de publicação predefinida, e implementamos a nova versão do código como uma versão lado a lado, denominada banana
. Tenha em atenção que os URLs dos microsserviços são os mesmos para ambos /user-service/v1/
, uma vez que estamos a implementar uma alteração menor da API não destrutiva.
O App Engine fornece mecanismos para migrar automaticamente o tráfego de apple
para banana
, marcando a nova versão do código banana
como a versão de publicação predefinida. Quando a nova versão de publicação predefinida é definida, não são encaminhados novos pedidos para apple
, e todos os novos pedidos são encaminhados para banana
. É assim que
avança para uma nova versão do código que implementa uma nova versão menor ou de patch da API
sem impacto nos microsserviços do cliente.
Em caso de erro, a reversão é feita através da inversão do processo acima: defina a versão de publicação predefinida novamente para a versão antiga, apple
no nosso exemplo. Todos os novos pedidos são encaminhados novamente para a versão do código antigo e nenhum novo pedido é encaminhado para banana
. Tenha em atenção que os pedidos em curso podem ser concluídos.
O App Engine também oferece a capacidade de direcionar apenas uma determinada percentagem do seu tráfego para a nova versão do código. Este processo é frequentemente denominado processo de lançamento canary, e o mecanismo é denominado divisão de tráfego no App Engine. Pode direcionar 1%, 10%, 50% ou qualquer percentagem de tráfego que quiser para as novas versões do código e ajustar este valor ao longo do tempo. Por exemplo, pode implementar a nova versão do código durante 15 minutos, aumentando gradualmente o tráfego e verificando se existem problemas que possam indicar que é necessário reverter a implementação. Este mesmo mecanismo permite-lhe fazer testes A/B de duas versões de código: defina a divisão do tráfego para 50% e compare as características de desempenho e taxa de erro das duas versões de código para confirmar as melhorias esperadas.
A imagem seguinte mostra as definições de divisão do tráfego na Google Cloud consola:
Implementação de novas versões principais da API com alterações significativas
Quando implementa versões principais da API com alterações significativas, o processo de implementação progressiva e
reversão é o mesmo que para versões secundárias da API sem alterações significativas. No entanto, normalmente, não realiza nenhuma divisão de tráfego nem testes A/B porque a versão da API com falhas é um URL lançado recentemente, como /user-service/v2/
. Claro que, se tiver alterado a implementação subjacente da sua versão principal antiga da API, pode querer usar a divisão de tráfego para testar se a versão principal antiga da API continua a funcionar como esperado.
Quando implementar uma nova versão principal da API, é importante lembrar que as versões principais antigas da API também podem continuar a ser publicadas. Por exemplo, o /user-service/v1/
pode continuar a ser publicado quando o /user-service/v2/
for lançado. Este facto é uma parte essencial dos lançamentos de código independentes. Só pode desativar versões principais antigas da API depois de verificar que nenhum outro microsserviço as requer, incluindo outros microsserviços que possam ter de reverter para uma versão de código mais antiga.
Como exemplo concreto, imagine que tem um microsserviço denominado web-app
, que depende de outro microsserviço denominado user-service
. Imagine que a equipa de user-service
precisa de alterar alguma implementação subjacente que vai tornar impossível suportar a versão principal antiga da API que a equipa de web-app
está a usar atualmente, como reduzir firstName
e lastName
a um único campo denominado name
. Ou seja, a user-service
tem de desativar uma versão principal antiga da API.
Para realizar esta alteração, têm de ser feitas três implementações separadas:
Primeiro, o
user-service
tem de implementar o/user-service/v2/
, ao mesmo tempo que continua a suportar o/user-service/v1/
. Esta implementação pode exigir a escrita de código temporário para suportar a retrocompatibilidade, o que é uma consequência comum em aplicações baseadas em microsserviçosEm seguida,
web-app
tem de implementar o código atualizado que altera a respetiva dependência de/user-service/v1/
para/user-service/v2/
Por último, depois de a equipa
user-service
verificar queweb-app
já não requer/user-service/v1/
e queweb-app
não precisa de reverter, a equipa pode implementar código que remova o ponto final/user-service/v1/
antigo e qualquer código temporário de que necessitava para o suportar.
Embora toda esta atividade possa parecer onerosa, é um processo essencial nas aplicações baseadas em microsserviços e é precisamente o processo que permite ciclos de lançamento de desenvolvimento independentes. Para esclarecer, este processo parece ser bastante dependente, mas, o que é importante, cada passo acima pode ocorrer em prazos independentes, e a implementação e a reversão ocorrem no âmbito de um único microsserviço. Apenas a ordem dos passos é fixa e os passos podem ocorrer ao longo de muitas horas, dias ou até semanas.
O que se segue?
- Obtenha uma vista geral da arquitetura de microsserviços no App Engine.
- Compreenda como criar e atribuir nomes a ambientes de desenvolvimento, teste, controlo de qualidade, preparação e produção com microsserviços no App Engine.
- Saiba mais sobre as práticas recomendadas para o desempenho dos microsserviços.
- Saiba como migrar uma aplicação monolítica existente para uma com microserviços.