Goa(3) The goa Runtime - KerwinKoo/KerwinKoo.github.io GitHub Wiki

goa Runtime 在goa package中做了实现,包括HTTP request stateresponse行为管理、基本数据结构和算法的实现、日志记录、错误处理和支持版本控制等功能。

Goa 行为上下文管理(action context)

Goa 通过实现一个数据结构,并存在于所有的Goa controller实现(以第一个参数传参的方式),来实现行为的上下文管理。该方法是参考google实现的接口间信息传递的方法,同时根据Goa自身情况做了修改。

与其他Go实现的web框架类似,Goa的REST 资源(行为上下文)会开放接口供外部访问。像路径参数、查询结果这样的API可以直接通过GET方法来获取返回值。在此基础上Goa更进一步,用户通过goagen工具生成的代码中,根据design中指定的参数type进行强制准入检验。比如,我们在design时定义一个类型为IntegerID,则goagen生成的ID字段中也会定义为int,并对该资源的访问进行健壮性检验(比如对ID赋值string时会返回错误),防止在server内部将收到的值赋值给结构体时发生错误。

同样,在writing response阶段,Goa根据设计时的media type需求,对返回值进行写入操作。

之前讲到,每个controller的Action全过程都有一个action context管理,因此在开发过程中,每个Action的实现都包含deadlinecancelation信号管理。同时Goa内嵌Timeout中间件,可以为所有request定义timeout值。

Goa功能支持

Server Mux

Goa通过实现ServeMux接口来管理请求的路由,该接口包含一个Handle方法,可以将HTTP请求的method和url路径交付给指定HandleFunc,通过此函数进行信息处理和反馈。HandleFunc作为Handle method的最后一个参数,基本不需要人为修改,其作用主要是为goagen生成的代码服务。

This function is intended for the controller generated code. User code should not need to call it directly.

另外,ServeMux接口有个version方法,可以通过其进行版本控制。对不同版本的HTTP请求做对应版本的路由处理。该功能通过函数SelectVersionFunc实现,实现代码为:

// PathSelectVersionFunc returns a SelectVersionFunc that uses the given path pattern to extract the
// version from the request path. Use the same path pattern given in the DSL to define the API base
// path, e.g. "/api/:version".
// If the pattern matches zeroVersion then the empty version is returned (i.e. the unversioned
// controller handles the request).
func PathSelectVersionFunc(pattern, zeroVersion string) SelectVersionFunc {
	rgs := design.WildcardRegex.ReplaceAllLiteralString(pattern, `/([^/]+)`)
	rg := regexp.MustCompile("^" + rgs)
	return func(req *http.Request) (version string) {
		match := rg.FindStringSubmatch(req.URL.Path)
		if len(match) > 1 && match[1] != zeroVersion {
			version = match[1]
		}
		return
	}

同时Goa也提供其他访问方式的version 路由管理。包括PathSelectVersionFuncHeaderSelectVersionFuncQuerySelectVersionFunc

日志、错误处理及服务退出

日志存在于Goa server的整个运行时,同时包含通信过程中的全部信息:server namecontroller nameaction name和一个全局唯一的request ID

所有Goa的Action均会返回一个类型为error的变量,当此error变量不是nil时,定义的controller error handler会自动调用。默认情况下发生访问错误时会返回一个500 ERROR和具体的报错信息。

Goa server通过 NewGraceful 方式,在保证所有已发送的请求全部处理过后,再进行终止服务。

定制开发

Error Handling

Goa可以通过SetHandler方法来重写默认的service 错误处理。同时Goa也提供两个内嵌error handlers:

  • DefaultErrorHandler:当发生BadRequestError接口类error时返回400,否则返回500,并response body中记录错误日志。

  • TerseErrorHandler:返回错误的逻辑与DefaultErrorHandler相同,只不过该Handler不会将内部(internal)错误写入response body中。

路由规则和API版本控制

正如之前所讲,Goa支持API路由到不同的版本中,该功能可以在设计阶段通过Version字段进行指定:

e.g.:

package design

import (
        . "github.com/goadesign/goa/design"
        . "github.com/goadesign/goa/design/dsl"
)

var _ = API("cellar", func() {
        Description("A basic example of an API implemented with goa")
        Scheme("http")
        Host("localhost:8080")
})

var _ = Version("1.0", func() {
        Title("The virtual winecellar v1.0 API")
        // ... other API level properties
})

var _ = Version("2.0", func() {
        Title("The virtual winecellar v2.0 API")
        // ... other API level properties
})

var _ = Resource("bottle", func() {
        BasePath("/bottles")
        Version("1.0")
        Version("2.0")
        // ... other resource properties
})

var _ = Resource("bottle", func() {
        BasePath("/bottles")
        Version("3.0")
        // ... other resource properties
})

在代码生成阶段,goagen会将不同版本的ServerMux接口挂载到对应版本的API路由中。

在Design阶段中设计的每一个版本的API都会生成一个独立的package,来包含其对应的API controllers

在Goagen所生成的server代码中,全部都是围绕着ServerMux方法来检索(retrieve)顶层路由。而Goa底层路由的实现则是依赖于包httprouter。其他中间层路由均可ServerMux接口实现。

⚠️ **GitHub.com Fallback** ⚠️