我知道MaxMind GeoIP,并已经安装了它,但它只给我一个国家,据我所知,没有办法自动确定给定国家最接近我的两个镜像国家中的哪一个。我想我可以按照大陆来做:让亚洲、欧洲、非洲和澳大利亚的用户从德国获取内容,让北美和南美的访问者从美国获取文件。如果有人能想到更好的解决方案,我愿意听取建议。
嗯,我想我会采用按大陆分类的原始想法。对于其他想要做这种事情的人来说,这将是一个不错的起点。当我在欧洲有多个镜像时,问题就会出现,但大陆的想法现在必须起作用。
目前提出的解决方案中似乎存在很多开发人员的额外工作量。如果这是我自己应用程序中需要解决的问题,我可能会选择不重新发明轮子,从而节省一些工作时间。
确定最接近的镜像(使用邮政编码)
请记住,较短的距离并不一定意味着响应时间更快。然而,在您的情况下,一个国家内的镜像显然比另一个国家的镜像更快,假设两个镜像都正常运行。继续阅读以获取我认为更为“健壮”的解决方案。
资源和链接
PHP 5邮政编码范围和距离计算:http://www.micahcarrick.com/php5-zip-code-range-and-distance.html
基于国家的ip2nation重定向:http://www.ip2nation.com/ip2nation/Sample_Scripts/Country_Based_Redirect
也可以寻找帮助您获取给定IP地址的邮政编码或地区的库或MySQL转储(例如ip2nation)。例子:如何从IP地址确定邮政编码和城市?
“Maverick”方法
在我看来,Mavericks也被称为那些创新者、问题解决者和发明家,他们开发了我们今天都在使用的伟大库和框架。有时会错误地与“hackish”思想联系起来,但我们欣然接受这个称号 :)
在任何一个镜像服务器上创建您自己的API服务,该服务将接受$_GET或$_POST请求。
该API服务将获取所提供的IP地址并使用ping()进行计算响应时间,然后取平均值,并将其返回到请求接口(例如连接客户端和/或试图确定最近的镜像的前端门户)。响应平均值最低的服务器应该是您最快响应的服务器,但不一定是最近的。哪个对你更重要?有关可以使用不依赖于本地执行shell命令(例如跨平台)的工作ping()函数,请参见Ping site and return result in PHP 。
最后一步:在后台将请求客户端的IP地址传递给任一镜像服务器上运行的API服务。我们都知道如何派生IP地址,但并不如你认为的那样好。如果您进行负载平衡或在代理后面,则可能需要首先检查是否通过了任何这些标头(HTTP_FORWARDED,HTTP_FORWARDED_FOR,HTTP_X_FORWARDED,HTTP_X_FORWARDED_FOR,HTTP_CLIENT_IP)。如果是这样,则这可能是用户代理的真实 IP地址。
在此时(第3步),当每个镜像去ping用户代理时,您将比较所回复的平均响应时间。然后根据选择用户代理应该从哪个镜像下载。您将创建的服务流程类似于以下内容:
希望这可以帮助您愉快编码!
执行一个 traceroute(配置 traceroute 客户端不解析主机名和设置较短的超时时间)。
根据跳数和 traceroute 客户端的位置(我认为与 PHP 脚本相同),在美国和德国之间选择。
地理距离与网络距离、网络速度或带宽成本无关。
作为 traceroute 的替代方案(因为它是一个 hackish,小型代码解决方案),我建议您使用 $_SERVER["REMOTE_ADDR"] 并在 geo ip database 中查找以获取国家代码。 如果国家代码不是美洲国家之一,则为避免穿越拥挤的互联网骨干,回退到德国(此外,您可以将国家代码条件为来自欧洲)。
设置好 geo ip 数据库后,我建议您将 IP 地址从点分格式转换为整数格式,以获得速度和查询的便利性。
根据我对上述 geo ip 数据库的经验,它很少缺失,所以并不重要。
你觉得使用像geoip这样的库,然后使用纬度和经度来比较镜像和用户之间的距离不是更容易吗?
我认为这样做更简单,更易于实现,适用于N个镜像,并且您不需要请求Zip或其他类型的数据来进行参考。
我不记得有任何库可以做到这一点。但是,如果我有一个想法,可能能够帮助你。
使用此距离计算器计算两个IP地址之间的距离。或者找出两个IP地址(一个服务器和一个客户端)的纬度和经度,并计算它们之间的距离。以下是一个伪代码示例:
distance = ( 3956 *2 * ASIN( SQRT( POWER( SIN( ( 34.1012181 - ABS( latitude ) ) * PI( ) /180 /2 ) , 2 ) + COS( 34.1012181 * PI( ) /180 ) * COS( ABS( latitude ) * PI( ) /180 ) * POWER( SIN( ( ABS( - 118.325739 ) - ABS( longitude ) ) * PI( ) /180 /2 ) , 2 ) ) ))
抱歉:我来晚了。:-)
通过访问Maxmind GeoLite2 City Database,您可以获取镜像和访问者的纬度和经度,并进行相应的重定向。
我创建了一个小脚本,根据最近的服务器告诉您应该重定向到哪个服务器。您只需执行header("Location: .....);
重定向即可,而不是回显语句。
这是完整的示例:
<?php
/**
* Credit for the function: https://dev59.com/WGkw5IYBdhLWcg3wNX73#10054282
* Calculates the great-circle distance between two points, with
* the Vincenty formula.
* @param float $latitudeFrom Latitude of start point in [deg decimal]
* @param float $longitudeFrom Longitude of start point in [deg decimal]
* @param float $latitudeTo Latitude of target point in [deg decimal]
* @param float $longitudeTo Longitude of target point in [deg decimal]
* @param float $earthRadius Mean earth radius in [m]
* @return float Distance between points in [m] (same as earthRadius)
*/
function vincentyGreatCircleDistance(
$latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
// convert from degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$lonDelta = $lonTo - $lonFrom;
$a = pow(cos($latTo) * sin($lonDelta), 2) +
pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
$b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);
$angle = atan2(sqrt($a), $b);
return $angle * $earthRadius;
}
$download_servers = array( 0 => array( 'hostname' => "ftp.bu.edu",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null),
1 => array( 'hostname' => "www.softlayer.com",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null),
2 => array( 'hostname' => "download.nust.na",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null),
3 => array( 'hostname' => "mirror.isoc.org.il",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null),
4 => array( 'hostname' => "download.nus.edu.sg",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null),
5 => array( 'hostname' => "mirror.yandex.ru",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null),
6 => array( 'hostname' => "ftp.iij.ad.jp",
'longitude' => null,
'latitude' => null,
'city' => null,
'distance' => null)
);
require_once( __DIR__ . "/maxmind/geoip2.phar");
use GeoIp2\Database\Reader;
// City DB
$reader = new Reader( __DIR__ . '/maxmind/GeoLite2-City.mmdb');
$record = $reader->city($_SERVER['REMOTE_ADDR']);
$i = 0;
foreach ($download_servers as $server) {
//$record2 = $reader->city($server['ip']);
$record2 = $reader->city(gethostbyname($server['hostname']));
if (!isset($download_servers[$i]['longitude'])) {
$download_servers[$i]['longitude'] = $record2->location->longitude;
$download_servers[$i]['latitude'] = $record2->location->latitude;
}
$download_servers[$i]['city'] = $record2->city->name;
$download_servers[$i]['distance'] = vincentyGreatCircleDistance( $record->location->latitude, $record->location->longitude,
$download_servers[$i]['latitude'], $download_servers[$i]['longitude']);
$i++;
}
$dists = array_column($download_servers, 'distance');
$min = array_search(min($dists), $dists, true);
echo "Nearest download server is: " . $download_servers[$min]['hostname'] . " in " . $download_servers[$min]['city'] . "<br>";
echo "Distance to you (" . $record->city->name . ") is: " . $download_servers[$min]['distance'] / 1000 . " km";