signin pricing east aws api amazon-web-services aws-lambda aws-api-gateway

pricing - aws api gateway y lambda: mĂșltiples puntos finales/funciones frente a un solo punto final



https us east 1 signin aws amazon com (5)

Tengo un api de AWS que proxies funciona con lamba. Actualmente utilizo diferentes puntos finales con funciones lambda separadas:

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

El proceso para gestionar todos los puntos finales y funciones se vuelve engorroso. ¿Existe alguna desventaja cuando uso un solo punto final para una función lambda que decide qué hacer en función de la cadena de consulta?

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


Es perfectamente válido asignar múltiples métodos a una sola función lambda, y muchas personas están usando esta metodología hoy en lugar de crear un recurso de puerta de enlace api y una función lambda para cada método discreto.

Podría considerar enviar todas las solicitudes a una sola función. Eche un vistazo a la siguiente documentación sobre cómo crear una puerta de enlace API => integración de proxy Lambda: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html

Su ejemplo es genial aquí. Una solicitud como la siguiente:

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 }

Terminará enviando los siguientes datos de eventos a su función 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 } }

Ahora tiene acceso a todos los encabezados, parámetros de URL, cuerpo, etc. y puede usarlo para manejar las solicitudes de manera diferente en una sola función Lambda (básicamente implementando su propio enrutamiento).

Como opinión veo algunas ventajas y desventajas de este enfoque. Muchos de ellos dependen de su caso de uso específico:

  • Implementación : si cada función lambda es discreta, entonces puede implementarlas de forma independiente, lo que podría reducir el riesgo de cambios de código (estrategia de microservicios). A la inversa, es posible que la necesidad de implementar funciones por separado agregue complejidad y sea engorroso.
  • Descripción propia : la interfaz de la puerta de enlace API hace que sea extremadamente intuitivo ver el diseño de sus puntos finales RESTful: los sustantivos y los verbos son visibles de un vistazo. La implementación de su propio enrutamiento podría ser a expensas de esta visibilidad.
  • Tamaño y límites de Lambda : si apunta todo, entonces tendrá que elegir el tamaño de la instancia, el tiempo de espera, etc., que se ajustará a todos sus puntos finales RESTful. Si crea funciones discretas, puede elegir con más cuidado la huella de memoria, el tiempo de espera, el comportamiento de la carta muerta, etc. que mejor se adapte a las necesidades de la invocación específica.

Habría comentado que solo hubiera agregado un par de puntos a la excelente respuesta de Dave Maple, pero aún no tengo suficientes puntos de reputación, así que agregaré los comentarios aquí.

Comencé a descender por la ruta de varios puntos finales que apuntan a una función Lambda que podría tratar cada punto final de manera diferente accediendo a la propiedad ''recurso'' del Evento. Después de intentarlo, ahora los he separado en funciones separadas por las razones que Dave sugirió más:

  • Me resulta más fácil pasar por los registros y monitores cuando las funciones están separadas.
  • Un matiz que no aprendí al principio es que puedes tener un código base y desplegar exactamente el mismo código que las múltiples funciones Lambda. Esto le permite tener los beneficios de la separación de funciones y los beneficios de un enfoque consolidado en su base de código.
  • Puede utilizar la CLI de AWS para automatizar tareas en las múltiples funciones para reducir / eliminar la desventaja de administrar funciones separadas. Por ejemplo, tengo un script que actualiza 10 funciones con el mismo código.

He estado construyendo 5 ~ 6 microservicios con Lambda-API Gateway, y he pasado por varios intentos, fallos y éxitos.

en resumen, desde mi experiencia, es mejor delegar todas las llamadas de API a lambda con un solo mapeo comodín APIGateway, como

/api/{+proxy} -> Lambda

si alguna vez usó marcos como grape , sabe que al crear API, características como
"middleware"
"manejo global de excepciones"
"enrutamiento en cascada"
"validación de parámetros"
Son realmente cruciales. a medida que su API crece, es casi imposible administrar todas las rutas con la asignación de la puerta de enlace API, ni tampoco la compatibilidad de la puerta de enlace API de esas características.

Además, en realidad no es prácticamente una ruptura de lambda para cada punto final para el desarrollo o la implementación.

de tu ejemplo,

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

imagina que tienes ORM de datos, lógica de autenticación de usuario, archivo de vista común (como data.erb) ... entonces, ¿cómo vas a compartir eso?

usted podría romper como

api/auth/{+proxy} -> AuthServiceLambda api/data/{+proxy} -> DataServiceLambda

Pero no como "por punto final". puede buscar el concepto de microservicio y las mejores prácticas sobre cómo dividir el servicio

para las características del marco web, compruebe this , acabamos de crear el marco web para lambda ya que lo necesitaba en mi empresa.


La forma en que veo, la elección de API única frente a múltiple es una función de las siguientes consideraciones:

  1. Seguridad: Creo que este es el mayor desafío de tener una estructura de API única. Puede ser posible tener diferentes perfiles de seguridad para diferentes partes del requisito

  2. Piense en el modelo de microservicio desde la perspectiva del negocio: todo el propósito de cualquier API debe ser atender algunas solicitudes, por lo tanto, debe ser bien comprendido y fácil de usar. Por lo tanto, las API relacionadas deben ser combinadas. Por ejemplo, si tiene un cliente móvil y requiere 10 elementos para ingresar y salir de la base de datos, tiene sentido tener 10 puntos finales en una sola API. Pero esto debería estar dentro de lo razonable y debería verse en el contexto del diseño general de la solución. Por ejemplo, si diseña un producto de nómina, puede pensar en tener módulos separados para la gestión de permisos y la gestión de detalles de usuario. Incluso si a menudo son utilizados por un solo cliente, aún deben ser API diferentes, porque el significado de su negocio es diferente.

  3. Reutilización: se aplica tanto al código como a la funcionalidad reutilizable. La reutilización del código es un problema más fácil de resolver, es decir, crear módulos comunes para requisitos compartidos y construirlos como bibliotecas. La reutilización de la funcionalidad es más difícil de resolver. En mi opinión, la mayoría de los casos se pueden resolver rediseñando la forma en que se diseñan los puntos finales / funciones, porque si necesita duplicación de funciones, eso significa que su diseño inicial no está lo suficientemente detallado.

Acabo de encontrar un link en otra publicación SO que resume mejor


Que yo sepa, AWS permite solo un manejador por función Lambda. Es por eso que he creado un pequeño mecanismo de "enrutamiento" con Java Generics (para verificaciones de tipos más fuertes en tiempo de compilación). En el siguiente ejemplo, puede llamar a múltiples métodos y pasar diferentes tipos de objetos a Lambda y viceversa a través de un controlador Lambda :

Clase Lambda con manejador:

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; } } }

Clase 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; } }

Clase de 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; } }

Ejemplo de clase de usuario POJO:

public class User { private String name; public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } }

Método de prueba 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 .: si tiene problemas de deserialización ( LinkedTreeMap no se puede convertir a ...) en su función Lambda (debido a que en Genics / Gson), use la siguiente declaración:

YourObject yourObject = (YourObject)convertLambdaRequestData2Object(lambdaRequest, YourObject.class);

Método:

private <T> Object convertLambdaRequestData2Object(LambdaRequest<?> lambdaRequest, Class<T> clazz) { Gson gson = new Gson(); String json = gson.toJson(lambdaRequest.getData()); return gson.fromJson(json, clazz); }