gorilla/mux framework (rk-boot): add call chaining (Tracing) middleware

introduce

Through a complete example, add call chaining (Tracing) middleware to gorilla/mux -based microservices .

What is Tracing Middleware?

The call chain (Tracing) middleware records the tracing data for each API request, and users can use tools like Jaeger to view it.

We will use rk-boot to start the gorilla/mux microservice . rk-boot is a framework for launching various web services via YAML. Please refer to the last chapter of this article for details on rk-boot .

Please visit the following address for the full tutorial: https://github.com/rookie-ninja/rk-mux

Install

go get github.com/rookie-ninja/rk-boot/mux

quick start

rk-boot will use OpenTelemetry-CNCF to handle tracing by default.

1. Create boot.yaml

To verify, we enabled the following options:

  • commonService : commonService contains a series of common APIs. Details
  • jaeger exporter : The gorilla/mux service will send data to the local jaeger agent.
---
mux:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      tracingTelemetry:
        enabled: true                 # Optional, Enable tracing interceptor/middleware
        exporter:
          jaeger:
            agent:
              enabled: true           # Optional, Export to jaeger agent

2. Create main.go

Added /v1/greeter API.

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"fmt"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-boot/mux"
	"github.com/rookie-ninja/rk-mux/interceptor"
	"github.com/rookie-ninja/rk-mux/interceptor/context"
	"net/http"
)

func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Register handler
	entry := rkbootmux.GetMuxEntry("greeter")
	entry.Router.NewRoute().Methods(http.MethodGet).Path("/v1/greeter").HandlerFunc(Greeter)

	// Bootstrap
	boot.Bootstrap(context.TODO())

	boot.WaitForShutdownSig(context.TODO())
}

func Greeter(writer http.ResponseWriter, request *http.Request) {
	rkmuxinter.WriteJson(writer, http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", request.URL.Query().Get("name")),
	})
}

type GreeterResponse struct {
	Message string
}

3. Folder structure

$ tree
.
├── boot.yaml
├── go.mod
├── go.sum
└── main.go

0 directories, 4 files

4. Start jaeger locally

$ docker run -d --name jaeger \
    -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
    -p 5775:5775/udp \
    -p 6831:6831/udp \
    -p 6832:6832/udp \
    -p 5778:5778 \
    -p 16686:16686 \
    -p 14268:14268 \
    -p 14250:14250 \
    -p 9411:9411 \
    jaegertracing/all-in-one:1.23

5. Start main.go

$ go run main.go

6. Verify

  • Send a request to the /rk/v1/healthy API in CommonService.
$ curl -X GET localhost:8080/rk/v1/healthy
{"healthy":true}
  • Send a request to the /v1/greeter API.
$ curl -X GET "localhost:8080/v1/greeter?name=rk-dev"
{"Message":"Hello rk-dev!"}

If AppName is not specified through rkentry.GlobalAppCtx.GetAppInfoEntry().AppName, rk is used as AppName by default.

output to Stdout

The output path, such as STDOUT, can be modified by modifying the boot.yaml file.

  • boot.yaml
---
mux:
  - name: greeter                     # Required
    port: 8080                        # Required
    enabled: true                     # Required
    commonService:
      enabled: true                   # Optional, default: false
    interceptors:
      tracingTelemetry:
        enabled: true                 # Optional, Enable tracing interceptor/middleware
        exporter:
          file:
            enabled: true
            outputPath: "stdout"      # Optional, Output to stdout

output to file

Tracing information can be saved to the file by modifying the boot.yaml file.

  • boot.yaml
---
mux:
  - name: greeter                             # Required
    port: 8080                                # Required
    enabled: true                             # Required
    commonService:
      enabled: true                           # Optional, default: false
    interceptors:
      tracingTelemetry:
        enabled: true                         # Optional, Enable tracing interceptor/middleware
        exporter:
          file:
            enabled: true
            outputPath: "logs/tracing.log"    # Optional, Log to files

YAML options

