Heim >Backend-Entwicklung >Golang >Erstellen Sie einen OTP-basierten Authentifizierungsserver mit Go: Part File Uploads und Graceful Shutdown
Diese Ausgabe verfeinert unseren Go-Authentifizierungsserver, indem er Funktionen zum Hochladen von Dateien hinzufügt, die Entwicklung mit einem Makefile
rationalisiert und ein ordnungsgemäßes Herunterfahren des Servers implementiert. Dies verhindert ein abruptes Beenden und stellt sicher, dass alle laufenden Aufgaben abgeschlossen sind, bevor der Server stoppt.
Graceful Shutdown-Implementierung
Eine neue cmd/api/server.go
Datei zentralisiert die Serververwaltung. Eine Goroutine überwacht Beendigungssignale (SIGINT, SIGTERM). Beim Empfang eines Signals wird der Server ordnungsgemäß heruntergefahren. Kommentare verdeutlichen jeden Schritt.
<code class="language-go">package main import ( "context" "errors" "fmt" "net/http" "os" "os/signal" "syscall" "time" ) // ... (Existing application code) ... func (app *application) serve(router http.Handler) error { // Server initialization with timeouts for idle, read, and write operations. srv := &http.Server{ Addr: fmt.Sprintf(":%d", app.config.port), Handler: app.recoverPanic(app.authenticate(router)), IdleTimeout: time.Minute, ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second, } // Channel to manage errors during shutdown. shutdownError := make(chan error) // Goroutine to listen for termination signals and initiate graceful shutdown. go func() { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) s := <-quit // Wait for signal fmt.Println("Shutting down server...") // Context with timeout for graceful shutdown. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Attempt graceful shutdown. shutdownError <- srv.Shutdown(ctx) }() // Start the server and report any errors. fmt.Printf("Starting server on port %d\n", app.config.port) err := srv.ListenAndServe() if !errors.Is(err, http.ErrServerClosed) { return fmt.Errorf("server error: %w", err) } return <-shutdownError } // ... (Rest of application code) ...</code>
Ersetzen Sie in main.go
den srv.ListenAndServe()
-Block durch err = app.serve(router); if err != nil { logger.Fatal(err, nil) }
. Um dies zu testen, müssen Sie den Wiederholungsmechanismus verwenden (Anmeldeinformationen für kontinuierliche Wiederholungsversuche entfernen) und den Server mit Strg C unterbrechen. Es sollte warten, bis die Wiederholungsversuche abgeschlossen sind, bevor es heruntergefahren wird.
Aufgaben mit Makefile automatisieren
A Makefile
automatisiert sich wiederholende Befehle. Fügen Sie Folgendes zum Makefile
Ihres Projekts hinzu:
<code class="language-makefile"># Include variables from the .envrc file include .envrc ## help: print this help message .PHONY: help help: @echo 'Usage:' @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' .PHONY: confirm confirm: @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] ## run/api: run the cmd/api application .PHONY: run/api run/api: go run ./cmd/api ## db/psql: connect to the database using psql .PHONY: db/psql db/psql: psql ${GREENLIGHT_DB_DSN} ## db/migrations/new name=: create a new database migration .PHONY: db/migrations/new db/migrations/new: @echo "Migrating ${name}" migrate create -seq -ext=.sql -dir=./migrations ${name} ## db/migrations/up: apply all up database migrations .PHONY: db/migrations/up db/migrations/up: confirm @echo "Running migrations" migrate -path=./migrations -database=${GREENLIGHT_DB_DSN} up ## audit: tidy and vendor dependencies and format, vet and test all code .PHONY: audit audit: vendor @echo 'Formatting code...' go fmt ./... @echo 'Vetting code...' go vet ./... staticcheck ./... @echo 'Running tests...' go test -race -vet=off ./... ## vendor: tidy and vendor dependencies .PHONY: vendor vendor: @echo 'Tidying and verifying module dependencies...' go mod tidy go mod verify @echo 'Vendoring dependencies...' go mod vendor ## build/api: build the cmd/api application .PHONY: build/api build/api: @echo 'Building cmd/api...' go build -o=./bin/api ./cmd/api GOOS=linux GOARCH=amd64 go build -o=./bin/linux_amd64/api ./cmd/api</code>
Führen Sie Befehle wie make help
, make run/api
, make db/migrations/new name=my_migration
, make db/migrations/up
, make audit
oder make build/api
aus. Nach dieser Struktur können weitere Befehle hinzugefügt werden.
Datei-Upload und -Verfolgung
Eine neue Datenbanktabelle verfolgt hochgeladene Dateien:
Migration erstellen make db/migrations/new name=create-creatives
.
000003_create-creatives.up.sql
:
<code class="language-sql">CREATE TABLE IF NOT EXISTS creatives ( id bigserial PRIMARY KEY, user_id bigint NOT NULL REFERENCES users ON DELETE CASCADE, creative_url text NOT NULL, scheduled_at DATE NOT NULL, created_at timestamp(0) with time zone NOT NULL DEFAULT NOW() );</code>
000003_create-creatives.down.sql
:
<code class="language-sql">DROP TABLE IF EXISTS creatives;</code>
internal/data/creatives.go
:
<code class="language-go">package data import ( "context" "database/sql" "time" "github.com/lib/pq" ) // ... (Creative struct and CreativeModel struct) ... func (c *CreativeModel) Insert(creative *Creative) error { query := `INSERT INTO creatives (user_id, creative_url, scheduled_at) VALUES (, , ) RETURNING id, created_at` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() args := []interface{}{creative.UserID, creative.CreativeURL, creative.ScheduledAt} return c.DB.QueryRowContext(ctx, query, args...).Scan(&creative.ID, &creative.CreatedAt) } func (c *CreativeModel) GetScheduledCreatives() (map[string][]Creative, error) { query := ` SELECT id, user_id, creative_url, scheduled_at, created_at FROM creatives WHERE scheduled_at = ANY() ` dates := []time.Time{ time.Now().Truncate(24 * time.Hour), time.Now().AddDate(0, 0, 1).Truncate(24 * time.Hour), } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() rows, err := c.DB.QueryContext(ctx, query, pq.Array(dates)) if err != nil { return nil, err } defer rows.Close() creatives := map[string][]Creative{ "today": {}, "tomorrow": {}, } for rows.Next() { var creative Creative err := rows.Scan(&creative.ID, &creative.UserID, &creative.CreativeURL, &creative.ScheduledAt, &creative.CreatedAt) if err != nil { return nil, err } if creative.ScheduledAt.Equal(dates[0]) { creatives["today"] = append(creatives["today"], creative) } else if creative.ScheduledAt.Equal(dates[1]) { creatives["tomorrow"] = append(creatives["tomorrow"], creative) } } return creatives, rows.Err() }</code>
cmd/api/creatives.go
: (Verwaltet Datei-Uploads und den Abruf geplanter Motive)
<code class="language-go">package main import ( "fmt" "io" "net/http" "os" "path" "strings" "time" "github.com/google/uuid" "github.com/vishaaxl/cheershare/internal/data" ) const MaxFileSize = 10 << 20 // 10MB // ... (Existing code) ... func (app *application) uploadCreativeHandler(w http.ResponseWriter, r *http.Request) { // ... (File upload handling logic) ... } func (app *application) getScheduledCreativesHandler(w http.ResponseWriter, r *http.Request) { // ... (Retrieve scheduled creatives logic) ... }</code>
Fügen Sie Folgendes zu Ihrer main.go
oder Serverinitialisierung hinzu, um das Upload-Verzeichnis zu erstellen, falls es nicht vorhanden ist:
<code class="language-go">uploadDir := "./uploads" if _, err := os.Stat(uploadDir); os.IsNotExist(err) { err := os.MkdirAll(uploadDir, os.ModePerm) if err != nil { fmt.Println("Unable to create uploads directory:", err) } } router.HandlerFunc(http.MethodPost, "/upload-creative", app.requireAuthenticatedUser(app.uploadCreativeHandler)) router.HandlerFunc(http.MethodGet, "/scheduled", app.requireAuthenticatedUser(app.getScheduledCreativesHandler)) </code>
Denken Sie daran, Platzhalterkommentare durch tatsächliche Implementierungsdetails für die Dateiverarbeitung und Fehlerverwaltung innerhalb der Handler zu ersetzen. Damit ist die Kernfunktionalität abgeschlossen, mit Ausnahme der Zahlungsabwicklung (wird in zukünftigen Updates behandelt). Der bereitgestellte Link zu Teil 3 bleibt erhalten.
Das obige ist der detaillierte Inhalt vonErstellen Sie einen OTP-basierten Authentifizierungsserver mit Go: Part File Uploads und Graceful Shutdown. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!