วิธีจัดการอีเมลอัตโนมัติที่ส่งมาจากเว็บแอปพลิเคชัน


12

ฉันกำลังออกแบบโปรแกรมประยุกต์บนเว็บและฉันสงสัยว่าจะออกแบบสถาปัตยกรรมเพื่อจัดการการส่งอีเมลอัตโนมัติได้อย่างไร

ขณะนี้ฉันมีคุณสมบัตินี้ในเว็บแอปของฉันและอีเมลจะถูกส่งตามการป้อนข้อมูล / การโต้ตอบของผู้ใช้ (เช่นการสร้างผู้ใช้ใหม่) ปัญหาคือการเชื่อมต่อโดยตรงกับเซิร์ฟเวอร์อีเมลใช้เวลาสองสามวินาที การขยายตัวของแอปพลิเคชันของฉันจะเป็นคอขวดที่สำคัญในอนาคต

วิธีที่ดีที่สุดในการจัดการการส่งอีเมลอัตโนมัติจำนวนมากภายในสถาปัตยกรรมระบบของฉันคืออะไร

จะไม่มีการส่งอีเมลจำนวนมาก (สูงสุด 2000 ต่อวัน) ไม่จำเป็นต้องส่งอีเมลทันทีความล่าช้าสูงสุด 10 นาทีก็ใช้ได้

อัปเดต: ได้รับการจัดคิวข้อความเป็นคำตอบ แต่สิ่งนี้จะได้รับการออกแบบอย่างไร สิ่งนี้จะได้รับการจัดการในแอปและประมวลผลในช่วงเวลาที่เงียบหรือฉันต้องสร้าง 'แอปอีเมล' หรือบริการบนเว็บใหม่เพื่อจัดการคิวหรือไม่


คุณช่วยให้เรามีความรู้สึกคร่าวๆได้หรือไม่? จดหมายเป็นร้อยเป็นพันหรือล้าน? นอกจากนี้ควรส่งอีเมลทันทีหรือยอมรับความล่าช้าเล็กน้อยหรือไม่
yannis

การส่งอีเมลเกี่ยวข้องกับการส่งข้อความ SMTP ไปยังโฮสต์ของผู้รับที่ได้รับ แต่นั่นไม่ได้หมายความว่าข้อความนั้นถูกส่งจริง อย่างมีประสิทธิภาพการส่งอีเมลทั้งหมดเป็นแบบอะซิงโครนัสและไม่มีประโยชน์ที่จะ "รอความสำเร็จ"
Kilian Foth

1
ฉันไม่ได้ "รอความสำเร็จ" แต่ฉันต้องรอเซิร์ฟเวอร์ smtp เพื่อยอมรับคำขอของฉัน @YannisRizos ดูอัปเดตความคิดเห็นของคุณอีกครั้ง
Gaz_Edge

สำหรับ 2000 (ซึ่งเป็นจำนวนสูงสุดที่คุณอธิบาย) มันจะใช้งานได้ เมื่อพวกเขาเกิดขึ้นใน 10 ชั่วโมงทำการบอกว่ามันเป็น 3 อีเมลต่อนาทีซึ่งเป็นไปได้มาก เพียงให้แน่ใจว่าคุณตั้งค่าระเบียน DNS ของคุณได้ดีและผู้ให้บริการยอมรับคุณส่งจำนวนดังกล่าว คิดยังไง: "เมลเซิร์ฟเวอร์อะไรลง" ภาระในการส่งจดหมาย 2000 รายการไม่ใช่เรื่องที่ต้องกังวล
Luc Franken

คำตอบของ CRONTAB
Tulains Córdovaคือ

คำตอบ:


15

วิธีที่พบเป็นOzz กล่าวแล้วเป็นคิวข้อความ จากมุมมองของการออกแบบคิวข้อความคือคิว FIFOซึ่งเป็นประเภทข้อมูลพื้นฐาน:

คิว FIFO

สิ่งที่ทำให้คิวข้อความพิเศษคือในขณะที่แอปพลิเคชันของคุณรับผิดชอบในการเข้าคิวกระบวนการที่แตกต่างกันจะรับผิดชอบการยกเลิกคิว ในการจัดคิวศัพท์แอปพลิเคชันของคุณเป็นผู้ส่งข้อความและกระบวนการยกเลิกการจัดคิวเป็นผู้รับ ข้อได้เปรียบที่เห็นได้ชัดคือกระบวนการทั้งหมดเป็นแบบอะซิงโครนัสผู้รับจะทำงานโดยอิสระจากผู้ส่งตราบใดที่มีข้อความที่ต้องดำเนินการ ข้อเสียที่เห็นได้ชัดคือคุณต้องมีองค์ประกอบเพิ่มเติมผู้ส่งสำหรับการทำงานทั้งหมด

