怎麼利用 PHP 實現微服務

隨着互聯網瀏覽越來越大. 傳統的 MVC 單一架構隨着應用規模的不斷擴大,應用模塊不斷增加,整個應用也顯得越來越臃腫,維護起來也更加困難.

我們必須採取措施,按應用拆分,就是把原來的應用按照業務特點拆分成多個應用。比如一個大型電商系統可能包含用戶系統、商品系統、訂單系統、評價系統等等,我們可以把他們獨立出來形成一個個單獨的應用。多應用架構的特點是應用之間各自獨立 ,不相互調用。

多應用雖然解決了應用臃腫問題,但應用之間相互獨立,有些共同的業務或代碼無法複用。

 

單一應用的解決方案

對於一個大型的互聯網系統,一般會包含多個應用,而且應用之間往往還存在共同的業務,並且應用之間還存在調用關係。除此之外 ,對於大型的互聯網系統還有一些其它的挑戰,比如如何應對急劇增長的用戶,如何管理好研發團隊快速迭代產品研發,如何保持產品升級更加穩定等等 。

因此,爲了使業務得到很好的複用,模塊更加容易拓展和維護,我們希望業務與應用分離,某個業務不再屬於一個應用,而是作爲一個獨立的服務單獨進行維護。應用本身不再是一個臃腫的模塊堆積,而是由一個個模塊化的服務組件組合而成。

 

服務化

特點

那麼採用服務化給有那些亮點的特色呢 ?

應用按業務拆分成服務

各個服務均可獨立部署

服務可被多個應用共享

服務之間可以通信

架構上系統更加清晰

核心模塊穩定,以服務組件爲單位進行升級,避免了頻繁發佈帶來的風險

開發管理方便

單獨團隊維護、工作分明,職責清晰

業務複用、代碼複用

非常容易拓展

服務化面臨的挑戰

系統服務化之後, 增加了依賴關係複雜, 也會增加服務與服務之間交互的次數. 在 fpm 的開發模式下. 因爲無法常駐內存給我們帶來了, 每一次請求都要從零開始加載到退出進程, 增加了很多無用的開銷, 數據庫連接無法複用也得不到保護, 由於fpm是以進程爲單位的fpm的進程數也決定了併發數, 這也是是fpm開發簡單給我們帶來的問題. 所以說爲什麼現在互聯網平臺Java比較流行了,.NET和PHP在這方面都不行。PHP非內存常駐的就不用說了。除此之外,還有很多其他問題需要解決。

那麼有沒有好的方案呢?答案是有的,它就是-Swoft。Swoft就是一個帶有服務治理功能的RPC框架。Swoft是首個 PHP常駐內存協程全棧框架, 基於高性能協程swoole打造的一個 PHP界的Spring Boot

Swoft 提供了類似 Dubbo 更爲優雅的方式使用 RPC 服務, Swoft 性能是非常棒的有着類似Golang性能, 下面是 對Swoft 性能的壓測情況.

