Skip to content

v1.9.0

Welcome to go-zero v1.9.0! This release brings powerful new features, important fixes, and performance improvements to enhance your microservices development experience.

Enhanced security for sensitive data logging

What’s New: Support for masking sensitive data in log output by implementing the MaskSensitive() any method on data types.

Quick Start:

package main
import "github.com/zeromicro/go-zero/core/logx"
// Define a struct with sensitive data
type UserInfo struct {
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
}
// Implement MaskSensitive method to control what gets logged
func (u UserInfo) MaskSensitive() any {
return UserInfo{
Username: u.Username,
Password: "***", // Mask the password
Email: maskEmail(u.Email), // Partially mask email
}
}
func maskEmail(email string) string {
parts := strings.Split(email, "@")
if len(parts) != 2 {
return "***"
}
return parts[0][:1] + "***@" + parts[1]
}
func main() {
user := UserInfo{
Username: "john",
Password: "secret123",
Email: "john@example.com",
}
// This will automatically use MaskSensitive() method
logx.Infow("User login", logx.Field("user", user))
// Output: {"user":{"username":"john","password":"***","email":"j***@example.com"}}
}

Improved database field handling with skip tags

What’s New: Support for - field tag to skip fields during database operations.

Quick Start:

type User struct {
ID int64 `db:"id"`
Name string `db:"name"`
Password string `db:"-"` // This field will be skipped
Internal string `db:"-"` // This field will be skipped
Email string `db:"email"`
}
// Usage in queries - skipped fields are automatically ignored
var user User
err := sqlx.QueryRow(&user, "SELECT id, name, email FROM users WHERE id = ?", 1)

Modern MongoDB integration with improved performance

What’s New: Upgraded to mongo-driver v2 with v1 moved to zero-contrib for backward compatibility.

Migration Guide:

// Old way (v1 - now in zero-contrib)
import "github.com/zeromicro/zero-contrib/stores/mongo"
// New way (v2 - built-in)
import "github.com/zeromicro/go-zero/core/stores/mongo"
func main() {
// New v2 API with improved performance
client := mongo.MustNewModel("mongodb://localhost:27017", "mydb", "users")
// Enhanced query capabilities
var users []User
err := client.Find(context.Background(), &users, bson.M{"status": "active"})
}

High-performance generic set implementation

What’s New: Generic TypedSet with compile-time safety and 2x performance improvement.

Quick Start:

import "github.com/zeromicro/go-zero/core/collection"
func main() {
// Create a typed set for strings
stringSet := collection.NewTypedSet[string]()
// Add items
stringSet.Add("user1", "user2", "user3")
// Check membership (2x faster than previous implementation)
if stringSet.Contains("user1") {
fmt.Println("User found!")
}
// Work with integers
intSet := collection.NewTypedSet[int]()
intSet.Add(1, 2, 3)
// Set operations
union := stringSet.Union(otherStringSet)
intersection := stringSet.Intersect(otherStringSet)
}

5. Gateway Custom Middleware (Onion Model)

Section titled “5. Gateway Custom Middleware (Onion Model)”

Flexible middleware architecture for API gateways

What’s New: Support for custom middleware with onion model architecture in API gateways.

Quick Start:

import "github.com/zeromicro/go-zero/gateway"
// Define custom middleware
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Authentication logic
token := r.Header.Get("Authorization")
if !validateToken(token) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next(w, r)
}
}
func rateLimitMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Rate limiting logic
if !checkRateLimit(r.RemoteAddr) {
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next(w, r)
}
}
// Configure gateway with middleware stack
func main() {
gw := gateway.MustNewServer(gateway.GatewayConf{
Name: "api-gateway",
Port: 8080,
Upstreams: []gateway.Upstream{
{
Name: "user-service",
Mapping: "/api/users",
Target: "http://localhost:8081",
},
},
// Middleware stack (onion model - outer to inner)
Middlewares: []gateway.Middleware{
rateLimitMiddleware, // Outer layer
authMiddleware, // Inner layer
},
})
gw.Start()
}

Advanced Redis stream processing with consumer groups

What’s New: Native support for Redis consumer groups for scalable stream processing.

Quick Start:

import "github.com/zeromicro/go-zero/core/stores/redis"
func main() {
rds := redis.MustNewRedis(redis.RedisConf{
Host: "localhost:6379",
})
// Create consumer group
err := rds.XGroupCreate("events", "processors", "0", true)
if err != nil {
log.Fatal(err)
}
// Consumer function
go func() {
for {
// Read from consumer group
streams, err := rds.XReadGroup(&redis.XReadGroupArgs{
Group: "processors",
Consumer: "worker-1",
Streams: []string{"events", ">"},
Count: 10,
Block: time.Second * 5,
})
if err != nil {
log.Printf("Error reading: %v", err)
continue
}
// Process messages
for _, stream := range streams {
for _, message := range stream.Messages {
processMessage(message)
// Acknowledge message
rds.XAck("events", "processors", message.ID)
}
}
}
}()
}
func processMessage(msg redis.XMessage) {
fmt.Printf("Processing message %s: %v\n", msg.ID, msg.Values)
}

