Go: المشروع المتكامل: واجهة برمجة تطبيقات التجارة…

آخر تحديث: 2026-08-26

لدينا البنية التحتية، ولدينا نظام الأمان — والآن، دعونا نبدأ التشغيل.

من السطر الأول من الكود وحتى الجاهزية للإنتاج: وثائق Swagger، والإغلاق التدريجي، والنشر عبر Docker، وضبط pprof، والمراقبة بواسطة Prometheus. أصبحت واجهة برمجة تطبيقات التجارة الإلكترونية الخاصة بـ«بوب» جاهزة لاستقبال المستخدمين الحقيقيين.

1. ستتعلم



2. القصة: الليلة التي سبقت الإطلاق

(1) نقاط الضعف: عدم وجود توثيق، وعدم وجود مراقبة، والنشر اليدوي

واجهة برمجة تطبيقات التجارة الإلكترونية الخاصة بـ«بوب» تعمل بكامل طاقتها، لكن فريق العمليات رفض قبولها:

«قال فريق العمليات: "بدون وثائق واجهة برمجة التطبيقات (API)، لا يمكن للواجهة الأمامية التكامل معها. وبدون فحوصات الحالة، لا يمكننا معرفة ما إذا كانت الخدمة تعمل أم لا. وبدون المراقبة، لن نعرف حتى ما إذا كانت الخدمة قد تعطلت. وما زلنا مضطرين إلى نقل الملفات الثنائية يدويًّا عبر SCP من أجل النشر — وهذا أمر بدائي للغاية."»

قائمة التحقق قبل الإطلاق:

TEXT 📖 للعرض فقط
❌ API docs → Frontend has to ask "what fields does this endpoint return?" every time
❌ Graceful shutdown → Kill process causes in-flight orders to be lost
❌ Docker deploy → SCP binary to server, start manually
❌ Performance monitoring → Don't know which part of the API is slow

(2) هدف الدرس: جاهز للإنتاج

TEXT 📖 للعرض فقط
✅ Swagger docs → Frontend self-service browsing
✅ Graceful Shutdown → Signal handling + wait for connections to close
✅ Docker Compose → One command to start the full stack
✅ pprof + Prometheus → Performance visualization


3. التنفيذ الكامل

▶ مثال: الإغلاق السلس

⚙️ المتطلبات الأساسية: قم بتشغيل go get github.com/mattn/go-sqlite3 (يتطلب CGO؛ أو استخدم modernc.org/sqlite كبديل للحصول على برنامج تشغيل مخصص للعبة «غو» فقط)

GO 📖 للعرض فقط
// cmd/server/main.go
package main

import (
    "context"
    "database/sql"
    "encoding/json"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "ecommerce/internal/handler"
    "ecommerce/internal/repository"
    "ecommerce/internal/service"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    db, err := sql.Open("sqlite3", "./ecommerce.db")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(10)
    db.SetConnMaxLifetime(5 * time.Minute)

    // Dependency injection
    userRepo := repository.NewUserRepository(db)
    productRepo := repository.NewProductRepository(db)
    orderRepo := repository.NewOrderRepository(db)

    userSvc := service.NewUserService(userRepo)
    productSvc := service.NewProductService(productRepo)
    orderSvc := service.NewOrderService(orderRepo, productRepo, userRepo)

    userHandler := handler.NewUserHandler(userSvc)
    productHandler := handler.NewProductHandler(productSvc)
    orderHandler := handler.NewOrderHandler(orderSvc)

    mux := http.NewServeMux()
    userHandler.Register(mux)
    productHandler.Register(mux)
    orderHandler.Register(mux)

    // Health check endpoint
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
    })

    mux.HandleFunc("GET /ready", func(w http.ResponseWriter, r *http.Request) {
        if err := db.Ping(); err != nil {
            w.WriteHeader(http.StatusServiceUnavailable)
            json.NewEncoder(w).Encode(map[string]string{"status": "not ready"})
            return
        }
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{"status": "ready"})
    })

    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
        // Timeout settings (prevent slowloris attacks)
        ReadTimeout:       10 * time.Second,
        WriteTimeout:      10 * time.Second,
        IdleTimeout:       60 * time.Second,
        ReadHeaderTimeout: 5 * time.Second,
    }

    // Graceful Shutdown
    go func() {
        sigCh := make(chan os.Signal, 1)
        signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
        sig := <-sigCh
        log.Printf("Received signal %v, shutting down...", sig)

        // Give in-flight requests up to 30 seconds to complete
        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()

        if err := server.Shutdown(ctx); err != nil {
            log.Printf("Forced shutdown: %v", err)
        }
    }()

    log.Println("E-commerce API listening on :8080")
    if err := server.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatal(err)
    }
    log.Println("Server shut down safely")
}
76 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: التكامل بين pprof وPrometheus

⚙️ المتطلبات المسبقة: تشغيل go get github.com/prometheus/client_golang/prometheus وgo get github.com/prometheus/client_golang/prometheus/promhttp

GO 📖 للعرض فقط
// monitoring.go
package main

import (
    "net/http"
    "net/http/pprof"
    "runtime"
    "strconv"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

// Prometheus metrics
var (
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"method", "path", "status"},
    )

    httpRequestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration in seconds",
            Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5},
        },
        []string{"method", "path"},
    )

    activeGoroutines = prometheus.NewGaugeFunc(
        prometheus.GaugeOpts{
            Name: "go_goroutines_active",
            Help: "Current number of goroutines",
        },
        func() float64 {
            return float64(runtime.NumGoroutine())
        },
    )
)

func init() {
    prometheus.MustRegister(httpRequestsTotal)
    prometheus.MustRegister(httpRequestDuration)
    prometheus.MustRegister(activeGoroutines)
}

// Prometheus middleware
func prometheusMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        // Wrap ResponseWriter to get status code
        wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
        next.ServeHTTP(wrapped, r)

        duration := time.Since(start)
        httpRequestsTotal.WithLabelValues(
            r.Method, r.URL.Path, strconv.Itoa(wrapped.statusCode),
        ).Inc()
        httpRequestDuration.WithLabelValues(
            r.Method, r.URL.Path,
        ).Observe(duration.Seconds())
    })
}

type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}

// Register pprof and Prometheus endpoints in main
func registerMonitoring(mux *http.ServeMux) {
    // pprof endpoints
    mux.HandleFunc("/debug/pprof/", pprof.Index)
    mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
    mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
    mux.HandleFunc("/debug/pprof/trace", pprof.Trace)

    // Prometheus metrics
    mux.Handle("/metrics", promhttp.Handler())
}

func main() {
    mux := http.NewServeMux()
    registerMonitoring(mux)

    // Example business endpoint
    mux.HandleFunc("GET /api/products", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"products":[]}`))
    })

    // Health check
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"status":"ok"}`))
    })

    log.Println("Monitoring + API server listening on :8080")
    http.ListenAndServe(":8080", prometheusMiddleware(mux))
}
85 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: نشر Docker

DOCKERFILE
# Dockerfile (Multi-stage Build)
FROM golang:1.22-alpine AS builder
WORKDIR /app

# Dependency cache
COPY go.mod go.sum ./
RUN go mod download

# Source + compile
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server

# === Run stage ===
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata

COPY --from=builder /app/server /server

EXPOSE 8080
EXPOSE 6060  # pprof

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget -qO- http://localhost:8080/health || exit 1

CMD ["/server"]
YAML
# docker-compose.yml
version: '3.8'

services:
  api:
    build: .
    ports:
      - "8080:8080"
      - "6060:6060"   # pprof
    environment:
      - DB_PATH=/data/ecommerce.db
      - GIN_MODE=release
    volumes:
      - data:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 5s

  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  data:
  prometheus_data:
  grafana_data:
YAML
# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'ecommerce-api'
    static_configs:
      - targets: ['api:8080']
    metrics_path: '/metrics'

▶ مثال: وثائق Swagger

GO
// Generate OpenAPI docs from comments (use with swag tool)

// Package handler handles HTTP requests.
//
// E-commerce API
//
//     Schemes: http
//     Host: localhost:8080
//     BasePath: /api/v1
//     Version: 1.0.0
//
//     Consumes:
//     - application/json
//
//     Produces:
//     - application/json
//
// swagger:meta
package handler

