วิธีใหม่ในการใช้อีเมลสำหรับแอปสนับสนุน: บทช่วยสอนของ AWS

เผยแพร่แล้ว: 2022-03-11

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

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

โครงสร้างพื้นฐาน

ก่อนที่เราจะเขียนโค้ดใดๆ เราจะตั้งค่าบริการของ AWS ที่จำเป็นสำหรับการกำหนดเส้นทางและการใช้อีเมล เราจะใช้ SES เพื่อส่งและใช้อีเมล และ SNS+SQS สำหรับกำหนดเส้นทางข้อความขาเข้า

การใช้อีเมลโดยทางโปรแกรมโดยใช้ AWS

ฟื้นฟูอีเมลในแอปพลิเคชันการสนับสนุนด้วย Amazon SES
ทวีต

ทุกอย่างเริ่มต้นที่นี่ด้วย SES เริ่มต้นด้วยการเข้าสู่ระบบบัญชี AWS ของคุณและไปที่คอนโซล SES

ก่อนที่เราจะเริ่มต้น คุณจะต้องมีชื่อโดเมนที่ได้รับการยืนยันซึ่งคุณสามารถส่งอีเมลได้

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

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

เมื่อคุณมีโดเมนที่ได้รับการยืนยันแล้ว เราสามารถสร้างชุดกฎได้ ไปที่แท็บชุดกฎในคอนโซล SES และสร้างกฎการรับใหม่

ขั้นตอนแรกในการสร้างกฎการรับคือการกำหนดผู้รับ

ตัวกรองผู้รับจะช่วยให้คุณสามารถกำหนดว่าจะใช้อีเมลใดบ้างที่ SES จะใช้ และวิธีประมวลผลข้อความขาเข้าแต่ละข้อความ ผู้รับที่เรากำหนดที่นี่ต้องตรงกับโดเมนและข้อความของผู้ใช้แอปรูปแบบที่อยู่ที่ได้รับอีเมล กรณีที่ง่ายที่สุดในที่นี้คือการเพิ่มผู้รับสำหรับโดเมนที่เราตรวจสอบก่อนหน้านี้ ในกรณีของเรา example.com การดำเนินการนี้จะกำหนดค่า SES เพื่อใช้กฎของเรากับอีเมลทั้งหมดที่ส่งไปยัง example.com (เช่น [email protected], [email protected])

ในการสร้างกฎสำหรับทั้งโดเมนของเรา เราจะเพิ่มผู้รับสำหรับ example.com

นอกจากนี้ยังสามารถจับคู่รูปแบบที่อยู่ได้อีกด้วย สิ่งนี้มีประโยชน์หากคุณต้องการกำหนดเส้นทางข้อความขาเข้าไปยังคิว SQS ต่างๆ

สมมติว่าเรามีคิว A และคิว B เราสามารถเพิ่มผู้รับสองคน: [email protected] และ [email protected] หากเราต้องการแทรกข้อความในคิว A เราจะส่งอีเมลไปที่ [email protected] ส่วนหนึ่งของสิ่งนี้จะตรงกับผู้รับ [email protected] ของเรา ทุกอย่างที่อยู่ระหว่าง + และ @ เป็นข้อมูลผู้ใช้โดยอำเภอใจ จะไม่ส่งผลต่อการจับคู่ที่อยู่ของ SES หากต้องการแทรกลงในคิว B ให้แทนที่ a ด้วย b

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

เลือกสร้างหัวข้อ SNS และตั้งชื่อ

หลังจากสร้างหัวข้อแล้ว เราต้องเลือกการเข้ารหัสข้อความ ฉันจะใช้ Base64 เพื่อรักษาอักขระพิเศษ การเข้ารหัสที่คุณเลือกจะส่งผลต่อการถอดรหัสข้อความเมื่อเราใช้งานในบริการของเรา

เมื่อตั้งกฎแล้ว เราก็แค่ตั้งชื่อมัน

ขั้นตอนต่อไปคือการกำหนดค่า SQS และ SNS เพื่อที่เราต้องตรงไปที่คอนโซล SQS และสร้างคิวใหม่

เพื่อให้ง่ายขึ้น ฉันใช้ชื่อเดียวกับหัวข้อ SNS ของเรา

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

