你知道吗?月球上安装了反射器?阿波罗任务将这些反射器留在月球表面,以便地球上的科学家向其发射激光。通过测量激光往返月球所需的时间,科学家可以使用毫米级的精度计算出地球与月球之间的距离。这揭示了月球正在远离地球。这个实验被称为月球激光测距实验,我们将对其进行软件模拟!
看看我在 Filament 应用中完成的仪表盘
在这里,你可以看到我运行反射器应用实例的域。我将使用来自 fly.io 的网络妙术来测量发送简单消息来回所需的时间。
你能从这个数据计算出我在哪里住吗?(请不要。)
相反,我们来揭开盖子
我的 Filament 部分
我的基本目标是这样的:在我的数据库中,我保存有关Fly 域区和我反射器应用正在运行的机器数据。Machine 模型包含一个 machine_id
(Fly.io 上的机器 ID)和 region_id
,用于 Region
关系。稍后我们需要 machine_id
用于网络部分。
对于每个机器,我想要在仪表盘页面上使用 标题小部件 显示一个小部件。但是遇到了一些问题:在 Filament 中要使标题小部件正常工作,您需要配置您将使用的组件的类名。为每个机器实时制作一个组件并与每个组件共享其所属的机器似乎很困难并且很具破坏性。
因此,我使用 php artisan make:filament-page <page-name-here>
创建了自己的 Filament 页面,并通过一个 foreach
循环遍历机器并为每个机器显示一张卡片。小贴士:只需使用 <x-filament::card></x-filament::card>
即可利用 Filament 优秀的样式。
然后,我只需要添加机器。因为我非常懒惰,所以我在 ListMachines
指数页上添加了一个 操作 来获取所有机器并将它们添加到数据库中。如下所示
Actions\Action::make('fetchMachines')
->action(function() {
$response = Http::withToken(env('FLY_AUTH_TOKEN'))
->get('https://api.machines.dev/v1/apps/fly-filament-satellite/machines');
$machines = json_decode($response->body());
//delete machines with different ID
$machineIDs = array_column($machines, 'id');
Machine::whereNotIn('machine_id', $machineIDs)->delete();
// create the new machines
foreach($machines as $machine)
{
$attributes = ['machine_id' => $machine->id, 'region_id' => Region::where('iata_code', $machine->region)->first()->id];
Machine::firstOrCreate($attributes);
}
})
这会调用 Fly.io 机器 API 以获取应用程序 'fly-filament-satellite' 的所有机器,这是我们的 reflector
应用程序。要在机器 API 上进行身份验证,您需要一个身份验证令牌。您可以在终端中运行 fly auth token
获取此令牌。当我将其用于 Laravel 应用程序时,我通常将其保存在本地环境变量中,或者在 Fly.io 上运行的应用程序中作为 Fly Secret。
最近的区域
在我的仪表板上显示最近的区域。这是请求在其中的区域被接受并路由的区域。这可以是 Fly.io 的区域 中的任何一个,您的应用程序不需要在那个地区运行。请求将进入位于该地区的 Fly.io 网络并路由到您的应用程序有机器运行的最接近的区域。
我最近的区域是伦敦,英国。在上面的示例中,只有一个区域中有应用程序正在运行:荷兰的阿姆斯特丹。当我的请求到达伦敦的边缘节点时,它将请求发送到位于阿姆斯特丹的应用程序,然后响应将通过伦敦的边缘节点返回给我。
每当边缘节点将请求传递给应用程序实例时,它都会添加一些包含有用信息的标题。
在 fly-request-id
的末尾有 lhr
。这意味着我的请求进入 Fly.io 网络在伦敦。由于所有的 Fly 区域都已经在数据库中,我们可以轻松地将区域代码连接到相应的区域。太棒了!
延迟
这就是你一直等待的射击激光月亮部分。别担心,这并不涉及火箭科学。
让我们首先看看我们的 reflector
应用程序:该应用程序将对我们所投放的每个请求简单响应 OK。但这不是全部:我们需要能够控制处理我们的请求的确切区域。我们不想让 Fly 代理为我们选择最近的区域!
动态请求路由™️ 进入:如果我们的反射器应用程序在响应中设置一个 fly-replay
标题,Fly 代理将重放原始请求而不是将响应发送给客户端。如果在fly-replay
标题中放置一个区域,原始请求将在指定的区域中重放。 要让我们的反射器应用程序知道在哪里重放请求,我们将使用路由参数。所以,如果我们向 https://fly-filament-satellite.fly.dev/api/ping/nrt
发送请求,我们希望请求被重放到东京(nrt)。
让我们更详细地看看这个
离我最近的区域是伦敦,所以请求将会进入伦敦的网络,但最近的应用实例在阿姆斯特丹。以下是会发生的情况:位于伦敦的边缘节点将请求转发到位于阿姆斯特丹的区域
,因为这是地理上最短的距离。在那里,应用会发现我们希望通过nrt
路由参数将请求重放到东京。应用将发送包含此头部的响应:fly-replay: region=nrt
。飞代理将接收到这个响应,并看到响应不应发送给客户端。相反,它将重放原始请求到nrt
区域。
一个小问题:如果我们总是设置一个fly-replay
头部,我们会创建一个无限循环。幸运的是,对于我们的请求,当请求被重放时,将会有一个fly-replay-src
头部来显示请求已经重放,以及它从哪里开始的。因此,如果请求上存在该头部,我们将跳过fly-replay头部,这样请求就不会被重放。真是太棒了!
以下是我reflector
应用上的唯一控制器
// Route::get('/ping/{region}', PingMachine::class)->name('ping-machine');
public function __invoke(string $region, Request $request)
{
if ($request->hasHeader('fly-replay-src'))
{
return response([], 200, []);
}
else
{
return response(['current-time' => microtime(true)], 200, [
'fly-replay' => 'region=' . $region,
]);
}
}
因此,无论何时我们向/ping/nrt
发送请求,它都将重放到运行在东京的机器,然后它会响应客户端。
现在,我们需要为我们的应用运行的每个区域发送一个请求,并记录接收响应所需的时间。以下是仪表板上的“ping all”按钮背后的函数
public function pingAllMachines()
{
foreach($this->machines as $machine)
{
$response = Http::get('https://fly-filament-satellite.fly.dev/api/ping/' . $machine->region->iata_code);
$this->latencies[$machine->machine_id] = $response->transferStats->getTransferTime();
}
}
使用transferStats->getTransferTime()
,我们可以轻松地获得发送请求和接收响应之间的时间。
就是这样,我们使用Fly Machines重新创建了月球激光测距实验。然而,与月球不同,飞机制不会离我们远去。我相信你会同意这是好事。
driesvints, carlx1101 喜欢了这篇文章