Go est devenu une référence en matière de développement backend moderne, de services cloud et d'outils DevOps. Explorons comment écrire du code Go idiomatique qui exploite les atouts du langage.
Tout d'abord, mettons en place une structure de projet Go moderne :
# Initialize a new module go mod init myproject # Project structure myproject/ ├── cmd/ │ └── api/ │ └── main.go ├── internal/ │ ├── handlers/ │ ├── models/ │ └── services/ ├── pkg/ │ └── utils/ ├── go.mod └── go.sum
Voici un exemple de programme Go bien structuré :
package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" ) // Server configuration type Config struct { Port string ReadTimeout time.Duration WriteTimeout time.Duration ShutdownTimeout time.Duration } // Application represents our web server type Application struct { config Config logger *log.Logger router *http.ServeMux } // NewApplication creates a new application instance func NewApplication(cfg Config) *Application { logger := log.New(os.Stdout, "[API] ", log.LstdFlags) return &Application{ config: cfg, logger: logger, router: http.NewServeMux(), } } // setupRoutes configures all application routes func (app *Application) setupRoutes() { app.router.HandleFunc("/health", app.healthCheckHandler) app.router.HandleFunc("/api/v1/users", app.handleUsers) } // Run starts the server and handles graceful shutdown func (app *Application) Run() error { // Setup routes app.setupRoutes() // Create server srv := &http.Server{ Addr: ":" + app.config.Port, Handler: app.router, ReadTimeout: app.config.ReadTimeout, WriteTimeout: app.config.WriteTimeout, } // Channel to listen for errors coming from the listener. serverErrors := make(chan error, 1) // Start the server go func() { app.logger.Printf("Starting server on port %s", app.config.Port) serverErrors <- srv.ListenAndServe() }() // Listen for OS signals shutdown := make(chan os.Signal, 1) signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM) // Block until we receive a signal or an error select { case err := <-serverErrors: return fmt.Errorf("server error: %w", err) case <-shutdown: app.logger.Println("Starting shutdown...") // Create context for shutdown ctx, cancel := context.WithTimeout( context.Background(), app.config.ShutdownTimeout, ) defer cancel() // Gracefully shutdown the server err := srv.Shutdown(ctx) if err != nil { return fmt.Errorf("graceful shutdown failed: %w", err) } } return nil }
Le système d'interface de Go et la gestion des erreurs sont des fonctionnalités clés :
// UserService defines the interface for user operations type UserService interface { GetUser(ctx context.Context, id string) (*User, error) CreateUser(ctx context.Context, user *User) error UpdateUser(ctx context.Context, user *User) error DeleteUser(ctx context.Context, id string) error } // Custom error types type NotFoundError struct { Resource string ID string } func (e *NotFoundError) Error() string { return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID) } // Implementation type userService struct { db *sql.DB logger *log.Logger } func (s *userService) GetUser(ctx context.Context, id string) (*User, error) { user := &User{} err := s.db.QueryRowContext( ctx, "SELECT id, name, email FROM users WHERE id = ", id, ).Scan(&user.ID, &user.Name, &user.Email) if err == sql.ErrNoRows { return nil, &NotFoundError{Resource: "user", ID: id} } if err != nil { return nil, fmt.Errorf("querying user: %w", err) } return user, nil }
Les goroutines et les chaînes de Go simplifient la programmation simultanée :
// Worker pool pattern func processItems(items []string, numWorkers int) error { jobs := make(chan string, len(items)) results := make(chan error, len(items)) // Start workers for w := 0; w < numWorkers; w++ { go worker(w, jobs, results) } // Send jobs to workers for _, item := range items { jobs <- item } close(jobs) // Collect results for range items { if err := <-results; err != nil { return err } } return nil } func worker(id int, jobs <-chan string, results chan<- error) { for item := range jobs { results <- processItem(item) } } // Rate limiting func rateLimiter[T any](input <-chan T, limit time.Duration) <-chan T { output := make(chan T) ticker := time.NewTicker(limit) go func() { defer close(output) defer ticker.Stop() for item := range input { <-ticker.C output <- item } }() return output }
Go dispose d'un excellent support de test intégré :
// user_service_test.go package service import ( "context" "testing" "time" ) func TestUserService(t *testing.T) { // Table-driven tests tests := []struct { name string userID string want *User wantErr bool }{ { name: "valid user", userID: "123", want: &User{ ID: "123", Name: "Test User", }, wantErr: false, }, { name: "invalid user", userID: "999", want: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { svc := NewUserService(testDB) got, err := svc.GetUser(context.Background(), tt.userID) if (err != nil) != tt.wantErr { t.Errorf("GetUser() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("GetUser() = %v, want %v", got, tt.want) } }) } } // Benchmarking example func BenchmarkUserService_GetUser(b *testing.B) { svc := NewUserService(testDB) ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = svc.GetUser(ctx, "123") } }
Go facilite le profilage et l'optimisation du code :
// Use sync.Pool for frequently allocated objects var bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func processRequest(data []byte) string { buf := bufferPool.Get().(*bytes.Buffer) defer bufferPool.Put(buf) buf.Reset() buf.Write(data) // Process data... return buf.String() } // Efficiently handle JSON type User struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` CreatedAt time.Time `json:"created_at"` } func (u *User) MarshalJSON() ([]byte, error) { type Alias User return json.Marshal(&struct { *Alias CreatedAt string `json:"created_at"` }{ Alias: (*Alias)(u), CreatedAt: u.CreatedAt.Format(time.RFC3339), }) }
La simplicité et les fonctionnalités puissantes de Go en font un excellent choix pour le développement moderne. Points clés à retenir :
Quels aspects du développement de Go vous intéressent le plus ? Partagez vos expériences dans les commentaires ci-dessous !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!