ฟิลด์ค่าควรเติมด้วย ARN สำหรับหัวข้อ SNS ที่กำลังแจ้งเตือน SES

หลังจากตั้งค่า SQS แล้ว ก็ถึงเวลาเดินทางกลับไปที่คอนโซล SNS อีกครั้งเพื่อกำหนดค่าหัวข้อของคุณเพื่อแทรกการแจ้งเตือนลงในคิว SQS ใหม่ของคุณ

ในคอนโซล SNS เลือกหัวข้อที่ SES กำลังแจ้ง จากนั้นสร้างการสมัครใหม่ โปรโตคอลการสมัครควรเป็น Amazon SQS และปลายทางควรเป็น ARN ของคิว SQS ที่คุณเพิ่งสร้างขึ้น

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

บริการ Java เพื่อจัดการกับอีเมล

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

บันทึกย่อ เราจะมุ่งเน้นไปที่องค์ประกอบตรรกะทางธุรกิจของบริการนี้ และจะไม่กำหนดตำแหน่งข้อมูล REST หรือเลเยอร์การคงอยู่

ในการสร้างบริการ Spring เราจะใช้ Spring Boot และ Maven เราสามารถใช้ Spring Initializer เพื่อสร้างโครงการสำหรับเรา start.spring.io

ในการเริ่มต้น pom.xml ของเราควรมีลักษณะดังนี้:

 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.toptal.tutorials</groupId> <artifactId>email-processor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>email-processor</name> <description>A simple "micro-service" for emailing support on behalf of a user and processing replies</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

ส่งอีเมลสนับสนุนในนามของผู้ใช้

อันดับแรก ให้กำหนด bean สำหรับการส่งอีเมลถึงฝ่ายสนับสนุนของเราในนามของผู้ใช้ งานของ bean นี้คือการประมวลผลข้อความขาเข้าจาก ID ผู้ใช้ และอีเมลข้อความนั้นไปยังที่อยู่อีเมลของฝ่ายสนับสนุนที่กำหนดไว้ล่วงหน้าของเรา

เริ่มต้นด้วยการกำหนดอินเทอร์เฟซ

 public interface SupportBean { /** * Send a message to the application support desk on behalf of a user * @param fromUserId The ID of the originating user * @param message The message to send */ void messageSupport(long fromUserId, String message); }

