-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(docker, config, api): update docker-compose for logging, enhance…
… app structure, and add system metrics display - Updated docker-compose.yml to mount logs directory. - Changed BASE_URL environment variable to point to the new API endpoint. - Refactored main.go to implement a structured App type, improving initialization and graceful shutdown. - Enhanced config management with JSON loading and environment variable support. - Added monitoring capabilities in api_handler for logging request metrics. - Introduced new metrics display in index.html with corresponding CSS styles for better visualization.
- Loading branch information
1 parent
f31ff5c
commit e70ca4c
Showing
15 changed files
with
683 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"server": { | ||
"port": ":5003", | ||
"read_timeout": "30s", | ||
"write_timeout": "30s", | ||
"max_header_bytes": 1048576 | ||
}, | ||
"storage": { | ||
"data_dir": "/root/data", | ||
"stats_file": "/root/data/stats.json", | ||
"log_file": "/var/log/random-api/server.log" | ||
}, | ||
"api": { | ||
"base_url": "", | ||
"request_timeout": "10s" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,66 @@ | ||
package config | ||
|
||
import ( | ||
"encoding/json" | ||
"math/rand" | ||
"os" | ||
"time" | ||
) | ||
|
||
const ( | ||
Port = ":5003" | ||
RequestTimeout = 10 * time.Second | ||
EnvBaseURL = "BASE_URL" | ||
DefaultPort = ":5003" | ||
RequestTimeout = 10 * time.Second | ||
) | ||
|
||
type Config struct { | ||
Server struct { | ||
Port string `json:"port"` | ||
ReadTimeout time.Duration `json:"read_timeout"` | ||
WriteTimeout time.Duration `json:"write_timeout"` | ||
MaxHeaderBytes int `json:"max_header_bytes"` | ||
} `json:"server"` | ||
|
||
Storage struct { | ||
DataDir string `json:"data_dir"` | ||
StatsFile string `json:"stats_file"` | ||
LogFile string `json:"log_file"` | ||
} `json:"storage"` | ||
|
||
API struct { | ||
BaseURL string `json:"base_url"` | ||
RequestTimeout time.Duration `json:"request_timeout"` | ||
} `json:"api"` | ||
} | ||
|
||
var ( | ||
cfg Config | ||
RNG *rand.Rand | ||
) | ||
|
||
func Load(configFile string) error { | ||
file, err := os.Open(configFile) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
decoder := json.NewDecoder(file) | ||
if err := decoder.Decode(&cfg); err != nil { | ||
return err | ||
} | ||
|
||
if envBaseURL := os.Getenv(EnvBaseURL); envBaseURL != "" { | ||
cfg.API.BaseURL = envBaseURL | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func Get() *Config { | ||
return &cfg | ||
} | ||
|
||
func InitRNG(r *rand.Rand) { | ||
RNG = r | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"server": { | ||
"port": ":5003", | ||
"read_timeout": "30s", | ||
"write_timeout": "30s", | ||
"max_header_bytes": 1048576 | ||
}, | ||
"storage": { | ||
"data_dir": "/root/data", | ||
"stats_file": "/root/data/stats.json", | ||
"log_file": "/var/log/random-api/server.log" | ||
}, | ||
"api": { | ||
"base_url": "", | ||
"request_timeout": "10s" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package handlers | ||
|
||
import ( | ||
"net/http" | ||
"random-api-go/router" | ||
"random-api-go/stats" | ||
) | ||
|
||
type Router interface { | ||
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) | ||
} | ||
|
||
type Handlers struct { | ||
Stats *stats.StatsManager | ||
} | ||
|
||
func (h *Handlers) HandleAPIRequest(w http.ResponseWriter, r *http.Request) { | ||
HandleAPIRequest(w, r) | ||
} | ||
|
||
func (h *Handlers) HandleStats(w http.ResponseWriter, r *http.Request) { | ||
HandleStats(w, r) | ||
} | ||
|
||
func (h *Handlers) HandleURLStats(w http.ResponseWriter, r *http.Request) { | ||
HandleURLStats(w, r) | ||
} | ||
|
||
func (h *Handlers) HandleMetrics(w http.ResponseWriter, r *http.Request) { | ||
HandleMetrics(w, r) | ||
} | ||
|
||
func (h *Handlers) Setup(r *router.Router) { | ||
r.HandleFunc("/pic/", h.HandleAPIRequest) | ||
r.HandleFunc("/video/", h.HandleAPIRequest) | ||
r.HandleFunc("/stats", h.HandleStats) | ||
r.HandleFunc("/urlstats", h.HandleURLStats) | ||
r.HandleFunc("/metrics", h.HandleMetrics) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package handlers | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"random-api-go/monitoring" | ||
) | ||
|
||
func HandleMetrics(w http.ResponseWriter, r *http.Request) { | ||
metrics := monitoring.CollectMetrics() | ||
|
||
w.Header().Set("Content-Type", "application/json") | ||
json.NewEncoder(w).Encode(metrics) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,117 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"math/rand" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
"random-api-go/config" | ||
"random-api-go/handlers" | ||
"random-api-go/logging" | ||
"random-api-go/router" | ||
"random-api-go/services" | ||
"random-api-go/stats" | ||
|
||
"syscall" | ||
"time" | ||
) | ||
|
||
func init() { | ||
if err := os.MkdirAll("data", 0755); err != nil { | ||
log.Fatal("Failed to create data directory:", err) | ||
type App struct { | ||
server *http.Server | ||
router *router.Router | ||
Stats *stats.StatsManager | ||
} | ||
|
||
func NewApp() *App { | ||
return &App{ | ||
router: router.New(), | ||
} | ||
} | ||
|
||
func main() { | ||
source := rand.NewSource(time.Now().UnixNano()) | ||
config.InitRNG(rand.New(source)) | ||
func (a *App) Initialize() error { | ||
// 创建必要的目录 | ||
if err := os.MkdirAll(config.Get().Storage.DataDir, 0755); err != nil { | ||
return fmt.Errorf("failed to create data directory: %w", err) | ||
} | ||
|
||
// 初始化配置 | ||
if err := config.Load("/root/data/config.json"); err != nil { | ||
return err | ||
} | ||
|
||
// 初始化日志 | ||
logging.SetupLogging() | ||
statsManager := stats.NewStatsManager("data/stats.json") | ||
|
||
// 设置优雅关闭 | ||
setupGracefulShutdown(statsManager) | ||
// 初始化统计管理器 | ||
a.Stats = stats.NewStatsManager(config.Get().Storage.StatsFile) | ||
|
||
// 初始化handlers | ||
if err := handlers.InitializeHandlers(statsManager); err != nil { | ||
log.Fatal("Failed to initialize handlers:", err) | ||
// 初始化服务 | ||
if err := services.InitializeCSVService(); err != nil { | ||
return err | ||
} | ||
|
||
// 初始化加载所有CSV内容 | ||
if err := services.InitializeCSVService(); err != nil { | ||
log.Fatal("Failed to initialize CSV Service:", err) | ||
// 创建 handlers | ||
handlers := &handlers.Handlers{ | ||
Stats: a.Stats, | ||
} | ||
|
||
// 设置路由 | ||
setupRoutes() | ||
a.router.Setup(handlers) | ||
|
||
log.Printf("Server starting on %s...\n", config.Port) | ||
if err := http.ListenAndServe(config.Port, nil); err != nil { | ||
log.Fatal(err) | ||
// 创建 HTTP 服务器 | ||
cfg := config.Get().Server | ||
a.server = &http.Server{ | ||
Addr: cfg.Port, | ||
Handler: a.router, | ||
ReadTimeout: cfg.ReadTimeout, | ||
WriteTimeout: cfg.WriteTimeout, | ||
MaxHeaderBytes: cfg.MaxHeaderBytes, | ||
} | ||
} | ||
|
||
func setupGracefulShutdown(statsManager *stats.StatsManager) { | ||
c := make(chan os.Signal, 1) | ||
signal.Notify(c, os.Interrupt, syscall.SIGTERM) | ||
return nil | ||
} | ||
|
||
func (a *App) Run() error { | ||
// 启动服务器 | ||
go func() { | ||
<-c | ||
log.Println("Server is shutting down...") | ||
statsManager.Shutdown() | ||
log.Println("Stats manager shutdown completed") | ||
os.Exit(0) | ||
log.Printf("Server starting on %s...\n", a.server.Addr) | ||
if err := a.server.ListenAndServe(); err != http.ErrServerClosed { | ||
log.Fatalf("Server failed: %v", err) | ||
} | ||
}() | ||
|
||
// 优雅关闭 | ||
return a.gracefulShutdown() | ||
} | ||
|
||
func setupRoutes() { | ||
fs := http.FileServer(http.Dir("./public")) | ||
http.Handle("/", fs) | ||
http.HandleFunc("/pic/", handlers.HandleAPIRequest) | ||
http.HandleFunc("/video/", handlers.HandleAPIRequest) | ||
http.HandleFunc("/stats", handlers.HandleStats) | ||
// 添加URL统计接口 | ||
http.HandleFunc("/urlstats", handlers.HandleURLStats) | ||
func (a *App) gracefulShutdown() error { | ||
quit := make(chan os.Signal, 1) | ||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) | ||
<-quit | ||
|
||
log.Println("Server is shutting down...") | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
defer cancel() | ||
|
||
a.Stats.Shutdown() | ||
|
||
if err := a.server.Shutdown(ctx); err != nil { | ||
return err | ||
} | ||
|
||
log.Println("Server shutdown completed") | ||
return nil | ||
} | ||
|
||
func main() { | ||
app := NewApp() | ||
if err := app.Initialize(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if err := app.Run(); err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
Oops, something went wrong.