前言
Amazon Simple Notification Service (Amazon SNS) is a web service that coordinates and manages the delivery or sending of messages to subscribing endpoints or clients. In Amazon SNS, there are two types of clients—publishers and subscribers—also referred to as producers and consumers. Publishers communicate asynchronously with subscribers by producing and sending a message to a topic, which is a logical access point and communication channel. Subscribers (i.e., web servers, email addresses, Amazon SQS queues, AWS Lambda functions) consume or receive the message or notification over one of the supported protocols (i.e., Amazon SQS, HTTP/S, email, SMS, Lambda) when they are subscribed to the topic. [1]
正如SNS的介紹所述,SNS是AWS提供的一個消息收發服務,它包括了諸如消息推送、短信、電子郵件等服務。AWS官方文檔提供了非常多的內容,但提供的示例代碼是以Java或.Net爲主,關於Node.js的直接資料較少,所以這裏便來介紹如何使用AWS SNS服務發送短信。
Node.js中使用SNS發送短信
在Node.js中使用AWS的服務,需要先安裝aws-sdk
依賴。AWS SDK中包括了衆多服務的接口,在這裏我們需要的是AWS.SNS類。首先,需要實例化AWS.SNS對象,其構造函數的參數爲一個對象,通常需要包括accessKeyId
、secretAccessKey
、region
等屬性。在AWS IAM中,可生成並下載使用的用戶對應的accessKeyId
及secretAccessKey
。需要注意的是,使用的用戶需要在IAM中設置SNS對應的權限。
const AWS = require('aws-sdk');
const options = {
accessKeyId: 'String',
secretAccessKey: 'String',
apiVersion: '2010-03-31',
};
const snsService = new AWS.SNS(options);
通過AWS.SNS類的實例,就可以使用其進行SNS服務的相關操作。本文的主題爲使用SNS服務發送短信,所以接下來即可通過AWS.SNS實例的publish
方法以短信形式發送消息。
AWS SDK for Node.js中,publish
方法接收一個Object類型的參數,它其中包括Message
、MessageAttributes
、MessageStructure
、PhoneNumber
、Subject
、TargetArn
以及TopicArn
屬性。publish
是一個SNS中通用的方法,發送郵件、消息推送也是通過它進行完成,所以在發送短信時部分的參數不是必須的。下面是一個發送短信所需最少參數的例子。
const params = {
Message: text,
MessageAttributes: {
'AWS.SNS.SMS.SMSType': {
DataType: 'String',
StringValue: 'Transactional', // Transactional or Promotional
},
// AWS.SNS.SMS.MaxPrice
// AWS.SNS.SMS.SenderId
},
PhoneNumber: phoneNumber, // 電話號碼,需要遵從E.164格式
};
MessageAttributes
中可以包含多個屬性,例如上述例子中的AWS.SNS.SMS.SMSType
。MessageAttribute的值主要包括DataType
與StringValue
或BinaryValue
,其根據DataType
的值決定需要的是StringValue
或是BinaryValue
。StringValue
接受一組以UTF-8編碼的字符串,而BinaryValue
可以接受任何二進制值,例如壓縮後的數據、圖片等。
在MessageAttributes
的AWS.SNS.SMS.SMSType
屬性中,其值需爲Transactional
或Promotional
。二者的區別爲Transactional
更爲可靠,但其價格通常更爲昂貴,一般用於發送較爲重要的消息(如短信驗證碼等),而Promotional
一般用於發送推廣信息。另外AWS.SNS.SMS.MaxPrice
爲願意爲發送消息支付以美元爲單位的最高金額;AWS.SNS.SMS.SenderId
爲一個在接收設備上顯示的自定義ID,但其支持程度受所在地區限制,如中國便不支持SenderId的顯示。
使用上面定義的參數調用publish
方法即可發送短信。publish
方法提供了基於Promise的異步使用方法,只需將代碼修改爲:const response = await snsService.publish(params).promise();
。同理,AWS SDK中的其它方法也可通過添加.promise()
將其異步形式從回調改爲基於Promise。
snsService.publish(params, (err, data) => {
if (err) {
// ...
}
// ...
})
返回結果
在參數錯誤、權限不足等情況下,調用publish
方法將會拋出諸如InvalidParameter
、AuthorizationError
等錯誤,可根據其具體錯誤信息判斷錯誤原因。調用publish
方法將會返回下述結果。其中,MessageId
爲該消息的唯一標識符,當開啓CloudWatch Logs後可通過該標識符獲取消息的傳輸信息。
{
"ResponseMetadata":{
"RequestId":"bfb2a062-c201-5d34-a7d8-f5fd653b27f9"
},
"MessageId":"2b38eec7-a3f0-5679-a116-bb5804cadcb4"
}
未收到短信
調用publish接口成功並正常返回結果,不代表短信發送成功。短信發送失敗的原因可能是下列其中一個:
- 被電話運營商作爲垃圾消息屏蔽
- 目標已加入黑名單
- 電話號碼無效
- 消息正文無效
- 電話運營商已屏蔽此消息
- 電話運營商目前無法訪問/不可用
- 電話已屏蔽SMS
- 電話已加入黑名單
- 電話當前無法訪問/可用
- 電話號碼已退出
- 此傳輸會超過最高價格
- 嘗試聯繫電話時發生未知錯誤
publish
方法返回的結果無法得知短信發送是否成功,只有在開啓CloudWatch Logs功能後才能從日誌中獲取短信是否發送成功及發送失敗的原因。
發送短信至多個號碼
使用AWS SNS可以實現同時發送短信至多個手機號碼,其中一種實現方法即爲多次調用publish
方法向不同號碼發送短信。但重複多次調用publish
方法需要耗費更多的時間,所以在此也可選用另一種方法,即使用Topic發送短信至多個號碼。該方法的步驟是在創建一個topic後,爲多個手機號碼訂閱該topic。訂閱後在發送消息時,會將消息發送至訂閱該topic的端點中,此處的端點即爲電話號碼。
在發送信息至多個號碼前,需要創建一個新的Topic並使用subscribe
方法爲發送的號碼訂閱該Topic。Topic即一個消息信道,當有消息發送至topic中時,SNS服務將會將該消息發送至訂閱該topic的終端節點中。Topic可通過AWS Console創建,也可通過AWS SDK中的createTopic
方法創建。
通過SDK調用createTopic創建Topic,需要包括Name
、Attributes
、Tags
三個參數,其中Name
爲必須的參數。Attributes
參數。
const params = {
Name: 'STRING_VALUE', // Topic 名稱
Attributes: {
'<attributeName>': 'STRING_VALUE',
/* '<attributeName>': ... */
},
Tags: [
{
Key: 'STRING_VALUE', // Tag key
Value: 'STRING_VALUE', // Tag value
},
// ...
],
};
const response = await snsService.createTopic(params).promise();
// response.TopicArn
完成topic的創建之後,即可爲需要發送短信的電話號碼訂閱所創建的topic。爲電話號碼訂閱topic需要使用subscribe
方法,它必填的參數包括TopicArn
、Endpoint
以及Protocol
。其中,Protocol
的值必須爲sms
,TopicArn
的值爲創建的Topic Arn地址,Endpoint
爲訂閱Topic的電話號碼。另外,它的參數還包括有Attributes
以及ReturnSubscriptionArn
兩個屬性。Attributes
屬性如同publish
方法的MessageAttributes
屬性,通過它的DeliveryPolicy
、FilterPolicy
、RawMessageDelivery
配置訂閱的相關設置。
const params = {
Protocol: 'sms',
TopicArn: topicArn,
// Attributes: {
// '<attributeName>': 'STRING_VALUE',
// },
Endpoint: phoneNumber,
// ReturnSubscriptionArn: false,
};
const response = await snsService.subscribe(params).promise();
在爲需要發送短信的號碼訂閱topic後,即可調用publish
方法發送消息。發送短信至多個號碼時調用publish
方法的參數與發送至單個號碼大致相同,唯一需要修改的地方爲不再需要設置PhoneNumber
,而是需要設置TopicArn
屬性,它的值即爲上面調用createTopic
所得到的TopicArn
字段。
const params = {
Message: text,
MessageAttributes: {
// 發送短信至topic同樣可設置SMSType, MaxPrice, SenderId三個屬性
'AWS.SNS.SMS.SMSType': {
DataType: 'String',
StringValue: 'Transactional', // Transactional or Promotional
},
// AWS.SNS.SMS.MaxPrice
// AWS.SNS.SMS.SenderId
},
TopicArn: topicArn, // 羣發的topic ARN地址
};
const response = await snsService.publish(params).promise();
結束語
AWS的free tier提供了每月100條的免費短信可以使用,但需要注意的是此處的免費短信接收方爲美國號碼。若要發送短信至國內號碼,價格約爲0.01531 USD(約0.11元),該價格相對於國內服務商較爲昂貴。另外,SNS發送短信至國內時失敗率也較高,且在發送失敗的情況下也是按正常價格收取費用。在實際開發中,若不是以海外業務爲主的情況下,可考慮國內服務商提供的短信服務。