และการใช้งานที่ว่างเปล่า:

 @Component public class SupportBeanSesImpl implements SupportBean { /** * Email address for our application help-desk * This is the destination address user support emails will be sent to */ private static final String SUPPORT_EMAIL_ADDRESS = "[email protected]"; @Override public void messageSupport(long fromUserId, String message) { //todo: send an email to our support address } }

มาเพิ่ม AWS SDK ใน pom กันเถอะ เราจะใช้ไคลเอ็นต์ SES เพื่อส่งอีเมลของเรา:

 <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk</artifactId> <version>1.11.5</version> </dependency>

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

เพื่อให้บรรลุสิ่งนี้ เราจะรวม ID ผู้ใช้เริ่มต้นในที่อยู่อีเมลที่เราสร้างขึ้น เพื่อรักษาความสะอาด เราจะสร้างวัตถุที่มี ID ผู้ใช้ และใช้สตริง JSON ที่เข้ารหัส Base64 เป็นที่อยู่อีเมล

มาสร้าง bean ใหม่ที่รับผิดชอบในการเปลี่ยน ID ผู้ใช้เป็นที่อยู่อีเมล

 public interface UserEmailBean { /** * Returns a unique per user email address * @param userID Input user ID * @return An email address unique for the input userID */ String emailAddressForUserID(long userID); }

มาเริ่มการใช้งานของเราโดยเพิ่มการยินยอมที่จำเป็นและคลาสภายในที่เรียบง่ายที่จะช่วยให้เราจัดลำดับ JSON ของเรา

 @Component public class UserEmailBeanJSONImpl implements UserEmailBean { /** * The TLD for all our generated email addresses */ private static final String EMAIL_DOMAIN = "example.com"; /** * com.fasterxml.jackson.databind.ObjectMapper used to create a JSON object including our user ID */ private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String emailAddressForUserID(long userID) { //todo: create the email address return null; } /** * Simple helper class we will serialize. * The JSON representation of this class will become our user email address */ private static class UserDetails{ private Long userID; public Long getUserID() { return userID; } public void setUserID(Long userID) { this.userID = userID; } } }

การสร้างที่อยู่อีเมลของเรานั้นตรงไปตรงมา สิ่งที่เราต้องทำคือสร้างวัตถุ UserDetails และ Base64 เข้ารหัสการแสดง JSON เมธอด createAddressForUserID เวอร์ชันเสร็จแล้วควรมีลักษณะดังนี้:

 @Override public String emailAddressForUserID(long userID) { UserDetails userDetails = new UserDetails(); userDetails.setUserID(userID); //create a JSON representation. String jsonString = objectMapper.writeValueAsString(userDetails); //Base64 encode it String base64String = Base64.getEncoder().encodeToString(jsonString.getBytes()); //create an email address out of it String emailAddress = base64String + "@" + EMAIL_DOMAIN; return emailAddress; }

ตอนนี้เราสามารถกลับไปที่ SupportBeanSesImpl และอัปเดตเพื่อใช้อีเมลใหม่ที่เราเพิ่งสร้างขึ้น

 private final UserEmailBean userEmailBean; @Autowired public SupportBeanSesImpl(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; } @Override public void messageSupport(long fromUserId, String message) throws JsonProcessingException { //user specific email String fromEmail = userEmailBean.emailAddressForUserID(fromUserId); }

ในการส่งอีเมล เราจะใช้ไคลเอ็นต์ AWS SES ที่มาพร้อมกับ AWS SDK

 /** * SES client */ private final AmazonSimpleEmailService amazonSimpleEmailService = new AmazonSimpleEmailServiceClient( new DefaultAWSCredentialsProviderChain() //see http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html );

เรากำลังใช้ DefaultAWSCredentialsProviderChain เพื่อจัดการข้อมูลประจำตัวสำหรับเรา คลาสนี้จะค้นหาข้อมูลประจำตัว AWS ตามที่กำหนดไว้ที่นี่

เราจะไปที่คีย์การเข้าถึงของ AWS ที่จัดเตรียมด้วยการเข้าถึง SES และสุดท้ายคือ SQS สำหรับข้อมูลเพิ่มเติม โปรดดูเอกสารประกอบจาก Amazon

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

 @Override public void messageSupport(long fromUserId, String message) throws JsonProcessingException { //User specific email String fromEmail = userEmailBean.emailAddressForUserID(fromUserId); //create the email Message supportMessage = new Message( new Content("New support request from userID " + fromUserId), //Email subject new Body().withText(new Content(message)) //Email body, this contains the user's message ); //create the send request SendEmailRequest supportEmailRequest = new SendEmailRequest( fromEmail, //From address, our user's generated email new Destination(Collections.singletonList(SUPPORT_EMAIL_ADDRESS)), //to address, our support email address supportMessage //Email body defined above ); //Send it off amazonSimpleEmailService.sendEmail(supportEmailRequest); }

หากต้องการทดลองใช้ ให้สร้างคลาสทดสอบและใส่ SupportBean ตรวจสอบให้แน่ใจว่า SUPPORT_EMAIL_ADDRESS กำหนดไว้ใน SupportBeanSesImpl ชี้ไปที่ที่อยู่อีเมลที่คุณเป็นเจ้าของ หากบัญชี SES ของคุณอยู่ในแซนด์บ็อกซ์ ที่อยู่นี้จะต้องได้รับการยืนยันด้วย สามารถตรวจสอบที่อยู่อีเมลได้ในคอนโซล SES ในส่วนที่อยู่อีเมล

 @Test public void emailSupport() throws JsonProcessingException { supportBean.messageSupport(1, "Hello World!"); }

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

ใช้การตอบกลับจาก SQS

ขั้นตอนสุดท้ายคือการอ่านอีเมลจาก SQS แยกวิเคราะห์ข้อความอีเมล และค้นหาว่า ID ผู้ใช้ใดที่ควรส่งต่อตอบกลับเป็นของ

บริการจัดคิวข้อความ เช่น Amazon SQS มีบทบาทสำคัญในสถาปัตยกรรมที่เน้นบริการ โดยอนุญาตให้บริการสื่อสารกันเองโดยไม่ต้องลดความเร็ว ความน่าเชื่อถือ หรือความสามารถในการปรับขนาด

หากต้องการฟังข้อความ SQS ใหม่ เราจะใช้ SDK การส่งข้อความของ Spring Cloud AWS ซึ่งจะทำให้เราสามารถกำหนดค่าตัวฟังข้อความ SQS ผ่านคำอธิบายประกอบ ดังนั้นจึงหลีกเลี่ยงโค้ดต้นแบบได้ค่อนข้างน้อย

ขั้นแรก การพึ่งพาที่จำเป็น

เพิ่มการพึ่งพาข้อความ Spring Cloud:

 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws-messaging</artifactId> </dependency>

และเพิ่ม Spring Cloud AWS ในการจัดการการพึ่งพา pom ของคุณ:

 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws</artifactId> <version>1.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

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

สร้างไฟล์ XML ใหม่ที่ชื่อ aws-config.xml ในโฟลเดอร์ทรัพยากรของคุณ คำจำกัดความของเราควรมีลักษณะดังนี้:

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context" xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cloud/aws/context http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging.xsd"> <!--enable annotation driven queue listeners --> <aws-messaging:annotation-driven-queue-listener /> <!--define our region, this lets us reference queues by name instead of by URL. --> <aws-context:context-region region="us-east-1" /> </beans>

ส่วนสำคัญของไฟล์นี้คือ <aws-messaging:annotation-driven-queue-listener /> เรายังกำหนดขอบเขตเริ่มต้นอีกด้วย ไม่จำเป็น แต่การทำเช่นนี้จะทำให้เราสามารถอ้างอิงคิว SQS ของเราตามชื่อแทน URL เราไม่ได้กำหนดข้อมูลประจำตัว AWS ใดๆ โดยละเว้น Spring จะใช้ค่าเริ่มต้นเป็น DefaultAWSCredentialsProviderChain ซึ่งเป็นผู้ให้บริการเดียวกับที่เราใช้ก่อนหน้านี้ใน SES bean ดูข้อมูลเพิ่มเติมได้ในเอกสาร Spring Cloud AWS

ในการใช้การกำหนดค่า XML นี้ในแอป Spring Boot เราจำเป็นต้องนำเข้าอย่างชัดแจ้ง ไปที่คลาส @SpringBootApplication ของคุณและนำเข้า

 @SpringBootApplication @ImportResource("classpath:aws-config.xml") //Explicit import for our AWS XML bean definition public class EmailProcessorApplication { public static void main(String[] args) { SpringApplication.run(EmailProcessorApplication.class, args); } }

ตอนนี้ มากำหนด bean ที่จะจัดการกับข้อความ SQS ที่เข้ามา Spring Cloud AWS ช่วยให้เราบรรลุสิ่งนี้ได้ด้วยคำอธิบายประกอบเพียงรายการเดียว!

 /** * Bean reasonable for polling SQS and processing new emails */ @Component public class EmailSqsListener { @SuppressWarnings("unused") //IntelliJ isn't quite smart enough to recognize methods marked with @SqsListener yet @SqsListener(value = "com-example-ses", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS) //Mark this method as a SQS listener //Since we already set up our region we can use the logical queue name here //Spring will automatically delete messages if this method executes successfully public void consumeSqsMessage(@Headers Map<String, String> headers, //Map of headers returned when requesting a message from SQS //This map will include things like the relieved time, count and message ID @NotificationMessage String rawJsonMessage //JSON string representation of our payload //Spring Cloud AWS supports marshaling here as well //For the sake of simplicity we will work with incoming messages as a JSON object ) throws Exception{ //com.amazonaws.util.json.JSONObject included with the AWS SDK JSONObject jsonSqsMessage = new JSONObject(rawJsonMessage); } }

ความมหัศจรรย์ที่นี่อยู่ที่คำอธิบายประกอบ @SqsListener ด้วยเหตุนี้ Spring จะตั้งค่า Executor และเริ่มสำรวจ SQS ให้เรา ทุกครั้งที่พบข้อความใหม่ วิธีการที่มีคำอธิบายประกอบของเราจะถูกเรียกใช้พร้อมกับเนื้อหาข้อความ อีกทางเลือกหนึ่งคือ สามารถกำหนดค่า Spring Cloud เพื่อจัดการข้อความขาเข้า ให้คุณสามารถทำงานกับอ็อบเจ็กต์ที่พิมพ์อย่างเข้มงวดภายในตัวรอคิวของคุณ นอกจากนี้ คุณมีความสามารถในการฉีดส่วนหัวเดียวหรือแผนที่ของส่วนหัวทั้งหมดที่ส่งคืนจากการเรียก AWS พื้นฐาน

เราสามารถใช้ชื่อคิวแบบลอจิคัลได้ที่นี่ เนื่องจากก่อนหน้านี้เรากำหนดภูมิภาคใน aws-config.xml หากเราต้องการละเว้น เราจะสามารถแทนที่ค่าด้วย SQS URL ที่มีคุณสมบัติครบถ้วนของเราได้ เรากำลังกำหนดนโยบายการลบ ซึ่งจะกำหนดค่า Spring ให้ลบข้อความขาเข้าจาก SQS หากตรงตามเงื่อนไข มีหลายนโยบายที่กำหนดไว้ใน SqsMessageDeletionPolicy เรากำลังกำหนดค่า Spring ให้ลบข้อความของเราหากเมธอด ConsumerSqsMessage ของเราดำเนินการได้สำเร็จ

เรากำลังฉีดส่วนหัว SQS ที่ส่งคืนลงในวิธีการของเราโดยใช้ @Headers และแผนที่ที่ฉีดจะมีข้อมูลเมตาที่เกี่ยวข้องกับคิวและเพย์โหลดที่ได้รับ เนื้อหาข้อความถูกฉีดโดยใช้ @NotificationMessage Spring รองรับการจัดมาร์แชลโดยใช้ Jackson หรือผ่านตัวแปลงเนื้อหาข้อความที่กำหนดเอง เพื่อความสะดวก เราจะฉีดสตริง JSON ดิบและทำงานกับสตริงนี้โดยใช้คลาส JSONObject ที่รวมอยู่ใน AWS SDK

เพย์โหลดที่ดึงมาจาก SQS จะมีข้อมูลจำนวนมาก ดู JSONObject เพื่อทำความคุ้นเคยกับเพย์โหลดที่ส่งคืน เพย์โหลดของเรามีข้อมูลจากทุกบริการของ AWS ที่ส่งผ่าน, SES, SNS และสุดท้ายคือ SQS เพื่อประโยชน์ของบทช่วยสอนนี้ เราสนใจเพียงสองสิ่งเท่านั้น: รายชื่อที่อยู่อีเมลที่ส่งถึงและเนื้อหาอีเมล เริ่มต้นด้วยการแยกวิเคราะห์อีเมล

 //Pull out the array containing all email addresses this was sent to JSONArray emailAddressArray = jsonSqsMessage.getJSONObject("mail").getJSONArray("destination"); for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); }

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

กลับไปที่อินเทอร์เฟซ UserEmailBean ของเราและเพิ่มวิธีการอื่น

 /** * Returns true if the input email address matches our template * @param emailAddress Email to check * @return true if it matches */ boolean emailMatchesUserFormat(String emailAddress);

ใน UserEmailBeanJSONImpl เพื่อใช้วิธีนี้ เราจะต้องทำสองสิ่ง ขั้นแรก ให้ตรวจสอบว่าที่อยู่ลงท้ายด้วย EMAIL_DOMAIN ของเราหรือไม่ จากนั้นตรวจสอบว่าเราสามารถจัดการได้หรือไม่

 @Override public boolean emailMatchesUserFormat(String emailAddress) { //not our address, return right away if(!emailAddress.endsWith("@" + EMAIL_DOMAIN)){ return false; } //We just care about the email part, not the domain part String emailPart = splitEmail(emailAddress); try { //Attempt to decode our email UserDetails userDetails = objectMapper.readValue(Base64.getDecoder().decode(emailPart), UserDetails.class); //We assume this email matches if the address is successfully decoded and marshaled return userDetails != null && userDetails.getUserID() != null; } catch (IllegalArgumentException | IOException e) { //The Base64 decoder will throw an IllegalArgumentException it the input string is not Base64 formatted //Jackson will throw an IOException if it can't read the string into the UserDetails class return false; } } /** * Splits an email address on @ * Returns everything before the @ * @param emailAddress Address to split * @return all parts before @. If no @ is found, the entire address will be returned */ private static String splitEmail(String emailAddress){ if(!emailAddress.contains("@")){ return emailAddress; } return emailAddress.substring(0, emailAddress.indexOf("@")); }

เรากำหนดวิธีการใหม่สองวิธี ได้แก่ emailMatchesUserFormat ซึ่งเราเพิ่งเพิ่มลงในอินเทอร์เฟซของเรา และวิธีการยูทิลิตี้อย่างง่ายสำหรับการแยกที่อยู่อีเมลบน @ การใช้งาน emailMatchesUserFormat ของเราทำงานโดยพยายามถอดรหัส Base64 และรวบรวมส่วนของที่อยู่กลับเข้าไปในคลาสตัวช่วย UserDetails ของเรา หากสำเร็จ เราจะตรวจสอบเพื่อให้แน่ใจว่ามีการเติม ID ผู้ใช้ที่จำเป็น ถ้าทั้งหมดนี้ได้ผล เราสามารถสมมติให้ตรงกันได้อย่างปลอดภัย

กลับไปที่ EmailSqsListener ของเราและใส่ UserEmailBean ที่อัปเดตใหม่

 private final UserEmailBean userEmailBean; @Autowired public EmailSqsListener(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; }

ตอนนี้เรากำลังจะอัปเดต ConsumerSqsMethod ขั้นแรก มาแยกวิเคราะห์เนื้อหาอีเมล:

 //Pull our content, remember the content will be Base64 encoded as per our SES settings String encodedContent = jsonSqsMessage.getString("content"); //Create a new String after decoding our body String decodedBody = new String( Base64.getDecoder().decode(encodedContent.getBytes()) ); for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); }

ตอนนี้ มาสร้างวิธีการใหม่ที่จะประมวลผลที่อยู่อีเมลและเนื้อหาอีเมล

 private void processEmail(String emailAddress, String emailBody){ }

และสุดท้าย อัปเดตลูปอีเมลเพื่อเรียกใช้วิธีนี้หากพบว่าตรงกัน

 //Loop over all sent to addresses for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); //If we find a match, process the email and method if(userEmailBean.emailMatchesUserFormat(emailAddress)){ processEmail(emailAddress, decodedBody); } }

ก่อนที่เราจะใช้งาน processEmail เราจำเป็นต้องเพิ่มวิธีอื่นใน UserEmailBean ของเรา เราต้องการวิธีการส่งคืน ID ผู้ใช้จากอีเมล กลับไปที่อินเทอร์เฟซ UserEmailBean เพื่อเพิ่มวิธีสุดท้าย

 /** * Returns the userID from a formatted email address. * Returns null if no userID is found. * @param emailAddress Formatted email address, this address should be verified using {@link #emailMatchesUserFormat(String)} * @return The originating userID if found, null if not */ Long userIDFromEmail(String emailAddress);

เป้าหมายของวิธีนี้คือการส่งคืน userID จากที่อยู่ที่จัดรูปแบบ การใช้งานจะคล้ายกับวิธีการตรวจสอบของเรา ไปที่ UserEmailBeanJSONImpl แล้วกรอกวิธีนี้

 @Override public Long userIDFromEmail(String emailAddress) { String emailPart = splitEmail(emailAddress); try { //Attempt to decode our email UserDetails userDetails = objectMapper.readValue(Base64.getDecoder().decode(emailPart), UserDetails.class); if(userDetails == null || userDetails.getUserID() == null){ //We couldn't find a userID return null; } //ID found, return it return userDetails.getUserID(); } catch (IllegalArgumentException | IOException e) { //The Base64 decoder will throw an IllegalArgumentException it the input string is not Base64 formatted //Jackson will throw an IOException if it can't read the string into the UserDetails class //Return null since we didn't find a userID return null; } }

กลับไปที่ EmailSqsListener และอัปเดต processEmail เพื่อใช้วิธีใหม่นี้

 private void processEmail(String emailAddress, String emailBody){ //Parse out the email address Long userID = userEmailBean.userIDFromEmail(emailAddress); if(userID == null){ //Whoops, we couldn't find a userID. Abort! return; } }

ยอดเยี่ยม! ตอนนี้เรามีเกือบทุกอย่างที่ต้องการแล้ว สิ่งสุดท้ายที่เราต้องทำคือแยกวิเคราะห์คำตอบจากข้อความดิบ

โปรแกรมรับส่งเมล เช่นเดียวกับเว็บเบราว์เซอร์เมื่อไม่กี่ปีที่ผ่านมา ได้รับผลกระทบจากความไม่สอดคล้องกันในการใช้งาน

การแยกวิเคราะห์คำตอบจากอีเมลเป็นงานที่ค่อนข้างซับซ้อน รูปแบบข้อความอีเมลไม่ได้มาตรฐาน และความแตกต่างระหว่างไคลเอนต์อีเมลต่างๆ อาจมีขนาดใหญ่ การตอบสนองดิบจะรวมมากกว่าการตอบกลับและลายเซ็น ข้อความต้นฉบับมักจะรวมอยู่ด้วย คนฉลาดที่ Mailgun ได้รวบรวมโพสต์บล็อกที่ยอดเยี่ยมเพื่ออธิบายความท้าทายบางอย่าง พวกเขายังเปิดแหล่งที่มาของแนวทางการเรียนรู้ด้วยเครื่องเพื่อแยกวิเคราะห์อีเมล ลองดูที่นี่

ไลบรารี Mailgun เขียนด้วย Python ดังนั้นสำหรับบทช่วยสอนของเรา เราจะใช้โซลูชันที่ใช้ Java ที่ง่ายกว่า ผู้ใช้ GitHub edlio รวบรวมโปรแกรมแยกวิเคราะห์อีเมลที่ได้รับอนุญาตของ MIT ใน Java โดยอิงจากหนึ่งในไลบรารีของ GitHub เราจะใช้ห้องสมุดที่ยอดเยี่ยมนี้

ก่อนอื่นเรามาอัปเดต pom ของเรากันก่อน เราจะใช้ https://jitpack.io เพื่อดึง EmailReplyParser

 <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>

ตอนนี้เพิ่มการพึ่งพา GitHub

 <dependency> <groupId>com.github.edlio</groupId> <artifactId>EmailReplyParser</artifactId> <version>v1.0</version> </dependency>

เราจะใช้อีเมลทั่วไปของ Apache ด้วย เราจะต้องแยกวิเคราะห์อีเมลดิบเป็น javax.mail MimeMessage ก่อนส่งต่อไปยัง EmailReplyParser เพิ่มการพึ่งพาคอมมอนส์

 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.4</version> </dependency>

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

เพื่อให้บรรลุสิ่งนี้ เราจะใช้ javax.mail และ EmailReplyParser ของ edlio ร่วมกัน

 private void processEmail(String emailAddress, String emailBody) throws Exception { //Parse out the email address Long userID = userEmailBean.userIDFromEmail(emailAddress); if(userID == null){ //Whoops, we couldn't find a userID. Abort! return; } //Default javax.mail session Session session = Session.getDefaultInstance(new Properties()); //Create a new mimeMessage out of the raw email body MimeMessage mimeMessage = MimeMessageUtils.createMimeMessage( session, emailBody ); MimeMessageParser mimeMessageParser = new MimeMessageParser(mimeMessage); //Parse the message mimeMessageParser.parse(); //Parse out the reply for our message String replyText = EmailReplyParser.parseReply(mimeMessageParser.getPlainContent()); //Now we're done! //We have both the userID and the response! System.out.println("Processed reply for userID: " + userID + ". Reply: " + replyText); }

สรุป

และนั่นแหล่ะ! ตอนนี้เรามีทุกสิ่งที่เราต้องการเพื่อตอบกลับผู้ใช้ต้นทาง!

ดู? ฉันบอกคุณอีเมลสามารถสนุก!

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

ที่เกี่ยวข้อง: เพิ่มประสิทธิภาพของคุณด้วย Amazon Web Services