2017-01-02 87 views
10

我有一個代理lambda函數的AWS api。我目前使用具有不同lambda函數的不同端點:aws api網關和lambda:多個端點/函數vs單個端點

api.com/getData --> getData 
api.com/addData --> addData 
api.com/signUp --> signUp 

管理所有端點和函數的過程變得很麻煩。當我將一個端點用於一個基於查詢字符串決定做什麼的lambda函數時,是否有任何缺點?

api.com/exec&func=getData --> exec --> if(params.func === 'getData') { ... } 

回答

14

這是完全有效的映射多種方法來一個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 
    } 
} 

現在,您可以訪問所有標題,網址參數,車身等和你可以使用它在單個Lambda函數(基本上實現您自己的路由)中以不同的方式處理請求。

作爲意見,我看到這種方法的一些優點和缺點。他們中的許多依賴於你的具體使用情況:

  • 部署:如果每個lambda函數是離散的,那麼你可以單獨部署它們,這可能會降低代碼的變化(微服務策略)的風險。相反,您可能會發現需要單獨部署功能會增加複雜性並且很麻煩。
  • 自我描述:API網關的界面使其非常直觀地查看RESTful端點的佈局 - 名詞和動詞都一目瞭然。實現自己的路由可能會犧牲這種可見性。
  • Lambda的大小和限制:如果您代理全部 - 那麼您將需要選擇實例大小,超時等,以適應所有RESTful端點。如果您創建離散函數,那麼您可以更仔細地選擇最符合特定調用需求的內存佔用空間,超時,死信行爲等。
+1

大答案只是找到了一個link。對我而言,我沒有想到的唯一優勢是:Lambda的大小和限制。不過,我認爲這些優勢勝過這個因素。我也可能將它分成兩個端點來滿足不同類型的請求。 1.使用有限的lambda資源和2個簡單的數據庫查詢。用更強大的資源來計算繁重的功能。 – Chris

+0

當然。 2017年有什麼不錯的是他們已經爲我們提供了所有的選擇,所以我們可以選擇最適合我們需求的工作流程。 –

3

我一直在用Lambda-API Gateway構建5〜6個微服務,並且經歷了幾次嘗試,失敗和成功。

總之

,從我的經驗,最好是委託所有的API調用拉姆達只有一個APIGateway通配符映射,如

/api/{+proxy} -> Lambda 

,如果你曾經使用過任何框架,比如grape你知道製作時API的功能,如
「中間件」
「全球異常處理」
「串聯路由」
「參數驗證」

真的很重要。當您的API增長時,使用API​​網關映射來管理所有路由幾乎是不可能的,而API Gateway也不支持這些功能。

更進一步說,爲開發或部署每個端點打破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框架,結賬this我們剛建立了lambda的web框架,因爲我需要在我的公司。

1

我會留言給Dave Maple's添加幾個積分,但我還沒有足夠的聲望點,所以我會在這裏添加評論。

我開始走向多個端點指向一個Lambda函數的路徑,這個函數可以通過訪問Event的'resource'屬性來對待每個端點。嘗試它之後,我現在已將它們分離爲單獨的函數,原因是Dave建議加上:

  • 我發現在函數分離時更容易瀏覽日誌和監視器。
  • 作爲一個初學者,我一開始沒有選擇的一個細微差別就是,您可以擁有一個代碼庫並部署與多個Lambda函數完全相同的代碼。這使您可以在代碼庫中獲得功能分離的好處以及整合方法的好處。
  • 您可以使用AWS CLI在多個功能之間自動執行任務,以減少/消除管理單獨功能的負面影響。例如,我有一個使用相同代碼更新10個函數的腳本。
+0

這不提供問題的答案。一旦你有足夠的[聲譽](https://stackoverflow.com/help/whats-reputation),你將可以[對任何帖子發表評論](https://stackoverflow.com/help/privileges/comment);相反,[提供不需要提問者澄清的答案](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-c​​an- I-DO-代替)。 - [來自評論](/ review/low-quality-posts/17710284) – Blackwood

0

據我所知,AWS每個Lambda函數只允許一個處理函數。這就是爲什麼我用Java Generics創建了一個「路由」機制(在編譯時進行更強大的類型檢查)。在下面的例子中,你可以調用多種方法,並通過不同的對象類型的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 class:

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:如果你有deserialisation問題LinkedTreeMap不能轉換爲...)你LAMBDA函數(因爲泛型/ 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); 
} 
0

我看,選擇單VS多個API的方式是下列因素的函數:

  1. 安全:我認爲這是具有單一API的最大挑戰結構體。對於不同部分的需求,可能會有不同的安全配置文件

  2. 從業務角度思考微服務模型: 任何API的全部用途都應該滿足某些請求,因此必須理解並易於使用。所以相關的API應該被合併。例如,如果您有移動客戶端,並且需要從數據庫中取出10件東西,則將10個端點組合爲一個API是有意義的。 但是這應該是合理的,應該在整體解決方案設計的背景下看到。例如,如果您設計工資單產品,您可能會認爲有單獨的模塊用於假期管理和用戶詳細信息管理。即使他們經常被單個客戶使用,他們仍然應該是不同的API,因爲他們的商業意義是不同的。

  3. 可重用性:適用於代碼和功能的可重用性。代碼可重用性是一個更容易解決的問題,即爲共享需求構建通用模塊並將其構建爲庫。 功能可重用性很難解決。在我看來,大多數情況下可以通過重新設計端點/功能的佈局來解決,因爲如果您需要重複功能,那意味着您的初始設計不夠詳細。

在另一個SO後,總結好

相關問題