เนื่องจากสถาปัตยกรรมของคุณตอนนี้ต้องอาศัยสององค์ประกอบในการแลกเปลี่ยนข้อความคุณสามารถใช้การสื่อสารระหว่างกระบวนการแฟนซีคำสำหรับมัน

การแนะนำคิวมีผลต่อการออกแบบแอปพลิเคชันของคุณอย่างไร

การกระทำบางอย่างในแอปพลิเคชันของคุณสร้างอีเมล แนะนำคิวข้อความจะหมายความว่าการกระทำเหล่านั้นควรจะผลักข้อความไปยังคิวแทน (และไม่มีอะไรเพิ่มเติม) ข้อความเหล่านั้นควรมีจำนวนข้อมูลขั้นต่ำที่แน่นอนที่จำเป็นในการสร้างอีเมลเมื่อผู้รับของคุณได้รับการประมวลผล

รูปแบบและเนื้อหาของข้อความ

รูปแบบและเนื้อหาของข้อความนั้นขึ้นอยู่กับคุณ แต่คุณควรจำไว้ว่ายิ่งมีขนาดเล็ก คิวของคุณควรจะเขียนและดำเนินการให้เร็วที่สุดโดยการทิ้งข้อมูลจำนวนมากที่อาจทำให้เกิดปัญหาคอขวด

นอกจากนี้บริการจัดคิวตามคลาวด์หลายแห่งยังมีข้อ จำกัด เกี่ยวกับขนาดข้อความและอาจแบ่งข้อความที่ใหญ่กว่า คุณจะไม่สังเกตุเห็นข้อความแยกจะทำหน้าที่เป็นข้อความเดียวเมื่อคุณขอ แต่จะมีการคิดค่าบริการหลายข้อความ (โดยแน่นอนว่าคุณกำลังใช้บริการที่ต้องเสียค่าธรรมเนียม)

การออกแบบของผู้รับ

เนื่องจากเรากำลังพูดถึงเว็บแอปพลิเคชันวิธีการทั่วไปสำหรับผู้รับของคุณจะเป็นสคริปต์ cron อย่างง่าย มันจะทำงานทุก ๆxนาที (หรือวินาที) และมันจะ:

  • nจำนวนป๊อปอัพข้อความจากคิว
  • ประมวลผลข้อความ (เช่นส่งอีเมล)

โปรดสังเกตว่าฉันกำลังบอกว่าป๊อปแทนที่จะได้รับหรือดึงนั่นเป็นเพราะผู้รับของคุณไม่ได้แค่รับไอเท็มจากคิว แต่มันยังเคลียร์พวกมันด้วย (เช่นลบออกจากคิวหรือทำเครื่องหมายว่าถูกประมวลผลแล้ว) สิ่งที่จะเกิดขึ้นนั้นขึ้นอยู่กับการใช้คิวข้อความและความต้องการเฉพาะของแอปพลิเคชันของคุณ

แน่นอนสิ่งที่ฉันอธิบายคือการดำเนินการแบทช์เป็นวิธีที่ง่ายที่สุดในการประมวลผลคิว ขึ้นอยู่กับความต้องการของคุณคุณอาจต้องการประมวลผลข้อความในลักษณะที่ซับซ้อนมากขึ้น (ซึ่งอาจเรียกคิวที่ซับซ้อนมากขึ้น)

การจราจร

ผู้รับของคุณอาจพิจารณาปริมาณการใช้ข้อมูลและปรับจำนวนข้อความที่ประมวลผลตามปริมาณการใช้งานในเวลาที่เรียกใช้ วิธีการง่ายๆคือการคาดการณ์ชั่วโมงการจราจรสูงของคุณจากข้อมูลการจราจรในอดีตและสมมติว่าคุณใช้สคริปต์ cron ที่ทำงานทุกxนาทีคุณสามารถทำสิ่งนี้:

if( 
    now() > 2pm && now() < 7pm
) {
    process(10);
} else {
    process(100);
}

function process(count) {
    for(i=0; i<=count; i++) {
        message = dequeue();
        mail(message)
    }
}

