编写带有Go插件的Web服务(上篇)(译) 翻译

saltshaker 3月前 154

编写带有Go插件的Web服务

本文的目标是审查为什么以及如何构建模块化的web服务。


在一个自动化驱动的环境中,有许多条件会导致项目的演化失败。其中一些来自非模块化环境。即使你使用的是微服务体系结构,使用编译语言,所有这些情况都会导致重新构建整个项目:

         • 编辑项目的单个部分如何工作。

         • 优化单个端点的扩展。


而且,你还会面临一些不适的情况:

         • 将服务中的相同行为重用到另一个服务中

         • 你可能只能使用一种编程语言

         • 假如你有个团体,你不得不分享整个项目 (在离岸项目中这可能非常糟)


使用插件体系结构,你可以将服务开发为一组组件,使之能够:

         • 让团队更关注功能。

         • 使用滚动更新对组件进行分离部署。
         • 在多个项目中重用组件。
         • 通过配置必须包含的winch组件来自定义如何扩展服务。
         • 最后但不重要的是,你可以使用不同的语言进行组件的开发。


架构

以web服务为例,我们可以将我们的项目分为以下几个部分:

         • Core: 读取配置文件, 加载组件, 监听和HTTP服务。

         • Controller: HTTP 处理函数。

         • Middlewares: 授权访问控制组件

通过这种方式,如果你将Core更新为HTTPS,那么你只能重新部署核心文件,并且只能为需要符合HTTPS的服务重新部署。同样,如果你更新JWT插件以使用新的散列方法,你只需重新部署插件并重新加载Core。


架构组件

Controller

控制器是一个HTTP处理函数,它将对请求应用逻辑来构建和返回响应。

Middlewares

这类对象产生于对传入请求进行筛选、跟踪或日志等操作的需要,而不涉及管理它的所有项目。一种中间件可以是IP过滤器:  你希望仅允许某些IP访问你的服务。当请求达到你的服务时, 你可以直接检查源ip是否在你的白名单里。这样,你就不需要加载服务的其他部分。这样做的好处之一是可以减少在恶意意图下暴露细节的风险。

例如: 让我们创建另一个检查访问头的中间件。 现在,串联前面的IP中间件,只有在我们的IP池中授权的用户才能访问这个服务。


因此,如果控制器中的第一步是查询数据库中的用户数据,那么就不会受到SQL注入的影响。这是因为,在所有先决条件没有达到的情况下,你是不进行数据库的操作的。


Core
如前所述,对于这个项目,我们使用插件功能来分离组件。 在我们的范围内,核心组件将负责加载控制器和中间件插件,使用配置文件将其映射到端点,最后启动HTTP侦听器。所有其他路由将返回“404 not found”。
将中间件和控制器映射到端点

这种级别的独立性很容易被云用例所喜爱:


实现

让我们从一个基本的核心控制器(HTTP函数处理程序)响应我们的home页
package main
import (
    "fmt"
    "log"
    "net/http"
)
func controller(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there!")
}
func main() {
    http.HandleFunc("/", controller)
    log.Fatal(http.ListenAndServe(":8080", nil))
}
在此基础上,添加实现中间件所需的构造:
// middleware filter incoming HTTP requests.
// if the request pass the filter, it calls the next HTTP handler.
type middleware func(http.HandlerFunc) http.HandlerFunc
中间件的类型
func Chain(f http.HandlerFunc, mids ...middleware) http.HandlerFunc {
    for _, m := range mids {
        f = m(f)
    }
    return f
}
一个Chain函数,它提供了一种机制来串联中间件
现在构建一个示例中间件:  中间件检查HTTP方法和作为参数传递的方法之间是否匹配。 如果不是,则返回400 Bad Request。
在本例中,参数是一系列经过批准的HTTP方法,我们需要对这些方法进行分割和检查:
func pass(args string) func(http.HandlerFunc) http.HandlerFunc {
    return func(f http.HandlerFunc) http.HandlerFunc {
        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {
            //split args and check if the request as this method
            acceptedMethods := strings.Split(args, "|")
            for _, v := range acceptedMethods {
                if r.Method == v {
                    // Call the next middleware in chain
                    f(w, r)
                    return
                }
            }
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            return
        }
    }
}
现在是将中间件附加到Chain中的时候了,传递你希望在函数参数中允许的HTTP方法。

然后在我们的基本HTTP服务中把Chain传递给HandlerFunc

var chain []middleware //empty chain
func main() {
    chain = append(chain, pass("GET|POST"))
    http.HandleFunc("/", Chain(controller, chain...))
    log.Fatal(http.ListenAndServe(":8080", nil))
}
完整的例子源代码在这里


最新回复 (0)
    • 运维开源项目互助社区—致敬开源
      2
        立即登录 立即注册 
返回