ソースを参照

Add support for message throughput rate limiting

Kailash Nadh 5 年 前
コミット
894ede561a
3 ファイル変更31 行追加4 行削除
  1. 11 4
      config.toml.sample
  2. 9 0
      init.go
  3. 11 0
      internal/manager/manager.go

+ 11 - 4
config.toml.sample

@@ -25,10 +25,17 @@ from_email = "listmonk <from@mail.com>"
 notify_emails = ["admin1@mysite.com", "admin2@mysite.com"]
 
 # Maximum concurrent workers that will attempt to send messages
-# simultaneously. This should depend on the number of CPUs the
-# machine has and also the number of simultaenous e-mails the
-# mail server will
-concurrency = 100
+# simultaneously. This should ideally depend on the number of CPUs
+# available, and should be based on the maximum number of messages
+# a target SMTP server will accept.
+concurrency = 5
+
+# Maximum number of messages to be sent out per second per worker.
+# If concurrency = 10 and message_rate = 10, then up to 10x10=100 messages
+# may be pushed out every second. This, along with concurrency, should be
+# tweaked to keep the net messages going out per second under the target
+# SMTP's rate limits, if any.
+message_rate = 5
 
 # The number of errors (eg: SMTP timeouts while e-mailing) a running
 # campaign should tolerate before it is paused for manual

+ 9 - 0
init.go

@@ -176,8 +176,17 @@ func initCampaignManager(q *Queries, cs *constants, app *App) *manager.Manager {
 	campNotifCB := func(subject string, data interface{}) error {
 		return app.sendNotification(cs.NotifyEmails, subject, notifTplCampaign, data)
 	}
+
+	if ko.Int("app.concurrency") < 1 {
+		log.Fatal("app.concurrency should be at least 1")
+	}
+	if ko.Int("app.message_rate") < 1 {
+		log.Fatal("app.message_rate should be at least 1")
+	}
+
 	return manager.New(manager.Config{
 		Concurrency:   ko.Int("app.concurrency"),
+		MessageRate:   ko.Int("app.message_rate"),
 		MaxSendErrors: ko.Int("app.max_send_errors"),
 		FromEmail:     cs.FromEmail,
 		UnsubURL:      cs.UnsubURL,

+ 11 - 0
internal/manager/manager.go

@@ -84,6 +84,7 @@ type Message struct {
 // Config has parameters for configuring the manager.
 type Config struct {
 	Concurrency    int
+	MessageRate    int
 	MaxSendErrors  int
 	RequeueOnError bool
 	FromEmail      string
@@ -254,6 +255,9 @@ func (m *Manager) Run(tick time.Duration) {
 func (m *Manager) SpawnWorkers() {
 	for i := 0; i < m.cfg.Concurrency; i++ {
 		go func() {
+			// Counter to keep track of the message / sec rate limit.
+			numMsg := 0
+
 			for {
 				select {
 				// Campaign message.
@@ -262,6 +266,13 @@ func (m *Manager) SpawnWorkers() {
 						continue
 					}
 
+					// Pause on hitting the message rate.
+					if numMsg >= m.cfg.MessageRate {
+						time.Sleep(time.Second)
+						numMsg = 0
+					}
+					numMsg++
+
 					err := m.messengers[msg.Campaign.MessengerID].Push(
 						msg.from, []string{msg.to}, msg.Campaign.Subject, msg.body, nil)
 					if err != nil {