วิธีการที่ไร้เดียงสาและสกปรกมาก แต่ใช้งานได้ หากไม่เป็นเช่นนั้นอีกวิธีหนึ่งก็คือการค้นหาปริมาณการใช้งานปัจจุบันของเซิร์ฟเวอร์ของคุณในการทำซ้ำแต่ละครั้งและปรับจำนวนรายการกระบวนการตาม โปรดอย่าปรับขนาดเล็กถ้าไม่จำเป็นอย่างยิ่งแม้ว่าคุณจะเสียเวลา

ที่เก็บข้อมูลคิว

หากแอปพลิเคชันของคุณใช้ฐานข้อมูลอยู่แล้วตารางเดียวในนั้นจะเป็นทางออกที่ง่ายที่สุด:

CREATE TABLE message_queue (
  id int(11) NOT NULL AUTO_INCREMENT,
  timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  processed enum('0','1') NOT NULL DEFAULT '0',
  message varchar(255) NOT NULL,
  PRIMARY KEY (id),
  KEY timestamp (timestamp),
  KEY processed (processed)
) 

มันไม่ซับซ้อนกว่านั้นจริงๆ แน่นอนคุณสามารถทำให้มันซับซ้อนเท่าที่คุณต้องการตัวอย่างเช่นคุณสามารถเพิ่มเขตข้อมูลลำดับความสำคัญ (ซึ่งหมายความว่านี่ไม่ใช่คิว FIFO อีกต่อไป แต่ถ้าคุณต้องการจริงๆใครจะสนใจ?) คุณสามารถทำให้มันง่ายขึ้นโดยการข้ามเขตข้อมูลที่ดำเนินการแล้ว (แต่คุณต้องลบแถวหลังจากที่คุณประมวลผลแล้ว)

ตารางฐานข้อมูลจะเหมาะสำหรับข้อความ 2,000 ข้อความต่อวัน แต่มันอาจจะไม่ปรับขนาดสำหรับข้อความนับล้านต่อวัน มีหลายล้านปัจจัยที่ต้องพิจารณาทุกอย่างในโครงสร้างพื้นฐานของคุณมีบทบาทในการปรับขนาดโดยรวมของแอปพลิเคชันของคุณ

ไม่ว่าในกรณีใดสมมติว่าคุณระบุคิวฐานข้อมูลเป็นคอขวดแล้วขั้นตอนต่อไปคือการดูบริการบนคลาวด์ Amazon SQSเป็นบริการเดียวที่ฉันใช้และทำตามที่สัญญาไว้ ฉันแน่ใจว่ามีบริการที่คล้ายกันค่อนข้างน้อยที่นั่น

คิวที่ใช้หน่วยความจำเป็นสิ่งที่ต้องพิจารณาเช่นกันโดยเฉพาะสำหรับคิวที่มีอายุสั้น memcachedเป็นเลิศในการจัดเก็บคิวข้อความ

ไม่ว่าพื้นที่เก็บข้อมูลใดที่คุณตัดสินใจจะสร้างคิวของคุณก็ต้องฉลาดและเป็นนามธรรม ทั้งผู้ส่งและผู้รับของคุณไม่ควรเชื่อมโยงกับที่เก็บข้อมูลเฉพาะมิฉะนั้นการสลับไปยังที่เก็บข้อมูลอื่นในภายหลังจะเป็น PITA ที่สมบูรณ์

วิธีการในชีวิตจริง

ฉันสร้างคิวข้อความสำหรับอีเมลที่คล้ายกับสิ่งที่คุณทำ มันเป็นโครงการ PHP และฉันสร้างมันรอบ ๆZend Queueซึ่งเป็นส่วนประกอบของ Zend Framework ที่มีอะแดปเตอร์หลายตัวสำหรับการจัดเก็บที่แตกต่างกัน ห้องเก็บของของฉันอยู่ที่ไหน:

  • PHP อาร์เรย์สำหรับการทดสอบหน่วย
  • Amazon SQS เกี่ยวกับการผลิต
  • MySQL บนสภาพแวดล้อม dev และการทดสอบ

