orbit

package module
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2024 License: MIT Imports: 19 Imported by: 0

README

English | 中文

logo

Go Report Card Build Status Go Reference

Introduction

orbit is a lightweight HTTP web service wrapper framework. It is designed to be simple and easy to use, providing a series of convenient features to help you quickly build a web service.

Why is it called orbit? It provides a framework that encapsulates the complexities of building a web service, allowing you to focus on your core business logic. With orbit, you can easily develop and maintain your web service, just like a satellite smoothly orbiting the Earth.

Why not use gin directly? While gin is a great framework, starting a web service with it requires additional work such as logging and monitoring. orbit is built on top of gin and offers these features out of the box.

Advantages

  • Lightweight and easy to use
  • Supports zap logging with both async and sync modes
  • Provides prometheus monitoring integration
  • Includes swagger API documentation support
  • Enables graceful shutdown of the server
  • Supports cors middleware for handling cross-origin requests
  • Automatically recovers from panics
  • Allows customization of middleware
  • Supports custom router groups
  • Provides flexibility in defining access log format and fields
  • Offers repeat read of request/response body and caching of body bytes

Installation

go get github.com/shengyanli1982/orbit

Quick Start

orbit is incredibly easy to use, allowing you to quickly build a web service in just a few minutes. The typical steps involved are:

  1. Create the orbit start configuration.
  2. Define the orbit feature options.
  3. Create an instance of orbit.

Default URL Paths

