¿Cómo usar PHP para implementar microservicios?

Swoft


¿Por qué debería hablar de gobernanza de servicios?


Con la creciente popularidad de Internet, la arquitectura tradicional de MVC se ha vuelto cada vez más hinchada y muy difícil de mantener a medida que la escala de aplicaciones continúa expandiéndose.


Necesitamos tomar medidas para dividir un sistema grande en múltiples aplicaciones de acuerdo con las características del negocio. Por ejemplo, un gran sistema de comercio electrónico puede incluir un sistema de usuario, un sistema de productos, un sistema de pedidos, un sistema de evaluación, etc., y podemos separarlos en múltiples aplicaciones individuales. Las características de la arquitectura de aplicaciones múltiples son aplicaciones que se ejecutan de forma independiente y no pueden llamarse entre sí.


Aunque varias aplicaciones resuelven el problema de la aplicación hinchada, las aplicaciones son independientes entre sí y los servicios o códigos comunes no se pueden reutilizar.




Para un sistema de Internet grande, generalmente contiene múltiples aplicaciones con servicios comunes, y cada aplicación tiene una relación de llamada entre sí. Además, existen otros desafíos para el sistema de Internet a gran escala, tales como cómo tratar con usuarios en rápido crecimiento, cómo administrar equipos de I + D para iterar rápidamente el desarrollo del sistema, cómo mantener la actualización del sistema de manera estable, etc.


Por lo tanto, para reutilizar bien los servicios y mantener los módulos para expandirse fácilmente. Queremos que los servicios se separen de la aplicación. Un servicio ya no está en una aplicación, sino que se mantiene como un servicio separado. La aplicación en sí ya no es una pila de módulos hinchada, sino un componente de servicio modular.


Servitization


Caracteristicas


Entonces, ¿cuál es la característica de usar "Servitización"?


  • Una aplicación se divide en servicios por empresas
  • El servicio individual se puede implementar de forma independiente
  • El servicio puede ser compartido por múltiples aplicaciones
  • Los servicios pueden tener comunicación entre ellos.
  • La arquitectura del sistema es más clara.
  • Los módulos principales son estables y la actualización de componentes de servicio en unidades puede evitar el riesgo de nuevos lanzamientos frecuentes
  • Fácil para el desarrollo y la gestión.
  • El mantenimiento puede realizarlo un equipo individual con un flujo de trabajo claro y responsabilidades
  • Reutilización de servicios, reutilización de códigos
  • Muy fácil de expandir

El desafío de la servitización.


Después de la servitización del sistema, la dependencia del sistema es complicada y aumenta el número de interacciones entre los servicios. En el modo de desarrollo de fpm , debido a que no se puede proporcionar la memoria residente, cada solicitud debe comenzar desde cero al comenzar a cargar un proceso para salir del proceso, agregando una sobrecarga inútil. Además, la conexión de la base de datos no se puede reutilizar y no se puede proteger porque fpm se basa en el proceso y el número de procesos fpm también determina el número de concurrentes. Estos son los problemas que nos da el simple desarrollo de fpm . Entonces, estas son las razones por las cuales Java es más popular ahora en la plataforma de Internet en comparación con .NET y PHP . Además de PHP non-memory resident , hay muchos otros problemas que deben abordarse.


  • Más servicios, más complejidad en la gestión de la configuración.
  • Dependencias de servicio complejas
  • Balanceo de carga entre servicios
  • Servicio de expansión
  • Servicio de monitoreo
  • Servicio de degradación
  • Autenticación de servicio
  • Servicio en línea y fuera de línea
  • Documentación de servicio
    ......

Puedes imaginar los beneficios que nos brinda la memoria residente.


  • Solo inicie la inicialización del marco una vez que podamos concentrarnos en el procesamiento de solicitudes, ya que el marco solo se puede inicializar en la memoria al inicio de una vez para la memoria residente


  • Multiplexación de conexión , algunos ingenieros no pueden entender si no se usa el grupo de conexiones, ¿cuál es la consecuencia de hacer conexiones para cada solicitud? Causa demasiados recursos de back-end en las conexiones. Para algunos servicios básicos, como Redis, bases de datos, las conexiones son un drenaje costoso.



