AWS Lambda调用SNS发送SMS突然失效问题咨询
问题描述
- 此前通过API Gateway(REST API)触发Lambda函数调用SNS向本人手机发送SMS,功能长期运行正常,近期突然无法收到短信
- 使用AWS免费套餐账户,已确认未达到SMS发送条数配额上限
- 自前一日功能正常、短信可成功送达已验证手机号至今,相关IAM权限、角色配置未做任何修改
- 代码中定义了
test2变量返回SNS发送结果,但该返回值无法定位问题根因
核心疑问:通过什么方式可以定位该问题的具体错误原因?该故障为何会突然出现?
关联代码与返回值
Lambda函数代码
const AWS = require("aws-sdk"); const dynamo = new AWS.DynamoDB.DocumentClient(); const SNS = new AWS.SNS(); const tableName = "Testing"; const smsMessage = { PhoneNumber: '+39#mynumber#', Message: 'Someone called your api!' }; const httpMethodConverter = 'http-method'; const pathRequestConverter = "resource-path"; const headers = { "Access-Control-Allow-Origin":"https://#myaccount#.github.io", "Content-Type": "application/json", "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token" }; let body; let statusCode = 200; let test2; exports.handler = async (event, context) => { try { let test = JSON.stringify(event); let callContext = event.context; let httpMethod = callContext[httpMethodConverter]; let path = callContext[pathRequestConverter]; let methodAndPath = httpMethod + " "+ path; test2= await SNS.publish(smsMessage).promise(); switch (methodAndPath) { case "DELETE /items/{id}": await dynamo .delete({ TableName: tableName, Key: { Id: parseInt(JSON.parse(test).params.path.id) } }) .promise(); body = `Deleted item ${JSON.parse(test).params.path.id}`; break; case "GET /items/{id}": body = await dynamo .get({ TableName: tableName, Key: { Id: parseInt(JSON.parse(test).params.path.id) } }) .promise(); break; case "GET /items": body = await dynamo.scan({ TableName: tableName }).promise(); break; case "POST /items": var bodyParser = "body-json"; var bodyPassed = JSON.parse(test)[bodyParser]; await dynamo .put({ TableName: tableName, Item: { Id: parseInt(bodyPassed.Id), Type: bodyPassed.Type } }) .promise(); body = "Put item "+ bodyPassed.Type+ " id: " + parseInt(bodyPassed.Id); break; default: throw new Error(`Unsupported route: "${event.context}"`); } } catch (err) { statusCode = 400; body = err.message; } finally { body = JSON.stringify(body); } return { statusCode, body, headers, test2 }; };
SNS publish接口返回结果
"test2": { "ResponseMetadata": { "RequestId": "b2fbd9fc-4cc0-54e8-a660-82#########" }, "MessageId": "c94393bf-cbb3-5cb9-8155-c#########" }
注意:SNS接口返回MessageId仅代表发送请求被SNS服务端成功接收,不代表短信已经成功投递到运营商或最终送达用户手机,这是现有返回值无法定位问题的核心原因。
问题排查步骤
- 开启SNS SMS投递状态日志
进入SNS控制台的文本消息(SMS)设置页,配置CloudWatch日志投递,给SNS服务关联角色授予CloudWatch Logs写入权限。重新触发发送请求后,即可在CloudWatch日志中查询到单条短信的完整投递链路,包括运营商返回的错误码、具体失败原因,常见的失败原因包括号码格式错误、运营商拦截、目的地发送受限、账户发送权限被限制等。 - 核查SNS账户级SMS发送配额
在SNS SMS设置页查看当前账户的月度支出限额、单条短信发送限额、默认短信发送类型。SMS配额并非仅按发送条数计算,而是按目的地实际费率折算金额,免费套餐默认的支出限额极低,即使未触达条数上限,也可能因为费率调整、发送了高费率地区短信触达金额阈值,导致发送被静默拦截。 - 核查目标号码状态
+39为意大利区号,首先确认目标号码没有主动/被动退订过AWS SNS发送的短信——一旦号码回复过退订关键词(如STOP、退订,部分手机的垃圾短信拦截功能会自动回复这类内容),SNS会默认永久屏蔽该号码的所有短信,且API调用不会返回报错。同时确认号码当前状态正常,无停机、无开启高强度国际短信/平台类短信拦截规则。 - 核查SNS发送配置
确认SNS账户级别没有配置禁止向意大利地区发送短信,确认短信发送类型没有被自动调整为Promotional(营销类短信,在意大利等欧洲国家送达率极低,极易被运营商拦截),通知类短信建议设置为Transactional(交易类),发送优先级和送达率更高。
故障突发的常见原因
- 免费套餐SMS支出配额耗尽:意大利属于国际短信目的地,单条短信费率高于美国本土号码,若近期目的地费率上调,或账户下其他服务消耗了SMS额度,很容易触达免费套餐默认的低金额阈值,此时SNS会接收请求但不实际下发,API侧无报错。
- 目标号码被加入SNS退订黑名单:若之前收到短信后误操作回复退订,或手机拦截功能自动发送了退订指令,SNS会直接屏蔽该号码的所有发送请求,API调用依然返回成功的MessageId。
- 运营商侧管控规则升级:近期欧洲运营商对A2P(应用向个人发送)类短信管控收紧,未注册正规发送方ID的国际短信会被运营商直接拦截,不会投递给用户。
- AWS侧账户发送权限调整:免费套餐账户若被系统判定存在短信滥用风险,会自动限制SMS实际下发能力,API调用可正常返回结果,但消息不会进入运营商投递链路,这类限制可通过AWS支持中心查询。
内容的提问来源于stack exchange,提问作者Gianluca Fontana
相关产品推荐
相关产品推荐