import "ecommerce/pkg/model"

// User information
// swagger:model
type User struct {
    ID    int    `json:"id"`
    Email string `json:"email"`
    Name  string `json:"name"`
}

// RegisterUser registers a new user
// swagger:route POST /api/v1/register users registerUser
//
// Register a new user
//
//     Consumes:
//     - application/json
//
//     Produces:
//     - application/json
//
//     Responses:
//       201: User
//       400: ErrorResponse
//       409: ErrorResponse
func (h *UserHandler) RegisterUser(w http.ResponseWriter, r *http.Request) {
    // handler logic
}

// swagger:parameters registerUser
type RegisterUserParams struct {
    // in: body
    Body model.RegisterRequest
}

// ErrorResponse error response
// swagger:response
type ErrorResponse struct {
    // in: body
    Body struct {
        Error string `json:"error"`
    }
}
▶ جرّب الكود
BASH
# Install swag CLI
$ go install github.com/swaggo/swag/cmd/swag@latest

# Generate docs
$ swag init -g cmd/server/main.go

# Access docs
# http://localhost:8080/swagger/index.html

(5) قائمة التحقق من الإطلاق

TEXT 📖 للعرض فقط
✅ Graceful Shutdown — wait for requests to complete after kill
✅ Health Check — /health and /ready endpoints
✅ pprof — /debug/pprof/ performance analysis
✅ Prometheus — /metrics endpoint exposed
✅ Swagger — Auto-generated API documentation
✅ Docker — Multi-stage build, image < 20 MB
✅ docker-compose — One command to start the full stack
✅ ReadTimeout / WriteTimeout — prevent slowloris attacks
✅ DB connection pool — MaxOpenConns=25, MaxIdleConns=10
✅ server.Shutdown — SIGINT/SIGTERM handling
💡 نصيحة: تتطلب بيئات الإنتاج أيضًا: (1) وكيل عكسي (Nginx / Caddy) للتعامل مع بروتوكول TLS وأسماء النطاقات؛ (2) مسار CI/CD للإنشاء والنشر الآليين؛ (3) إدارة مركزية للسجلات (مثل Loki أو Elasticsearch)؛ (4) قواعد التنبيه (على سبيل المثال، تشغيل تنبيه عندما يتجاوز معدل أخطاء 5xx نسبة 1٪). تتجاوز هذه الموضوعات نطاق هذا الدرس، ولكن يُنصح بإعدادها قبل النشر.



❓ أسئلة شائعة