name describe Types of Defaults
mux.interceptors.tracingTelemetry.enabled Start the call chain interceptor boolean false
mux.interceptors.tracingTelemetry.exporter.file.enabled start file output boolean false
mux.interceptors.tracingTelemetry.exporter.file.outputPath output file path string stdout
mux.interceptors.tracingTelemetry.exporter.jaeger.agent.enabled jaeger agent as data output boolean false
mux.interceptors.tracingTelemetry.exporter.jaeger.agent.host jaeger agent address string localhost
mux.interceptors.tracingTelemetry.exporter.jaeger.agent.port jaeger agent port int 6831
mux.interceptors.tracingTelemetry.exporter.jaeger.collector.enabled jaeger collector as data output boolean false
mux.interceptors.tracingTelemetry.exporter.jaeger.collector.endpoint jaeger collector address string http://localhost:16368/api/trace
mux.interceptors.tracingTelemetry.exporter.jaeger.collector.username jaeger collector username string ""
mux.interceptors.tracingTelemetry.exporter.jaeger.collector.password jaeger collector password string ""

Introduction to rk-boot

rk-boot is a framework for launching various web services via YAML. Somewhat similar to Spring boot. By integrating the rk-xxx series of libraries, various web frameworks can be started. Of course, users can also customize the rk-xxx library and integrate it into rk-boot.

rk-boot highlights

Start different web frameworks through YAML files in the same format.

For example, we can start gRPC, Gin, Echo, GoFrame frameworks simultaneously in one process through the following files. Unify the microservice layout within the team.

  • Dependency installation
go get github.com/rookie-ninja/rk-boot/grpc
go get github.com/rookie-ninja/rk-boot/gin
go get github.com/rookie-ninja/rk-boot/echo
go get github.com/rookie-ninja/rk-boot/gf
  • boot.yaml
---
grpc:
  - name: grpc-server
    port: 8080
    enabled: true
    commonService:
      enabled: true
gin:
  - name: gin-server
    port: 8081
    enabled: true
    commonService:
      enabled: true
echo:
  - name: echo-server
    port: 8082
    enabled: true
    commonService:
      enabled: true
gf:
  - name: gf-server
    port: 8083
    enabled: true
    commonService:
      enabled: true
  • main.go
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-boot/echo"
	_ "github.com/rookie-ninja/rk-boot/gf"
	_ "github.com/rookie-ninja/rk-boot/gin"
	_ "github.com/rookie-ninja/rk-boot/grpc"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}
  • verify
# gRPC throuth grpc-gateway
$ curl localhost:8080/rk/v1/healthy
{"healthy":true}

# Gin
$ curl localhost:8081/rk/v1/healthy
{"healthy":true}

# Echo
$ curl localhost:8082/rk/v1/healthy
{"healthy":true}

# GoFrame
$ curl localhost:8083/rk/v1/healthy
{"healthy":true}

Web frameworks supported by rk-boot

Contributions of new web frameworks to the rk-boot series are welcome.

Refer to docs & rk-gin for example.

frame development status Install rely
Gin Stable go get github.com/rookie-ninja/rk-boot/gin rk-gin
gRPC Stable go get github.com/rookie-ninja/rk-boot/grpc rk-grpc
Echo Stable go get github.com/rookie-ninja/rk-boot/echo rk-echo
GoFrame Stable go get github.com/rookie-ninja/rk-boot/gf rk-gf
Fiber Testing go get github.com/rookie-ninja/rk-boot/fiber rk-fiber
go-zero Testing go get github.com/rookie-ninja/rk-boot/zero rk-zero
GorillaMux Testing go get github.com/rookie-ninja/rk-boot/mux rk-mux

rk-mux 介绍

rk-mux 用于通过 YAML 启动 gorilla/mux Web 服务。

支持的功能

根据 YAML 文件初始化如下的实例,如果是外部以来,均保持原生用法。

实例 介绍
mux.Router 原生 gorilla/mux
Config 原生 spf13/viper 参数实例
Logger 原生 uber-go/zap 日志实例
EventLogger 用于记录 RPC 请求日志,使用 rk-query
Credential 用于从远程服务,例如 ETCD 拉取 Credential
Cert 从远程服务(ETCD 等等)中获取 TLS/SSL 证书,并启动 SSL/TLS
Prometheus 启动 Prometheus 客户端,并根据需要推送到 pushgateway
Swagger 本地启动 Swagger UI
CommonService 暴露通用 API
TV TV 网页,展示微服务的基本信息
StaticFileHandler 启动 Web 形式的静态文件下载服务,后台存储支持本地文件系统 和 pkger.

