Ele*_*Ele 4 amazon-web-services node.js amazon-sns aws-lambda aws-api-gateway
我们在 Backend 类中有一个服务,该服务如下所示:
// Setup AWS SNS
AWS.config.update({
region: 'eu-west-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
var sns = new AWS.SNS();
var params = {
Message: "SMS message test",
MessageStructure: 'string',
PhoneNumber: '0045xxxxxxxx',
Subject: 'Alarm',
MessageAttributes :{
'AWS.SNS.SMS.SenderID': {
'DataType': 'String',
'StringValue': 'MySender'
},
'AWS.SNS.SMS.SMSType': 'Transactional'
}
};
Run Code Online (Sandbox Code Playgroud)
如果我们需要发送短信,我们只需调用此服务。
以下是不好的地方,我们知道:
我们在 EC2 中使用密钥。但是,我们正在努力为实例设置具有特定权限的角色。
想象一下,我们需要修改发送 SMS 的方式,我们必须为应用程序的那一小部分重新部署整个应用程序。
最糟糕的是,假设我们在 AutoScaling 上有我们的应用程序。我们必须删除所有实例才能更新我们应用程序的那一小部分。
另一个问题是,如果我们必须在其他应用程序中使用该服务怎么办?当前的方法导致在应用程序之间复制服务。
最后,如何进行日志记录、监控等。
我们认为有更好的方法可以避免此类问题,因此您可以查看我们避免上述问题的方法。
这种架构允许您提供一个 Restful Endpoint,它将消息传递给特定的接收者。此微服务可以从您的应用程序、设备应用程序等的不同部分执行,因此不仅限于一个后端用途。
###简单视图
#解释
我们将描述流程,逐步解释发送 SMS 的流程。
{
"target": "554542121245",
"type": "sms",
"message": "Hello World!",
"region": "us-east-1"
}
Run Code Online (Sandbox Code Playgroud)
API 网关验证 API 以授予访问权限并将接收到的负载发送到 Lambda 函数。
Lambda 函数验证接收到的有效负载并执行以下操作:
{
"status": 200,
"message": "The message has been sent!"
}
Run Code Online (Sandbox Code Playgroud)
var AWS = require('aws-sdk');
/**
* Entry function for this
* Lambda.
*
* This function delivers a message
* to a specific number.
*
* First approach will only handle
* delivery type sms.
*/
exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event));
if (event.type === undefined || event.type === null || event.type === '' || event.type.trim() === '') {
callback(get_response_message('Type of delivery is required.'), 412);
return;
}
if (event.type.trim() !== 'sms') {
callback(get_response_message('The available delivery type is \'sms\'.', 412));
return;
}
if (event.type.trim() === 'sms' && (event.target === '' || isNaN(event.target))) {
callback(get_response_message('The target must be a number.', 412));
return;
}
deliver(event.target, event.message, event.region, callback);
};
/**
* This function delivers a
* message to a specific number.
*
* The function will create a topic
* from scratch to avoid any
* clash among subscriptions.
*
* @param number in context.
* @param message that will be sent.
* @param region in context.
* @param cb a callback function to
* return a response to the
* caller of this service.
*/
var deliver = (number, message, region, cb) => {
var sns = new AWS.SNS({region: region});
console.log(`${number} - ${region} - ${Date.now()}`);
var params = { Name: `${number}_${region}_${Date.now()}` };
sns.createTopic(params, function(err, tdata) {
if (err) {
console.log(err, err.stack);
cb(get_response_message(err, 500));
} else {
console.log(tdata.TopicArn);
sns.subscribe({
Protocol: 'sms',
TopicArn: tdata.TopicArn,
Endpoint: number
}, function(error, data) {
if (error) {
//Rollback to the previous created services.
console.log(error, error.stack);
params = { TopicArn: tdata.TopicArn};
sns.deleteTopic(params, function() { cb(get_response_message(error, 500)); });
return;
}
console.log('subscribe data', data);
var SubscriptionArn = data.SubscriptionArn;
params = { TargetArn: tdata.TopicArn, Message: message, Subject: 'dummy' };
sns.publish(params, function(err_publish, data) {
if (err_publish) {
console.log(err_publish, err_publish.stack);
//Rollback to the previous created services.
params = { TopicArn: tdata.TopicArn};
sns.deleteTopic(params, function() {
params = {SubscriptionArn: SubscriptionArn};
sns.unsubscribe(params, function() { cb(get_response_message(err_publish, 500)); });
});
return;
} else console.log('Sent message:', data.MessageId);
params = { SubscriptionArn: SubscriptionArn };
sns.unsubscribe(params, function(err, data) {
if (err) console.log('err when unsubscribe', err);
params = { TopicArn: tdata.TopicArn };
sns.deleteTopic(params, function(rterr, rtdata) {
if (rterr) {
console.log(rterr, rterr.stack);
cb(get_response_message(rterr, 500));
} else {
console.log(rtdata);
cb(null, get_response_message('Message has been sent!', 200));
}
});
});
});
});
}
});
};
/**
* This function returns the response
* message that will be sent to the
* caller of this service.
*/
var get_response_message = (msg, status) => {
if (status == 200) {
return `{'status': ${status}, 'message': ${msg}}`;
} else {
return `${status} - ${msg}`;
}
};
Run Code Online (Sandbox Code Playgroud)
此 cloudformation 模板描述了整套服务、API 网关、Lambda 函数、角色、权限、API 的使用计划、API 密钥等。
下载请点击 这里
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "This template deploys the necessary resources for sending MSG through a API-Gateway endpoint, Lambda function and SNS service.",
"Metadata": {
"License": {
"Description": "MIT license - Copyright (c) 2017"
}
},
"Resources": {
"LambdaRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": "LambdaSnsNotification",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSnsActions",
"Effect": "Allow",
"Action": [
"sns:Publish",
"sns:Subscribe",
"sns:Unsubscribe",
"sns:DeleteTopic",
"sns:CreateTopic"
],
"Resource": "*"
}
]
}
}
]
}
},
"LambdaFunctionMessageSNSTopic": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Description": "Send message to a specific topic that will deliver MSG to a receiver.",
"Handler": "index.handler",
"MemorySize": 128,
"Role": {
"Fn::GetAtt": [
"LambdaRole",
"Arn"
]
},
"Runtime": "nodejs6.10",
"Timeout": 60,
"Environment": {
"Variables": {
"sns_topic_arn": ""
}
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"var AWS = require('aws-sdk');",
"",
"/**",
" * Entry function for this",
" * Lambda.",
" * ",
" * This function delivers a message ",
" * to a specific number.",
" * ",
" * First approach will only handle ",
" * delivery type sms.",
" */",
"exports.handler = (event, context, callback) => {",
" console.log(JSON.stringify(event));",
"",
" if (event.type === undefined || event.type === null || event.type === '' || event.type.trim() === '') {",
" callback(get_response_message('Type of delivery is required.'), 412);",
" return;",
" }",
" ",
" if (event.type.trim() !== 'sms') {",
" callback(get_response_message('The available delivery type is \'sms\'.', 412));",
" return;",
" }",
"",
" if (event.type.trim() === 'sms' && (event.target === '' || isNaN(event.target))) {",
" callback(get_response_message('The target must be a number.', 412));",
" return;",
" }",
"",
" deliver(event.target, event.message, event.region, callback);",
"};",
"",
"/**",
" * This function delivers a",
" * message to a specific number.",
" * ",
" * The function will create a topic",
" * from scratch to avoid any",
" * clash among subscriptions.",
" * ",
" * @param number in context.",
" * @param message that will be sent.",
" * @param region in context.",
" * @param cb a callback function to ",
" * return a response to the ",
" * caller of this service.",
" */",
"var deliver = (number, message, region, cb) => {",
" var sns = new AWS.SNS({region: region});",
" console.log(`${number} - ${region} - ${Date.now()}`);",
" var params = { Name: `${number}_${region}_${Date.now()}` };",
"",
" sns.createTopic(params, function(err, tdata) {",
" if (err) {",
" console.log(err, err.stack);",
" cb(get_response_message(err, 500));",
" } else {",
" console.log(tdata.TopicArn);",
" sns.subscribe({",
" Protocol: 'sms',",
" TopicArn: tdata.TopicArn,",
" Endpoint: number",
" }, function(error, data) {",
" if (error) {",
" //Rollback to the previous created services.",
" console.log(error, error.stack);",
" params = { TopicArn: tdata.TopicArn};",
" sns.deleteTopic(params, function() { cb(get_response_message(error, 500)); });",
"",
" return;",
" }",
"",
" console.log('subscribe data', data);",
" var SubscriptionArn = data.SubscriptionArn;",
"",
" params = { TargetArn: tdata.TopicArn, Message: message, Subject: 'dummy' };",
" sns.publish(params, function(err_publish, data) {",
" if (err_publish) {",
" console.log(err_publish, err_publish.stack);",
" //Rollback to the previous created services.",
" params = { TopicArn: tdata.TopicArn};",
" sns.deleteTopic(params, function() {",
" params = {SubscriptionArn: SubscriptionArn};",
" sns.unsubscribe(params, function() { cb(get_response_message(err_publish, 500)); });",
" });",
"",
" return;",
" } else console.log('Sent message:', data.MessageId);",
"",
" params = { SubscriptionArn: SubscriptionArn };",
" sns.unsubscribe(params, function(err, data) {",
" if (err) console.log('err when unsubscribe', err);",
"",
" params = { TopicArn: tdata.TopicArn };",
" sns.deleteTopic(params, function(rterr, rtdata) {",
" if (rterr) {",
" console.log(rterr, rterr.stack);",
" cb(get_response_message(rterr, 500));",
" } else {",
" console.log(rtdata);",
" cb(null, get_response_message('Message has been sent!', 200));",
" }",
" });",
" });",
" });",
" });",
" }",
" });",
"};",
"",
"/**",
" * This function returns the response",
" * message that will be sent to the ",
" * caller of this service.",
" */",
"var get_response_message = (msg, status) => {",
" if (status == 200) {",
" return `{'status': ${status}, 'message': ${msg}}`;",
" } else {",
" return `${status} - ${msg}`;",
" }",
"};"
]
]
}
}
}
},
"MSGGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "MSG RestApi",
"Description": "API used for sending MSG",
"FailOnWarnings": true
}
},
"MSGGatewayRestApiUsagePlan": {
"Type": "AWS::ApiGateway::UsagePlan",
"Properties": {
"ApiStages": [
{
"ApiId": {
"Ref": "MSGGatewayRestApi"
},
"Stage": {
"Ref": "MSGGatewayRestApiStage"
}
}
],
"Description": "Usage plan for stage v1",
"Quota": {
"Limit": 5000,
"Period": "MONTH"
},
"Throttle": {
"BurstLimit": 200,
"RateLimit": 100
},
"UsagePlanName": "Usage_plan_for_stage_v1"
}
},
"RestApiUsagePlanKey": {
"Type": "AWS::ApiGateway::UsagePlanKey",
"Properties": {
"KeyId": {
"Ref": "MSGApiKey"
},
"KeyType": "API_KEY",
"UsagePlanId": {
"Ref": "MSGGatewayRestApiUsagePlan"
}
}
},
"MSGApiKey": {
"Type": "AWS::ApiGateway::ApiKey",
"Properties": {
"Name": "MSGApiKey",
"Description": "CloudFormation API Key v1",
"Enabled": "true",
"StageKeys": [
{
"RestApiId": {
"Ref": "MSGGatewayRestApi"
},
"StageName": {
"Ref": "MSGGatewayRestApiStage"
}
}
]
}
},
"MSGGatewayRestApiStage": {
"DependsOn": [
"ApiGatewayAccount"
],
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref"
| 归档时间: |
|
| 查看次数: |
915 次 |
| 最近记录: |