[!NOTE] The default URL paths are system-defined and cannot be changed.

  • /metrics - Prometheus metrics
  • /swagger/*any - Swagger API documentation
  • /debug/pprof/*any - PProf debug
  • /ping - Health check

1. Configuration

orbit provides several configuration options that can be set before starting the orbit instance.

  • WithSugaredLogger - Use zap sugared logger (default: DefaultSugaredLogger).
  • WithLogger - Use zap logger (default: DefaultConsoleLogger).
  • WithAddress - HTTP server listen address (default: 127.0.0.1).
  • WithPort - HTTP server listen port (default: 8080).
  • WithRelease - HTTP server release mode (default: false).
  • WithHttpReadTimeout - HTTP server read timeout (default: 15s).
  • WithHttpWriteTimeout - HTTP server write timeout (default: 15s).
  • WithHttpReadHeaderTimeout - HTTP server read header timeout (default: 15s).
  • WithAccessLogEventFunc - HTTP server access log event function (default: DefaultAccessEventFunc).
  • WithRecoveryLogEventFunc - HTTP server recovery log event function (default: DefaultRecoveryEventFunc).
  • WithPrometheusRegistry - HTTP server Prometheus registry (default: prometheus.DefaultRegister).

You can use NewConfig to create a default configuration and WithXXX methods to set the configuration options. DefaultConfig is an alias for NewConfig().

2. Features

orbit provides several feature options that can be set before starting the orbit instance:

  • EnablePProf - enable pprof debug (default: false)
  • EnableSwagger - enable swagger API documentation (default: false)
  • EnableMetric - enable Prometheus metrics (default: false)
  • EnableRedirectTrailingSlash - enable redirect trailing slash (default: false)
  • EnableRedirectFixedPath - enable redirect fixed path (default: false)
  • EnableForwardedByClientIp - enable forwarded by client IP (default: false)
  • EnableRecordRequestBody - enable record request body (default: false)

You can use NewOptions to create a null feature, and use EnableXXX methods to set the feature options.

  • DebugOptions is used for debugging and is an alias of NewOptions().EnablePProf().EnableSwagger().EnableMetric().EnableRecordRequestBody().
  • ReleaseOptions is used for release and is an alias of NewOptions().EnableMetric().

[!NOTE] It is recommended to use DebugOptions for debugging and ReleaseOptions for release.

3. Creating an Instance

Once you have created the orbit configuration and feature options, you can create an orbit instance.

[!IMPORTANT] When you run the orbit instance, it will not block the current goroutine. This means you can continue doing other things after running the orbit instance.

If you want to block the current goroutine, you can use the project GS to provide a Waitting function to block the current goroutine.

[!TIP] To simplify the process, you can use NewHttpService to wrap the func(*gin.RouterGroup) into an implementation of the Service interface.

NewHttpService(func(g *gin.RouterGroup) {
  g.GET("/demo", func(c *gin.Context) {
      c.String(http.StatusOK, "demo")
  })
})

Example

package main

import (
	"time"

	"github.com/shengyanli1982/orbit"
)

func main() {
	// 创建一个新的 Orbit 配置
	// Create a new Orbit configuration
	config := orbit.NewConfig()

	// 创建一个新的 Orbit 功能选项
	// Create a new Orbit feature options
	opts := orbit.NewOptions()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 启动引擎
	// Start the engine
	engine.Run()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
{"level":"INFO","time":"2024-01-10T17:00:13.139+0800","logger":"default","caller":"orbit/gin.go:160","message":"http server is ready","address":"127.0.0.1:8080"}

Testing

$ curl -i http://127.0.0.1:8080/ping
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Wed, 10 Jan 2024 09:07:26 GMT
Content-Length: 7

successs

4. Custom Middleware

orbit is based on gin, so you can directly use gin middleware. This allows you to implement custom middleware for specific tasks. For example, you can use the demo middleware to print >>>>>>!!! demo in the console.

Here is an example of using custom middleware in orbit:

Example

package main

import (
	"fmt"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
)

// customMiddleware 函数定义了一个自定义的中间件
// The customMiddleware function defines a custom middleware
func customMiddleware() gin.HandlerFunc {
	// 返回一个 Gin 的 HandlerFunc
	// Return a Gin HandlerFunc
	return func(c *gin.Context) {
		// 打印一条消息
		// Print a message
		fmt.Println(">>>>>>!!! demo")

		// 调用下一个中间件或处理函数
		// Call the next middleware or handler function
		c.Next()
	}
}

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 在 "/demo" 路径上注册一个 GET 方法的处理函数
	// Register a GET method handler function on the "/demo" path
	g.GET("/demo", func(c *gin.Context) {})
}

func main() {
	// 创建一个新的 Orbit 配置
	// Create a new Orbit configuration
	config := orbit.NewConfig()

	// 创建一个新的 Orbit 功能选项,并启用 metric
	// Create a new Orbit feature options and enable metric
	opts := orbit.NewOptions().EnableMetric()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的中间件
	// Register a custom middleware
	engine.RegisterMiddleware(customMiddleware())

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

$ go run demo.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
[GIN-debug] GET    /metrics                  --> github.com/shengyanli1982/orbit/utils/wrapper.WrapHandlerToGin.func1 (2 handlers)
[GIN-debug] GET    /demo                     --> main.(*service).RegisterGroup.func1 (7 handlers)
{"level":"INFO","time":"2024-01-10T20:03:38.869+0800","logger":"default","caller":"orbit/gin.go:162","message":"http server is ready","address":"127.0.0.1:8080"}
>>>>>>!!! demo
{"level":"INFO","time":"2024-01-10T20:03:41.275+0800","logger":"default","caller":"log/default.go:10","message":"http server access log","id":"","ip":"127.0.0.1","endpoint":"127.0.0.1:59787","path":"/demo","method":"GET","code":200,"status":"OK","latency":"780ns","agent":"curl/8.1.2","query":"","reqContentType":"","reqBody":""}

5. Custom Router Group

The custom router group feature in orbit allows you to register a custom router group for the demo service. For example, you can register routes like /demo and /demo/test.

Example

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	ocom "github.com/shengyanli1982/orbit/common"
)

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 注册一个自定义的路由组 "/demo"
	// Register a custom router group "/demo"
	g = g.Group("/demo")

	// 在 "/demo" 路径上注册一个 GET 方法的处理函数
	// Register a GET method handler function on the "/demo" path
	g.GET(ocom.EmptyURLPath, func(c *gin.Context) {
		// 返回 HTTP 状态码 200 和 "demo" 字符串
		// Return HTTP status code 200 and the string "demo"
		c.String(http.StatusOK, "demo")
	})

	// 在 "/demo/test" 路径上注册一个 GET 方法的处理函数
	// Register a GET method handler function on the "/demo/test" path
	g.GET("/test", func(c *gin.Context) {
		// 返回 HTTP 状态码 200 和 "test" 字符串
		// Return HTTP status code 200 and the string "test"
		c.String(http.StatusOK, "test")
	})
}

func main() {
	// 创建一个新的 Orbit 配置
	// Create a new Orbit configuration
	config := orbit.NewConfig()

	// 创建一个新的 Orbit 功能选项,并启用 metric
	// Create a new Orbit feature options and enable metric
	opts := orbit.NewOptions().EnableMetric()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

$ curl -i http://127.0.0.1:8080/demo
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, UPDATE
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type
Access-Control-Max-Age: 172800
Content-Type: text/plain; charset=utf-8
Date: Wed, 10 Jan 2024 12:09:37 GMT
Content-Length: 4

demo

$ curl -i http://127.0.0.1:8080/demo/test
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, UPDATE
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type
Access-Control-Max-Age: 172800
Content-Type: text/plain; charset=utf-8
Date: Wed, 10 Jan 2024 12:09:43 GMT
Content-Length: 4

test

6. Custom Access Log

To customize the access log format and fields in orbit, you can use the following example:

Default LogEvent Fields

// LogEvent 结构体用于记录日志事件
// The LogEvent struct is used to log events
type LogEvent struct {
	// Message 字段表示日志消息
	// The Message field represents the log message
	Message string `json:"message,omitempty" yaml:"message,omitempty"`

	// ID 字段表示事件的唯一标识符
	// The ID field represents the unique identifier of the event
	ID string `json:"id,omitempty" yaml:"id,omitempty"`

	// IP 字段表示发起请求的IP地址
	// The IP field represents the IP address of the request initiator
	IP string `json:"ip,omitempty" yaml:"ip,omitempty"`

	// EndPoint 字段表示请求的终端点
	// The EndPoint field represents the endpoint of the request
	EndPoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`

	// Path 字段表示请求的路径
	// The Path field represents the path of the request
	Path string `json:"path,omitempty" yaml:"path,omitempty"`

	// Method 字段表示请求的HTTP方法
	// The Method field represents the HTTP method of the request
	Method string `json:"method,omitempty" yaml:"method,omitempty"`

	// Code 字段表示响应的HTTP状态码
	// The Code field represents the HTTP status code of the response
	Code int `json:"statusCode,omitempty" yaml:"statusCode,omitempty"`

	// Status 字段表示请求的状态
	// The Status field represents the status of the request
	Status string `json:"status,omitempty" yaml:"status,omitempty"`

	// Latency 字段表示请求的延迟时间
	// The Latency field represents the latency of the request
	Latency string `json:"latency,omitempty" yaml:"latency,omitempty"`

	// Agent 字段表示发起请求的用户代理
	// The Agent field represents the user agent of the request initiator
	Agent string `json:"agent,omitempty" yaml:"agent,omitempty"`

	// ReqContentType 字段表示请求的内容类型
	// The ReqContentType field represents the content type of the request
	ReqContentType string `json:"reqContentType,omitempty" yaml:"reqContentType,omitempty"`

	// ReqQuery 字段表示请求的查询参数
	// The ReqQuery field represents the query parameters of the request
	ReqQuery string `json:"query,omitempty" yaml:"query,omitempty"`

	// ReqBody 字段表示请求的主体内容
	// The ReqBody field represents the body of the request
	ReqBody string `json:"reqBody,omitempty" yaml:"reqBody,omitempty"`

	// Error 字段表示请求中的任何错误
	// The Error field represents any errors in the request
	Error any `json:"error,omitempty" yaml:"error,omitempty"`

	// ErrorStack 字段表示错误的堆栈跟踪
	// The ErrorStack field represents the stack trace of the error
	ErrorStack string `json:"errorStack,omitempty" yaml:"errorStack,omitempty"`
}

Example

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/log"
	"go.uber.org/zap"
)

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 在 "/demo" 路径上注册一个 GET 方法的处理函数
	// Register a GET method handler function on the "/demo" path
	g.GET("/demo", func(c *gin.Context) {
		// 返回 HTTP 状态码 200 和 "demo" 字符串
		// Return HTTP status code 200 and the string "demo"
		c.String(http.StatusOK, "demo")
	})
}

// customAccessLogger 函数定义了一个自定义的访问日志记录器
// The customAccessLogger function defines a custom access logger
func customAccessLogger(logger *zap.SugaredLogger, event *log.LogEvent) {
	// 记录访问日志,包括路径和方法
	// Log the access, including the path and method
	logger.Infow("access log", "path", event.Path, "method", event.Method)
}

func main() {
	// 创建一个新的 Orbit 配置,并设置访问日志事件函数
	// Create a new Orbit configuration and set the access log event function
	config := orbit.NewConfig().WithAccessLogEventFunc(customAccessLogger)

	// 创建一个新的 Orbit 功能选项
	// Create a new Orbit feature options
	opts := orbit.NewOptions()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

{"level":"INFO","time":"2024-01-10T20:22:01.244+0800","logger":"default","caller":"accesslog/demo.go:24","message":"access log","path":"/demo","method":"GET"}

7. Custom Recovery Log

Http server recovery log allows you to understand what happened when your service encounters a panic. With orbit, you can customize the recovery log format and fields. Here is an example of how to customize the recovery log format and fields:

Example

package main

import (
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/log"
	"go.uber.org/zap"
)

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 在 "/demo" 路径上注册一个 GET 方法的处理函数,该函数会触发 panic
	// Register a GET method handler function on the "/demo" path, this function will trigger a panic
	g.GET("/demo", func(c *gin.Context) {
		panic("demo")
	})
}

// customRecoveryLogger 函数定义了一个自定义的恢复日志记录器
// The customRecoveryLogger function defines a custom recovery logger
func customRecoveryLogger(logger *zap.SugaredLogger, event *log.LogEvent) {
	// 记录恢复日志,包括路径、方法、错误和错误堆栈
	// Log the recovery, including the path, method, error, and error stack
	logger.Infow("recovery log", "path", event.Path, "method", event.Method, "error", event.Error, "errorStack", event.ErrorStack)
}

func main() {
	// 创建一个新的 Orbit 配置,并设置恢复日志事件函数
	// Create a new Orbit configuration and set the recovery log event function
	config := orbit.NewConfig().WithRecoveryLogEventFunc(customRecoveryLogger)

	// 创建一个新的 Orbit 功能选项
	// Create a new Orbit feature options
	opts := orbit.NewOptions()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

{"level":"INFO","time":"2024-01-10T20:27:10.041+0800","logger":"default","caller":"recoverylog/demo.go:22","message":"recovery log","path":"/demo","method":"GET","error":"demo","errorStack":"goroutine 6 [running]:\nruntime/debug.Stack()\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x65\ngithub.com/shengyanli1982/orbit/internal/middleware.Recovery.func1.1()\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:145 +0x559\npanic({0x170ec80, 0x191cb70})\n\t/usr/local/go/src/runtime/panic.go:884 +0x213\nmain.(*service).RegisterGroup.func1(0x0?)\n\t/Volumes/DATA/programs/GolandProjects/orbit/example/recoverylog/demo.go:17 +0x27\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.AccessLogger.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:59 +0x1a5\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.Cors.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:35 +0x139\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.BodyBuffer.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/buffer.go:18 +0x92\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/shengyanli1982/orbit/internal/middleware.Recovery.func1(0xc0001e6300)\n\t/Volumes/DATA/programs/GolandProjects/orbit/internal/middleware/system.go:166 +0x82\ngithub.com/gin-gonic/gin.(*Context).Next(...)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/context.go:173\ngithub.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0000076c0, 0xc0001e6300)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/gin.go:616 +0x66b\ngithub.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0000076c0, {0x1924a30?, 0xc0000c02a0}, 0xc0001e6200)\n\t/Volumes/CACHE/programs/gopkgs/pkg/mod/github.com/gin-gonic/gin@v1.8.2/gin.go:572 +0x1dd\nnet/http.serverHandler.ServeHTTP({0xc00008be30?}, {0x1924a30, 0xc0000c02a0}, 0xc0001e6200)\n\t/usr/local/go/src/net/http/server.go:2936 +0x316\nnet/http.(*conn).serve(0xc0000962d0, {0x19253e0, 0xc00008bd40})\n\t/usr/local/go/src/net/http/server.go:1995 +0x612\ncreated by net/http.(*Server).Serve\n\t/usr/local/go/src/net/http/server.go:3089 +0x5ed\n"}

8. Prometheus Metrics

orbit supports prometheus metrics. You can enable it using EnableMetric. Here is an example of how to collect demo metrics using the demo service.

[!TIP] Use curl http://127.0.0.1:8080/metrics to get metrics.

Example

package main

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
)

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 在 "/demo" 路径上注册一个 GET 方法的处理函数
	// Register a GET method handler function on the "/demo" path
	g.GET("/demo", func(c *gin.Context) {
		// 返回 HTTP 状态码 200 和 "demo" 字符串
		// Return HTTP status code 200 and the string "demo"
		c.String(http.StatusOK, "demo")
	})
}

func main() {
	// 创建一个新的 Orbit 配置
	// Create a new Orbit configuration
	config := orbit.NewConfig()

	// 创建一个新的 Orbit 功能选项,并启用 metric
	// Create a new Orbit feature options and enable metric
	opts := orbit.NewOptions().EnableMetric()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

# HELP orbit_http_request_latency_seconds HTTP request latencies in seconds.
# TYPE orbit_http_request_latency_seconds gauge
orbit_http_request_latency_seconds{method="GET",path="/demo",status="200"} 0
# HELP orbit_http_request_latency_seconds_histogram HTTP request latencies in seconds(Histogram).
# TYPE orbit_http_request_latency_seconds_histogram histogram
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="0.1"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="0.5"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="1"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="2"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="5"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="10"} 3
orbit_http_request_latency_seconds_histogram_bucket{method="GET",path="/demo",status="200",le="+Inf"} 3
orbit_http_request_latency_seconds_histogram_sum{method="GET",path="/demo",status="200"} 0
orbit_http_request_latency_seconds_histogram_count{method="GET",path="/demo",status="200"} 3

9. Repeat Read Request/Response Body

orbit supports repeating the read request/response body. By default, this behavior is enabled and requires no additional configuration. Here is an example of how to use the demo service to repeat read the request/response body.

9.1 Repeat Read Request Body

You can use the httptool.GenerateRequestBody method to obtain the request body bytes and cache them. This allows you to read the cached bytes when needed.

[!IMPORTANT] The request body is an io.ReadCloser, which is a stream that can only be read once. If you need to read it again, do not read it directly. Instead, use orbit to cache it.

Example

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/httptool"
)

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 在 "/demo" 路径上注册一个 POST 方法的处理函数
	// Register a POST method handler function on the "/demo" path
	g.POST("/demo", func(c *gin.Context) {
		// 重复读取请求体内容 20 次
		// Repeat the read request body content 20 times
		for i := 0; i < 20; i++ {
			// 生成请求体
			// Generate the request body
			if body, err := httptool.GenerateRequestBody(c); err != nil {
				// 如果生成请求体出错,返回 HTTP 状态码 500 和错误信息
				// If there is an error generating the request body, return HTTP status code 500 and the error message
				c.String(http.StatusInternalServerError, err.Error())
			} else {
				// 如果生成请求体成功,返回 HTTP 状态码 200 和请求体内容
				// If the request body is successfully generated, return HTTP status code 200 and the request body content
				c.String(http.StatusOK, fmt.Sprintf(">> %d, %s\n", i, string(body)))
			}
		}
	})
}

func main() {
	// 创建一个新的 Orbit 配置
	// Create a new Orbit configuration
	config := orbit.NewConfig()

	// 创建一个新的 Orbit 功能选项
	// Create a new Orbit feature options
	opts := orbit.NewOptions()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 模拟一个请求
	// Simulate a request
	resp, _ := http.Post("http://localhost:8080/demo", "text/plain", io.Reader(bytes.NewBuffer([]byte("demo"))))
	defer resp.Body.Close()

	// 打印响应体
	// Print the response body
	buf := new(bytes.Buffer)
	_, _ = buf.ReadFrom(resp.Body)
	fmt.Println(buf.String())

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

$ go run demo.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
[GIN-debug] POST   /demo                     --> main.(*service).RegisterGroup.func1 (5 handlers)
{"level":"INFO","time":"2024-01-13T10:27:37.531+0800","logger":"default","caller":"orbit/gin.go:165","message":"http server is ready","address":"127.0.0.1:8080"}
{"level":"INFO","time":"2024-01-13T10:27:37.534+0800","logger":"default","caller":"log/default.go:10","message":"http server access log","id":"","ip":"127.0.0.1","endpoint":"127.0.0.1:58618","path":"/demo","method":"POST","code":200,"status":"OK","latency":"50.508µs","agent":"Go-http-client/1.1","query":"","reqContentType":"text/plain","reqBody":""}

>> 0, demo
>> 1, demo
>> 2, demo
>> 3, demo
>> 4, demo
>> 5, demo
>> 6, demo
>> 7, demo
>> 8, demo
>> 9, demo
>> 10, demo
>> 11, demo
>> 12, demo
>> 13, demo
>> 14, demo
>> 15, demo
>> 16, demo
>> 17, demo
>> 18, demo
>> 19, demo

{"level":"INFO","time":"2024-01-13T10:28:07.537+0800","logger":"default","caller":"orbit/gin.go:190","message":"http server is shutdown","address":"127.0.0.1:8080"}
9.2 Repeat Read Response Body

The httptool.GenerateResponseBody method can be used to retrieve the response body bytes from the cache. It is important to note that you should call httptool.GenerateResponseBody after writing the actual response body, such as using c.String(http.StatusOK, "demo").

[!NOTE] The response body is always written to an io.Writer, so direct reading is not possible. If you need to read it, you can use orbit to cache it.

httptool.GenerateResponseBody is often used in custom middleware to retrieve the response body bytes and perform additional actions.

Example

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shengyanli1982/orbit"
	"github.com/shengyanli1982/orbit/utils/httptool"
)

// customMiddleware 函数定义了一个自定义的中间件
// The customMiddleware function defines a custom middleware
func customMiddleware() gin.HandlerFunc {
	// 返回一个 Gin 的 HandlerFunc
	// Return a Gin HandlerFunc
	return func(c *gin.Context) {
		// 调用下一个中间件或处理函数
		// Call the next middleware or handler function
		c.Next()

		// 从上下文中获取响应体缓冲区
		// Get the response body buffer from the context
		for i := 0; i < 20; i++ {
			// 生成响应体
			// Generate the response body
			body, _ := httptool.GenerateResponseBody(c)
			// 打印响应体
			// Print the response body
			fmt.Printf("# %d, %s\n", i, string(body))
		}
	}
}

// 定义 service 结构体
// Define the service struct
type service struct{}

// RegisterGroup 方法将路由组注册到 service
// The RegisterGroup method registers a router group to the service
func (s *service) RegisterGroup(g *gin.RouterGroup) {
	// 在 "/demo" 路径上注册一个 GET 方法的处理函数
	// Register a GET method handler function on the "/demo" path
	g.GET("/demo", func(c *gin.Context) {
		// 返回 HTTP 状态码 200 和 "demo" 字符串
		// Return HTTP status code 200 and the string "demo"
		c.String(http.StatusOK, "demo")
	})
}

func main() {
	// 创建一个新的 Orbit 配置
	// Create a new Orbit configuration
	config := orbit.NewConfig()

	// 创建一个新的 Orbit 功能选项,并启用 metric
	// Create a new Orbit feature options and enable metric
	opts := orbit.NewOptions().EnableMetric()

	// 创建一个新的 Orbit 引擎
	// Create a new Orbit engine
	engine := orbit.NewEngine(config, opts)

	// 注册一个自定义的中间件
	// Register a custom middleware
	engine.RegisterMiddleware(customMiddleware())

	// 注册一个自定义的路由组
	// Register a custom router group
	engine.RegisterService(&service{})

	// 启动引擎
	// Start the engine
	engine.Run()

	// 模拟一个请求
	// Simulate a request
	resp, _ := http.Get("http://localhost:8080/demo")
	defer resp.Body.Close()

	// 等待 30 秒
	// Wait for 30 seconds
	time.Sleep(30 * time.Second)

	// 停止引擎
	// Stop the engine
	engine.Stop()
}

Result

$ go run demo.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> github.com/shengyanli1982/orbit.healthcheckService.func1 (1 handlers)
[GIN-debug] GET    /metrics                  --> github.com/shengyanli1982/orbit/utils/wrapper.WrapHandlerToGin.func1 (2 handlers)
[GIN-debug] GET    /demo                     --> main.(*service).RegisterGroup.func1 (7 handlers)
{"level":"INFO","time":"2024-01-13T10:32:25.191+0800","logger":"default","caller":"orbit/gin.go:165","message":"http server is ready","address":"127.0.0.1:8080"}
{"level":"INFO","time":"2024-01-13T10:32:25.194+0800","logger":"default","caller":"log/default.go:10","message":"http server access log","id":"","ip":"127.0.0.1","endpoint":"127.0.0.1:59139","path":"/demo","method":"GET","code":200,"status":"OK","latency":"20.326µs","agent":"Go-http-client/1.1","query":"","reqContentType":"","reqBody":""}

# 0, demo
# 1, demo
# 2, demo
# 3, demo
# 4, demo
# 5, demo
# 6, demo
# 7, demo
# 8, demo
# 9, demo
# 10, demo
# 11, demo
# 12, demo
# 13, demo
# 14, demo
# 15, demo
# 16, demo
# 17, demo
# 18, demo
# 19, demo

{"level":"INFO","time":"2024-01-13T10:32:55.195+0800","logger":"default","caller":"orbit/gin.go:190","message":"http server is shutdown","address":"127.0.0.1:8080"}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Accounts

type Accounts = gin.Accounts

Accounts 是 gin.Accounts 的别名,表示账户集合。 Accounts is an alias for gin.Accounts, representing a collection of accounts.

type Config

type Config struct {
	// Address 是监听的地址。
	// Address is the address to listen on.
	Address string `json:"address,omitempty" yaml:"address,omitempty"`

	// Port 是监听的端口。
	// Port is the port to listen on.
	Port uint16 `json:"port,omitempty" yaml:"port,omitempty"`

	// ReleaseMode 是发布模式标志。
	// ReleaseMode is the release mode flag.
	ReleaseMode bool `json:"releaseMode,omitempty" yaml:"releaseMode,omitempty"`

	// HttpReadTimeout 是 HTTP 读取超时时间。
	// HttpReadTimeout is the HTTP read timeout.
	HttpReadTimeout uint32 `json:"httpReadTimeout,omitempty" yaml:"httpReadTimeout,omitempty"`

	// HttpWriteTimeout 是 HTTP 写入超时时间。
	// HttpWriteTimeout is the HTTP write timeout.
	HttpWriteTimeout uint32 `json:"httpWriteTimeout,omitempty" yaml:"httpWriteTimeout,omitempty"`

	// HttpReadHeaderTimeout 是 HTTP 读取头部超时时间。
	// HttpReadHeaderTimeout is the HTTP read header timeout.
	HttpReadHeaderTimeout uint32 `json:"httpReadHeaderTimeout,omitempty" yaml:"httpReadHeaderTimeout,omitempty"`
	// contains filtered or unexported fields
}

Config 结构体表示 Orbit 框架的配置。 The Config struct represents the configuration for the Orbit framework.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig 返回一个带有默认值的新 Config 实例。 DefaultConfig returns a new Config instance with default values.

func NewConfig

func NewConfig() *Config

NewConfig 创建一个新的 Config 实例,并使用默认值。 NewConfig creates a new Config instance with default values.

func (*Config) WithAccessLogEventFunc

func (c *Config) WithAccessLogEventFunc(fn com.LogEventFunc) *Config

WithAccessLogEventFunc 为 Config 实例设置一个新的访问日志事件函数。 WithAccessLogEventFunc sets a new access log event function for the Config instance.

func (*Config) WithAddress

func (c *Config) WithAddress(address string) *Config

WithAddress 为 Config 实例设置一个新的监听地址。 WithAddress sets a new address for the Config instance.

func (*Config) WithHttpReadHeaderTimeout

func (c *Config) WithHttpReadHeaderTimeout(timeout uint32) *Config

WithHttpReadHeaderTimeout 为 Config 实例设置一个新的 HTTP 读取头部超时时间。 WithHttpReadHeaderTimeout sets a new HTTP read header timeout for the Config instance.

func (*Config) WithHttpReadTimeout

func (c *Config) WithHttpReadTimeout(timeout uint32) *Config

WithHttpReadTimeout 为 Config 实例设置一个新的 HTTP 读取超时时间。 WithHttpReadTimeout sets a new HTTP read timeout for the Config instance.

func (*Config) WithHttpWriteTimeout

func (c *Config) WithHttpWriteTimeout(timeout uint32) *Config

WithHttpWriteTimeout 为 Config 实例设置一个新的 HTTP 写入超时时间。 WithHttpWriteTimeout sets a new HTTP write timeout for the Config instance.

func (*Config) WithLogger

func (c *Config) WithLogger(logger *zap.Logger) *Config

WithLogger 为 Config 实例设置一个新的 logger。 WithLogger sets a new logger for the Config instance.

func (*Config) WithPort

func (c *Config) WithPort(port uint16) *Config

WithPort 为 Config 实例设置一个新的监听端口。 WithPort sets a new port for the Config instance.

func (*Config) WithPrometheusRegistry

func (c *Config) WithPrometheusRegistry(registry *prometheus.Registry) *Config

WithPrometheusRegistry 为 Config 实例设置一个新的 Prometheus 注册表。 WithPrometheusRegistry sets a new Prometheus registry for the Config instance.

func (*Config) WithRecoveryLogEventFunc

func (c *Config) WithRecoveryLogEventFunc(fn com.LogEventFunc) *Config

WithRecoveryLogEventFunc 为 Config 实例设置一个新的恢复日志事件函数。 WithRecoveryLogEventFunc sets a new recovery log event function for the Config instance.

func (*Config) WithRelease

func (c *Config) WithRelease() *Config

WithRelease 将 Config 实例设置为发布模式。 WithRelease sets the Config instance to release mode.

func (*Config) WithSugaredLogger

func (c *Config) WithSugaredLogger(logger *zap.SugaredLogger) *Config

WithSugaredLogger 为 Config 实例设置一个新的 sugared logger。 WithSugaredLogger sets a new sugared logger for the Config instance.

type Context

type Context = gin.Context

Context 是 gin.Context 的别名,表示请求上下文。 Context is an alias for gin.Context, representing the request context.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine 是表示 Orbit 引擎的主要结构体。 Engine is the main struct that represents the Orbit engine.

func NewEngine

func NewEngine(config *Config, options *Options) *Engine

NewEngine 函数用于创建一个新的 Engine 实例。 The NewEngine function is used to create a new instance of the Engine.

func (*Engine) GetListenEndpoint

func (e *Engine) GetListenEndpoint() string

GetListenEndpoint 方法返回 Orbit 引擎的监听端点。 The GetListenEndpoint method returns the listen endpoint of the Orbit engine.

func (*Engine) GetLogger

func (e *Engine) GetLogger() *zap.SugaredLogger

GetLogger 方法返回 Orbit 引擎的日志记录器。 The GetLogger method returns the logger of the Orbit engine.

func (*Engine) GetPrometheusRegistry

func (e *Engine) GetPrometheusRegistry() *prometheus.Registry

GetPrometheusRegistry 方法返回 Orbit 引擎的 Prometheus 注册表。 The GetPrometheusRegistry method returns the Prometheus registry of the Orbit engine.

func (*Engine) IsMetricEnabled

func (e *Engine) IsMetricEnabled() bool

IsMetricEnabled 方法返回 Orbit 引擎的 metric 状态。 The IsMetricEnabled method returns the metric status of the Orbit engine.

func (*Engine) IsReleaseMode

func (e *Engine) IsReleaseMode() bool

IsReleaseMode 方法返回 Orbit 引擎的运行模式。 The IsReleaseMode method returns the running mode of the Orbit engine.

func (*Engine) IsRunning

func (e *Engine) IsRunning() bool

IsRunning 方法返回 Orbit 引擎是否正在运行。 The IsRunning method returns whether the Orbit engine is running.

func (*Engine) RegisterMiddleware

func (e *Engine) RegisterMiddleware(handler gin.HandlerFunc)

RegisterMiddleware 方法将一个中间件注册到 Orbit 引擎。 The RegisterMiddleware method registers a middleware to the Orbit engine.

func (*Engine) RegisterService

func (e *Engine) RegisterService(service Service)

RegisterService 方法将一个服务注册到 Orbit 引擎。 The RegisterService method registers a service to the Orbit engine.

func (*Engine) Run

func (e *Engine) Run()

Run 方法用于启动 Engine The Run method is used to start the Engine

func (*Engine) Stop

func (e *Engine) Stop()

Stop 方法用于停止 Engine The Stop method is used to stop the Engine

type HandlerFunc

type HandlerFunc = gin.HandlerFunc

HandlerFunc 是 gin.HandlerFunc 的别名,表示处理 HTTP 请求的函数。 HandlerFunc is an alias for gin.HandlerFunc, representing a function that handles HTTP requests.

type HandlersChain

type HandlersChain = gin.HandlersChain

HandlersChain 是 gin.HandlersChain 的别名,表示处理器链。 HandlersChain is an alias for gin.HandlersChain, representing a chain of handlers.

type Options

type Options struct {
	// contains filtered or unexported fields
}

Options 表示应用程序的配置选项。 Options represents the configuration options for the application.

func DebugOptions

func DebugOptions() *Options

DebugOptions 返回一个 Options 实例,该实例启用了 pprof、swagger、metric 和请求体记录功能,用于调试环境。 DebugOptions returns an Options instance that enables pprof, swagger, metric, and request body recording, for debugging environment.

func NewOptions

func NewOptions() *Options

NewOptions 创建一个新的 Options 实例。 NewOptions creates a new instance of Options.

func ReleaseOptions

func ReleaseOptions() *Options

ReleaseOptions 返回一个 Options 实例,该实例仅启用了 metric 功能,用于生产环境。 ReleaseOptions returns an Options instance that only enables the metric function, for production environment.

func (*Options) EnableForwardedByClientIp

func (o *Options) EnableForwardedByClientIp() *Options

EnableForwardedByClientIp 启用客户端 IP 转发。 EnableForwardedByClientIp enables the client IP forwarding.

func (*Options) EnableMetric

func (o *Options) EnableMetric() *Options

EnableMetric 启用度量收集。 EnableMetric enables the metric collection.

func (*Options) EnablePProf

func (o *Options) EnablePProf() *Options

EnablePProf 启用 pprof 端点。 EnablePProf enables the pprof endpoint.

func (*Options) EnableRecordRequestBody

func (o *Options) EnableRecordRequestBody() *Options

EnableRecordRequestBody 启用请求体记录。 EnableRecordRequestBody enables the recording request body.

func (*Options) EnableRedirectFixedPath

func (o *Options) EnableRedirectFixedPath() *Options

EnableRedirectFixedPath 启用固定路径重定向。 EnableRedirectFixedPath enables the fixed path redirection.

func (*Options) EnableRedirectTrailingSlash

func (o *Options) EnableRedirectTrailingSlash() *Options

EnableRedirectTrailingSlash 启用尾部斜杠重定向。 EnableRedirectTrailingSlash enables the trailing slash redirection.

func (*Options) EnableSwagger

func (o *Options) EnableSwagger() *Options

EnableSwagger 启用 swagger 文档。 EnableSwagger enables the swagger documentation.

type RouterGroup

type RouterGroup = gin.RouterGroup

RouterGroup 是 gin.RouterGroup 的别名,表示路由组。 RouterGroup is an alias for gin.RouterGroup, representing a group of routes.

type Service

type Service interface {
	// RegisterGroup 方法用于将服务注册到路由组。
	// The RegisterGroup method is used to register the service to the router group.
	RegisterGroup(routerGroup *gin.RouterGroup)
}

Service 是表示服务的接口。 Service is the interface that represents a service.

type WrapRegisterService

type WrapRegisterService struct {
	// contains filtered or unexported fields
}

WrapRegisterService 是服务注册函数的包装器。 WrapRegisterService is a wrapper for the service registration function.

func NewHttpService

func NewHttpService(registerFunc func(*gin.RouterGroup)) *WrapRegisterService

NewHttpService 函数创建一个新的 WrapRegisterService 实例。 The NewHttpService function creates a new instance of the WrapRegisterService.

func (*WrapRegisterService) RegisterGroup

func (w *WrapRegisterService) RegisterGroup(group *gin.RouterGroup)

RegisterGroup 函数将服务注册到给定的路由组。 The RegisterGroup function registers the service to the given router group.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL