mirror of
https://github.com/nprasad2077/NBA_Go.git
synced 2026-09-22 05:55:13 +00:00
fleet docker
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
.git
|
||||||
|
.idea
|
||||||
|
*.log
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# STEP 1 - build with CGO enabled
|
||||||
|
FROM golang:1.22-bullseye AS builder
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
ENV GOOS=linux
|
||||||
|
ENV GOARCH=arm64
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod ./
|
||||||
|
COPY go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN go build -o /nba_go main.go
|
||||||
|
|
||||||
|
# STEP 2 - final image
|
||||||
|
FROM debian:bullseye-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create data directory
|
||||||
|
RUN mkdir -p /app/data
|
||||||
|
|
||||||
|
COPY --from=builder /nba_go /nba_go
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
CMD ["/nba_go"]
|
||||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func InitDB() *gorm.DB {
|
func InitDB() *gorm.DB {
|
||||||
db, err := gorm.Open(sqlite.Open("/Users/ravi/code/projects/NBA_Go/data/nba.db"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open("/app/data/nba.db"), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to connect database")
|
log.Fatal("Failed to connect database")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
db-init:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
command: "/nba_go import-data"
|
||||||
|
|
||||||
|
api1:
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "5001:5000"
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
db-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks:
|
||||||
|
- api.network
|
||||||
|
|
||||||
|
# Add the same depends_on configuration for api2 and api3
|
||||||
|
api2:
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "5002:5000"
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
db-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks:
|
||||||
|
- api.network
|
||||||
|
|
||||||
|
api3:
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "5003:5000"
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
db-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
networks:
|
||||||
|
- api.network
|
||||||
|
|
||||||
|
# Nginx load balancer
|
||||||
|
nginx:
|
||||||
|
image: nginx:stable
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
depends_on:
|
||||||
|
- api1
|
||||||
|
- api2
|
||||||
|
- api3
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
networks:
|
||||||
|
- api.network
|
||||||
|
|
||||||
|
# Prometheus for metrics
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus
|
||||||
|
volumes:
|
||||||
|
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
networks:
|
||||||
|
- api.network
|
||||||
|
|
||||||
|
# Grafana for dashboards
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana
|
||||||
|
ports:
|
||||||
|
- "3001:3000"
|
||||||
|
networks:
|
||||||
|
- api.network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
api.network:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/nprasad2077/NBA_Go
|
module github.com/nprasad2077/NBA_Go
|
||||||
|
|
||||||
go 1.24.2
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
|
|||||||
@@ -2,56 +2,69 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/nprasad2077/NBA_Go/config"
|
"github.com/nprasad2077/NBA_Go/config"
|
||||||
"github.com/nprasad2077/NBA_Go/routes"
|
"github.com/nprasad2077/NBA_Go/routes"
|
||||||
"github.com/nprasad2077/NBA_Go/services"
|
"github.com/nprasad2077/NBA_Go/services"
|
||||||
_"github.com/nprasad2077/NBA_Go/docs" // swag docs
|
_"github.com/nprasad2077/NBA_Go/docs" // swag docs
|
||||||
fiberswagger "github.com/swaggo/fiber-swagger"
|
fiberswagger "github.com/swaggo/fiber-swagger"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
// importData handles fetching and storing all player stats
|
||||||
app := fiber.New()
|
func importData(db *gorm.DB) {
|
||||||
|
log.Println("Starting data import process...")
|
||||||
app.Use(logger.New())
|
|
||||||
|
|
||||||
db := config.InitDB()
|
|
||||||
|
|
||||||
// Automatically fetch player stats on startup
|
|
||||||
go func() {
|
|
||||||
for season := 1993; season <= 2025; season++ {
|
|
||||||
if err := services.FetchAndStorePlayerAdvancedStats(db, season); err != nil {
|
|
||||||
log.Printf("Fetch failed for player advanced season %d: %v\n", season, err)
|
|
||||||
} else {
|
|
||||||
log.Printf("Fetch successful for player advanced season %d\n", season)
|
|
||||||
}
|
|
||||||
time.Sleep(1100 * time.Millisecond) // optional delay
|
|
||||||
}
|
|
||||||
log.Printf("player advanced Import Success")
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for season := 1993; season <= 2025; season++ {
|
|
||||||
if err := services.FetchAndStorePlayerTotalStats(db, season); err != nil {
|
|
||||||
log.Printf("Fetch failed for player totals season %d: %v\n", season, err)
|
|
||||||
} else {
|
|
||||||
log.Printf("Fetch successful for player totals season %d\n", season)
|
|
||||||
}
|
|
||||||
time.Sleep(1000 * time.Millisecond) // optional delay
|
|
||||||
}
|
|
||||||
log.Printf("player totals Import Success")
|
|
||||||
}()
|
|
||||||
|
|
||||||
|
|
||||||
|
// Import advanced stats
|
||||||
|
for season := 2020; season <= 2025; season++ {
|
||||||
|
if err := services.FetchAndStorePlayerAdvancedStats(db, season); err != nil {
|
||||||
|
log.Printf("Fetch failed for player advanced season %d: %v\n", season, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Fetch successful for player advanced season %d\n", season)
|
||||||
|
}
|
||||||
|
time.Sleep(1100 * time.Millisecond) // optional delay
|
||||||
|
}
|
||||||
|
log.Printf("Player advanced import completed")
|
||||||
|
|
||||||
|
// Import total stats
|
||||||
|
for season := 2020; season <= 2025; season++ {
|
||||||
|
if err := services.FetchAndStorePlayerTotalStats(db, season); err != nil {
|
||||||
|
log.Printf("Fetch failed for player totals season %d: %v\n", season, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Fetch successful for player totals season %d\n", season)
|
||||||
|
}
|
||||||
|
time.Sleep(1000 * time.Millisecond) // optional delay
|
||||||
|
}
|
||||||
|
log.Printf("Player totals import completed")
|
||||||
|
|
||||||
|
log.Println("Data import process completed successfully!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Check if we're running in import-data mode
|
||||||
|
if len(os.Args) > 1 && os.Args[1] == "import-data" {
|
||||||
|
db := config.InitDB()
|
||||||
|
importData(db)
|
||||||
|
return // Exit after import is complete
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular API server mode
|
||||||
|
app := fiber.New()
|
||||||
|
app.Use(logger.New())
|
||||||
|
|
||||||
|
db := config.InitDB()
|
||||||
|
|
||||||
|
// No automatic data fetching in API server mode
|
||||||
|
// Only the db-init container will handle data imports
|
||||||
|
|
||||||
routes.RegisterPlayerAdvancedRoutes(app, db)
|
routes.RegisterPlayerAdvancedRoutes(app, db)
|
||||||
routes.RegisterPlayerTotalRoutes(app, db)
|
routes.RegisterPlayerTotalRoutes(app, db)
|
||||||
|
|
||||||
app.Get("/swagger/*", fiberswagger.WrapHandler)
|
app.Get("/swagger/*", fiberswagger.WrapHandler)
|
||||||
|
|
||||||
app.Listen(":3001")
|
log.Println("Starting API server on port 5000...")
|
||||||
|
app.Listen(":5000")
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
user nginx;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
upstream go_backend {
|
||||||
|
server api1:5000;
|
||||||
|
server api2:5000;
|
||||||
|
server api3:5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://go_backend;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection keep-alive;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
global:
|
||||||
|
scrape_interval: 5s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'nba_go_api'
|
||||||
|
metrics_path: /metrics
|
||||||
|
static_configs:
|
||||||
|
- targets: ['nginx:8080']
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
"log"
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Response struct to track which server handled the request
|
||||||
|
type Response struct {
|
||||||
|
StatusCode int
|
||||||
|
Body string
|
||||||
|
Duration time.Duration
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Define command line flags
|
||||||
|
numRequests := flag.Int("n", 100, "Number of requests to send")
|
||||||
|
concurrency := flag.Int("c", 10, "Number of concurrent requests")
|
||||||
|
endpoint := flag.String("url", "http://localhost:8080/api/player/advanced", "API endpoint to test")
|
||||||
|
logFile := flag.String("log", "loadtest.log", "Log file path")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Setup logging
|
||||||
|
f, err := os.OpenFile(*logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error opening log file: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
log.SetOutput(f)
|
||||||
|
|
||||||
|
fmt.Printf("Starting load test with %d requests, %d concurrent\n", *numRequests, *concurrency)
|
||||||
|
fmt.Printf("Testing endpoint: %s\n", *endpoint)
|
||||||
|
|
||||||
|
// Channel to collect results
|
||||||
|
results := make(chan Response, *numRequests)
|
||||||
|
|
||||||
|
// Use a WaitGroup to manage concurrency
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Semaphore to limit concurrency
|
||||||
|
sem := make(chan bool, *concurrency)
|
||||||
|
|
||||||
|
// Start the timer
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// Launch goroutines for requests
|
||||||
|
for i := 0; i < *numRequests; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(id int) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// Acquire semaphore
|
||||||
|
sem <- true
|
||||||
|
defer func() { <-sem }()
|
||||||
|
|
||||||
|
// Make the request
|
||||||
|
start := time.Now()
|
||||||
|
resp, err := http.Get(*endpoint)
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
result := Response{
|
||||||
|
Duration: duration,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Request %d failed: %v", id, err)
|
||||||
|
results <- result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to read response body: %v", err)
|
||||||
|
result.Error = err
|
||||||
|
results <- result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result.StatusCode = resp.StatusCode
|
||||||
|
result.Body = string(body)
|
||||||
|
results <- result
|
||||||
|
|
||||||
|
// Log request details
|
||||||
|
log.Printf("Request %d: Status=%d, Time=%v",
|
||||||
|
id, resp.StatusCode, duration)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the results channel when all requests are done
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Process results
|
||||||
|
var successCount, errorCount int
|
||||||
|
var totalDuration time.Duration
|
||||||
|
|
||||||
|
for result := range results {
|
||||||
|
if result.Error != nil {
|
||||||
|
errorCount++
|
||||||
|
} else if result.StatusCode == 200 {
|
||||||
|
successCount++
|
||||||
|
totalDuration += result.Duration
|
||||||
|
} else {
|
||||||
|
errorCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate statistics
|
||||||
|
totalTime := time.Since(startTime)
|
||||||
|
avgDuration := totalDuration / time.Duration(successCount)
|
||||||
|
requestsPerSecond := float64(*numRequests) / totalTime.Seconds()
|
||||||
|
|
||||||
|
// Print summary
|
||||||
|
fmt.Printf("\nLoad Test Summary:\n")
|
||||||
|
fmt.Printf("Total Requests: %d\n", *numRequests)
|
||||||
|
fmt.Printf("Successful Requests: %d\n", successCount)
|
||||||
|
fmt.Printf("Failed Requests: %d\n", errorCount)
|
||||||
|
fmt.Printf("Total Time: %v\n", totalTime)
|
||||||
|
fmt.Printf("Average Response Time: %v\n", avgDuration)
|
||||||
|
fmt.Printf("Requests Per Second: %.2f\n", requestsPerSecond)
|
||||||
|
}
|
||||||
+2200
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user