提高API可靠性的5种方法
#api #go #体系结构 #sre

API使我们的数字世界滴答作响,允许相互交流。但是,这些API的可靠性对于确保取决于它们的应用程序的无缝功能和性能至关重要。在此博客文章中,我们将探讨提高API可靠性的五种关键策略。

5 ways to improve your api reliability

1.实施强大的测试实践

确保API可靠性是全面测试的第一道防线。这包括功能测试,以验证单个API的正确操作,集成测试以确保API与其他系统合并正确工作,以及负载测试以了解API在大量使用情况下的行为。

自动测试可以帮助在开发周期的早期捕捉问题,并且回归测试可以确保新的更改不会破坏现有功能。虚拟化或模拟技术的使用可以模拟API依赖性,以进行更全面的测试。此外,合同测试对于确保API的提供商和消费者都符合商定的接口很重要。

让我们看一下如何使用GO的内置testing软件包对假设的API端点进行简单测试。

假设我们有一个端点GET /users/{id},它返回用户的详细信息。这是我们可能为其编写测试的方法:

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

// This is a simplified function that your actual handler might look like
func UserHandler(w http.ResponseWriter, r *http.Request) {
    // ... handler logic
}

func TestUserHandler(t *testing.T) {
    req, err := http.NewRequest("GET", "/users/1", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(UserHandler)

    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // You can also check the response body with expected output
    expected := `{"id": "1", "name": "John Doe"}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
    }
}

此测试创建了一个新的HTTP请求,该请求模仿对我们的/users/{id}端点的调用,然后将该请求传递给处理程序函数。测试检查响应状态是否为200 OK(这是我们对成功请求的期望)以及响应主体是否与预期的输出匹配。
这是一个简单的示例,在现实世界中,您将拥有更复杂的场景,包括测试各种边缘案例,错误路径等。此外,net/http/httptest软件包提供了更多用于测试HTTP客户端和服务器的工具。
此外,您可以将其与单元测试,性能测试和连续的合成测试相结合,从而为您的API创建全面的测试套件。
单位测试有助于确保API中各个组件的正确性。通过隔离每个部分并验证其功能,您可以在早期阶段识别和纠正问题。单位测试可以通过模拟依赖项和隔离测试功能来完成。在旅途中,这可以在testify之类的包装的帮助下实现。
另一方面,性能测试旨在在负载条件下压力测试您的API。他们有助于确定系统在重载下的行为,识别瓶颈并确保API可以处理现实世界的使用。 JMeterGatling等工具可用于进行性能测试。
最后,连续的合成测试不断在API上运行一系列操作,以模拟用户或客户端通过系统的旅程。这些测试可以提供有关端到端工作流程,潜在障碍或放缓以及整体用户体验的见解。该过程可以自动化并集成到您的CI/CD管道中,从而可以持续监视并立即对任何代码更改的影响进行反馈。
通过实施包括功能,单元,性能和连续合成测试的强大测试框架,您可以确保API不仅可靠且性能,而且还可以为其消费者提供无缝的体验。当出现问题时,这种多元化的测试方法可以帮助您快速找到并解决根本原因。

Development & Deployment Lifecycle

2.拥抱版本控制

API版本控制在维持软件系统的可靠性中起着至关重要的作用。随着API的发展,可以引入更改,如果无法正确管理,可能会破坏现有客户应用程序。这就是API版本控制的位置。通过维护API的不同版本,您可以引入新功能,改进或更改,而无需负面影响依赖以前API版本的应用程序。
这种实践可促进可靠性,因为它可以确保客户应用程序可以继续可预测地运行,即使API发生变化和发展。它允许开发人员将更新部署到API,而不必担心对实时应用程序进行破坏更改,从而维持系统稳定性和正常运行时间。
向后兼容是此可靠性的重要方面。这是较新系统与API较旧版本进行交互的能力。保持向后兼容性意味着即使引入了较新版本,使用较旧的API版本的应用程序也会继续运行。它防止了对用户体验的中断,并使开发人员有时间更新其应用程序,以按照自己的节奏适应新的API更改,而不是被迫这样做的风险。这导致总体上更可靠,稳健和弹性的系统。

例子

在GO中,您可以通过几种不同的方法来处理API的版本化。
这是一个示例,说明了如何通过将API版本嵌入URL中。这种方法通常称为“路径版本”:

package main

import (
    "fmt"
    "net/http"
)

func handleRequest(w http.ResponseWriter, r *http.Request) {
    switch r.URL.Path {
    case "/v1/users":
        fmt.Fprintf(w, "You've hit the version 1 of the users API!")
    case "/v2/users":
        fmt.Fprintf(w, "You've hit the version 2 of the users API!")
    default:
        http.NotFound(w, r)
    }
}

func main() {
    http.HandleFunc("/", handleRequest)
    http.ListenAndServe(":8080", nil)
}

在此示例中,我们定义一个单个处理程序函数,该功能在请求的URL上打开。当访问/v1/users路径时,我们认为这是对API的第一个版本的请求。同样,/v2/users对应于我们的API的第二版。通过添加更多情况,您可以轻松地将此模式扩展到其他版本和端点。
另外,您可以通过自定义标头或媒体类型版本(也称为“内容谈判”)实现版本控制。
至关重要的是要注意,无论您选择的方法如何,每种API的清晰和最新文档都是最佳实践。
但是,应明智地使用版本控制。尽可能长时间保持向后兼容性,并提供有关每个新版本中哪些更改的清晰文档,以及贬低较旧版本的合理时间表。

3.设计失败

在一个完美的世界中,API会一直完美地工作。实际上,失败可以而且确实会发生。考虑到可容忍度的容错设计API很重要。这可能涉及诸如优雅降解(系统继续运行,但功能降低)或故障转移机制(如果发生故障时操作切换到备份系统)等策略。
在API中包括定义明确的错误消息和代码可以帮助消费应用程序了解出了什么问题以及如何反应。重试逻辑,限制和断路器可以帮助系统从临时问题中恢复并避免失败。

示例:断路器模式

至于断路器模式,GO中有一个流行的库,称为Go-HyStrix,它是延迟和容错库。这个想法是通过在服务下降时快速失败来停止级联故障。这是一个基本示例:

package main

import (
    "github.com/afex/hystrix-go/hystrix"
    "log"
    "net/http"
    "errors"
)

func main() {
    hystrix.ConfigureCommand("my_command", hystrix.CommandConfig{
        Timeout:               1000,
        MaxConcurrentRequests: 100,
        ErrorPercentThreshold: 25,
    })

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        err := hystrix.Do("my_command", func() error {
            // talk to other services
            return nil
        }, nil)

        if err != nil {
            log.Printf("Failed to talk to other services: %v", err)
            http.Error(w, "Failed to talk to other services", http.StatusInternalServerError)
        }
    })

    log.Fatal(http.ListenAndServe(":1234", nil))
}

在上面的示例中,我们在hystrix.Do()中包裹了一个命令。如果该函数传递到Do()的失败或根据我们的配置出现,则它将trip trip the Breaker the Breaker,并且在不调用函数的情况下会立即失败。

请记住,这只是一个基本示例,现实世界的使用将涉及更复杂的用法,并仔细调整该库中涉及的各种参数和其他弹性实用程序库。请务必阅读各种库的文档,以彻底了解如何在您自己的代码中有效利用它们。

4.监视和分析

在维持API可靠性方面,没有什么能超过实时监控和及时分析的力量。实施包括正常运行时间,性能和错误检测在内的可靠的API监视策略可以帮助识别和减轻问题。
对API使用模式的分析也可能非常有见地。通过了解峰值负载时间,最常用的端点和其他用法细节,您可以主动识别潜在的弱点并相应地优化您的API。
跟踪正确的指标对于了解API的健康和性能至关重要。这是您应该考虑的一些关键指标:

  • 吞吐量:您的API每单位时间处理的请求数。可以通过端点,http方法(获取,发布,put,删除等)或响应状态代码进一步分解。
  • 错误率:每单位时间的错误响应数量(通常为4xx或5xx状态代码的响应)。像吞吐量一样,可以通过端点,http方法或特定状态代码进一步分解。
  • 延迟:服务请求所需的时间。这通常被跟踪为一组百分位数(例如第50,第95和第99个百分位数),这可以使您对典型和最差的表现有所了解。您可能需要为不同的端点或HTTP方法单独跟踪此。
  • 流量:发送和接收的数据量。这可以通过端点,http方法或响应状态代码分解。
  • 可用性:API的时间百分比并能够处理请求。可以总体或单个端点进行测量。
  • 饱和度:您的系统与最大容量有多近。这可以根据CPU使用,内存使用情况,磁盘I/O或任何其他可能限制您系统处理更多负载的能力的资源来衡量。
  • 断路器旅行:如果您使用断路器模式来处理故障,则可能会跟踪断路器被绊倒的频率。这可以使您了解您的API或其依赖性失败的频率。

请记住,您选择跟踪的特定指标可能会因API的性质和应用程序的需求而有所不同。关键是选择指标,使您对API的健康和表现有意义。

Prometheus的例子:

Prometheus是一种开源系统监视和警报工具包,具有客户库,可让您用各种语言来启动服务。这是您如何使用GO客户端库在HTTP端点上公开指标的示例。
我们将利用Prometheus go client揭示指标并创建它们。

package main

import (
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Number of HTTP requests",
        },
        []string{"path"},
    )

    httpRequestDuration = prometheus.NewSummaryVec(
        prometheus.SummaryOpts{
            Name: "http_request_duration_seconds",
            Help: "Duration of HTTP requests in seconds",
        },
        []string{"path"},
    )
)

func init() {
    // Register the metrics.
    prometheus.MustRegister(httpRequestsTotal)
    prometheus.MustRegister(httpRequestDuration)
}

func handler(w http.ResponseWriter, r *http.Request) {
    // Increment the counter for the received requests.
    httpRequestsTotal.WithLabelValues(r.URL.Path).Inc()

    // Measure the time it took to serve the request.
    timer := prometheus.NewTimer(httpRequestDuration.WithLabelValues(r.URL.Path))
    defer timer.ObserveDuration()

    // Handle the request.
    w.Write([]byte("Hello, world!"))
}

func main() {
    http.HandleFunc("/", handler)

    // Expose the registered metrics via HTTP.
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":8080", nil)
}

在此示例中,我们创建并注册两个指标: http_requests_total http_request_duration_seconds 。前者是每次收到请求时增加的计数器,而后者是记录服务每个请求所需的持续时间的摘要。
然后,我们创建一个HTTP处理程序,该处理程序每​​次处理请求时都会增加计数器并测量请求持续时间。我们使用promhttp.Handler()/metrics端点上公开这些指标。
现在,如果您启动服务器并提出请求,则可以通过在Web浏览器中导航到http://localhost:8080/metrics或使用curl等工具来查看指标。
这是一个简单的示例,在现实世界中,您可能想跟踪更多的指标,并可能会通过其他维度(例如HTTP方法,响应状态代码等)对其进行分解。

5.利用API网关

API网关是提高API可靠性的强大工具。它们充当进入系统的单点,并且可以处理诸如路由,负载平衡,身份验证,速率限制等多种功能。通过将这些问题从API本身中抽象出来,您可以更多地关注业务逻辑,而不是基础架构。
此外,API网关可以提供其他弹性功能,例如自动故障转移,更快性能的缓存响应以及在高负载期间的缓冲或排队请求。
以下是API网关提供的一些常见功能;此功能列表绝不是详尽无遗的,但是它可以帮助您为堆栈选择一个API网关:

  1. 请求路由:API网关路由客户端请求根据请求中指定的路由。
  2. API版本管理:API网关可以管理多个API版本,允许客户端同时使用不同的版本。
  3. 利率限制:为了保护后端服务免于太多请求,API网关可以限制客户或一组客户的传入请求率。
  4. 身份验证和授权:API网关经常处理客户请求的身份验证和授权,确保仅有效和授权的请求到达后端服务。
  5. API密钥管理:API网关通常管理API键,用于跟踪和控制如何使用API​​。
  6. 缓存:为了提高性能并减少后端服务的负载,API网关可以从后端服务缓存响应,并在提出相同的请求时提供缓存的响应。
  7. 请求和响应转换:API网关可以将请求和响应转换为客户或后端服务期望的格式。
  8. 断路器功能:在服务故障的情况下,API网关可以通过将请求远离失败服务来防止应用程序故障。
  9. 监视和分析:API网关可以收集有关API使用和性能的数据,可用于分析,监视和警报。
  10. 安全策略:API网关可以执行安全策略,例如IP白名单,并防止SQL注入和跨站点脚本(XSS)等攻击。

这是一些流行的开源API网关的列表:

  1. Kong:云原生,快速,可扩展和分布式微服务抽象层(也称为API网关或API中间件)。它在2015年作为开源项目提供,其核心功能是在LUA中编写的,并在Nginx Web服务器上运行。
  2. Tyk:一个快速且可扩展的开源API网关,在其自己的独立服务器上或与现有的Nginx安装一起运行。
  3. Express Gateway:构建在Express.js的微服务API网关。它是完全可扩展的,框架不可知,立即提供了可靠的可扩展解决方案。
  4. KrakenD:高性能开源API网关。它通过消除SOA架构的所有复杂性,同时提供独特的性能来帮助应用程序开发人员快速释放功能。

总而言之,提高API可靠性不是一次性的任务,而是一项持续的承诺。它涉及严格的测试,声音设计原理,智能使用API​​网关等工具以及不断的监视和分析。有了这些策略,您可以构建经受时间考验的API并作为应用程序的可靠基础。


喜欢这篇文章,想要更多的东西吗?我们很想继续与您分享我们的见解!请在我们的免费newsletter中订阅。