You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

134 lines
4.3 KiB

  1. package main
  2. import (
  3. "context"
  4. "log"
  5. "net/http"
  6. _ "net/http/pprof"
  7. "os"
  8. "os/signal"
  9. "strings"
  10. "syscall"
  11. "time"
  12. redisprom "github.com/globocom/go-redis-prometheus"
  13. "github.com/go-redis/redis/v8"
  14. "github.com/gorilla/mux"
  15. "github.com/prometheus/client_golang/prometheus/promhttp"
  16. "github.com/tevino/abool/v2"
  17. )
  18. const (
  19. ItemChannelBuffer = 100000
  20. ItemWrapSize = 100000
  21. )
  22. func main() {
  23. log.SetFlags(log.Flags() | log.Lshortfile)
  24. trackerRedisOptions, err := redis.ParseURL(os.Getenv("REDIS_TRACKER"))
  25. if err != nil {
  26. log.Panicf("invalid REDIS_TRACKER url: %s", err)
  27. }
  28. trackerRedisOptions.ReadTimeout = 15 * time.Minute
  29. trackerRedisClient := redis.NewClient(trackerRedisOptions)
  30. backfeedRedisClient := redis.NewClusterClient(&redis.ClusterOptions{
  31. Addrs: strings.Split(os.Getenv("REDIS_BACKFEED_ADDRS"), ","),
  32. Username: os.Getenv("REDIS_BACKFEED_USERNAME"),
  33. Password: os.Getenv("REDIS_BACKFEED_PASSWORD"),
  34. ReadTimeout: 15 * time.Minute,
  35. PoolSize: 256,
  36. })
  37. backfeedRedisMetricsHook := redisprom.NewHook(
  38. redisprom.WithInstanceName("backfeed"),
  39. )
  40. backfeedRedisClient.AddHook(backfeedRedisMetricsHook)
  41. trackerRedisMetricsHook := redisprom.NewHook(
  42. redisprom.WithInstanceName("tracker"),
  43. )
  44. trackerRedisClient.AddHook(trackerRedisMetricsHook)
  45. if err := trackerRedisClient.Ping(context.Background()).Err(); err != nil {
  46. log.Panicf("unable to ping tracker redis: %s", err)
  47. }
  48. if err := backfeedRedisClient.Ping(context.Background()).Err(); err != nil {
  49. log.Panicf("unable to ping backfeed redis: %s", err)
  50. }
  51. err = backfeedRedisClient.ForEachShard(context.Background(), func(ctx context.Context, client *redis.Client) error {
  52. client.ClientGetName(ctx)
  53. return client.Ping(ctx).Err()
  54. })
  55. globalBackfeedManager := &GlobalBackfeedManager{
  56. ActiveFeeds: map[string]*ProjectBackfeedManager{},
  57. ActiveSlugs: map[string]string{},
  58. TrackerRedis: trackerRedisClient,
  59. BackfeedRedis: backfeedRedisClient,
  60. Populated: abool.New(),
  61. }
  62. globalBackfeedManager.Context, globalBackfeedManager.Cancel = context.WithCancel(context.Background())
  63. defer globalBackfeedManager.CancelAllFeeds()
  64. if err := globalBackfeedManager.RefreshFeeds(); err != nil {
  65. log.Panicf("unable to set up backfeed projects: %s", err)
  66. }
  67. r := mux.NewRouter()
  68. r.Methods(http.MethodPost).Path("/legacy/{slug}").HandlerFunc(globalBackfeedManager.HandleLegacy)
  69. r.Methods(http.MethodGet).Path("/ping").HandlerFunc(globalBackfeedManager.HandlePing)
  70. r.Methods(http.MethodGet).Path("/health").HandlerFunc(globalBackfeedManager.HandleHealth)
  71. r.Methods(http.MethodGet).Path("/manage/lastaccessstats").HandlerFunc(globalBackfeedManager.HandleLastAccessStats)
  72. r.Methods(http.MethodGet).Path("/manage/dump/{key}").HandlerFunc(globalBackfeedManager.HandleDump)
  73. r.Methods(http.MethodPut).Path("/manage/load").HandlerFunc(globalBackfeedManager.HandleLoad)
  74. r.Methods(http.MethodDelete).Path("/manage/unlink/{key}").HandlerFunc(globalBackfeedManager.HandleUnlink)
  75. r.Methods(http.MethodGet).Path("/manage/redisinfo").HandlerFunc(globalBackfeedManager.HandleRedisInfo)
  76. rMetrics := mux.NewRouter()
  77. rMetrics.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
  78. rMetrics.Path("/metrics").Handler(promhttp.Handler())
  79. doneChan := make(chan bool)
  80. serveErrChan := make(chan error)
  81. go func() {
  82. s := &http.Server{
  83. Addr: os.Getenv("HTTP_ADDR"),
  84. IdleTimeout: 1 * time.Hour,
  85. MaxHeaderBytes: 1 * 1024 * 1024,
  86. Handler: r,
  87. }
  88. serveErrChan <- s.ListenAndServe()
  89. }()
  90. metricsErrChan := make(chan error)
  91. go func() {
  92. if os.Getenv("METRICS_ADDR") != "" {
  93. s := &http.Server{
  94. Addr: os.Getenv("METRICS_ADDR"),
  95. IdleTimeout: 1 * time.Hour,
  96. MaxHeaderBytes: 1 * 1024 * 1024,
  97. Handler: rMetrics,
  98. }
  99. metricsErrChan <- s.ListenAndServe()
  100. } else {
  101. <-doneChan
  102. metricsErrChan <- nil
  103. }
  104. }()
  105. log.Printf("backfeed listening on %s", os.Getenv("HTTP_ADDR"))
  106. if os.Getenv("METRICS_ADDR") != "" {
  107. log.Printf("metrics/debug listening on %s", os.Getenv("METRICS_ADDR"))
  108. }
  109. sc := make(chan os.Signal, 1)
  110. signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
  111. ticker := time.NewTicker(1 * time.Second)
  112. for {
  113. select {
  114. case <-sc:
  115. return
  116. case <-ticker.C:
  117. }
  118. if err := globalBackfeedManager.RefreshFeeds(); err != nil {
  119. log.Printf("unable to refresh backfeed projects: %s", err)
  120. }
  121. }
  122. }