支持的中间件

rk-mux 会根据 YAML 文件初始化中间件。

Middleware Description
Metrics 收集 RPC Metrics,并启动 prometheus
Log 使用 rk-query 记录每一个 RPC 日志
Trace 收集 RPC 调用链,并且发送数据到 stdout, 本地文件或者 jaeger open-telemetry/opentelemetry-go.
Panic Recover from panic for RPC requests and log it.
Meta 收集服务元信息,添加到返回 Header 中
Auth 支持 [Basic Auth] & [API Key] 验证中间件
RateLimit RPC 限速中间件
Timeout RPC 超时中间件
CORS CORS 中间件
JWT JWT 验证
Secure 服务端安全中间件
CSRF CSRF 中间件

Mux 完整 YAML 配置

---
#app:
#  description: "this is description"                      # Optional, default: ""
#  keywords: ["rk", "golang"]                              # Optional, default: []
#  homeUrl: "http://example.com"                           # Optional, default: ""
#  iconUrl: "http://example.com"                           # Optional, default: ""
#  docsUrl: ["http://example.com"]                         # Optional, default: []
#  maintainers: ["rk-dev"]                                 # Optional, default: []
#zapLogger:
#  - name: zap-logger                                      # Required
#    description: "Description of entry"                   # Optional
#eventLogger:
#  - name: event-logger                                    # Required
#    description: "Description of entry"                   # Optional
#cred:
#  - name: "local-cred"                                    # Required
#    provider: "localFs"                                   # Required, etcd, consul, localFs, remoteFs are supported options
#    description: "Description of entry"                   # Optional
#    locale: "*::*::*::*"                                  # Optional, default: *::*::*::*
#    paths:                                                # Optional
#      - "example/boot/full/cred.yaml"
#cert:
#  - name: "local-cert"                                    # Required
#    provider: "localFs"                                   # Required, etcd, consul, localFs, remoteFs are supported options
#    description: "Description of entry"                   # Optional
#    locale: "*::*::*::*"                                  # Optional, default: *::*::*::*
#    serverCertPath: "example/boot/full/server.pem"        # Optional, default: "", path of certificate on local FS
#    serverKeyPath: "example/boot/full/server-key.pem"     # Optional, default: "", path of certificate on local FS
#    clientCertPath: "example/client.pem"                  # Optional, default: "", path of certificate on local FS
#    clientKeyPath: "example/client.pem"                   # Optional, default: "", path of certificate on local FS
#config:
#  - name: rk-main                                         # Required
#    path: "example/boot/full/config.yaml"                 # Required
#    locale: "*::*::*::*"                                  # Required, default: *::*::*::*
#    description: "Description of entry"                   # Optional
mux:
  - name: greeter                                          # Required
    port: 8080                                             # Required
    enabled: true                                          # Required
