英文:
Firebase HTTP v1 API and no batch send anymore?
问题
我收到了一封来自Google的电子邮件,通知我以下服务将被停用:
"将多个发送请求包含在单个HTTP请求中发送到FCM,称为批量发送。"
建议的方法是:
"通过已经优化了扩展性能的HTTP v1 API发送消息。"
在这个页面上:
https://firebase.google.com/support/faq#fcm-23-deprecation
提到了:
https://fcm.googleapis.com/batch
"在2024年6月21日后,对该端点的请求将开始失败。"
建议的操作是:
"迁移到标准的HTTP v1 API发送方法,该方法支持HTTP/2多路复用。"
现在,我对此事有一个问题。
目前,我正在使用PHP和cURL通过fcm.googleapis.com/batch发送FCM消息。由于明年将不再起作用,我已经停用了这种方法,现在我已经将消息发送过程放入了一个foreach(while)循环中。这意味着,例如,如果我发送400条FCM消息,我将连续触发或联系以下URL 400次/行:
https://fcm.googleapis.com/v1/projects/my-app/messages:send
这是否是预期的行为,而不是问题?我的项目已经使用了HTTP/2。我只是想知道这是否是正确的方法,因为我无法想象这是否比批量发送或一次性发送更好。感谢您的澄清。
如果您需要进一步的帮助,请告诉我。
这是我的foreach代码:
foreach ($deviceTokens as $token) {
$data = json_encode(array(
"message" => array(
"token" => $token,
"notification" => array(
"message_title" => "Test",
"message_body" => "Test",
"website_link" => "example.com",
"notification_type" => "message",
"image" => "example.com/test.jpg"
)
)
));
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://fcm.googleapis.com/v1/projects/my-app/messages:send',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $request,
CURLOPT_HTTPHEADER => array(
'Content-Type: multipart/mixed; boundary="subrequest_boundary"',
'Authorization: Bearer ' . $accessToken
),
));
$response = curl_exec($curl);
echo $response . '<br />';
curl_close($curl);
}
希望这对您有所帮助。
英文:
I received an email from Google informing me that the following service is being decommissioned:
"Including multiple send requests in a single HTTP request to FCM known as Batch Send."
The recommended approach is to:
"Send messages via the HTTP v1 API, which has been optimized for fanout performance."
On this page:
https://firebase.google.com/support/faq#fcm-23-deprecation
it is mentioned that:
https://fcm.googleapis.com/batch
"Requests to the endpoint will start failing after 6/21/2024."
The recommended action is to:
"Migrate to the standard HTTP v1 API send method, which supports HTTP/2 for multiplexing."
Now, I have a question regarding this matter.
Currently, I am sending FCM messages using PHP and cURL via fcm.googleapis.com/batch. Since this will no longer work next year, I have already discontinued this method and now I have put the message sending process in a foreach (while) loop. This means that if, for example, I send 400 FCM messages, I will trigger or contact the following URL 400 times in succession / row:
https://fcm.googleapis.com/v1/projects/my-app/messages:send
Is this the intended behavior and not a problem? My project already uses HTTP/2. I'm just wondering if this is the correct approach, as I can't imagine that it is better than sending in batches or all at once. Thank you for clarifying.
Please let me know if you need any further assistance.
Here my foreach code:
foreach ($deviceTokens as $token) {
$data = json_encode(array(
"message" => array(
"token" => $token,
"notification" => array(
"message_title" => "Test",
"message_body" => "Test",
"website_link" => "example.com",
"notification_type" => "message",
"image" => "example.com/test.jpg"
)
)
));
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://fcm.googleapis.com/v1/projects/my-app/messages:send',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $request,
CURLOPT_HTTPHEADER => array(
'Content-Type: multipart/mixed; boundary="subrequest_boundary"',
'Authorization: Bearer ' . $accessToken
),
));
$response = curl_exec($curl);
echo $response . '<br />';
curl_close($curl);
}
答案1
得分: 1
如果您想继续使用原始的cURL请求,您可以查看异步的curl_multi_*
函数 - 说实话,我已经有一段时间没有直接使用cURL了,所以我将只参考PHP文档,这里还提供了一个用作模板的用法示例。
然而,我建议采用的方式是使用google/auth
PHP库,Guzzle HTTP客户端进行身份验证和向FCM API发送请求,并使用Guzzle Promises异步执行请求。
更糟糕的是,您不仅需要使用HTTP V1端点,还需要使用HTTP V1 FCM消息格式。下面的代码显示了您原始帖子中的消息如何更改。
所以,这是我会如何从头开始使用Composer在一个单独的脚本中完成的:
# 初始化项目
mkdir myproject
cd myproject
composer require google/auth
<?php
# run.php
# 此示例使用通过`GOOGLE_APPLICATION_CREDENTIALS`环境变量公开的Google应用凭据
# 有关更多替代身份验证请求的方法,请参阅https://github.com/googleapis/google-auth-library-php/blob/main/README.md
declare(strict_types=1);
require 'vendor/autoload.php';
use Google\Auth\ApplicationDefaultCredentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise;
### 创建一个用于验证对FCM API端点的请求的Guzzle客户端
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/credentials.json');
// https://developers.google.com/identity/protocols/oauth2/scopes#fcm
$scopes = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/firebase.messaging',
];
// 创建中间件
$middleware = ApplicationDefaultCredentials::getMiddleware($scopes);
$stack = HandlerStack::create();
$stack->push($middleware);
$client = new Client([
'handler' => $stack,
'auth' => 'google_auth'
]);
### 设置消息
$deviceTokens = [/* ... */];
$messages = [];
foreach ($deviceTokens as $token) {
$messages[] = [
'token' => $token,
'notification' => [
'title' => 'Notification Title',
'body' => 'Notification Body',
'image' => 'https://example.com/test.jpg',
],
'webpush' => [
'fcm_options' => [
'link' => 'https://example.com',
],
],
];
}
### 创建消息请求的Promise
$promises = function() use ($client, $messages) {
foreach ($messages as $message) {
yield $client->requestAsync('POST', 'https://fcm.googleapis.com/v1/projects/my-app/messages:send', [
'json' => ['message' => $message],
]);
}
};
### 创建响应处理程序
$handleResponses = function (array $responses) {
foreach ($responses as $response) {
if ($response['state'] === Promise\PromiseInterface::FULFILLED) {
// $response['value']是\Psr\Http\Message\RequestInterface的实例
echo $response['value']->getBody();
} elseif ($response['state'] === Promise\PromiseInterface::REJECTED) {
// $response['reason']是一个异常
echo $response['reason']->getMessage();
}
}
};
Promise\Utils::settle($promises())
->then($handleResponses)
->wait();
我使用一个有效和一个无效的注册令牌测试了这个脚本,并获得了以下结果:
❯ php run.php
客户端错误:`POST https://fcm.googleapis.com/v1/projects/beste-firebase/messages:send` 导致`400 Bad Request`响应:
{
"error": {
"code": 400,
"message": "注册令牌无效",
"stat (truncated...)"
{
"name": "projects/beste-firebase/messages/e39032c2-866d-4263-83e6-b1ce3770dfe6"
}
或者,如果您不想手动完成所有这些操作,您还可以使用(非官方的)Firebase Admin PHP SDK,在其最新的7.5.0版本中切换到了这个确切的方法(免责声明:我是维护者,显然是有偏见的):
# 初始化项目
mkdir myproject
cd myproject
composer require kreait/firebase-php
<?php
# run.php
# 同样,此示例使用通过`GOOGLE_APPLICATION_CREDENTIALS`环境变量公开的Google应用凭据。SDK会自动选择它们。
declare(strict_types=1);
require 'vendor/autoload.php';
use Kreait\Firebase\Factory;
### 初始化FCM
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/credentials.json');
$fcm = (new Factory())->createMessaging();
### 设置消息(没有目标)
$deviceTokens = [/* ... */];
$message = [
'notification' => [
'title' => 'Notification Title',
'body' => 'Notification Body',
'image' => 'https://example.com/test.jpg',
],
'webpush' => [
'fcm_options' => [
'link' => 'https://example.com',
],
],
];
### 将消息发送到给定的令牌
$reports = $fcm->sendMulticast($message, $deviceTokens);
# https://firebase-php.readthedocs.io/en/stable/cloud-messaging.html#send-messages-to-multiple-devices-multicast
# 更详细地介绍了您可以使用报告做什么
希望这可以帮助到您!
英文:
If you'd like to continue raw cURL requests, you could have a look at the asynchronous curl_multi_*
functions - truth be told, it's been a while since I've used cURL directly, so I will just refer to the PHP docs which also provide a usage example that you could use as a template.
However, the way I would recommend to take is using the google/auth
PHP Library, the Guzzle HTTP Client to authenticate and send requests to the FCM API, and Guzzle Promises to execute the requests asynchronously.
To make things "worse", you not only have to use the HTTP V1 endpoint, but the HTTP V1 FCM message format as well. The code below shows how the message in your original post would change.
So, here's how I would do it from scratch in a single script with the help of Composer:
# Initialize the project
mkdir myproject
cd myproject
composer require google/auth
<?php
# run.php
# This example uses Google Application Credentials exposed via the
# `GOOGLE_APPLICATION_CREDENTIALS` environment variable
# See https://github.com/googleapis/google-auth-library-php/blob/main/README.md
# for more alternative ways to authenticate requests
declare(strict_types=1);
require 'vendor/autoload.php';
use Google\Auth\ApplicationDefaultCredentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise;
### Create a Guzzle client that authenticates requests to the FCM API Endpoints
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/credentials.json');
// https://developers.google.com/identity/protocols/oauth2/scopes#fcm
$scopes = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/firebase.messaging',
];
// create middleware
$middleware = ApplicationDefaultCredentials::getMiddleware($scopes);
$stack = HandlerStack::create();
$stack->push($middleware);
$client = new Client([
'handler' => $stack,
'auth' => 'google_auth'
]);
### Setup the messages
$deviceTokens = [/* ... */];
$messages = [];
foreach ($deviceTokens as $token) {
$messages[] = [
'token' => $token,
'notification' => [
'title' => 'Notification Title',
'body' => 'Notification Body',
'image' => 'https://example.com/test.jpg',
],
'webpush' => [
'fcm_options' => [
'link' => 'https://example.com'
],
],
];
}
### Create message request promises
$promises = function() use ($client, $messages) {
foreach ($messages as $message) {
yield $client->requestAsync('POST', 'https://fcm.googleapis.com/v1/projects/my-app/messages:send', [
'json' => ['message' => $message],
]);
}
};
### Create response handler
$handleResponses = function (array $responses) {
foreach ($responses as $response) {
if ($response['state'] === Promise\PromiseInterface::FULFILLED) {
// $response['value'] is an instance of \Psr\Http\Message\RequestInterface
echo $response['value']->getBody();
} elseif ($response['state'] === Promise\PromiseInterface::REJECTED) {
// $response['reason'] is an exception
echo $response['reason']->getMessage();
}
}
};
Promise\Utils::settle($promises())
->then($handleResponses)
->wait();
I tested this script with one valid and one invalid registration token and got this result:
❯ php run.php
Client error: `POST https://fcm.googleapis.com/v1/projects/beste-firebase/messages:send` resulted in a `400 Bad Request` response:
{
"error": {
"code": 400,
"message": "The registration token is not a valid FCM registration token",
"stat (truncated...)
{
"name": "projects/beste-firebase/messages/e39032c2-866d-4263-83e6-b1ce3770dfe6"
}
Or, if you don't want to do this all manually, you could also use the (unofficial) Firebase Admin PHP SDK, which switch to this exact approach
in its latest 7.5.0 release. (Disclaimer: I'm the maintainer and obviously biased):
# Initialize the project
mkdir myproject
cd myproject
composer require kreait/firebase-php
<?php
# run.php
# Again, this example uses Google Application Credentials exposed via the
# `GOOGLE_APPLICATION_CREDENTIALS` environment variable. The SDK picks
# them up automatically.
declare(strict_types=1);
require 'vendor/autoload.php';
use Kreait\Firebase\Factory;
### Initialize FCM
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/credentials.json');
$fcm = (new Factory())->createMessaging();
### Setup the message (without target)
$deviceTokens = [/* ... */];
$message = [
'notification' => [
'title' => 'Notification Title',
'body' => 'Notification Body',
'image' => 'https://example.com/test.jpg',
],
'webpush' => [
'fcm_options' => [
'link' => 'https://example.com'
],
],
];
### Send the message to the given tokens
$reports = $fcm->sendMulticast($message, $deviceTokens);
# https://firebase-php.readthedocs.io/en/stable/cloud-messaging.html#send-messages-to-multiple-devices-multicast
# goes more into detail what you can do with the reports
I hope this helps!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论