教你利用 PHP 实现微服务
off999 2024-12-12 14:24 13 浏览 0 评论
随着互联网浏览越来越大. 传统的 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);
}
}
相关推荐
- Python四种常用的高阶函数,你会用了吗
-
每天进步一点点,关注我们哦,每天分享测试技术文章本文章出自【码同学软件测试】码同学公众号:自动化软件测试码同学抖音号:小码哥聊软件测试1、什么是高阶函数把函数作为参数传入,这样的函数称为高阶函数例如:...
- Python之函数进阶-函数加强(上)(python函数的作用增强代码的可读性)
-
一.递归函数递归是一种编程技术,其中函数调用自身以解决问题。递归函数需要有一个或多个终止条件,以防止无限递归。递归可以用于解决许多问题,例如排序、搜索、解析语法等。递归的优点是代码简洁、易于理解,并...
- 数据分析-一元线性回归分析Python
-
前面几篇介绍了数据的相关性分析,通过相关性分析可以看出变量之间的相关性程度。如果我们已经发现变量之间存在明显的相关性了,接下来就可以通过回归分析,计算出具体的相关值,然后可以用于对其他数据的预测。本篇...
- python基础函数(python函数总结)
-
Python函数是代码复用的核心工具,掌握基础函数的使用是编程的关键。以下是Python函数的系统总结,包含内置函数和自定义函数的详细用法,以及实际应用场景。一、Python内置函数(...
- python进阶100集(9)int数据类型深入分析
-
一、基本概念int数据类型基本上来说这里指的都是整形,下一届我们会讲解整形和浮点型的转化,以及精度问题!a=100b=a这里a是变量名,100就是int数据对象,b指向的是a指向的对象,...
- Python学不会来打我(73)python常用的高阶函数汇总
-
python最常用的高阶函数有counter(),sorted(),map(),reduce(),filter()。很多高阶函数都是将一个基础函数作为第一个参数,将另外一个容器集合作为第二个参数,然...
- python中有哪些内置函数可用于编写数值表达式?
-
在Python中,用于编写数值表达式的内置函数很多,它们可以帮助你处理数学运算、类型转换、数值判断等。以下是常用的内置函数(不需要导入模块)按类别归类说明:一、基础数值处理函数函数作用示例ab...
- 如何在Python中获取数字的绝对值?
-
Python有两种获取数字绝对值的方法:内置abs()函数返回绝对值。math.fabs()函数还返回浮点绝对值。abs()函数获取绝对值内置abs()函数返回绝对值,要使用该函数,只需直接调用:a...
- 【Python大语言模型系列】使用dify云版本开发一个智能客服机器人
-
这是我的第359篇原创文章。一、引言上篇文章我们介绍了如何使用dify云版本开发一个简单的工作流:【Python大语言模型系列】一文教你使用dify云版本开发一个AI工作流(完整教程)这篇文章我们将引...
- Python3.11版本使用thriftpy2的问题
-
Python3.11于2022年10月24日发布,但目前thriftpy2在Python3.11版本下无法安装,如果有使用thriftpy2的童鞋,建议晚点再升级到最新版本。...
- uwsgi的python2+3多版本共存(python多版本兼容)
-
一、第一种方式(virtualenv)1、首先,机器需要有python2和python3的可执行环境。确保pip和pip3命令可用。原理就是在哪个环境下安装uwsgi。uwsgi启动的时候,就用的哪个...
- 解释一下Python脚本中版本号声明的作用
-
在Python脚本中声明版本号(如__version__变量)是一种常见的元数据管理实践,在IronPython的兼容性验证机制中具有重要作用。以下是版本号声明的核心作用及实现原理:一、版本号...
- 除了版本号声明,还有哪些元数据可以用于Python脚本的兼容性管理
-
在Python脚本的兼容性管理中,除了版本号声明外,还有多种元数据可以用于增强脚本与宿主环境的交互和验证。以下是一些关键的元数据类型及其应用场景:一、环境依赖声明1.Python版本要求pyth...
- 今年回家没票了?不,我有高科技抢票
-
零基础使用抢票开源软件Py12306一年一度的抢票季就要到了,今天给大家科普一下一款软件的使用方法。软件目前是开源的,禁止用于商用。首先需要在电脑上安装python3.7,首先从官网下载对应的安装包,...
- 生猛!春运抢票神器成GitHub热榜第一,过年回家全靠它了
-
作者:车栗子发自:凹非寺量子位报道春节抢票正在如火如荼的进行,过年回家那肯定需要抢票,每年的抢票大战,都是一场硬战,没有一个好工具,怎么能上战场死锁呢。今天小编推荐一个Python抢票工具,送到...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python字典遍历 (54)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (60)
- python读取文件夹下所有文件 (59)
- java调用python脚本 (56)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)