享受PHP与Go的强大合体【RoadRunner】的乐趣!

我们将通过大量的 Go 和 PHP 经验告诉你,如何用它解决实际的开发问题,以及我们如何把它变成一个工具,来消除与 PHP 死亡模型 相关的一些问题

享受它的乐趣吧!

在过去的十年中,我们一直在为 财富 500 强公司 以及用户人数不超过 500 人的企业开发应用程序。 一直以来,我们的工程师主要使用 PHP 来开发后端。 但是两年前,出现了一些问题不仅严重影响了我们的产品性能,还影响了它们的可扩展性——因此我们将 Golang (Go) 引入了我们的技术栈。

几乎同时,我们发现 Go 不仅允许我们创建更大的应用程序,并且能够将性能提高多达 40 倍。 有了它,我们能够扩展使用 PHP 编写的现有产品,并通过结合两种语言的优势来改进它们。

我们将通过大量的 Go 和 PHP 经验告诉你,如何用它解决实际的开发问题,以及我们如何把它变成一个工具,来消除与 PHP 死亡模型 相关的一些问题。

常规 PHP 开发环境

在讲述 Go 如何改善 PHP 死亡模型前,先了解一下常规 PHP 开发环境。

通常,应用运行于 nginx 和 PHP-FPM 上。nginx 处理静态请求,而动态请求则被重定向给 PHP-FPM,并由其执行 PHP 代码。也许你用的是 Apache 和 mod_php,但是他们原理相同,运行起来只有细微的差别。

看看 PHP-FPM 是如何执行代码的。当收到请求,PHP-FPM 初始化 PHP 子进程,并将请求的详细信息转发给它,作为其状态的一部分(_GET, _POST, _SERVER 等)。

在 PHP 脚本执行期间,状态将无法更改,因此只能通过一种方式获取一组新的输入数据:清除进程内存并再次初始化它。

这种性能模型有许多优点。你不需要太担心内存消耗,所有进程都是完全隔离的,如果其中一个进程「死亡」,它将自动重新创建,并且不会影响其他进程。但是,当你尝试扩展应用程序时,这种方式会有缺点产生。

典型 PHP 环境的缺点和低效性

如果你从事 PHP 的专业开发,那么你就知道从哪儿开始创建一个新项目——选择框架。它是一个用于依赖注入、ORM、转化和模板方法的库。当然,所有用户输入的数据都可以方便地放在一个对象中(Symfony / HttpFoundation 或者 PSR-7)。这些框架很棒!

但一切都有它的代价。在任何企业框架中,为了处理一个简单的用户请求或访问数据库,您必须加载至少几十个文件,创建许多类,并解析多个配置。但最糟糕的是,在每个任务完成后,您需要重置所有内容并重新启动:您刚刚启动的所有代码都将变得无用,在它的帮助下,您将无法处理另一个请求。把这件事告诉任何用其他语言编写的程序员——你会看到他脸上的困惑。

多年来,PHP工程师一直在寻找解决此问题的方法,他们使用了延迟加载技术、微帧、优化库、缓存等。但最终,您仍然必须放弃整个应用程序,重新开始*(译者注:随着PHP7.4中预加载的出现,这个问题将得到部分解决)

一个PHP进程能处理多个请求吗?

您可以编写持续时间超过几分钟的PHP脚本(最多几小时或几天):例如Cron任务、CSV解析器、队列处理程序。所有这些工作遵循一个模式:他们获取一条任务,处理完它,然后获取下一个任务。代码常驻在内存中,因此避免了额外的操作来加载框架和应用程序,节约了宝贵时间。

但是开发长时间运行的脚本并不是那么容易。任何错误都会杀死进程,内存溢出会导致崩溃,而且不能用F5来调试程序了。

自PHP 7后情况有所改善:可靠的垃圾收集器出现了,它变得更容易处理错误,内核的扩展可以避免内存泄漏。是的,工程师仍然需要仔细处理内存并记住代码中的状态的问题(有哪一种语言能让你可以不关注这些事情呢?)当然,在PHP 7中,惊喜并不多。

是否可以采用一种 常驻 PHP 脚本的模型,将其用于处理 HTTP 请求等更琐碎的任务,从而消除对每个请求都从头开始下载所有内容的需要?

