使用 Geolocation API 网络服务的最佳实践

Google Maps Platform 网络服务是一系列 HTTP 接口,可为您的地图应用提供地理数据。

本指南介绍了一些常见做法,这些做法有助于设置 Web 服务请求和处理服务响应。如需查看 Geolocation API 的完整文档,请参阅开发者指南。

什么是网络服务?

Google Maps Platform Web 服务是一个接口,用于从外部服务请求地图 API 数据,并在您的地图应用中使用这些数据。根据 Google Maps Platform 服务条款中的许可限制,这些服务应与地图结合使用。

Maps API Web 服务使用 HTTP(S) 请求访问特定网址,并将网址参数和/或 JSON 格式的 POST 数据作为参数传递给服务。通常,这些服务会在响应正文中以 JSON 格式返回数据,供您的应用解析和/或处理。

使用 POST 将地理位置请求发送到以下网址:

https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_API_KEY

注意:所有 Geolocation API 应用都需要进行身份验证。 详细了解身份验证凭证。

SSL/TLS 访问

对于使用 API 密钥或包含用户数据的所有 Google Maps Platform 请求,必须采用 HTTPS 协议。通过 HTTP 发出的包含敏感数据的请求可能会被拒绝。

合理使用 Google API

设计不当的 API 客户端可能会给互联网和 Google 服务器带来不必要的负载。本部分包含一些适用于 API 客户端的最佳实践。遵循这些最佳实践有助于避免您的应用因无意中滥用 API 而被屏蔽。

指数退避算法

在极少数情况下,处理您的请求时可能会出现问题;您可能会收到 4XX 或 5XX HTTP 响应代码,或者 TCP 连接可能只是在客户端和 Google 服务器之间的某个位置失败。通常,值得重新尝试请求,因为当原始请求失败时,后续请求可能会成功。不过,请务必不要只是重复循环向 Google 服务器发出请求。这种循环行为可能会使客户端与 Google 之间的网络过载,从而给许多方带来问题。

更好的方法是在每次尝试之间增加延迟时间,然后重试。通常,每次尝试都会按乘数因子增加延迟时间,这种方法称为指数退避。

例如,假设某个应用希望向 Time Zone API 发出以下请求:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

以下 Python 示例展示了如何使用指数退避算法发出请求:

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

您还应注意,应用调用链中不应存在会导致快速连续重复请求的重试代码。

同步请求

对 Google API 的大量同步请求可能看起来像是对 Google 基础设施的分布式拒绝服务 (DDoS) 攻击,并会受到相应处理。为避免这种情况,您应确保 API 请求不会在客户端之间同步。

例如,假设某个应用用于显示当前时区的时间。此应用可能会在客户端操作系统中设置一个闹钟,以便在每分钟开始时唤醒操作系统,从而更新显示的时间。应用不应在与该闹钟关联的处理过程中进行任何 API 调用。

为响应固定闹钟而发出 API 调用是不好的,因为这会导致 API 调用同步到分钟的开始时间(即使在不同设备之间也是如此),而不是在一段时间内均匀分布。如果应用设计不当,在每分钟开始时,流量会激增至正常水平的 60 倍。

相反,一种可能的好设计是将第二个闹钟设置为随机选择的时间。 当第二个闹钟触发时,应用会调用所需的任何 API 并存储结果。当应用想要在分钟开始时更新其显示时,它会使用之前存储的结果,而不是再次调用 API。通过这种方法,API 调用会随着时间的推移均匀分布。此外,在更新显示内容时,API 调用不会延迟渲染。

除了分钟开始时间之外,您还应注意不要将小时开始时间和每天午夜开始时间作为同步时间。

处理响应

此部分介绍如何以动态方式从 Web 服务响应中提取这些值。

Google Maps Web 服务提供的响应易于理解,但对用户来说并不友好。执行查询时,您可能希望提取一些特定值,而不是显示一组数据。通常,您需要解析来自 Web 服务的响应,并仅提取您感兴趣的值。

您使用的解析方案取决于您是否以 JSON 格式返回输出。JSON 响应已经采用 Javascript 对象的形式,因此可以在客户端上通过 Javascript 本身进行处理。