ข้อความของฉันเรียบง่ายที่สุดเท่าที่จะเป็นไปได้แอปพลิเคชันของฉันสร้างอาร์เรย์ขนาดเล็กพร้อมข้อมูลที่จำเป็น ( [user_id, reason]) ที่เก็บข้อความเป็นรุ่นต่อเนื่องของอาเรย์นั้น (อย่างแรกคือรูปแบบการทำให้เป็นอนุกรมภายในของ PHP จากนั้น JSON ฉันจำไม่ได้ว่าทำไมฉันถึงเปลี่ยน) นี่reasonคือค่าคงที่และแน่นอนว่าฉันมีตารางขนาดใหญ่อยู่ที่ไหนสักแห่งที่แมปreasonไปยังคำอธิบายแบบเต็ม (ฉันจัดการเพื่อส่งอีเมลประมาณ 500 ถึงลูกค้าด้วยรหัสลับreasonแทนที่จะเป็นข้อความแบบเต็มครั้งเดียว)

อ่านเพิ่มเติม

มาตรฐาน:

เครื่องมือ

อ่านที่น่าสนใจ:


ว้าว. คำตอบที่ดีที่สุดที่ฉันเคยได้รับจากที่นี่! ขอบคุณมากพอ!
Gaz_Edge

ฉันและอีกหลายล้านคนใช้ FIFO นี้กับ Gmail & Google Apps Script ตัวกรอง Gmail จะติดป้ายกำกับจดหมายที่เข้ามาตามเกณฑ์และทุกอย่างจะจัดคิวไว้ สคริปต์ของ Google Apps จะทำงานทุก ๆ ระยะเวลา X, รับข้อความ y ก่อน, ส่งไป, ตัดออก ล้างและทำซ้ำ
DavChana

6

คุณต้องการระบบคิวบางประเภท

วิธีง่ายๆอย่างหนึ่งคือการเขียนลงในตารางฐานข้อมูลและมีแถวกระบวนการแอปพลิเคชันภายนอกอื่นในตารางนี้ แต่มีเทคโนโลยีการจัดคิวอื่น ๆ มากมายที่คุณสามารถใช้ได้

คุณสามารถให้ความสำคัญกับอีเมลเพื่อให้บางอย่างมีการดำเนินการเกือบจะในทันที (เช่นรีเซ็ตรหัสผ่าน) และสิ่งที่มีความสำคัญน้อยกว่าอาจถูกส่งเป็นแบตช์เพื่อส่งในภายหลัง


คุณมีแผนภาพสถาปัตยกรรมหรือตัวอย่างที่แสดงวิธีการทำงาน ตัวอย่างเช่นคิวจะนั่งอยู่ในแอปอีเมล 'แอป' ที่แตกต่างกันหรือไม่หรือได้รับกระบวนการจากภายในแอปพลิเคชันเว็บในช่วงเวลาที่เงียบ หรือฉันควรสร้างเว็บเซอร์วิสเพื่อประมวลผล?
Gaz_Edge

1
@Gaz_Edge แอปพลิเคชันของคุณดันรายการไปยังคิว กระบวนการพื้นหลัง (สคริปต์ cron น่าจะ) ปรากฏ x รายการจากคิวทุก n วินาทีและประมวลผลพวกเขา (ในกรณีของคุณส่งอีเมล) ตารางฐานข้อมูลเดียวทำงานได้ดีเป็นที่เก็บคิวสำหรับจำนวนเงินที่เล็ก ๆ ของรายการ แต่โดยทั่วไปการพูดการเขียนการดำเนินงานในฐานข้อมูลมีราคาแพงและสำหรับจำนวนเงินที่มีขนาดใหญ่คุณอาจต้องการที่จะมองไปที่การบริการเช่นAmazon ของ SQS
yannis

1
@Gaz_Edge ฉันไม่แน่ใจว่าฉันสามารถทำแผนภาพได้ง่ายกว่าที่ฉันเขียน "... เขียนไปยังตารางฐานข้อมูลและมีแถวกระบวนการแอปพลิเคชันภายนอกอื่นในตารางนี้ .... " และสำหรับตารางให้อ่าน "คิวใด ๆ "เทคโนโลยีอะไรก็ตามที่อาจจะเป็น
ozz

1
(ต่อ ... ) คุณสามารถสร้างกระบวนการพื้นหลังที่ล้างคิวในลักษณะที่คำนึงถึงปริมาณข้อมูลของคุณตัวอย่างเช่นคุณสามารถสั่งให้ประมวลผลรายการน้อยลง (หรือไม่มีเลย) ในบางครั้งเมื่อเซิร์ฟเวอร์ของคุณประสบปัญหา . คุณจะต้องทำนายเวลาที่เครียดด้วยการดูข้อมูลการจราจรในอดีตของคุณ (ง่ายกว่าที่จะฟัง แต่มีข้อผิดพลาดที่ขอบขนาดใหญ่) หรือโดยให้กระบวนการพื้นหลังของคุณตรวจสอบสถานะการจราจรทุกครั้งที่ทำงาน (แม่นยำยิ่งขึ้น ค่าใช้จ่ายที่เพิ่มเข้ามานั้นไม่ค่อยจำเป็น)
yannis