#    description: "greeter server"                         # Optional, default: ""
#    cert:
#      ref: "local-cert"                                   # Optional, default: "", reference of cert entry declared above
#    sw:
#      enabled: true                                       # Optional, default: false
#      path: "sw"                                          # Optional, default: "sw"
#      jsonPath: ""                                        # Optional
#      headers: ["sw:rk"]                                  # Optional, default: []
#    commonService:
#      enabled: true                                       # Optional, default: false
#    static:
#      enabled: true                                       # Optional, default: false
#      path: "/rk/v1/static"                               # Optional, default: /rk/v1/static
#      sourceType: local                                   # Required, options: pkger, local
#      sourcePath: "."                                     # Required, full path of source directory
#    tv:
#      enabled:  true                                      # Optional, default: false
#    prom:
#      enabled: true                                       # Optional, default: false
#      path: ""                                            # Optional, default: "metrics"
#      pusher:
#        enabled: false                                    # Optional, default: false
#        jobName: "greeter-pusher"                         # Required
#        remoteAddress: "localhost:9091"                   # Required
#        basicAuth: "user:pass"                            # Optional, default: ""
#        intervalMs: 10000                                 # Optional, default: 1000
#        cert:                                             # Optional
#          ref: "local-test"                               # Optional, default: "", reference of cert entry declared above
#    logger:
#      zapLogger:
#        ref: zap-logger                                   # Optional, default: logger of STDOUT, reference of logger entry declared above
#      eventLogger:
#        ref: event-logger                                 # Optional, default: logger of STDOUT, reference of logger entry declared above
#    interceptors:
#      loggingZap:
#        enabled: true                                     # Optional, default: false
#        zapLoggerEncoding: "json"                         # Optional, default: "console"
#        zapLoggerOutputPaths: ["logs/app.log"]            # Optional, default: ["stdout"]
#        eventLoggerEncoding: "json"                       # Optional, default: "console"
#        eventLoggerOutputPaths: ["logs/event.log"]        # Optional, default: ["stdout"]
#      metricsProm:
#        enabled: true                                     # Optional, default: false
#      auth:
#        enabled: true                                     # Optional, default: false
#        basic:
#          - "user:pass"                                   # Optional, default: []
#        ignorePrefix:
#          - "/rk/v1"                                      # Optional, default: []
#        apiKey:
#          - "keys"                                        # Optional, default: []
#      meta:
#        enabled: true                                     # Optional, default: false
#        prefix: "rk"                                      # Optional, default: "rk"
#      tracingTelemetry:
#        enabled: true                                     # Optional, default: false
#        exporter:                                         # Optional, default will create a stdout exporter
#          file:
#            enabled: true                                 # Optional, default: false
#            outputPath: "logs/trace.log"                  # Optional, default: stdout
#          jaeger:
#            agent:
#              enabled: false                              # Optional, default: false
#              host: ""                                    # Optional, default: localhost
#              port: 0                                     # Optional, default: 6831
#            collector:
#              enabled: true                               # Optional, default: false
#              endpoint: ""                                # Optional, default: http://localhost:14268/api/traces
#              username: ""                                # Optional, default: ""
#              password: ""                                # Optional, default: ""
#      rateLimit:
#        enabled: false                                    # Optional, default: false
#        algorithm: "leakyBucket"                          # Optional, default: "tokenBucket"
#        reqPerSec: 100                                    # Optional, default: 1000000
#        paths:
#          - path: "/rk/v1/healthy"                        # Optional, default: ""
#            reqPerSec: 0                                  # Optional, default: 1000000
#      jwt:
#        enabled: true                                     # Optional, default: false
#        signingKey: "my-secret"                           # Required
#        ignorePrefix:                                     # Optional, default: []
#          - "/rk/v1/tv"
#          - "/sw"
#          - "/rk/v1/assets"
#        signingKeys:                                      # Optional
#          - "key:value"
#        signingAlgo: ""                                   # Optional, default: "HS256"
#        tokenLookup: "header:<name>"                      # Optional, default: "header:Authorization"
#        authScheme: "Bearer"                              # Optional, default: "Bearer"
#      secure:
#        enabled: true                                     # Optional, default: false
#        xssProtection: ""                                 # Optional, default: "1; mode=block"
#        contentTypeNosniff: ""                            # Optional, default: nosniff
#        xFrameOptions: ""                                 # Optional, default: SAMEORIGIN
#        hstsMaxAge: 0                                     # Optional, default: 0
#        hstsExcludeSubdomains: false                      # Optional, default: false
#        hstsPreloadEnabled: false                         # Optional, default: false
#        contentSecurityPolicy: ""                         # Optional, default: ""
#        cspReportOnly: false                              # Optional, default: false
#        referrerPolicy: ""                                # Optional, default: ""
#        ignorePrefix: []                                  # Optional, default: []
#      csrf:
#        enabled: true
#        tokenLength: 32                                   # Optional, default: 32
#        tokenLookup: "header:X-CSRF-Token"                # Optional, default: "header:X-CSRF-Token"
#        cookieName: "_csrf"                               # Optional, default: _csrf
#        cookieDomain: ""                                  # Optional, default: ""
#        cookiePath: ""                                    # Optional, default: ""
#        cookieMaxAge: 86400                               # Optional, default: 86400
#        cookieHttpOnly: false                             # Optional, default: false
#        cookieSameSite: "default"                         # Optional, default: "default", options: lax, strict, none, default
#        ignorePrefix: []                                  # Optional, default: []
{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324129547&siteId=291194637