س لماذا يُعد الإغلاق السلس أمرًا مهمًا؟
ج عند استلام إشارة SIGTERM (مثلما يحدث عندما يقوم Kubernetes بإنهاء عمل Pod)، ينتظر server.Shutdown حتى تكتمل جميع طلبات HTTP النشطة (حتى انتهاء المهلة المحددة) قبل إغلاق المستمع. وإذا تم إنهاء العملية على الفور، فستتوقف الطلبات التي يجري معالجتها حاليًا — مما يؤدي إلى عدم اتساق ترتيب البيانات.
س ما هي إعدادات مهلة الخادم؟
ج (1) ReadTimeout: مهلة قراءة الطلب بالكامل (بما في ذلك النص الأساسي)؛ (2) WriteTimeout: مهلة إرسال الاستجابة؛ (3) IdleTimeout: مهلة الخمول لاتصالات Keep-Alive؛ (4) ReadHeaderTimeout: مهلة قراءة رؤوس الطلبات. يوصى بتكوين جميع هذه الإعدادات لمنع هجمات الاتصال البطيء.
س هل يُعد pprof آمنًا في بيئة الإنتاج؟
ج يجب ألا تكون نقطة نهاية pprof مكشوفة للعالم الخارجي. الحل: (1) قم بتشغيلها على منفذ داخلي (على سبيل المثال: :6060) لا يتم مشاركته مع منافذ الأعمال؛ (2) قم بحماية المسار /debug/pprof/ باستخدام برمجيات وسيطة للتوثيق؛ (3) قم بتمكينها فقط في بيئة الاختبار. في بيئة الإنتاج، يوصى بتمكين تحليل الأداء مؤقتًا فقط عند الحاجة.
س كيف يمكنني عرض مقاييس Prometheus؟
ج استخدم promhttp.Handler() لعرض المقاييس على نقطة النهاية /metrics. يقوم خادم Prometheus باستخراج البيانات من هذه النقطة بشكل دوري. يتصل Grafana بمصدر بيانات Prometheus ويقوم بإنشاء تمثيلات مرئية. المقاييس القياسية: عدد الطلبات، وتوزيع زمن الاستجابة، ومعدل الأخطاء، وعدد goroutines، وعدد أحداث GC.
س كيف يمكنني إنشاء وثائق OpenAPI تلقائيًا؟
ج استخدم أداة swaggo/swag — اكتب تعليقات بتنسيق محدد داخل كود المعالج الخاص بك، ثم قم بتشغيل swag init لإنشاء المجلد docs/. استخدم swaggo/http-swagger لربط الوثائق بنقطة النهاية /swagger/. تنسيق التعليقات: // swagger:route، // swagger:model، // swagger:parameters.
س كيف يمكنك تنسيق خدمات متعددة باستخدام Docker Compose؟
ج قم بتعريف خدمات متعددة (API، Prometheus، Grafana)، واستخدم depends_on للتحكم في ترتيب بدء التشغيل، واستخدم volumes لحفظ البيانات. قم بتشغيل docker-compose up -d لبدء تشغيل جميع الخدمات بأمر واحد. في بيئة الإنتاج، استخدم Docker Stack أو Kubernetes.
س هل توجد ثلاثة مستويات من الدقة لفحوصات الحالة؟
ج (1) /health (alive) — الفحص الأبسط؛ حيث تشير الاستجابة 200 إلى أن العملية قيد التشغيل؛ (2) /ready (ready) — يتحقق مما إذا كان يمكن الوصول إلى التبعيات (قاعدة البيانات، ذاكرة التخزين المؤقت)؛ (3) /status (مفصل) — يعرض الحالة وزمن الاستجابة لجميع التبعيات.

📖 ملخص


📝 تمارين

  1. أساسي (مستوى الصعوبة ⭐): أضف إعدادات الإغلاق التدريجي ووقت الانتظار إلى واجهة برمجة تطبيقات التجارة الإلكترونية التي تم تناولها في هذا الدرس. استخدم curl لاختبار نقاط النهاية /health و/ready. استخدم kill -SIGTERM <pid> لاختبار الإغلاق التدريجي.

  2. متقدم (صعوبة ⭐⭐): تنفيذ نشر كامل قائم على Docker. المتطلبات: (1) ملف Dockerfile مع بناء متعدد المراحل؛ (2) ملف docker-compose.yml (API + Prometheus + Grafana)؛ (3) HEALTHCHECK؛ (4) نقطة نهاية pprof (منفذ داخلي فقط)؛ (5) التحقق من أن جميع الخدمات تبدأ بشكل طبيعي بعد تشغيل docker-compose up.

  3. التحدي (الصعوبة ⭐⭐⭐): تنفيذ مراقبة Prometheus + لوحات معلومات Grafana. المتطلبات: (1) إضافة برمجيات الوسيطة Prometheus إلى واجهة برمجة التطبيقات (عدد الطلبات، زمن الاستجابة، معدل الأخطاء)؛ (2) إضافة المقاييس التشغيلية (مثل عدد الطلبات التي تم إنشاؤها، وعدد المستخدمين المسجلين)؛ (3) تضمين Prometheus و Grafana في ملف Docker Compose؛ (4) استيراد أو إنشاء لوحة معلومات Grafana لعرض QPS، وزمن الاستجابة P99، ومعدل الأخطاء، وعدد goroutines؛ (5) التحقق من صحة المقاييس باستخدام Grafana بعد إجراء اختبار الحمل.

🎉 تهانينا! لقد أكملت جميع الدروس الثلاثين في دورة Go التعليمية! بدءًا من "Hello World" وصولاً إلى واجهة برمجة تطبيقات (API) للتجارة الإلكترونية جاهزة للاستخدام في بيئة الإنتاج، فقد أتقنت مجموعة المهارات الكاملة اللازمة لتطوير الخلفية باستخدام لغة Go.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%