Backend Development 32 min read

Go Language Practice and Ngo Framework Development at NetEase Media

Facing high memory usage and slow startup after containerizing its Java services, NetEase Media adopted Go in 2020, leveraging its fast compilation, low‑resource footprint and goroutine‑based concurrency to build the high‑performance Ngo framework, which outperforms Spring‑Boot in throughput while using far less memory.

NetEase Media Technology Team
NetEase Media Technology Team
NetEase Media Technology Team
Go Language Practice and Ngo Framework Development at NetEase Media

NetEase Media migrated its core business to containers in 2020, deploying both online and offline services on the same infrastructure, achieving CPU utilization rates above 50%. However, after adopting containers for online services, several challenges emerged: high memory consumption (Java services using SpringBoot typically consume over 2G memory), slow compilation and startup speeds, and large image sizes due to JVM overhead.

This article introduces NetEase Media's exploration of Go language starting from late 2020 to address these resource and performance issues.

Go Language Key Features

Fast Compilation: Go's compilation speed is typically 5x faster than Java and C++. Large C++ projects may require 10+ minutes to compile, while equivalent Go projects often compile in under a minute.

Simple Syntax: Go originated from a C++ new features sharing session at Google, created by a team including C language creator, UTF8 inventor, and V8 JS engine developers. It maintains C-like syntax while incorporating optimizations from years of language evolution.

Dynamic-Language-Like Development: Go's interface mechanism provides duck typing similar to dynamic languages, but with compile-time type checking. Objects only need to implement all methods defined in an interface to satisfy it:

type ReadIF interface {
    Read()
}
type WriteIF interface {
    Write()
}
type ReadWriteIF interface {
    ReadIF
    WriteIF
}

Low Resource Consumption: Compared to Java, Go eliminates JVM overhead. A simple HTTP program comparison shows Java consumes approximately 20% more CPU and two orders of magnitude more memory than Go.

Built for Concurrent I/O: Go uses goroutines instead of threads. The go keyword creates lightweight coroutines that are automatically scheduled to a limited number of OS threads, using epoll for efficient event-driven I/O:

func process(client *RPCClient) {
    response := client.Call() // blocking
    compute(response) // CPU-intensive business
}

func main() {
    client := NewRPCClient()
    for i := 0; i < 100; i++ {
        go process(client)
    }
    select {} // dead wait
}

Good Operability: Go compiles to static binaries with no external dependencies. Cross-compilation to any supported platform is straightforward:

GOOS=linux GOARCH=amd64 go build ./...

C/C++ Compatibility: Go can easily interoperate with C libraries through cgo.

Unified Toolchain: Go provides integrated tools including go run, go build, go install, go fmt, go get, go mod, go test, go doc, and go tool.

Ngo Framework Introduction

Ngo is a Java Spring Boot-like framework developed entirely in Go for NetEase Media's business needs. It provides higher performance and lower resource consumption than Java frameworks, includes comprehensive business libraries, embeds Sentinel monitoring for automatic data reporting, and handles configuration loading and environment initialization.

Why Not Use Existing Frameworks: The popular Beego framework was not adopted due to: suboptimal HTTP server performance, lack of required business libraries (kafka, redis, RPC), difficulty adding transparent sentinel monitoring, and unsatisfactory ORM implementation.

Main Modules:

HTTP Server: Based on gin with URL sentinel monitoring, trackable goroutines, health check interfaces, and panic handling

Graceful Shutdown: Implements /health endpoints (online, offline, check, status) for Kubernetes probes

MySQL ORM: Based on gorm with automatic configuration and sentinel monitoring

Logging: Based on logrus with customizable formatting, file rotation, and log level separation

Redis: Based on go-redis supporting client, cluster, and sentinel modes

Kafka: Based on sarama with simplified producer/consumer APIs

HTTP Client: Based on fasthttp with Java-like chain calling

RPC: Enhanced Go standard library RPC with connection pooling and reconnection

Configuration: Based on viper with automatic injection

Sentinel: Unified monitoring data collection and reporting

Performance Benchmarking Results

Test 1 - 100 Concurrent Requests, 10 Minutes:

Metric

Java (2C, 500M)

Go (2C, 100M)

TPS

34,472.59

40,082.52

MRT(ms)

28.74

24.44

99%RT

102

93

MaxRT

997

227

CPU Usage

100%

100%

Memory Usage

80%

50%

Test 2 - 200 Concurrent Requests, 10 Minutes:

Metric

Java (2C, 700M)

Go (2C, 100M)

TPS

34,410.55

40,061.38

MRT(ms)

67.53

49.16

99%RT

211

103

MaxRT

24,402

533

CPU Usage

100%

100%

Memory Usage

80%

57%

Results show Go outperforms Java in concurrent request handling with better throughput and significantly lower resource usage. Go has no cold start, while Java shows longer warm-up periods under increased load.

Migration Challenges and Solutions

1. Value vs Pointer: Unlike Java's pass-by-reference for objects, Go requires explicit choice between value and pointer passing. Pointer passing only copies the address, while value passing copies the entire struct.

2. Short Variable Declaration: Using := creates new local variables, which can accidentally shadow global variables without IDE warnings.

3. nil vs null vs Empty: Go's nil is a null pointer address (unallocated), Java's null is a reference null address, while empty strings are allocated but contain no content.

4. String Type: In Java, String is a reference type; in Go, string is a primitive type.

5. No Wrapper Classes: Go lacks Java's wrapper classes. For optional integer fields, prefer using zero values rather than pointer types.

6. Database NULL Handling: Use sql.NullString and similar types from the sql package for handling database NULL values.

7. Redis SDK Differences: Java returns empty string for missing keys, while Go returns an error that must be distinguished from other errors.

8. Error Handling: Go's error handling is more explicit than Java's exception system, requiring immediate error checking and logging rather than centralized catch blocks.

microservicesbackend developmentgo-languagegoroutineJava to Go migrationNgo frameworkperformance benchmarkingSpring Boot comparison
NetEase Media Technology Team
Written by

NetEase Media Technology Team

NetEase Media Technology Team

0 followers
Reader feedback

How this landed with the community

login 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.