要解决这个问题,首先需要实现一个服务器应用程序,该应用程序可以接收 HTTP 请求并将它们逐个重定向到 PHP worker,而不是每次都杀死它。

我们知道我们可以用纯 PHP(PHP-PM)或 C 扩展(Swoole)编写 web 服务器。尽管每种方法都有其优点,但这两种选择都不适合我们 —— 我想要更多的东西。我们需要的不仅仅是一个 web 服务器 —— 我们希望得到一个解决方案,可以使我们避免与 PHP 中的「重启动」相关的问题,同时可以轻松地为特定的应用程序进行调整和扩展。也就是说,我们需要一个应用服务器。

Go 可以帮助解决这个问题吗?我们知道它可以,因为这种语言将应用程序编译成单个的二进制文件; 它是跨平台的; 使用自己的并行处理模型(并发)和用于处理 HTTP 的库; 最后,我们可以把更多的开源库集成到我们的程序中。

合并两种编程语言遇到的困难

首先,有必要确定两个或多个应用程序之间如何相互通信。

例如,使用 Alex Palaestras 的 go-php 库, 可以实现 PHP 和 Go 进程 (如 Apache 中的 mod_php) 之间的内存共享。但是这个库的功能限制了我们使用它解决问题。

我们决定使用另一种更常见的方法:通过使用 sockets / pipelines 来构建进程之间的交互。 这种方法在过去十年中已经证明了其可靠性,并且在操作系统级别得到了很好的优化。

首先,我们创建了一个简单的二进制协议,用于在进程之间交换数据和处理传输错误。在其最简单的形式中, 这种类型的协议类似于 一个具有固定大小的 packet 头 (在我们的示例中为 17 个字节) 的 netstring ,其中包含的信息有 packet 的类型,其大小和二进制掩码的信息,用来检查数据的完整性。

在 PHP 端,我们使用了 pack 函数 ,在 Go 端,使用了 编码 / 二进制 库。

有一个协议对我们来说有点过时,我们添加了直接 从 PHP 调用 net / rpc Go 服务 的功能。 这个功能在后面的开发中对我们有很大的帮助,因为我们可以轻松地将 Go 库集成到 PHP 应用程序中。这项工作的结果可以在我们的另一个开源产品 Goridge 中看到。

在多个 PHP Worker 之间分配任务

在交互机制实现之后,我们开始思考如何更好地将任务转移到 PHP 进程中。当任务到达时,应用服务器必须选择一个空闲的 worker 来执行它。 如果 worker 进程因错误而终止或「死亡」,我们将清除它并创建一个新的。 如果 worker 进程成功执行,我们会将它返回到可用于执行任务的 worker 池中。

享受PHP与Go的强大合体【RoadRunner】的乐趣!

php入门到就业线上直播课:进入学习API 文档、设计、调试、自动化测试一体化协作工具:点击使用

为了存储活跃的 worker 进程池,我们使用了一个 缓冲通道 , 为了从池中清除意外「死亡」的工作进程,我们添加了一种跟踪错误和 worker 进程状态的机制。

最终,我们得到了一个可以运行的 PHP 服务器,它能够处理任何以二进制形式呈现的请求。

为了让我们的应用程序作为 web 服务器开始工作,我们必须选择一个可靠的 PHP 标准来处理任何传入的 HTTP 请求。在我们的例子中,我们只需将简单的 net / http 请求从 Go 转换 为 PSR-7 格式,这样它就可以与目前大多数可用的 PHP 框架兼容。

由于 PSR-7 被认为是不可变的(有人会说在技术上不是),开发人员必须编写那些在原则上不将请求视为全局实体的应用程序。这完全符合 PHP 常驻进程的概念。我们的最终实现(尚未收到名称)如下所示:

享受PHP与Go的强大合体【RoadRunner】的乐趣!

RoadRunner – 高 – 性能 PHP 应用服务器

我们的第一个测试任务是一个 API 后端,在该后端上,会周期性地出现不可预测的突发请求(比平时更频繁)。虽然在大多数情况下 nginx capabilities 是足够的,但是我们经常因为无法在预期的负载增加下快速平衡系统而遇到 502 错误。

