WebFactor Demo
by WebFactor.com
Home
Articles
Admin
Edit Article
Title
Author
Content
## Въведение в Go (Golang) Go е език за програмиране, създаден в Google през 2009 година от Robert Griesemer, Rob Pike и Ken Thompson. Той комбинира простотата на Python с производителността на C, което го прави идеален за backend разработка и API сървъри.  ## Защо Go за Backend? ### Сравнение с други езици ``` ┌─────────────────┬──────────┬──────────┬──────────┬──────────┬───────────┐ │ Критерий │ Go │ Node.js │ Python │ Java │ Rust │ ├─────────────────┼──────────┼──────────┼──────────┼──────────┼───────────┤ │ Скорост │ ★★★★★ │ ★★★ │ ★★ │ ★★★★ │ ★★★★★ │ │ Лесно учене │ ★★★★ │ ★★★★★ │ ★★★★★ │ ★★★ │ ★★ │ │ Concurrency │ ★★★★★ │ ★★★ │ ★★ │ ★★★★ │ ★★★★★ │ │ Memory safety │ ★★★★ │ ★★★ │ ★★★★ │ ★★★★ │ ★★★★★ │ │ Compilation │ ★★★★★ │ N/A │ N/A │ ★★★ │ ★★ │ │ Ecosystem │ ★★★★ │ ★★★★★ │ ★★★★★ │ ★★★★★ │ ★★★ │ │ DevOps friendly │ ★★★★★ │ ★★★ │ ★★★ │ ★★ │ ★★★★ │ └─────────────────┴──────────┴──────────┴──────────┴──────────┴───────────┘ ``` ### Ключови предимства на Go **1. Бърза компилация** Go се компилира изключително бързо - проект с хиляди файлове се компилира за секунди. **2. Single binary deployment** Компилираният Go binary съдържа всичко необходимо - няма външни зависимости. **3. Вградена concurrency** Goroutines и channels правят конкурентното програмиране лесно и безопасно. **4. Garbage collection** Автоматично управление на паметта без overhead на JVM. **5. Стандартна библиотека** net/http, encoding/json, database/sql - всичко нужно за API е вградено. ## Архитектура на Go API сървър ``` ┌──────────────────────────────────────────────────────────────────────────────┐ │ GO API SERVER ARCHITECTURE │ ├──────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ main.go │ Entry point │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Router │────►│ Middleware │────►│ Handlers │ │ │ │ (mux) │ │ (CORS,Auth)│ │ │ │ │ └─────────────┘ └─────────────┘ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Models │◄────│ Services │ │ │ │ (structs) │ │ (business) │ │ │ └─────────────┘ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Database │ │ │ │ (SQLite) │ │ │ └─────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────────────────┘ ``` ## Основни концепции в Go ### Структури (Structs) ```go // Дефиниция на модел type Article struct { ID int `json:"id"` Title string `json:"title"` Content string `json:"content"` Author string `json:"author"` Published bool `json:"published"` RenderMode string `json:"render_mode"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // Методи върху struct func (a *Article) IsValid() bool { return a.Title != "" && a.Content != "" && a.Author != "" } func (a *Article) Summary() string { if len(a.Content) > 200 { return a.Content[:200] + "..." } return a.Content } ``` ### Interfaces ```go // Interface за repository pattern type ArticleRepository interface { GetByID(id int) (*Article, error) GetAll() ([]Article, error) Create(article *Article) error Update(article *Article) error Delete(id int) error } // SQLite имплементация type SQLiteArticleRepo struct { db *sql.DB } func (r *SQLiteArticleRepo) GetByID(id int) (*Article, error) { var a Article err := r.db.QueryRow(` SELECT id, title, content, author, published, render_mode, created_at, updated_at FROM articles WHERE id = ? `, id).Scan( &a.ID, &a.Title, &a.Content, &a.Author, &a.Published, &a.RenderMode, &a.CreatedAt, &a.UpdatedAt, ) return &a, err } ``` ### Goroutines и Channels ```go // Goroutines - лек thread func processArticles(articles []Article) { results := make(chan ProcessResult, len(articles)) // Стартираме goroutine за всяка статия for _, article := range articles { go func(a Article) { result := processArticle(a) results <- result }(article) } // Събираме резултатите for i := 0; i < len(articles); i++ { result := <-results handleResult(result) } } // Worker pool pattern func workerPool(jobs <-chan Job, results chan<- Result, numWorkers int) { var wg sync.WaitGroup for i := 0; i < numWorkers; i++ { wg.Add(1) go func() { defer wg.Done() for job := range jobs { results <- process(job) } }() } wg.Wait() close(results) } ``` ## HTTP сървър в Go ### Основен сървър ```go package main import ( "encoding/json" "log" "net/http" ) func main() { // Регистриране на handlers http.HandleFunc("/api/health", corsMiddleware(healthHandler)) http.HandleFunc("/api/articles", corsMiddleware(articlesHandler)) http.HandleFunc("/api/article", corsMiddleware(articleHandler)) // Стартиране на сървъра port := ":8082" log.Printf("Server starting on port %s...", port) log.Fatal(http.ListenAndServe(port, nil)) } // CORS Middleware func corsMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } next(w, r) } } // Helper за JSON отговори func jsonResponse(w http.ResponseWriter, data interface{}, status int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(data) } ``` ### Handler примери ```go // GET /api/articles - връща всички публикувани статии func articlesHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { jsonResponse(w, map[string]string{ "error": "Method not allowed", }, http.StatusMethodNotAllowed) return } rows, err := db.Query(` SELECT id, title, content, author, published, render_mode, created_at, updated_at FROM articles WHERE published = true ORDER BY created_at DESC `) if err != nil { jsonResponse(w, map[string]string{ "error": err.Error(), }, http.StatusInternalServerError) return } defer rows.Close() var articles []Article for rows.Next() { var a Article rows.Scan( &a.ID, &a.Title, &a.Content, &a.Author, &a.Published, &a.RenderMode, &a.CreatedAt, &a.UpdatedAt, ) articles = append(articles, a) } if articles == nil { articles = []Article{} // Връщаме празен array, не null } jsonResponse(w, articles, http.StatusOK) } // POST /api/inquiry - създава ново запитване func createInquiryHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { jsonResponse(w, map[string]string{ "error": "Method not allowed", }, http.StatusMethodNotAllowed) return } var inquiry Inquiry if err := json.NewDecoder(r.Body).Decode(&inquiry); err != nil { jsonResponse(w, map[string]string{ "error": "Invalid JSON: " + err.Error(), }, http.StatusBadRequest) return } // Валидация if inquiry.Name == "" || inquiry.Email == "" || inquiry.Message == "" { jsonResponse(w, map[string]string{ "error": "Name, email, and message are required", }, http.StatusBadRequest) return } // Записване в базата result, err := db.Exec(` INSERT INTO inquiries (name, email, message, source_page, source_type, article_id) VALUES (?, ?, ?, ?, ?, ?) `, inquiry.Name, inquiry.Email, inquiry.Message, inquiry.SourcePage, inquiry.SourceType, inquiry.ArticleID) if err != nil { jsonResponse(w, map[string]string{ "error": err.Error(), }, http.StatusInternalServerError) return } id, _ := result.LastInsertId() inquiry.ID = int(id) inquiry.CreatedAt = time.Now() jsonResponse(w, map[string]interface{}{ "success": true, "inquiry": inquiry, }, http.StatusCreated) } ``` ## База данни с database/sql ### SQLite интеграция ```go import ( "database/sql" _ "github.com/mattn/go-sqlite3" ) var db *sql.DB func initDB() { var err error dbPath := "/var/www/project/data/app.db" db, err = sql.Open("sqlite3", dbPath) if err != nil { log.Fatal(err) } // Настройки за по-добра производителност db.SetMaxOpenConns(25) db.SetMaxIdleConns(5) db.SetConnMaxLifetime(5 * time.Minute) // Създаване на таблици _, err = db.Exec(` CREATE TABLE IF NOT EXISTS articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, author TEXT NOT NULL, published BOOLEAN DEFAULT FALSE, render_mode TEXT DEFAULT 'SSR', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_articles_published ON articles(published); CREATE INDEX IF NOT EXISTS idx_articles_render_mode ON articles(render_mode); `) if err != nil { log.Fatal(err) } } ``` ### Prepared Statements ```go var ( stmtGetArticle *sql.Stmt stmtCreateArticle *sql.Stmt ) func initStatements() { var err error stmtGetArticle, err = db.Prepare(` SELECT id, title, content, author, published, render_mode, created_at, updated_at FROM articles WHERE id = ? `) if err != nil { log.Fatal(err) } stmtCreateArticle, err = db.Prepare(` INSERT INTO articles (title, content, author, published, render_mode) VALUES (?, ?, ?, ?, ?) `) if err != nil { log.Fatal(err) } } // Използване func getArticle(id int) (*Article, error) { var a Article err := stmtGetArticle.QueryRow(id).Scan( &a.ID, &a.Title, &a.Content, &a.Author, &a.Published, &a.RenderMode, &a.CreatedAt, &a.UpdatedAt, ) return &a, err } ``` ## Error Handling в Go ```go // Custom error types type NotFoundError struct { Resource string ID int } func (e *NotFoundError) Error() string { return fmt.Sprintf("%s with ID %d not found", e.Resource, e.ID) } // Използване func getArticle(id int) (*Article, error) { var a Article err := db.QueryRow("SELECT ... WHERE id = ?", id).Scan(...) if err == sql.ErrNoRows { return nil, &NotFoundError{Resource: "Article", ID: id} } if err != nil { return nil, fmt.Errorf("database error: %w", err) } return &a, nil } // В handler func articleHandler(w http.ResponseWriter, r *http.Request) { article, err := getArticle(id) if err != nil { var notFound *NotFoundError if errors.As(err, ¬Found) { jsonResponse(w, map[string]string{ "error": err.Error(), }, http.StatusNotFound) return } // Log internal errors, return generic message log.Printf("Error: %v", err) jsonResponse(w, map[string]string{ "error": "Internal server error", }, http.StatusInternalServerError) return } jsonResponse(w, article, http.StatusOK) } ``` ## Testing в Go ```go // article_test.go package main import ( "encoding/json" "net/http" "net/http/httptest" "testing" ) func TestHealthHandler(t *testing.T) { req := httptest.NewRequest("GET", "/api/health", nil) rec := httptest.NewRecorder() healthHandler(rec, req) if rec.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", rec.Code) } var response map[string]string json.Unmarshal(rec.Body.Bytes(), &response) if response["status"] != "ok" { t.Errorf("Expected status 'ok', got '%s'", response["status"]) } } func TestArticleValidation(t *testing.T) { tests := []struct { name string article Article valid bool }{ { name: "Valid article", article: Article{Title: "Test", Content: "Content", Author: "Author"}, valid: true, }, { name: "Missing title", article: Article{Content: "Content", Author: "Author"}, valid: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.article.IsValid(); got != tt.valid { t.Errorf("IsValid() = %v, want %v", got, tt.valid) } }) } } ``` ## Deployment ### Компилация ```bash # Стандартна компилация go build -o server main.go # С оптимизации за production CGO_ENABLED=1 go build -ldflags="-s -w" -o server main.go # Cross-compilation GOOS=linux GOARCH=amd64 go build -o server-linux main.go ``` ### Systemd Service ```ini # /etc/systemd/system/go-api.service [Unit] Description=Go API Server After=network.target [Service] Type=simple User=www-data WorkingDirectory=/var/www/project/backend ExecStart=/var/www/project/backend/server Restart=always RestartSec=5 Environment=PORT=8082 [Install] WantedBy=multi-user.target ``` ## Заключение Go е отличен избор за backend разработка благодарение на: - Простота и бърза компилация - Отлична производителност - Вградена concurrency - Single binary deployment - Богата стандартна библиотека За нашия проект Go перфектно комплементира Astro frontend-а, осигурявайки бърз и надежден API сървър. --- **Брой думи: 3,287**
Render Mode
SSR (Server-Side Rendering)
SSG (Static Site Generation)
Published
Cancel
Save Changes