Why NetEase’s ngo Go Framework Boosts Backend Efficiency and Observability

The article explains how NetEase Media rebuilt its core services with Go, created the open‑source ngo framework to unify dependencies, health‑check, tracing, configuration and OpenID support, and provides detailed code examples and integration guidelines for backend developers.

NetEase Media Technology Team
NetEase Media Technology Team
NetEase Media Technology Team
Why NetEase’s ngo Go Framework Boosts Backend Efficiency and Observability

Background

In 2021 NetEase Media began migrating core online clusters to Go. To avoid duplicated research across teams, a task force surveyed, wrapped, and tested common dependencies and released a unified Go foundation called ngo for internal services.

Why existing open‑source Go frameworks were rejected

Missing essential libraries such as Kafka, Redis, RPC, XXL‑Job.

Inability to integrate NetEase‑wide internal services.

Poor HTTP server performance.

Lack of callback injection for seamless monitoring.

ORM and other modules were not developer‑friendly.

ngo goals and core features

Higher performance and lower resource consumption than the previous Java stack.

All commonly‑used tool libraries bundled (Redis, Kafka, Memcache, etc.).

Zero‑code automatic monitoring data upload.

Automatic configuration loading and environment initialization.

Alignment with health‑check and ops interfaces used in production.

Application health‑check integration

ngo provides four standard endpoints similar to SpringBoot’s Actuator: /health/online – called when a container is brought online; enables traffic. /health/offline – called before a container is taken offline; disables traffic. /health/check – Kubernetes liveness probe; restarts the pod on failure. /health/status – Kubernetes readiness probe; removes the endpoint from service if unhealthy.

Offline handler ensures all in‑flight requests finish before returning success:

func (s *Server) offlineHandler(c *gin.Context) {
    atomic.StoreInt32(&s.active, 0) // mark service as unavailable
    if s.requestsFinished() {
        c.String(http.StatusOK, "ok")
        log.Info("Server offline requested!")
    } else {
        c.String(http.StatusBadRequest, "bad")
        log.Info("Server offline failed!")
    }
}

Online handler simply marks the service as active:

func (s *Server) onlineHandler(c *gin.Context) {
    atomic.StoreInt32(&s.active, 1) // enable traffic
    c.String(http.StatusOK, "ok")
    log.Info("Server online requested!")
}

Additional check and status handlers expose liveness and readiness information:

func (s *Server) checkHandler(c *gin.Context) {
    ss := Get()
    if !ss.Healthz() {
        return
    }
    if s.healthy != nil && !s.healthy() {
        c.String(http.StatusForbidden, "error")
        return
    }
    c.String(http.StatusOK, "ok")
    log.Info("Server check requested!")
}

func (s *Server) statusHandler(c *gin.Context) {
    if atomic.LoadInt32(&s.active) == 1 {
        c.String(http.StatusOK, "ok")
    } else {
        c.String(http.StatusForbidden, "error")
    }
}

Tracing integration

ngo’s tracing module abstracts both OpenTracing‑compatible systems (Jaeger, Zipkin, SkyWalking, Optimus) and non‑standard solutions (Pinpoint). The unified API lets users switch tracing back‑ends without code changes.

Key interface definitions:

type Tracer interface {
    Enabled() bool
    Type() string
    StartSpanFromCarrier(ctx context.Context, op string, carrier interface{}) (Span, context.Context)
    StartSpanFromContext(ctx context.Context, op string) (Span, context.Context)
    Inject(ctx context.Context, carrier interface{})
    SetSamplingRate(rate int)
    Stop()
}

type Span interface {
    SetTag(key string, value interface{}) Span
    Finish()
    GetTraceId() string
}

For HTTP servers, ngo supplies a Gin middleware that creates a root span, records request/response metadata, and finishes the span after the handler returns:

func ServerTraceMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        if tracing.Enabled() && !strings.HasPrefix(c.Request.RequestURI, "/health") {
            span, ctx := tracing.StartSpanFromCarrier(c.Request.Context(), "server", c.Request.Header)
            tracing.SpanKind.Set(span, "server")
            tracing.SpanType.Set(span, "HTTP_SERVER")
            tracing.HttpServerRequestUrl.Set(span, c.FullPath())
            tracing.HttpServerRequestHost.Set(span, c.Request.Host)
            tracing.HttpServerRequestMethod.Set(span, c.Request.Method)
            if ip, portStr, err := net.SplitHostPort(c.Request.RemoteAddr); err == nil {
                tracing.HttpServerPeerHost.Set(span, ip)
                if port, err := strconv.Atoi(portStr); err == nil {
                    tracing.HttpServerPeerPort.Set(span, uint16(port))
                }
            }
            tracing.HttpServerRequestPath.Set(span, c.Request.URL.Path)
            tracing.HttpServerRequestSize.Set(span, c.Request.ContentLength)
            c.Request = c.Request.WithContext(ctx)
            defer func() {
                code := c.Writer.Status()
                tracing.HttpServerResponseStatus.Set(span, uint16(code))
                tracing.HttpServerResponseSize.Set(span, c.Writer.Size())
                span.Finish()
            }()
        }
        c.Next()
    }
}
trace flow diagram
trace flow diagram

Configuration (Apollo) integration

ngo’s config module abstracts multiple data sources (local files, etcd, Apollo, NetEase Config Center). Users start the module with command‑line flags: -c {schema}://{addr} – selects the data source (default file). -w true|false – enables or disables watching for configuration changes.

Example for Apollo:

-c apollo://host:port?appId=xx&cluster=xxx&namespaceNames=xx.yaml,xx.yaml -w true

The module implements a generic DataSource interface:

type DataSource interface {
    ReadConfig() ([]byte, error)
    IsConfigChanged() <-chan struct{}
    io.Closer
}

OpenID / JWT authentication

ngo provides an OpenIdClient that fetches tokens and user info from an OpenID provider. A typical deployment uses JWT for external authentication while OpenID supplies the login entry point.

Authentication is added as a Gin middleware with configurable options (header name, token type, expiration, encryption, OIDC client ID/secret, route prefix, ignored paths):

httpServer:
  middlewares:
    jwtAuth:
      enabled: false   # set true to enable
      authHeader: Authorization
      tokenType: Bearer
      accessTokenExpiresIn: 3600
      refreshTokenExpiresIn: 7200
      encryption: HS256
      oidc:
        clientId: xxxxxxxx
        clientSecret: xxxxxxxx
        encryption: HS256
      routePathPrefix: ""
      ignorePaths:
        - /xxx

Endpoints for token handling:

GET /auth/access-token?code=&redirectUri=&detail=true|false

– obtains a token; detail=true also fetches user info. GET /auth/refresh-token?refreshToken= – refreshes an access token.

Custom authentication logic can be plugged via AddAdminAuthHandler:

func (s *Server) AddAdminAuthHandler(auth Authenticator, gtRsp GenTokenResponse, unauthRsp UnauthenticatedResponse) *Server

Application lifecycle hooks

ngo allows custom code to run before start and after stop:

app.PreStart = func() error { /* initialization */ return nil }
app.AfterStop = func() error { /* cleanup */ return nil }

Signal handling gracefully stops the service on SIGQUIT, SIGINT or SIGTERM:

func (a *application) waitSignals() {
    signals.Shutdown(func(grace bool) {
        if grace {
            a.GracefulStop()
        } else {
            a.Stop()
        }
    })
}

Getting the code

The full source, examples and documentation are hosted at:

https://github.com/NetEase-Media/ngo

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendMicroservicesConfigurationGotracinghealth checkOpenID
NetEase Media Technology Team
Written by

NetEase Media Technology Team

NetEase Media Technology Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.