@YannisRizos ต้องการรวมความคิดเห็นของคุณเป็นคำตอบหรือไม่ นอกจากนี้ไดอะแกรมสถาปัตยกรรมและการออกแบบก็มีประโยชน์เช่นกัน (ฉันตั้งใจที่จะรับพวกเขาจากคำถามนี้ในเวลานี้! ;-))
Gaz_Edge

2

จะไม่มีการส่งอีเมลจำนวนมาก (สูงสุด 2000 ต่อวัน)

นอกเหนือจากคิวสิ่งที่สองที่คุณควรพิจารณาคือการส่งอีเมลผ่านบริการพิเศษ: MailChimp เป็นต้น (ฉันไม่ได้เกี่ยวข้องกับบริการนี้) ไม่เช่นนั้นบริการอีเมลจำนวนมากเช่น gmail จะส่งจดหมายของคุณไปยังโฟลเดอร์สแปมทันที


2

ฉันสร้างแบบจำลองระบบคิวของฉันในตาราง 2 ที่ต่างกันเช่น;

CREATE TABLE [dbo].[wMessages](
  [Id] [uniqueidentifier]  NOT NULL,
  [FromAddress] [nvarchar](255) NOT NULL,
  [FromDisplayName] [nvarchar](255) NULL,
  [ToAddress] [nvarchar](255) NOT NULL,
  [ToDisplayName] [nvarchar](255) NULL,
  [Graph] [xml] NOT NULL,
  [Priority] [int] NOT NULL,
  PRIMARY KEY CLUSTERED ( [Id] ASC ))

CREATE TABLE [dbo].[wMessageStates](
  [MessageId] [uniqueidentifier] NOT NULL,
  [Status] [int] NOT NULL,
  [LastChange] [datetimeoffset](7) NOT NULL,
  [SendAfter] [datetimeoffset](7) NULL,
  [SendBefore] [datetimeoffset](7) NULL,
  [DeleteAfter] [datetimeoffset](7) NULL,
  [SendDate] [datetimeoffset](7) NULL,
  PRIMARY KEY CLUSTERED ( [MessageId] ASC )) ON [PRIMARY]
) ON [PRIMARY]

มีความสัมพันธ์ 1-1 ระหว่างตารางเหล่านี้

ตารางข้อความสำหรับจัดเก็บเนื้อหาข้อความ เนื้อหาจริง (ถึง, CC, BCC, Subject, Body ฯลฯ ) ได้รับการจัดลำดับเป็นฟิลด์กราฟในรูปแบบ XML ข้อมูลอื่น ๆ จากไปยังใช้สำหรับการรายงานปัญหาโดยไม่ต้องทำการดีซีเรียลไลซ์กราฟ การแยกตารางนี้อนุญาตให้แบ่งพาร์ติชันเนื้อหาของตารางไปยังที่เก็บดิสก์อื่น เมื่อคุณพร้อมที่จะส่งข้อความคุณต้องอ่านข้อมูลทั้งหมดดังนั้นจึงไม่มีอะไรผิดปกติกับการทำให้เป็นอนุกรมเนื้อหาทั้งหมดไปยังหนึ่งคอลัมน์ที่มีดัชนีคีย์หลัก

ตารางMessageStateสำหรับจัดเก็บสถานะของเนื้อหาข้อความพร้อมข้อมูลเพิ่มเติมตามวันที่ การแยกตารางนี้อนุญาตให้กลไกการเข้าถึงอย่างรวดเร็วพร้อมดัชนีเพิ่มเติมเกี่ยวกับที่เก็บข้อมูล IO อย่างรวดเร็ว คอลัมน์อื่นอธิบายตนเองได้แล้ว

คุณสามารถใช้พูลเธรดแยกต่างหากที่สแกนตารางนี้ หากแอปพลิเคชันและพูลอาศัยอยู่ในเครื่องเดียวกันคุณสามารถใช้คลาสEventWaitHandleเพื่อส่งสัญญาณพูลจากแอปพลิเคชันเกี่ยวกับสิ่งที่แทรกตารางเหล่านี้มิฉะนั้นการสแกนเป็นระยะด้วยการหมดเวลาเป็นสิ่งที่ดีที่สุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.