Building a High‑Concurrency Student Authentication System with Go‑Zero

The article details a production‑grade student verification system built on the Go‑Zero microservice framework, covering business challenges, a layered API/RPC/Model architecture, state‑machine management, AES encryption, dual‑channel OCR failover, event‑driven design with Redis Streams, and a push‑pull WebSocket notification scheme.

Golang Shines
Golang Shines
Golang Shines
Building a High‑Concurrency Student Authentication System with Go‑Zero

In vertical applications such as campus social platforms, verifying a user’s student identity is essential for trust. Traditional manual review of uploaded ID photos suffers from slow turnaround, high labor cost, and data‑security risks.

Business challenges and solution

The CampusHub project replaces manual review with an OCR‑assisted workflow that combines machine pre‑screening and occasional human spot‑checks, achieving near‑instant verification.

System architecture

The solution adopts Go‑Zero’s layered microservice architecture, separating concerns into an API gateway, RPC business logic, and a Model persistence layer.

State‑machine design

To prevent illegal state transitions, the verification flow is modeled as a finite‑state machine with explicit states (e.g., Pending, WaitConfirm, ManualReview, etc.). The encrypted student ID is stored directly as a unique index.

Sensitive data encryption (AES)

PII such as real name and student ID are stored encrypted. The model layer automatically decrypts on read while persisting ciphertext.

type StudentVerification struct {
    // Real name (AES‑encrypted, auto‑decrypt on query)
    RealName string `gorm:"column:real_name;size:100" json:"real_name"`
    // Student ID (AES‑encrypted + unique index)
    StudentID string `gorm:"uniqueIndex:uk_school_student;column:student_id" json:"student_id"`
    // ... other fields
}

High‑availability OCR with dual‑channel failover

OCR relies on third‑party cloud providers (Tencent Cloud and Alibaba Cloud). A ProviderFactory implements a primary‑fallback strategy with a Redis‑backed circuit‑breaker: after three failures within five minutes, the primary channel is opened and calls are routed to the backup.

func (f *ProviderFactory) Recognize(ctx context.Context, frontURL, backURL string) (*OcrResult, error) {
    // Try primary provider (Tencent Cloud)
    if f.primary != nil {
        result, err := f.tryProvider(ctx, f.primary, frontURL, backURL)
        if err == nil { return result, nil }
        logx.Errorf("主OCR失败: %v", err)
    }
    // Fallback provider (Alibaba Cloud)
    if f.fallback != nil {
        result, err := f.tryProvider(ctx, f.fallback, frontURL, backURL)
        if err == nil { return result, nil }
        logx.Errorf("备用OCR失败: %v", err)
    }
    // All providers failed
    return nil, errorx.ErrOcrServiceUnavailable()
}

Event‑driven architecture (EDA)

Using Watermill and Redis Streams, the system decouples the verification workflow. The ApplyStudentVerify API publishes a verify:events message; an OCR worker consumes it, processes the images, and publishes a verify:progress event for the WebSocket service.

// Publish verification request event
eventData := messaging.VerifyApplyEventData{VerifyID: verifyID, UserID: in.UserId /* ... */}
l.svcCtx.MsgClient.Publish(l.ctx, messaging.TopicVerifyEvent, eventData)

Real‑time progress push‑pull via WebSocket

When OCR completes or the verification state changes, the RPC service emits a lightweight push signal (e.g., {"type":"verify_progress","refresh":true}) through Redis Streams. The WebSocket service forwards the signal to online clients, which then pull the latest verification data via a regular HTTP GET, keeping the data flow separate from the control signal.

func publishVerifyProgress(ctx context.Context, svcCtx *svc.ServiceContext, userID int64, status int) {
    event := messaging.VerifyProgressEventData{
        UserID:    userID,
        Status:    int32(status),
        Refresh:   true,
        Timestamp: time.Now().Unix(),
    }
    svcCtx.MsgClient.Publish(ctx, messaging.TopicVerifyProgress, event)
}

Asynchronous OCR processing and concurrency protection

The OCR worker runs as a separate consumer. It guards against race conditions by performing a double‑check of the verification status before persisting OCR results. If the user cancels the request during OCR, the result is discarded.

func (l *ProcessOcrVerifyLogic) ProcessOcrVerify(in *pb.ProcessOcrVerifyReq) (*pb.ProcessOcrVerifyResp, error) {
    // Call OCR with 30‑second timeout
    ocrResult, err := l.svcCtx.OcrFactory.Recognize(ocrCtx, in.FrontImageUrl, in.BackImageUrl)
    // Post‑check: ensure status unchanged
    if l.isStatusChanged(in.VerifyId) {
        return &pb.ProcessOcrVerifyResp{Message: "OCR期间状态已变更,丢弃结果"}, nil
    }
    // Update DB and push progress if OCR succeeded
    if err == nil {
        l.svcCtx.StudentVerificationModel.UpdateOcrResult(..., ocrResult)
        publishVerifyProgress(..., constants.VerifyStatusWaitConfirm)
    }
    return &pb.ProcessOcrVerifyResp{}, err
}

Design Q&A

Why not call OCR synchronously in the API? OCR takes 1‑3 seconds and may be unstable; synchronous calls would block the HTTP thread and drastically reduce QPS. Asynchronous processing keeps the API response fast.

What if OCR produces wrong data? The system uses a WaitConfirm state; users can correct OCR‑extracted fields, after which the flow moves to ManualReview for admin verification.

How is OCR high‑availability ensured? The ProviderFactory with primary‑fallback and Redis‑based circuit‑breaker automatically switches providers after repeated failures.

Conclusion

The module demonstrates how Go‑Zero can be leveraged to build a robust, production‑grade service: clear separation of API/RPC/Model layers, event‑driven decoupling, dual‑channel OCR failover, and efficient real‑time notifications, embodying high cohesion and low coupling.

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.

MicroservicesOCRwebsocketencryptionevent-drivengo-zerostudent-verification
Golang Shines
Written by

Golang Shines

We share daily the latest Golang technical articles, practical resources, language news, tutorials, and real-world projects to help everyone learn and improve.

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.