Como usar o PHP para implementar microsserviços?

Swoft


Por que falar sobre governança de serviços?


Com a crescente popularidade da Internet, a arquitetura MVC tradicional tornou-se cada vez mais inchada e muito difícil de manter, à medida que a escala de aplicativos continua a se expandir.


Precisamos tomar ações para dividir um sistema grande em vários aplicativos de acordo com as características do negócio. Por exemplo, um grande sistema de comércio eletrônico pode incluir sistema de usuário, sistema de produtos, sistema de pedidos, sistema de avaliação etc., e podemos separá-los em vários aplicativos individuais. As características da arquitetura de aplicativos múltiplos são aplicativos executados de forma independente e não podem se chamar.


Embora vários aplicativos resolvam o problema do aplicativo inchado, os aplicativos são independentes um do outro e serviços ou códigos comuns não podem ser reutilizados.




Para um sistema de Internet grande, geralmente contém vários aplicativos com serviços comuns, e cada aplicativo tem relação de chamada entre si. Além disso, existem outros desafios para o sistema de Internet em larga escala, como lidar com usuários em rápido crescimento, como gerenciar equipes de P&D para iterar rapidamente o desenvolvimento do sistema, como manter a atualização do sistema de maneira estável e assim por diante.


Portanto, para reutilizar bem os serviços e manter os módulos para expandir facilmente. Queremos que os serviços sejam separados do aplicativo. Um serviço não está mais em um aplicativo, mas é mantido como um serviço separado. O aplicativo em si não é mais uma pilha de módulos inchada, mas um componente de serviço modular.


Servitização


Funcionalidades


Então, qual é o recurso do "Servitization"?


  • Um aplicativo se divide em serviços por empresas
  • Serviço individual pode ser implantado independentemente
  • O serviço pode ser compartilhado por vários aplicativos
  • Os serviços podem ter comunicação entre si
  • A arquitetura do sistema é mais clara
  • Os módulos principais são estáveis ​​e a atualização dos componentes de serviço nas unidades pode evitar o risco de novos lançamentos frequentes
  • Fácil para desenvolvimento e gerenciamento
  • A manutenção pode ser feita por equipe individual com fluxo de trabalho e responsabilidades claros
  • Reutilização de serviços, reutilização de códigos
  • Muito fácil de expandir

O desafio da servitização


Após a manutenção do sistema, a dependência do sistema é complicada e o número de interações entre serviços é aumentado. No modo de desenvolvimento do fpm , como a memória residente não pode ser fornecida, toda solicitação deve começar do zero, iniciando o carregamento de um processo para sair do processo, adicionando uma sobrecarga inútil. Além disso, a conexão com o banco de dados não pode ser reutilizada e não pode ser protegida porque o fpm é baseado no processo e o número de processos do fpm também determina o número de concorrentes. Estes são os problemas que nos são dados pelo simples desenvolvimento do fpm . Portanto, essas são as razões pelas quais o Java é mais popular agora na plataforma da Internet, comparado ao .NET e PHP . Além do PHP non-memory resident , há muitos outros problemas que precisam ser resolvidos.


  • Mais serviços, mais complexidade de gerenciamento de configuração
  • Dependências de serviço complexas
  • Balanceamento de carga entre serviços
  • Expansão de serviço
  • Monitoramento de serviço
  • Downgrade de serviço
  • Autenticação de serviço
  • Serviço online e offline
  • Documentação de serviço
    ......

Você pode imaginar os benefícios que a memória residente nos traz.


  • Somente inicie a inicialização da estrutura uma vez que possamos nos concentrar no processamento de solicitações, pois a estrutura só poderá ser inicializada na memória na inicialização uma vez para a memória residente


  • Multiplexação de conexão , alguns engenheiros não conseguem entender se não estiver usando o conjunto de conexões, qual é a conseqüência de fazer conexões para cada solicitação? Causa muito recurso de back-end nas conexões. Para alguns serviços básicos, como Redis, bancos de dados, as conexões são um dreno caro.



Então, existe uma boa solução? A resposta é sim, e muitas pessoas estão usando a estrutura chamada Swoft . Swoft é uma estrutura RPC com o recurso de Service Governance . Swoft é a primeira estrutura de pilha completa de corotina de memória residente em PHP, com base no conceito principal do Spring Boot , a convenção é maior que a configuração.


Swoft oferece uma maneira mais elegante de usar serviços RPC , como o Dubbo e o Swoft possui um desempenho semelhante ao do Golang . Aqui está o resultado do teste de estresse do desempenho do Swoft no meu PC .

Swoft


