Configurando e usando afinidade de serviço no Service Fabric
Affinity é um controle que é fornecido principalmente para ajudar a facilitar a transição de aplicativos monolíticos maiores para o mundo da nuvem e microsserviços. Também é usado como uma otimização para melhorar o desempenho dos serviços, embora isso possa ter efeitos colaterais.
Digamos que você esteja trazendo um aplicativo maior, ou que simplesmente não foi projetado com microsserviços em mente, para o Service Fabric (ou qualquer ambiente distribuído). Este tipo de transição é comum. Você começa levantando todo o aplicativo para o ambiente, empacotando-o e certificando-se de que ele está funcionando sem problemas. Então você começa a dividi-lo em diferentes serviços menores que todos conversam entre si.
Eventualmente, você pode achar que o aplicativo está enfrentando alguns problemas. As questões geralmente se enquadram em uma destas categorias:
- Alguns componentes X no aplicativo monolítico tinham uma dependência não documentada do componente Y, e você acabou de transformar esses componentes em serviços separados. Como esses serviços agora estão sendo executados em nós diferentes no cluster, eles estão quebrados.
- Esses componentes se comunicam via (pipes nomeados locais | memória compartilhada | arquivos no disco) e eles realmente precisam ser capazes de gravar em um recurso local compartilhado por motivos de desempenho agora. Essa dependência dura é removida mais tarde, talvez.
- Tudo está bem, mas acontece que esses dois componentes são realmente tagarelas / sensíveis ao desempenho. Quando eles os moveram para serviços separados, o desempenho geral do aplicativo aumentou ou a latência. Consequentemente, a aplicação global não está a corresponder às expectativas.
Nestes casos, não queremos perder o nosso trabalho de refatoração e não queremos voltar ao monólito. A última condição pode até ser desejável como uma simples otimização. No entanto, até que possamos redesenhar os componentes para funcionar naturalmente como serviços (ou até que possamos resolver as expectativas de desempenho de outra forma), precisaremos de algum senso de localidade.
O que fazer? Bem, você pode tentar ativar a afinidade.
Como configurar a afinidade
Para configurar a afinidade, você define uma relação de afinidade entre dois serviços diferentes. Você pode pensar em afinidade como "apontar" um serviço para outro e dizer "Este serviço só pode ser executado onde esse serviço está sendo executado". Às vezes, nos referimos à afinidade como uma relação pai/filho (onde você aponta a criança para o pai). O Affinity garante que as réplicas ou instâncias de um serviço sejam colocadas nos mesmos nós que as de outro serviço.
ServiceCorrelationDescription affinityDescription = new ServiceCorrelationDescription();
affinityDescription.Scheme = ServiceCorrelationScheme.Affinity;
affinityDescription.ServiceName = new Uri("fabric:/otherApplication/parentService");
serviceDescription.Correlations.Add(affinityDescription);
await fabricClient.ServiceManager.CreateServiceAsync(serviceDescription);
Nota
Um serviço infantil só pode participar de uma única relação de afinidade. Se você queria que a criança fosse afiliada a dois serviços parentais ao mesmo tempo, você tem algumas opções:
- Inverter as relações (ter parentService1 e parentService2 ponto no serviço filho atual) ou
- Designe um dos pais como um hub por convenção e faça com que todos os serviços apontem para esse serviço.
O comportamento de posicionamento resultante no cluster deve ser o mesmo.
Diferentes opções de afinidade
A afinidade é representada através de um dos vários esquemas de correlação e tem dois modos diferentes. O modo mais comum de afinidade é o que chamamos de NonAlignedAffinity. Em NonAlignedAffinity, as réplicas ou instâncias dos diferentes serviços são colocadas nos mesmos nós. O outro modo é AlignedAffinity. O Aligned Affinity é útil apenas com serviços com monitoração de estado. A configuração de dois serviços com monitoração de estado para ter afinidade alinhada garante que as primárias desses serviços sejam colocadas nos mesmos nós uma da outra. Isso também faz com que cada par de secundários para esses serviços sejam colocados nos mesmos nós. Também é possível (embora menos comum) configurar NonAlignedAffinity para serviços com monitoração de estado. Para NonAlignedAffinity, as diferentes réplicas dos dois serviços stateful seriam executadas nos mesmos nós, mas suas primárias poderiam acabar em nós diferentes.
Melhor esforço desejado estado
Uma relação de afinidade é o melhor esforço. Ele não fornece as mesmas garantias de colocação ou confiabilidade que a execução no mesmo processo executável. Os serviços em uma relação de afinidade são entidades fundamentalmente diferentes que podem falhar e ser movidas de forma independente. Uma relação de afinidade também pode se romper, embora essas ruturas sejam temporárias. Por exemplo, as limitações de capacidade podem significar que apenas alguns dos objetos de serviço na relação de afinidade podem caber em um determinado nó. Nesses casos, mesmo que exista uma relação de afinidade, ela não pode ser aplicada devido às outras restrições. Se for possível fazê-lo, a violação é automaticamente corrigida mais tarde.
Correntes vs. estrelas
Atualmente, o Gerenciador de Recursos de Cluster não é capaz de modelar cadeias de relações de afinidade. O que isto significa é que um serviço que é uma criança numa relação de afinidade não pode ser pai noutra relação de afinidade. Se você quiser modelar esse tipo de relacionamento, você efetivamente tem que modelá-lo como uma estrela, em vez de uma cadeia. Para passar de uma corrente para uma estrela, a criança mais baixa seria entregue ao pai do primeiro filho. Dependendo da organização dos seus serviços, poderá ter de o fazer várias vezes. Se não houver um serviço pai natural, talvez seja necessário criar um que sirva como espaço reservado. Dependendo de suas necessidades, você também pode querer examinar Grupos de Aplicativos.
Outra coisa a notar sobre as relações de afinidade hoje é que elas são direcionais por padrão. Isto significa que a regra de afinidade apenas impõe que a criança tenha sido colocada com o progenitor. Não garante que o progenitor esteja localizado com a criança. Portanto, se houver uma violação de afinidade e para corrigir a violação por algum motivo, não é viável mover a criança para o nó do pai, então - mesmo que mover o pai para o nó da criança tenha corrigido a violação - o pai não será movido para o nó da criança. Definir a configuração MoveParentToFixAffinityViolation como true removeria a direcionalidade. Também é importante notar que a relação de afinidade não pode ser perfeita ou aplicada instantaneamente, uma vez que diferentes serviços têm ciclos de vida diferentes e podem falhar e mover-se de forma independente. Por exemplo, digamos que o pai de repente falha para outro nó porque ele travou. O Gerenciador de Recursos de Cluster e o Gerenciador de Failover lidam primeiro com o failover, pois manter os serviços ativos, consistentes e disponíveis é a prioridade. Quando o failover for concluído, a relação de afinidade será interrompida, mas o Gerenciador de Recursos de Cluster achará que está tudo bem até perceber que a criança não está localizada com o pai. Este tipo de verificações são realizadas periodicamente. Mais informações sobre como o Gerenciador de Recursos de Cluster avalia restrições estão disponíveis neste artigo, e este fala mais sobre como configurar a cadência na qual essas restrições são avaliadas.
Suporte de particionamento
A última coisa a notar sobre afinidade é que as relações de afinidade não são suportadas onde o pai é particionado. Serviços pai particionados podem ser suportados eventualmente, mas hoje não é permitido.
Próximos passos
- Para obter mais informações sobre como configurar serviços, Saiba mais sobre como configurar serviços
- Para limitar os serviços a um pequeno conjunto de máquinas ou agregar a carga de serviços, use Grupos de Aplicativos