How to use PHP to implement microservices?

    Swoft


    Why should talk service governance?


    With the increasing popularity of the Internet, the traditional MVC architecture has become more and more bloated and very difficult to maintain as the scale of applications continues to expand.


    We need to take actions to split a large system into multiple applications according to business characteristics. For example, a large e-commerce system may include user system, product system, order system, evaluation system, etc., and we can separate them into multiple individual applications. The characteristics of multi-application architecture are applications run independently and they are unable to call each other.


    Although multiple applications solve the problem of bloated application, the applications are independent of each other and common services or codes cannot be reused.




    For a large Internet system, it usually contains multiple applications with common services, and each application has call relation among each other. In addition, there are other challenges for the large-scale Internet system, such as how to deal with rapidly growing users, how to manage R&D teams to quickly iterate system development, how to keep system upgrade in a stable manner, and so on.


    Therefore, in order to reuse services well and maintain modules to expand easily. We want the services to be separated from the application. A service is no longer in an application, but is maintained as a separated service. The application itself is no longer a bloated module stack, but a modular service component.


    Servitization


    Features


    So what is the feature of using "Servitization"?


    • An application splits into services by businesses
    • Individual service can be deployed independently
    • Service can be shared by multiple applications
    • Services can have communication among each other
    • The architecture of system is more clearer
    • Core modules are stable, and upgrading of service components in units can avoid the risk of frequent new releases
    • Easy for development and management
    • Maintenance can be done by individual team with clear work flow and responsibilities
    • Services reuse, codes reuse
    • Very easy to expand

    The challenge of servitization


    After system servitization, the dependency of system is complicated, and the number of interactions among services is increased. In the development mode of fpm, because the resident memory cannot be provided, every request must begin from zero by starting to load a process to exit the process, adding a lot of useless overhead. Also, the database connection can not be reused and can not be protected because fpm is the process-based and the number of fpm process also determines the number of concurrent. These are the problems given to us by the simple of the fpm development. So, these are the reasons why Java is more popular now in the Internet platform comparing to .NET and PHP. Apart from PHP non-memory resident, there are many other issues are need to be addressed.


    • More services, more complexity of configuration management
    • Complex service dependencies
    • Load balancing between services
    • Service expansion
    • Service monitoring
    • Service downgrade
    • Service authentication
    • Service online and offline
    • Service documentation
      ......

    You can imagine the benefits that resident memory brings to us.


    • Only start framework initialization once we can concentrate on processing requests as framework can be only initialized in memory on startup in once for resident memory


    • Connection multiplexing, some engineers can not understand if not using connections pool, what is the consequence of making connections for every request? It causes too much backend resource in connections. For some basic services, such as Redis, databases, connections are an expensive drain.



    So, is there a good solution? The answer is yes, and many people are using the framework called Swoft. Swoft is a RPC framework with the Service Governance feature. Swoft is the first PHP resident memory coroutine full-stack framework, based on the core concept of the Spring Boot, convention is greater than the configuration.


    Swoft provides a more elegant way to use RPC services like Dubbo and Swoft has great performance similar to Golang performance. Here is the stress test result of Swoft performance happening in my PC.
     
    Swoft


    Processing speed is very amazing in the ab stress test. With the i7 generation 8 CPU and 16GB memory, 100000 requests only use 5s. The time is basically impossible to be achieved in the fpm development mode. The test is also sufficient to demonstrate the high performance and stability of Swoft.


    Elegant service governance


    Service Registration and Discovery


    In the microservices governance process, registration of services initiated to third-party clusters, such as consul / etcd, is often involved. This chapter uses the swoft-consul component in the Swoft framework to implement service registration and discovery.
    Swoft
    Implementation logic


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

    Service Circuit Breaker


    In a distributed environment, especially a distributed system of microservice architecture, it is very common for one software system to call another remote system. The callee of such a remote call may be another process, or another host across the network. The biggest difference between this remote call and the internal call of the process is that the remote call may fail or hang. No response until timeout. Worse, if there are multiple callers calling the same suspended service, it is very likely that a service's timeout waits quickly spread to the entire distributed system, causing a chain reaction that consumes the entire A large amount of resources in distributed systems. Eventually, it can lead to system paralysis.


    The Circuit Breaker mode is designed to prevent disasters caused by such waterfall-like chain reactions in distributed systems.



    In the basic circuit breaker mode, to ensure supplier is not called when the circuit breaker is in the open state, but we also need additional method to reset the circuit breaker after the supplier resumes service. One possible solution is that the circuit breaker periodically detects whether the service of the supplier is resumed. Once resumed, the status is set to close. The state is a half-open state when the circuit breaker retries.


    The use of the fuse is simple and powerful. It can be annotated with a @Breaker. The fuse of the Swoft can be used in any scenario, such as a service is called. It can be downgraded or not called when requesting a third party service.


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

    Service Restriction


    Flow Restriction, Circuit Breaker, Service Downgrade These can be emphasized repeatedly because they are really important. When the service is not working, it must be broken. Flow restriction is a tool to protect itself. If there is no self-protection mechanism and connections are received no matter how many they are, the front-end will definitely hang when traffic is very large while the back-end can not handle all connections.


    The flow restriction is to limit the number of concurrent and number of requests when accessing scarce resources, such as flash sale goods, so as to effectively cut the peak and smooth the flow curve. The purpose of flow restriction is to limit the rate of concurrent access and concurrent requests, or to limit the speed of request within a window of time to protect the system. Once the rate limit is reached or exceeded, the requests can be denied, or queued.


    The bottom layer of the flow restriction of Swoft uses the token bucket algorithm, and the underlying layer relies on Redis to implement distributed flow restriction.


    The Swoft flow restriction not only limits controllers, it also limits the methods in any bean and controls the access rate of the methods. The following example is the explanation in detail.


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

    This supports the symfony/expression-language expression. If the speed is limited, the limiterFallback method defined in fallback will be called.


    Configuration Center


    Before we talk about the configuration center, let's talk about the configuration file. We are no stranger to it. It provides us with the ability to dynamically modify the program. A quote from someone is:


    Dynamic adjustment of the flight attitude of the system runtime!

    I can call our work to repair parts on fast-flying airplanes. We humans are always unable to control and predict everything. For our system, we always need to reserve some control lines to make adjustments when we need them, to control the system direction (such as gray control, flow restriction adjustment), which is especially important for the Internet industry that embraces changes.


    For the stand-alone version, we call it the configuration (file); for the distributed cluster system, we call it the configuration center (system);


    What exactly is a distributed configuration center?


    With the development of the business and the upgrade of the micro-service architecture, the number of services and the configuration of programs are increasing (various micro-services, various server addresses, various parameters), and the traditional configuration file method and database method cannot meet the needs of developers in configuration management:


    • Security: The configuration follows the source code stored in the code base, which is easy to cause configuration leaks;
    • Timeliness: Modify the configuration and restart the service to take effect.
    • Limitations: Dynamic adjustments cannot be supported: for example, log switches, function switches;

    Therefore, we need to configure the center to manage the configuration! Freeing business developers from complex and cumbersome configurations, they only need to focus on the business code itself, which can significantly improve development and operational efficiency. At the same time, the configuration and release of the package will further enhance the success rate of the release, and provide strong support for the fine tune control and emergency handling of operation and maintenance.


    About distributed configuration centers, there are many open source solutions on the web, such as:


    Apollo is a distributed configuration center developed by Ctrip's framework department. It can centrally manage the configuration of different environments and different clusters of applications. It can be pushed to the application end in real time after configuration modification. It has the features of standardized authority and process management, and is suitable for the scenarios of configuring and managing microservices.


    This chapter uses Apollo as an example to pull configuration and secure restart services from the remote configuration center. If you are unfamiliar with Apollo, you can first look at the Swoft extension Apollo component and read Apollo Official documentation.


    This chapter uses Apollo in Swoft as an example. When the Apollo configuration changes, restart the service (http-server / rpc-server/ ws-server). The following is an example of an agent:


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

    The above is a simple Apollo configuration pull, in addition to this method, Swoft-Apollo provides more ways to use.



    Share post

    Comments 1

    Only users with full accounts can post comments. Log in, please.