A velocidade de processamento é muito surpreendente no teste de estresse ab . Com a CPU da i7 generation 8 e memória de 16GB , 100000 solicitações usam apenas 5s . O tempo é basicamente impossível de ser alcançado no modo de desenvolvimento de fpm . O teste também é suficiente para demonstrar o alto desempenho e a estabilidade do Swoft .


Governança de serviço elegante


Registro e descoberta de serviços


No processo de governança de microsserviços, o registro de serviços iniciados em clusters de terceiros, como consul / etcd, geralmente está envolvido. Este capítulo usa o componente swoft-consul na estrutura Swoft para implementar o registro e a descoberta de serviços.
Swoft
Lógica de implementação


 <?php declare(strict_types=1); namespace App\Common; use ReflectionException; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Bean\Annotation\Mapping\Inject; use Swoft\Bean\Exception\ContainerException; use Swoft\Consul\Agent; use Swoft\Consul\Exception\ClientException; use Swoft\Consul\Exception\ServerException; use Swoft\Rpc\Client\Client; use Swoft\Rpc\Client\Contract\ProviderInterface; /** * Class RpcProvider * * @since 2.0 * * @Bean() */ class RpcProvider implements ProviderInterface { /** * @Inject() * * @var Agent */ private $agent; /** * @param Client $client * * @return array * @throws ReflectionException * @throws ContainerException * @throws ClientException * @throws ServerException * @example * [ * 'host:port', * 'host:port', * 'host:port', * ] */ public function getList(Client $client): array { // Get health service from consul $services = $this->agent->services(); $services = [ ]; return $services; } } 

Disjuntor de serviço


Em um ambiente distribuído, especialmente em um sistema distribuído de arquitetura de microsserviço, é muito comum que um sistema de software chame outro sistema remoto. O chamado de uma chamada remota pode ser outro processo ou outro host na rede. A maior diferença entre essa chamada remota e a chamada interna do processo é que a chamada remota pode falhar ou travar. Nenhuma resposta até o tempo limite. Pior, se houver vários chamadores chamando o mesmo serviço suspenso, é muito provável que o tempo limite do serviço espere rapidamente se espalhe por todo o sistema distribuído, causando uma reação em cadeia que consome toda uma grande quantidade de recursos em sistemas distribuídos. Eventualmente, pode levar à paralisia do sistema.


O modo Disjuntor foi projetado para evitar desastres causados ​​por reações em cadeia semelhantes a cascatas em sistemas distribuídos.



No modo básico do disjuntor, para garantir que o fornecedor não seja chamado quando o disjuntor estiver no estado aberto, também precisamos de um método adicional para redefinir o disjuntor depois que o fornecedor retomar o serviço. Uma solução possível é que o disjuntor detecte periodicamente se o serviço do fornecedor é retomado. Depois de retomado, o status é definido para fechar. O estado é um estado semi-aberto quando o disjuntor tenta novamente.


O uso do fusível é simples e poderoso. Pode ser anotado com um @Breaker . O fusível do Swoft pode ser usado em qualquer cenário, como é chamado um serviço. Ele pode ser rebaixado ou não chamado ao solicitar um serviço de terceiros.


 <?php declare(strict_types=1); namespace App\Model\Logic; use Exception; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Breaker\Annotation\Mapping\Breaker; /** * Class BreakerLogic * * @since 2.0 * * @Bean() */ class BreakerLogic { /** * @Breaker(fallback="loopFallback") * * @return string * @throws Exception */ public function loop(): string { // Do something throw new Exception('Breaker exception'); } /** * @return string * @throws Exception */ public function loopFallback(): string { // Do something } } 

Restrição de serviço


Restrição de fluxo, disjuntor, downgrade de serviço Estes podem ser enfatizados repetidamente porque são realmente importantes. Quando o serviço não está funcionando, ele deve estar quebrado. A restrição de fluxo é uma ferramenta para se proteger. Se não houver um mecanismo de autoproteção e as conexões forem recebidas, não importando quantas sejam, o front-end ficará paralisado quando o tráfego for muito grande, enquanto o back-end não conseguirá lidar com todas as conexões.


A restrição de fluxo é limitar o número de solicitações simultâneas e o número de solicitações ao acessar recursos escassos, como produtos de venda instantânea, para reduzir efetivamente o pico e suavizar a curva de fluxo. O objetivo da restrição de fluxo é limitar a taxa de acesso simultâneo e solicitações simultâneas ou limitar a velocidade da solicitação dentro de uma janela de tempo para proteger o sistema. Depois que o limite da taxa é atingido ou excedido, as solicitações podem ser negadas ou colocadas em fila.


A camada inferior da restrição de fluxo do Swoft usa o algoritmo de token bucket, e a camada subjacente depende do Redis para implementar a restrição de fluxo distribuído.


A restrição de fluxo Swoft não apenas limita os controladores, mas também limita os métodos em qualquer bean e controla a taxa de acesso dos métodos. O exemplo a seguir é a explicação em detalhes.


 <?php declare(strict_types=1); namespace App\Model\Logic; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Limiter\Annotation\Mapping\RateLimiter; /** * Class LimiterLogic * * @since 2.0 * * @Bean() */ class LimiterLogic { /** * @RequestMapping() * @RateLimiter(rate=20, fallback="limiterFallback") * * @param Request $request * * @return array */ public function requestLimiter2(Request $request): array { $uri = $request->getUriPath(); return ['requestLimiter2', $uri]; } /** * @param Request $request * * @return array */ public function limiterFallback(Request $request): array { $uri = $request->getUriPath(); return ['limiterFallback', $uri]; } } 

Isso suporta a symfony/expression-language . Se a velocidade for limitada, o método limiterFallback definido em fallback será chamado.


Centro de configuração


Antes de falarmos sobre o centro de configuração, vamos falar sobre o arquivo de configuração. Não somos estranhos a isso. Ele nos fornece a capacidade de modificar dinamicamente o programa. Uma citação de alguém é:


Ajuste dinâmico da atitude de vôo do tempo de execução do sistema!

Posso chamar nosso trabalho para consertar peças em aviões que voam rápido. Nós humanos sempre somos incapazes de controlar e prever tudo. Para nosso sistema, sempre precisamos reservar algumas linhas de controle para fazer ajustes quando necessário, para controlar a direção do sistema (como controle de cinza, ajuste de restrição de fluxo), o que é especialmente importante para a indústria da Internet que adota mudanças.


Para a versão autônoma, chamamos de configuração (arquivo); para o sistema de cluster distribuído, chamamos de centro de configuração (sistema);


O que exatamente é um centro de configuração distribuído?


Com o desenvolvimento dos negócios e a atualização da arquitetura de microsserviços, o número de serviços e a configuração de programas estão aumentando (vários microsserviços, vários endereços de servidores, vários parâmetros) e o método tradicional de arquivo de configuração e o método de banco de dados não pode atender às necessidades dos desenvolvedores no gerenciamento de configurações:


  • Segurança: A configuração segue o código fonte armazenado na base de códigos, o que é fácil de causar vazamentos na configuração;
  • Oportunidade: Modifique a configuração e reinicie o serviço para entrar em vigor.
  • Limitações: Ajustes dinâmicos não podem ser suportados: por exemplo, comutadores de log, comutadores de função;

Portanto, precisamos configurar o centro para gerenciar a configuração! Liberando os desenvolvedores de negócios de configurações complexas e complicadas, eles precisam apenas se concentrar no próprio código de negócios, o que pode melhorar significativamente o desenvolvimento e a eficiência operacional. Ao mesmo tempo, a configuração e o lançamento do pacote aumentarão ainda mais a taxa de sucesso do lançamento e fornecerão forte suporte para o controle de ajuste fino e o manuseio de emergência da operação e manutenção.


Sobre os centros de configuração distribuídos, existem muitas soluções de código aberto na web, como:


Apollo é um centro de configuração distribuído desenvolvido pelo departamento de estrutura da Ctrip. Ele pode gerenciar centralmente a configuração de diferentes ambientes e diferentes clusters de aplicativos. Ele pode ser enviado ao final do aplicativo em tempo real após a modificação da configuração. Possui os recursos de autoridade padronizada e gerenciamento de processos e é adequado para os cenários de configuração e gerenciamento de microsserviços.


Este capítulo usa o Apollo como um exemplo para obter configuração e proteger serviços de reinicialização do centro de configuração remota. Se você não está familiarizado com o Apollo , pode primeiro examinar o componente Apollo extensão Swoft e ler a documentação oficial do Apollo .


Este capítulo usa o Apollo no Swoft como exemplo. Quando a configuração do Apollo mudar, reinicie o serviço (http-server / rpc-server / ws-server). A seguir, é apresentado um exemplo de um agente:


 <?php declare(strict_types=1); namespace App\Model\Logic; use Swoft\Apollo\Config; use Swoft\Apollo\Exception\ApolloException; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Bean\Annotation\Mapping\Inject; /** * Class ApolloLogic * * @since 2.0 * * @Bean() */ class ApolloLogic { /** * @Inject() * * @var Config */ private $config; /** * @throws ApolloException */ public function pull(): void { $data = $this->config->pull('application'); // Print data var_dump($data); } } 

A Swoft-Apollo acima é um simples puxão de configuração da Apollo. Além desse método, o Swoft-Apollo fornece mais maneiras de usar.



Source: https://habr.com/ru/post/pt460855/


All Articles