我有一个AWS api代理lamba函数。我目前使用不同的端点和单独的lambda函数:
api.com/getData --> getData
api.com/addData --> addData
api.com/signUp --> signUp
管理所有端点和功能的过程变得很麻烦。当我将一个端点用于一个基于查询字符串决定做什么的lambda函数时,是否有任何不利之处?
api.com/exec&func=getData --> exec --> if(params.func === 'getData') { ... }
答案 0 :(得分:34)
将多个方法映射到单个lambda函数是完全有效的,今天很多人都在使用这种方法,而不是为每个离散方法创建api网关资源和lambda函数。
您可以考虑将所有请求代理到单个函数。请查看以下有关创建API网关的文档=> Lambda代理集成: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html
他们的榜样很棒。请求如下:
POST /testStage/hello/world?name=me HTTP/1.1
Host: gy415nuibc.execute-api.us-east-1.amazonaws.com
Content-Type: application/json
headerName: headerValue
{
"a": 1
}
最终将以下事件数据发送到您的AWS Lambda函数:
{
"message": "Hello me!",
"input": {
"resource": "/{proxy+}",
"path": "/hello/world",
"httpMethod": "POST",
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"cache-control": "no-cache",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Content-Type": "application/json",
"headerName": "headerValue",
"Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com",
"Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f",
"User-Agent": "PostmanRuntime/2.4.5",
"Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==",
"X-Forwarded-For": "54.240.196.186, 54.182.214.83",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"queryStringParameters": {
"name": "me"
},
"pathParameters": {
"proxy": "hello/world"
},
"stageVariables": {
"stageVariableName": "stageVariableValue"
},
"requestContext": {
"accountId": "12345678912",
"resourceId": "roq9wj",
"stage": "testStage",
"requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"apiKey": null,
"sourceIp": "192.168.196.186",
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "PostmanRuntime/2.4.5",
"user": null
},
"resourcePath": "/{proxy+}",
"httpMethod": "POST",
"apiId": "gy415nuibc"
},
"body": "{\r\n\t\"a\": 1\r\n}",
"isBase64Encoded": false
}
}
现在您可以访问所有标题,url参数,正文等,您可以使用它来在单个Lambda函数中以不同方式处理请求(基本上实现您自己的路由)。
作为一种观点,我看到这种方法有一些优点和缺点。其中许多取决于您的具体用例:
答案 1 :(得分:11)
我一直在使用Lambda-API网关构建5~6个微服务,经过多次尝试和试验。失败和成功。
简而言之,根据我的经验,最好只使用一个APIGateway通配符映射将所有API调用委托给lambda,例如
/api/{+proxy} -> Lambda
如果您使用过grape之类的任何框架,那么您在制作API时会知道诸如此类的功能
的 “中间件”
“全球异常处理”
“级联路由”
“参数验证”
真的很重要
随着API的增长,几乎不可能使用API网关映射管理所有路由,也不支持API网关支持这些功能。
此外,为了开发或部署,为每个端点打破lambda实际上并不是真的。
来自你的例子,
api.com/getData --> getData
api.com/addData --> addData
api.com/signUp --> signUp
想象你有数据ORM,用户身份验证逻辑,公共视图文件(例如data.erb)..然后你将如何分享它?
你可能会破坏,
api/auth/{+proxy} -> AuthServiceLambda
api/data/{+proxy} -> DataServiceLambda
但不喜欢“每个端点”。您可以查找有关如何拆分服务的微服务概念和最佳实践
对于那些类似Web功能的框架,checkout this我们刚刚为lambda构建了web框架,因为我需要在我的公司使用它。
答案 2 :(得分:7)
我会评论只是为Dave Maple's很好的答案添加几点,但我还没有足够的声誉点,所以我会在这里添加评论。
我开始沿着指向一个Lambda函数的多个端点的路径前进,该函数可以通过访问“资源”来处理每个端点的不同。活动的财产。在尝试之后,我现在将它们分成单独的函数,原因是Dave建议加上:
答案 3 :(得分:2)
据我所知,AWS每个Lambda函数只允许一个处理程序。这就是为什么我用Java Generics创建了一个小的“路由”机制(用于在编译时进行更强大的类型检查)。在以下示例中,您可以调用多个方法并将不同的对象类型传递给Lambda并通过一个Lambda处理程序返回:
带处理程序的Lambda类:
public class GenericLambda implements RequestHandler<LambdaRequest<?>, LambdaResponse<?>> {
@Override
public LambdaResponse<?> handleRequest(LambdaRequest<?> lambdaRequest, Context context) {
switch (lambdaRequest.getMethod()) {
case WARMUP:
context.getLogger().log("Warmup");
LambdaResponse<String> lambdaResponseWarmup = new LambdaResponse<String>();
lambdaResponseWarmup.setResponseStatus(LambdaResponse.ResponseStatus.IN_PROGRESS);
return lambdaResponseWarmup;
case CREATE:
User user = (User)lambdaRequest.getData();
context.getLogger().log("insert user with name: " + user.getName()); //insert user in db
LambdaResponse<String> lambdaResponseCreate = new LambdaResponse<String>();
lambdaResponseCreate.setResponseStatus(LambdaResponse.ResponseStatus.COMPLETE);
return lambdaResponseCreate;
case READ:
context.getLogger().log("read user with id: " + (Integer)lambdaRequest.getData());
user = new User(); //create user object for test, instead of read from db
user.setName("name");
LambdaResponse<User> lambdaResponseRead = new LambdaResponse<User>();
lambdaResponseRead.setData(user);
lambdaResponseRead.setResponseStatus(LambdaResponse.ResponseStatus.COMPLETE);
return lambdaResponseRead;
default:
LambdaResponse<String> lambdaResponseIgnore = new LambdaResponse<String>();
lambdaResponseIgnore.setResponseStatus(LambdaResponse.ResponseStatus.IGNORED);
return lambdaResponseIgnore;
}
}
}
LambdaRequest类:
public class LambdaRequest<T> {
private Method method;
private T data;
private int languageID;
public static enum Method {
WARMUP, CREATE, READ, UPDATE, DELETE
}
public LambdaRequest(){
}
public Method getMethod() {
return method;
}
public void setMethod(Method create) {
this.method = create;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getLanguageID() {
return languageID;
}
public void setLanguageID(int languageID) {
this.languageID = languageID;
}
}
LambdaResponse类:
public class LambdaResponse<T> {
private ResponseStatus responseStatus;
private T data;
private String errorMessage;
public LambdaResponse(){
}
public static enum ResponseStatus {
IGNORED, IN_PROGRESS, COMPLETE, ERROR, COMPLETE_DUPLICATE
}
public ResponseStatus getResponseStatus() {
return responseStatus;
}
public void setResponseStatus(ResponseStatus responseStatus) {
this.responseStatus = responseStatus;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
示例POJO用户类:
public class User {
private String name;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
JUnit测试方法:
@Test
public void GenericLambda() {
GenericLambda handler = new GenericLambda();
Context ctx = createContext();
//test WARMUP
LambdaRequest<String> lambdaRequestWarmup = new LambdaRequest<String>();
lambdaRequestWarmup.setMethod(LambdaRequest.Method.WARMUP);
LambdaResponse<String> lambdaResponseWarmup = (LambdaResponse<String>) handler.handleRequest(lambdaRequestWarmup, ctx);
//test READ user
LambdaRequest<Integer> lambdaRequestRead = new LambdaRequest<Integer>();
lambdaRequestRead.setData(1); //db id
lambdaRequestRead.setMethod(LambdaRequest.Method.READ);
LambdaResponse<User> lambdaResponseRead = (LambdaResponse<User>) handler.handleRequest(lambdaRequestRead, ctx);
}
ps。:如果您有反序列化问题( LinkedTreeMap 无法转换为...)在您的Lambda函数中(因为uf Generics / Gson),使用以下语句:
YourObject yourObject = (YourObject)convertLambdaRequestData2Object(lambdaRequest, YourObject.class);
方法:
private <T> Object convertLambdaRequestData2Object(LambdaRequest<?> lambdaRequest, Class<T> clazz) {
Gson gson = new Gson();
String json = gson.toJson(lambdaRequest.getData());
return gson.fromJson(json, clazz);
}
答案 4 :(得分:0)
我认为,选择单个API和多个API是以下考虑因素的函数:
安全性:我认为这是拥有单一API结构的最大挑战。对于需求的不同部分,可能有不同的安全配置文件
从业务角度思考微服务模型: 任何API的整个目的应该是提供一些请求,因此必须很好地理解它并且易于使用。因此应该结合相关的API。例如,如果您有一个移动客户端,并且需要从数据库中取出10个内容,那么将10个端点放入单个API中是有意义的。 但这应该是合理的,应该在整体解决方案设计的背景下看待。例如,如果您设计工资单产品,您可能会认为有单独的模块用于休假管理和用户详细信息管理。即使它们经常被单个客户使用,它们仍然应该是不同的API,因为它们的商业含义是不同的。
可重用性:适用于代码和功能可重用性。代码可重用性是一个更容易解决的问题,即为共享需求构建通用模块并将它们构建为库。 功能可重用性更难解决。在我看来,大多数情况都可以通过重新设计端点/功能的布局来解决,因为如果你需要重复功能,这意味着你的初始设计不够详细。
刚刚在另一个SO帖子中找到link,总结了更好的