ab壓力測試處理速度十分驚人, 在 i78代CPU, 16GB 內存下100000萬個請求只用了5s時間在fpm開發模式下基本不可能達到. 這也足以證明Swoft` 的高性能和穩定性,

優雅的服務治理

服務註冊與發現

微服務治理過程中,經常會涉及註冊啓動的服務到第三方集羣,比如 consul / etcd 等等,以 Swoft 框架中使用 swoft-consul 組件,實現服務註冊與發現爲例。

實現邏輯

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

服務熔斷

在分佈式環境下,特別是微服務結構的分佈式系統中, 一個軟件系統調用另外一個遠程系統是非常普遍的。這種遠程調用的被調用方可能是另外一個進程,或者是跨網路的另外一臺主機, 這種遠程的調用和進程的內部調用最大的區別是,遠程調用可能會失敗,或者掛起而沒有任何迴應,直到超時。更壞的情況是, 如果有多個調用者對同一個掛起的服務進行調用,那麼就很有可能的是一個服務的超時等待迅速蔓延到整個分佈式系統,引起連鎖反應, 從而消耗掉整個分佈式系統大量資源。最終可能導致系統癱瘓。

斷路器(Circuit Breaker)模式就是爲了防止在分佈式系統中出現這種瀑布似的連鎖反應導致的災難。

基本的斷路器模式下,保證了斷路器在open狀態時,保護supplier不會被調用, 但我們還需要額外的措施可以在supplier恢復服務後,可以重置斷路器。一種可行的辦法是斷路器定期探測supplier的服務是否恢復, 一但恢復, 就將狀態設置成close。斷路器進行重試時的狀態爲半開(half-open)狀態。

熔斷器的使用想到簡單且功能強大,使用一個 @Breaker 註解即可,Swoft 的熔斷器可以用於任何場景, 例如 服務調用的時候使用, 請求第三方的時候都可以對它進行熔斷降級

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

服務限流

限流、熔斷、降級這個強調多少遍都不過分,因爲確實很重要。服務不行的時候一定要熔斷。限流是一個保護自己最大的利器,如果沒有自我保護機制,不管有多少連接都會接收,如果後端處理不過來,前端流量又很大的時候肯定就掛了。

限流是對稀缺資源訪問時,比如秒殺,搶購的商品時,來限制併發和請求的數量,從而有效的進行削峯並使得流量曲線平滑。限流的目的是對併發訪問和併發請求進行限速,或者一個時間窗口內請求進行限速從而來保護系統,一旦達到或超過限制速率就可以拒絕服務,或者進行排隊等待等。

Swoft 限流器底層採用的是令牌桶算法,底層依賴於 Redis 實現分佈式限流。

Swoft 限速器不僅可以限流控制器,也可以限制任何 bean 裏面的方法,可以控制方法的訪問速率。這裏以下面使用示例詳解

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

key 這裏支持 symfony/expression-language 表達式, 如果被限速會調用 fallback中定義的limiterFallback 方法

配置中心

說起配置中心前我們先說說配置文件,我們並不陌生,它提供我們可以動態修改程序運行能力。引用別人的一句話就是:

系統運行時(runtime)飛行姿態的動態調整!

我可以把我們的工作稱之爲在快速飛行的飛機上修理零件。我們人類總是無法掌控和預知一切。對於我們系統來說,我們總是需要預留一些控制線條,以便在我們需要的時候做出調整,控制系統方向(如灰度控制、限流調整),這對於擁抱變化的互聯網行業尤爲重要。

對於單機版,我們稱之爲配置(文件);對於分佈式集羣系統,我們稱之爲配置中心(系統);

到底什麼是分佈式配置中心

隨着業務的發展、微服務架構的升級,服務的數量、程序的配置日益增多(各種微服務、各種服務器地址、各種參數),傳統的配置文件方式和數據庫的方式已無法滿足開發人員對配置管理的要求:

安全性:配置跟隨源代碼保存在代碼庫中,容易造成配置泄漏;

時效性:修改配置,需要重啓服務才能生效;

侷限性:無法支持動態調整:例如日誌開關、功能開關;

因此,我們需要配置中心來統一管理配置!把業務開發者從複雜以及繁瑣的配置中解脫出來,只需專注於業務代碼本身,從而能夠顯著提升開發以及運維效率。同時將配置和發佈包解藕也進一步提升發佈的成功率,併爲運維的細力度管控、應急處理等提供強有力的支持。

 

關於分佈式配置中心,網上已經有很多開源的解決方案,例如:

Apollo是攜程框架部門研發的分佈式配置中心,能夠集中化管理應用不同環境、不同集羣的配置,配置修改後能夠實時推送到應用端,並且具備規範的權限、流程治理等特性,適用於微服務配置管理場景。

以Apollo 爲例,從遠端配置中心拉取配置以及安全重啓服務。如果對 Apollo 不熟悉,可以先看Swoft 擴展 Apollo 組件以及閱讀 Apollo 官方文檔。

以 Swoft 中使用 Apollo 爲例,當 Apollo 配置變更後,重啓服務(http-server / rpc-server/ ws-server)。如下是一個 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);
    }
}

以上就是一個簡單的 Apollo 配置拉取,Swoft-Apollo除此方法外,還提供了更多的使用方法。

以上內容希望幫助到大家,更多PHP大廠PDF面試文檔,PHP進階架構視頻資料,PHP精彩好文免費獲取可以微信搜索關注公衆號:PHP開源社區,或者訪問:

2021金三銀四大廠面試真題集錦,必看!

四年精華PHP技術文章整理合集——PHP框架篇

四年精華PHP技術文合集——微服務架構篇

四年精華PHP技術文合集——分佈式架構篇

四年精華PHP技術文合集——高併發場景篇

四年精華PHP技術文章整理合集——數據庫篇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章