为解决此问题,我们在 2018 年初部署了第一台 PHP / Go 应用服务器。并立即取得了惊人的效果!我们不仅完全消除了 502 错误,并且还将服务器的数量减少了三分之二,节省了大量资金并解决了令工程师和产品经理头痛的问题。

在年中的时候,我们改进了我们的方案,在 MIT 许可下将其发布在 GitHub 上,并命名为 RoadRunner, 从而强调了它惊人的速度和效率。

RoadRunner 是如何改进你的开发堆栈的

RoadRunner 的使用允许我们在 Go 端使用中间件 net/http ,甚至在请求进入 PHP 之前进行 JWT 验证,以及在 Prometheus 中处理 WebSocket 和全局聚合状态。

由于内置的 RPC,你可以在不编写扩展包的情况下,在 PHP 中打开任何 Go 库的 API。更重要的是,使用 RoadRunner,你可以部署不同于 HTTP 的新服务器。示例包括在 PHP 中运行 AWS Lambda 处理器 , 创建强大的队列 选择器, 甚至将 gRPC 添加到我们的应用程序中。

同时使用 PHP 和 Go ,对解决方案有了稳定的提升,在一些测试中将应用程序性能提高了 40 倍,改进了调试工具,实现了与 Symfony 框架的集成,并添加了对 HTTPS、HTTP/2、插件和 PSR-17 的支持。

结论

有些人仍然被过时的 PHP 概念所束缚,认为 PHP 是一种缓慢、繁琐的语言,只适合在 WordPress 下编写插件。这些人甚至还说 PHP 有这样一个限制:当应用程序变得足够大时,你必须选择一种更「成熟」的语言,并重写多年积累的代码库。

对于这些问题,我的回答是:再想一想。我们相信只是你自己为 PHP 设置了一些限制。你可以用一生的时间从一种语言迁移到另一种语言,试图找到与你的需求完美结合的语言,或者你可以将语言视为工具。像 PHP 这样的语言,它的假想缺陷可能是其成功的真正原因。如果你将它与另一种语言(如 Go)相结合,那么你将创造出比只使用一种语言更强大的产品。

在交替使用过Go和PHP之后,我们可以说我们很喜欢它们。我们不打算牺牲其中一个来换取另一个,相反,我们会想办法从这个双重架构中获得更多收益。

原文地址:https://sudonull.com/post/6470-RoadRunne…

译文地址:https://learnku.com/php/t/61733

PHP8.1新特性大讲解之Enums枚举

原创2021-11-10 15:09:312336 + php学习QQ群(点击入群)

本文系翻译,原文地址:https://stitcher.io/blog/php-enums

PHP 8.1:枚举

它们终于来了——PHP 8.1中将添加对枚举的内置支持!有些人可能认为他们早就应该这样做了,但你没有听到我的抱怨;我很高兴他们做到了!这篇文章致力于深入研究新添加的功能。

像往常一样,在我的 PHP 功能帖子中,我们首先对枚举的外观进行高级概述:

enum Status
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
}登录后复制

枚举的好处是它们代表了一组常量值,但最重要的是这些值可以被键入,如下所示:

class BlogPost
{
public function __construct(
public Status $status,
) {}
}登录后复制

在这个例子中,创建一个枚举并将其传递给 aBlogPost看起来像这样:

$post = new BlogPost(Status::DRAFT);登录后复制

这就是基础知识,正如您所看到的,它们一点也不复杂。虽然有很多旁注需要做,让我们深入看看枚举!

#枚举方法

枚举可以定义方法,就像类一样。这是一个非常强大的功能,尤其是与match运算符结合使用时:

enum Status
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;

public function color(): string
{
return match($this)
{
Status::DRAFT => 'grey',
Status::PUBLISHED => 'green',
Status::ARCHIVED => 'red',
};
}
}登录后复制

方法可以像这样使用:

$status = Status::ARCHIVED;
$status->color(); // 'red'登录后复制

静态方法也是允许的:

enum Status
{
// …

public static function make(): Status
{
// …
}
}登录后复制

您还可以self在枚举中使用:

enum Status
{
// …

public function color(): string
{
return match($this)
{
self::DRAFT => 'grey',
self::PUBLISHED => 'green',
self::ARCHIVED => 'red',
};
}
}登录后复制

#枚举接口

枚举可以实现接口,就像普通类一样:

interface HasColor
{
public function color(): string;
}
enum Status implements HasColor
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;

public function color(): string { /* … */ }
}登录后复制

#枚举值——又名“支持枚举”

枚举值在内部由对象表示,但您可以根据需要为它们赋值;这对于例如很有用。将它们序列化到数据库中。

enum Status: string
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
}登录后复制

注意枚举定义中的类型声明。它表示所有枚举值都属于给定类型。您也可以将其设为int. 请注意, onlyint和string允许作为枚举值。

enum Status: int
{
case DRAFT = 1;
case PUBLISHED = 2;
case ARCHIVED = 3;
}登录后复制

类型枚举的技术术语称为“支持枚举”,因为它们由更简单的值“支持”。如果您决定分配枚举值,则所有案例都应该有一个值。你不能混合和匹配它们。没有“支持”的枚举被称为“纯枚举”。

#带接口的支持枚举

如果您将支持的枚举和接口结合使用,则枚举类型必须直接位于枚举名称之后,implements关键字之前。

enum Status: string implements HasColor
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';

// …
}登录后复制

#序列化支持的枚举

如果您要为枚举案例分配值,您可能需要一种方法来序列化和反序列化它们。序列化它们意味着您需要一种访问枚举值的方法。这是通过只读公共属性完成的:

$value = Status::PUBLISHED->value; // 2登录后复制

可以使用以下方法从值中恢复枚举:Enum::from

$status = Status::from(2); // Status::PUBLISHED登录后复制

如果传递了未知值tryFrom,还有一个返回null。如果你会使用from会有一个例外。

$status = Status::from('unknown'); // ValueError
$status = Status::tryFrom('unknown'); // null登录后复制

请注意,您还可以在枚举上使用内置函数serialize和unserialize函数。此外,您可以json_encode与支持的枚举结合使用,其结果将是枚举值。可以通过实现来覆盖此行为JsonSerializable。

#列出枚举值

您可以使用静态方法获取枚举中所有可用案例的列表:Enum::cases()

Status::cases();
/* [
Status::DRAFT,
Status::PUBLISHED,
Status::ARCHIVED
] */登录后复制

请注意,此数组包含实际的枚举对象:

array_map(
fn(Status $status) => $status->color(),
Status::cases()
);登录后复制

#枚举是对象

我已经提到枚举值表示为对象,实际上它们是单例对象。这意味着您可以像这样与它们进行比较:

$statusA = Status::PENDING;
$statusB = Status::PENDING;
$statusC = Status::ARCHIVED;
$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true登录后复制

#枚举作为数组键

由于枚举值实际上是对象,因此目前无法将它们用作数组键。以下将导致错误:

$list = [
Status::DRAFT => 'draft',
// …
];登录后复制

有一个 RFC来改变这种行为,但它还没有被投票。

这意味着您只能使用枚举作为SplObjectStorage和 中的键WeakMaps。

#性状

枚举可以像类一样使用特征,但有更多限制。不允许覆盖内置的枚举方法,并且它们不能包含类属性 – 枚举中禁止使用这些属性。

#反射和属性

正如预期的那样,添加了一些反射类来处理枚举:ReflectionEnum、ReflectionEnumUnitCase和ReflectionEnumBackedCase。还有一个新enum_exists功能,正如它的名字所暗示的那样。

就像普通的类和属性一样,枚举及其案例可以使用attributes进行注释。请注意,TARGET_CLASS过滤器还将包括枚举。

最后一件事:枚举也有一个只读属性,RFC 提到这是一个实现细节,应该只用于调试目的。不过还是值得一提的。$enum->name

php入门到就业线上直播课:立即学习全程直播 + 实战授课 + 边学 + 边练 + 边辅导

以上就是PHP8.1新特性大讲解之Enums枚举的详细内容,更多请关注钦钦技术栈其它相关文章!

转载至:php中文网【www.php.cn】

版权声明:本文(即:原文链接:https://www.qin1qin.com/catagory/26627/)内容由互联网用户自发投稿贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 630367839@qq.com 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022-09-27 8:46:21
下一篇 2022-09-27 8:46:49

软件定制开发公司

相关阅读

发表回复

登录后才能评论
通知:禁止投稿所有关于虚拟货币,币圈类相关文章,发现立即永久封锁账户ID!