服务通信:REST 和 gRPC
以同步且高效的方式连接服务
服务通信:REST 和 gRPC 是 CoddyKit 上的免费 PHP Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PHP Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PHP Academy 课程共包含 4 节课。
REST 与 gRPC
服务需要进行同步通信:请求发出后,返回一个响应。两种主流选择是基于 HTTP/JSON 的 REST和基于 HTTP/2 + 协议缓冲区的 gRPC。它们针对的目标不同——REST 注重覆盖范围和易读性,gRPC 注重速度和严格的契约。
本课将从 PHP 的角度介绍两者,以及应该如何选择。
REST:通用语言
REST 通过 URL 表示资源,并使用 HTTP 方法和状态码表达语义。它的优势包括工具普及、可缓存、可以使用 curl 调试,以及无需特殊客户端。它的不足包括 JSON 冗长、没有强制模式,并且只支持请求与响应。
对于公共接口和面向浏览器的端点,REST 几乎总是正确的选择。
调用 REST 服务
请使用符合 PSR-18 的 HTTP 客户端(此处使用 Guzzle)。始终设置连接超时和请求超时——对缓慢同级服务发起无上限的调用,可能耗尽您的 PHP-FPM 工作进程,并使故障级联扩散。
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$http = new Client([
'base_uri' => 'http://customers-svc/',
'connect_timeout' => 1.0, // never block forever on connect
'timeout' => 3.0, // total request budget
'http_errors' => false,
]);
$res = $http->get('customers/42', ['headers' => ['Accept' => 'application/json']]);
if ($res->getStatusCode() === 200) {
$customer = json_decode((string) $res->getBody(), true);
echo $customer['email'] . "\n";
}状态码就是契约
在服务间的 REST 通信中,HTTP 状态码就是您的错误协议。请有意识地处理它们:
2xx表示成功;4xx表示调用方出错(不要盲目重试);5xx或超时表示可以重试。- 使用
409表示冲突,使用422表示校验失败,使用429表示速率限制(遵守Retry-After)。
重试 400 只会浪费调用;采用退避策略重试 503 才是正确做法。
<?php
function isRetryable(int $status): bool {
return $status === 0 // timeout/connection error
|| $status === 429
|| ($status >= 500 && $status !== 501);
}
var_dump(isRetryable(503)); // true
var_dump(isRetryable(400)); // falsegRPC:契约优先且高性能
gRPC 使用协议缓冲区:您在 .proto 文件中定义服务和消息,然后生成强类型的客户端与服务器桩代码。借助 HTTP/2 和二进制协议缓冲区,它比 JSON 紧凑得多、延迟也低得多,并且支持流式传输。
syntax = "proto3";
package customers;
service Customers {
rpc GetCustomer (GetCustomerRequest) returns (Customer);
}
message GetCustomerRequest { string id = 1; }
message Customer {
string id = 1;
string email = 2;
int32 loyalty_points = 3;
}生成 PHP 桩代码
请安装 gRPC PHP 扩展和 protoc 插件,然后根据 .proto 生成客户端类。PHP 可以通过 ext-grpc 充当功能完整的 gRPC 客户端;运行原生 PHP gRPC 服务器通常需要 Roadrunner 或 Swoole。
pecl install grpc
composer require grpc/grpc google/protobuf
protoc --proto_path=. \
--php_out=./generated \
--grpc_out=./generated \
--plugin=protoc-gen-grpc=$(which grpc_php_plugin) \
customers.proto一次 gRPC 客户端调用
生成的桩代码会为您提供类型明确的请求和响应。一次 gRPC 调用会返回消息和一个 status 对象——在信任响应之前,务必先检查状态码。
<?php
require 'vendor/autoload.php';
use Customers\CustomersClient;
use Customers\GetCustomerRequest;
use Grpc\ChannelCredentials;
$client = new CustomersClient('customers-svc:50051', [
'credentials' => ChannelCredentials::createInsecure(),
]);
$req = (new GetCustomerRequest())->setId('42');
[$reply, $status] = $client->GetCustomer($req)->wait();
if ($status->code === \Grpc\STATUS_OK) {
echo $reply->getEmail(), "\n";
} else {
fwrite(STDERR, "gRPC error: {$status->details}\n");
}模式演进
只要遵守相关规则,协议缓冲区就能支持向前和向后兼容:
- 绝不要重复使用或更改字段编号。请使用新编号添加新字段。
- 将删除的字段标记为
reserved,以防该编号被重新使用。 - 旧客户端会忽略未知字段;缺失字段会采用其类型的默认值。
JSON/REST 不会免费提供这些能力——您必须通过约定来保证兼容性(理想情况下,还应配合共享的 OpenAPI 模式和契约测试)。
message Customer {
string id = 1;
string email = 2;
reserved 3; // old 'loyalty_points', never reuse 3
reserved "loyalty_points";
string display_name = 4; // new field, safe additive change
}流式传输
gRPC 支持四种调用类型;REST 原生只支持第一种:
- 一元调用——一个请求,一个响应。
- 服务器流式传输——一个请求,一系列响应(例如实时更新)。
- 客户端流式传输——一系列请求,一个响应(例如批量上传)。
- 双向流式传输——双方同时进行流式传输。
如果您的用例涉及推送或长期数据流,gRPC 流式传输比轮询 REST 端点更合适。
传播上下文与截止时间
同步调用会形成调用链,因此每个请求都必须携带两项内容:用于端到端追踪的关联 ID/追踪 ID,以及防止缓慢末端服务让整条链路一直挂起的截止时间。gRPC 原生支持截止时间;在 REST 中,您可以通过向下游传递不断缩减的超时预算来模拟这一机制。
<?php
// REST: shrink the remaining budget as the call chain deepens
function forwardHeaders(array $incoming, float $remainingMs): array {
return [
'X-Correlation-Id' => $incoming['X-Correlation-Id'] ?? bin2hex(random_bytes(8)),
// downstream must finish within what's left of our budget
'X-Timeout-Ms' => (string) max(0, (int) $remainingMs),
];
}
print_r(forwardHeaders(['X-Correlation-Id' => 'trace-9'], 1500));如何选择
一种实用的选择指南:
- 对于公共接口或合作伙伴接口、浏览器客户端、简单 CRUD、便捷调试以及广泛的缓存支持,请选择REST。
- 对于内部的高流量、低延迟服务间调用、严格的类型化契约以及流式传输,请选择gRPC。
许多系统会同时使用两者:在网关之后的服务之间使用 gRPC,在面向外部世界的边缘使用 REST。不要强迫一种工具承担另一种工具的职责。
快速检查
将协议与用例匹配。
回顾
同步服务通信:
- REST/JSON——通用、易调试、可缓存;状态码就是契约;务必设置超时。
- gRPC/协议缓冲区——契约优先、紧凑、高速并支持流式传输;生成类型明确的 PHP 桩代码。
- 根据状态码或 gRPC 状态码判断是否可重试;绝不要重试客户端错误。
- 以增量方式演进模式——绝不要重复使用协议缓冲区字段编号。
- 在边缘使用 REST、在内部服务之间使用 gRPC,是一种常见且可靠的划分方式。
下一步:通过网关和服务发现,为所有这些服务进行路由和定位。
用 AI 导师学习 PHP — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 49
- 课程
- 195
常见问题解答
「服务通信:REST 和 gRPC」课时是免费的吗?
是的 — 「服务通信:REST 和 gRPC」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PHP Academy 课程的其余内容,请升级到 CoddyKit PRO。 PHP Academy 课程共包含 4 节课。
「服务通信:REST 和 gRPC」这节课中我会学到什么?
以同步且高效的方式连接服务 你通过在浏览器中直接运行的动手代码来练习 PHP Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 PHP Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 PHP Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「服务通信:REST 和 gRPC」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 PHP Academy 课中编写并运行代码吗?
能。每节 PHP Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 从单体架构到微服务
- 服务通信:REST 和 gRPC
- API 网关与服务发现
- 韧性:熔断器与重试