Improved flexibility with unknown field handling

What’s New: Gateway now ignores unknown fields in request parsing, improving API compatibility.

Benefits:

  • Backward Compatibility: Older clients can send requests with deprecated fields
  • Forward Compatibility: Newer clients can send additional fields safely
  • Graceful Degradation: Services continue working despite schema mismatches

Example:

// Service expects this structure
type UserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
}
// Client sends this (extra fields are now ignored, not rejected)
{
"name": "John Doe",
"email": "john@example.com",
"deprecated_field": "old_value", // Ignored gracefully
"future_field": "new_value" // Ignored gracefully
}

Enhanced gRPC metadata forwarding to HTTP headers in gateway

What’s New: Gateway now automatically forwards gRPC metadata (headers and trailers) from backend services to HTTP response headers.

Benefits:

  • Full Metadata Propagation: All gRPC headers and trailers are forwarded to HTTP clients
  • Tracing Support: Trace IDs and debugging information flow through the gateway
  • Custom Headers: Backend services can set custom headers that reach frontend clients
  • Seamless Integration: Works with existing gateway configurations

Quick Start:

// Backend gRPC service setting metadata
func (s *Service) SomeMethod(ctx context.Context, req *pb.Request) (*pb.Response, error) {
// Set headers that will be forwarded to HTTP response
err := grpc.SendHeader(ctx, metadata.Pairs(
"X-Custom-Header", "custom-value",
"X-Trace-ID", "trace-12345",
))
if err != nil {
return nil, err
}
// Set trailers that will also be forwarded
grpc.SetTrailer(ctx, metadata.Pairs(
"X-Server-Info", "grpc-backend-v1",
"X-Processing-Time", "150ms",
))
return &pb.Response{}, nil
}
// Frontend HTTP client will now receive these headers:
// X-Custom-Header: custom-value
// X-Trace-ID: trace-12345
// X-Server-Info: grpc-backend-v1
// X-Processing-Time: 150ms

Resolved service discovery reliability issues

Problem Fixed: Services failing to auto-re-register when ETCD keys disappeared.

Impact: Improved service discovery reliability and automatic recovery.

Before vs After:

// Before: Manual re-registration required
// After: Automatic recovery
config := etcd.EtcdConf{
Hosts: []string{"localhost:2379"},
Key: "user.rpc",
// Auto-recovery now works reliably
}

Fixed Server-Sent Events timeout handling

Problem Fixed: SSE connections affected by http.Server’s WriteTimeout causing premature disconnections.

Solution: SSE now properly handles timeouts independently of server write timeout.

// SSE now works correctly with server timeouts
server := &http.Server{
WriteTimeout: 10 * time.Second, // Won't affect SSE
}
// SSE connections maintain their own timeout handling
http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
// SSE stream works regardless of server WriteTimeout
sse.StreamEvents(w, r, eventSource)
})

Fixed configuration loading from environment variables

Problem Fixed: Issues with unmarshaling environment variables for type time.Duration.

Example Fix:

type Config struct {
Timeout time.Duration `json:",env=TIMEOUT"` // Now works correctly
}

Reduced memory usage in detailed request logging

Problem Fixed: Large memory consumption when logging POST request details.

Impact: Significantly reduced memory footprint for applications with large request bodies when setting log to verbose.


Fixed HTML escaping issues in httpx responses

Problem Fixed: Unwanted HTML escaping during JSON serialization causing malformed responses.

Before:

{"message": "Hello \\u003cworld\\u003e"} // Incorrect escaping

After:

{"message": "Hello <world>"} // Correct output

Fixed race conditions in ImmutableResource

Problem Fixed: Concurrent access to ImmutableResource could lead to empty results.

Impact: Improved thread safety and reliability in high-concurrency scenarios.

Enhanced logging performance with structured field handling.

Improvement: Faster log processing with reduced allocations.

Usage:

// Optimized field logging
logx.Infow("User action",
logx.Field("user_id", userID),
logx.Field("action", "login"),
logx.Field("timestamp", time.Now()),
)
  1. Update Dependencies:
Terminal window
go get github.com/zeromicro/go-zero@v1.9.0
  1. MongoDB Driver:

    • Update imports from zero-contrib if using MongoDB v1
    • Consider migrating to v2 for better performance
  2. Consumer Groups:

    • Update Redis stream processing to use new consumer group APIs

Welcome to our amazing new contributors who made their first contributions:


Happy coding with go-zero v1.9.0! 🎉