Entonces, ¿hay una buena solución? La respuesta es sí, y muchas personas están usando el marco llamado Swoft . Swoft es un marco RPC con la función de Service Governance . Swoft es el primer framework de pila completa de rutina residente en PHP, basado en el concepto central del Spring Boot , la convención es mayor que la configuración.


Swoft proporciona una forma más elegante de usar servicios RPC como Dubbo y Swoft tiene un gran rendimiento similar al rendimiento de Golang . Aquí está el resultado de la prueba de estrés del rendimiento de Swoft en mi PC .

Swoft


La velocidad de procesamiento es muy sorprendente en la prueba de esfuerzo abdominal. Con la CPU i7 generation 8 y la memoria de 16GB , 100000 solicitudes solo usan 5s . El tiempo es básicamente imposible de alcanzar en el modo de desarrollo fpm . La prueba también es suficiente para demostrar el alto rendimiento y la estabilidad de Swoft .


Gobierno de servicio elegante


Registro de servicio y descubrimiento


En el proceso de gobernanza de microservicios, el registro de servicios iniciados a grupos de terceros, como cónsul / etcd, a menudo está involucrado. Este capítulo utiliza el componente swoft-consul en el marco de Swoft para implementar el registro y descubrimiento de servicios.
Swoft
Lógica de implementación


 <?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; } } 

Servicio de disyuntor


En un entorno distribuido, especialmente un sistema distribuido de arquitectura de microservicios, es muy común que un sistema de software llame a otro sistema remoto. El destinatario de dicha llamada remota puede ser otro proceso u otro host en la red. La mayor diferencia entre esta llamada remota y la llamada interna del proceso es que la llamada remota puede fallar o bloquearse. No hay respuesta hasta el tiempo de espera. Peor aún, si hay varias personas que llaman que llaman al mismo servicio suspendido, es muy probable que el tiempo de espera de un servicio se extienda rápidamente a todo el sistema distribuido, causando una reacción en cadena que consume una gran cantidad de recursos en sistemas distribuidos. Eventualmente, puede conducir a la parálisis del sistema.


El modo de disyuntor está diseñado para prevenir desastres causados ​​por tales reacciones en cadena similares a una cascada en sistemas distribuidos.



En el modo básico del interruptor automático, para garantizar que no se llame al proveedor cuando el interruptor automático está en estado abierto, pero también necesitamos un método adicional para restablecer el interruptor automático después de que el proveedor reanude el servicio. Una posible solución es que el interruptor automático detecta periódicamente si se reanuda el servicio del proveedor. Una vez reanudado, el estado se establece en cerrar. El estado es un estado medio abierto cuando el disyuntor vuelve a intentarlo.


El uso del fusible es simple y poderoso. Se puede anotar con un @Breaker . El fusible del Swoft se puede usar en cualquier escenario, como se llama a un servicio. Se puede degradar o no llamar cuando se solicita un servicio de terceros.


 <?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 } } 

Restricción de servicio


Restricción de flujo, disyuntor, reducción de servicio Estos pueden enfatizarse repetidamente porque son realmente importantes. Cuando el servicio no funciona, debe estar roto. La restricción de flujo es una herramienta para protegerse. Si no hay un mecanismo de autoprotección y las conexiones se reciben sin importar cuántas sean, el front-end definitivamente se bloqueará cuando el tráfico sea muy grande, mientras que el back-end no puede manejar todas las conexiones.


La restricción de flujo es limitar el número de solicitudes simultáneas y el número de solicitudes al acceder a recursos escasos, como los productos de venta flash, para reducir efectivamente el pico y suavizar la curva de flujo. El propósito de la restricción de flujo es limitar la velocidad de acceso concurrente y solicitudes concurrentes, o limitar la velocidad de solicitud dentro de un período de tiempo para proteger el sistema. Una vez que se alcanza o se supera el límite de velocidad, las solicitudes se pueden denegar o poner en cola.


La capa inferior de la restricción de flujo de Swoft utiliza el algoritmo de depósito de tokens, y la capa subyacente depende de Redis para implementar la restricción de flujo distribuido.


La restricción de flujo de Swoft no solo limita los controladores, también limita los métodos en cualquier bean y controla la velocidad de acceso de los métodos. El siguiente ejemplo es la explicación en detalle.


 <?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]; } } 

Esto admite la symfony/expression-language . Si la velocidad es limitada, se limiterFallback método limiterFallback definido en limiterFallback .


Centro de configuración


Antes de hablar sobre el centro de configuración, hablemos sobre el archivo de configuración. No somos ajenos a eso. Nos proporciona la capacidad de modificar dinámicamente el programa. Una cita de alguien es:


¡Ajuste dinámico de la actitud de vuelo del tiempo de ejecución del sistema!

Puedo llamar a nuestro trabajo para reparar piezas en aviones de vuelo rápido. Los humanos siempre somos incapaces de controlar y predecir todo. Para nuestro sistema, siempre necesitamos reservar algunas líneas de control para hacer ajustes cuando los necesitemos, para controlar la dirección del sistema (como control gris, ajuste de restricción de flujo), lo cual es especialmente importante para la industria de Internet que acepta los cambios.


Para la versión independiente, la llamamos configuración (archivo); para el sistema de clúster distribuido, lo llamamos el centro de configuración (sistema);


¿Qué es exactamente un centro de configuración distribuido?


Con el desarrollo del negocio y la actualización de la arquitectura de microservicios, el número de servicios y la configuración de programas están aumentando (varios microservicios, varias direcciones de servidores, diversos parámetros), y el método tradicional de archivo de configuración y método de base de datos no puede satisfacer las necesidades de los desarrolladores en la gestión de la configuración:


  • Seguridad: la configuración sigue el código fuente almacenado en la base del código, que es fácil de causar fugas de configuración;
  • Puntualidad: modifique la configuración y reinicie el servicio para que surta efecto.
  • Limitaciones: No se admiten ajustes dinámicos: por ejemplo, interruptores de registro, interruptores de función;

¡Por lo tanto, necesitamos configurar el centro para gestionar la configuración! Al liberar a los desarrolladores de negocios de configuraciones complejas y engorrosas, solo necesitan enfocarse en el código de negocios en sí mismo, lo que puede mejorar significativamente el desarrollo y la eficiencia operativa. Al mismo tiempo, la configuración y el lanzamiento del paquete mejorarán aún más la tasa de éxito del lanzamiento y proporcionarán un fuerte soporte para el control de ajuste fino y el manejo de emergencia de la operación y mantenimiento.


Sobre los centros de configuración distribuidos, hay muchas soluciones de código abierto en la web, como:


Apollo es un centro de configuración distribuido desarrollado por el departamento de framework de Ctrip. Puede gestionar centralmente la configuración de diferentes entornos y diferentes grupos de aplicaciones. Se puede enviar al final de la aplicación en tiempo real después de la modificación de la configuración. Tiene las características de autoridad estandarizada y gestión de procesos, y es adecuado para los escenarios de configuración y gestión de microservicios.


Este capítulo utiliza Apollo como ejemplo para extraer la configuración y asegurar los servicios de reinicio desde el centro de configuración remoto. Si no está familiarizado con Apollo , primero puede mirar el componente Apollo extensión Swoft y leer la documentación oficial de Apollo .


Este capítulo utiliza Apollo en Swoft como ejemplo. Cuando cambie la configuración de Apollo , reinicie el servicio (http-server / rpc-server / ws-server). El siguiente es un ejemplo de un 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); } } 

Lo anterior es un simple tirón de configuración de Apollo, además de este método, Swoft-Apollo proporciona más formas de uso.



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


All Articles