it-swarm.asia

aws api gateway & lambda: beberapa titik akhir / fungsi vs titik akhir tunggal

Saya memiliki api AWS yang proksi fungsi lamba. Saat ini saya menggunakan titik akhir yang berbeda dengan fungsi lambda yang terpisah:

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

Proses untuk mengelola semua titik akhir dan fungsi menjadi rumit. Apakah ada kerugian ketika saya menggunakan satu titik akhir ke satu fungsi lambda yang memutuskan apa yang harus dilakukan berdasarkan string kueri?

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

Ini sangat valid untuk memetakan beberapa metode ke fungsi lambda tunggal dan banyak orang menggunakan metodologi ini hari ini sebagai lawan untuk menciptakan sumber daya gerbang api dan fungsi lambda untuk setiap metode diskrit.

Anda dapat mempertimbangkan mem-proxy semua permintaan ke satu fungsi. Lihatlah dokumentasi berikut tentang cara membuat API Gateway => Integrasi proxy Lambda: http://docs.aws.Amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple- proxy.html

Teladan mereka sangat bagus di sini. Permintaan seperti berikut:

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
}

Akan berakhir mengirim data acara berikut ke fungsi AWS Lambda Anda:

{
  "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
  }
}

Sekarang Anda memiliki akses ke semua tajuk, url params, badan, dll. Dan Anda bisa menggunakannya untuk menangani permintaan secara berbeda dalam satu fungsi Lambda (pada dasarnya menerapkan perutean Anda sendiri).

Sebagai pendapat saya melihat beberapa kelebihan dan kekurangan dari pendekatan ini. Banyak dari mereka bergantung pada kasus penggunaan khusus Anda:

  • Penempatan : jika setiap fungsi lambda diskrit maka Anda dapat menggunakan mereka secara independen, yang mungkin mengurangi risiko dari perubahan kode (strategi layanan mikro). Sebaliknya, Anda mungkin perlu menggunakan fungsi secara terpisah menambah kompleksitas dan memberatkan.
  • Cukup Deskripsi : Antarmuka API Gateway membuatnya sangat intuitif untuk melihat tata letak titik akhir RESTful Anda - kata benda dan kata kerja semuanya terlihat dalam sekejap. Menerapkan perutean sendiri dapat mengorbankan visibilitas ini.
  • Ukuran dan batas Lambda : Jika Anda proxy semua - maka Anda akan perlu memilih ukuran instance, batas waktu dll. Yang akan mengakomodasi semua titik akhir tenang Anda. Jika Anda membuat fungsi-fungsi diskrit maka Anda dapat lebih hati-hati memilih jejak memori, batas waktu, perilaku deadletter, dll. Yang paling memenuhi kebutuhan permintaan khusus.
38
Dave Maple

saya telah membangun 5 ~ 6 microservices dengan Lambda-API Gateway, dan telah melalui beberapa percobaan & kegagalan dan kesuksesan.

singkatnya, dari pengalaman saya, lebih baik untuk mendelegasikan semua panggilan API ke lambda hanya dengan satu pemetaan wildcard APIG.

/api/{+proxy} -> Lambda

jika Anda pernah menggunakan kerangka kerja seperti grape Anda tahu itu saat membuat API, fitur seperti
"middleware"
"penanganan pengecualian global"
"perutean kaskade"
"validasi parameter"

Sangat penting. seiring API Anda tumbuh, hampir tidak mungkin untuk mengelola semua rute dengan pemetaan API Gateway, atau dukungan API Gateway selain dari fitur tersebut.

lebih jauh lagi, itu tidak benar-benar praktis untuk memecahkan lambda untuk setiap titik akhir untuk pengembangan atau penyebaran.

dari teladan Anda,

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

bayangkan Anda memiliki ORM data, logika otentikasi pengguna, file tampilan umum (seperti data.erb) .. lalu bagaimana Anda akan membagikannya?

anda mungkin bisa istirahat seperti,

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

tetapi tidak seperti "per titik akhir". Anda mungkin dapat mencari konsep microservice dan praktik terbaik tentang bagaimana Anda dapat membagi layanan

untuk kerangka kerja web seperti fitur, checkout ini kami baru membangun kerangka web untuk lambda karena saya membutuhkan ini di perusahaan saya.

13
Kurt Lee

Saya akan berkomentar untuk hanya menambahkan beberapa poin ke Dave Maple's jawaban yang bagus tapi saya belum memiliki poin reputasi yang cukup jadi saya akan menambahkan komentar di sini.

Saya mulai menyusuri jalan beberapa titik akhir yang menunjuk ke satu fungsi Lambda yang dapat memperlakukan setiap titik akhir berbeda dengan mengakses properti 'sumber daya' dari Acara. Setelah mencobanya saya sekarang telah memisahkan mereka menjadi fungsi yang terpisah untuk alasan yang disarankan Dave plus:

  • Saya merasa lebih mudah untuk pergi melalui log dan monitor ketika fungsinya terpisah.
  • Satu nuansa bahwa sebagai pemula saya tidak mengambil pada awalnya adalah bahwa Anda dapat memiliki satu basis kode dan menggunakan kode yang sama persis dengan beberapa fungsi Lambda. Ini memungkinkan Anda untuk mendapatkan manfaat pemisahan fungsi dan manfaat pendekatan gabungan dalam basis kode Anda.
  • Anda dapat menggunakan AWS CLI untuk mengotomatiskan tugas-tugas di beberapa fungsi untuk mengurangi/menghilangkan kelemahan mengelola fungsi yang terpisah. Misalnya, saya memiliki skrip yang memperbarui 10 fungsi dengan kode yang sama.
8
Inspector6

Sejauh yang saya tahu, AWS hanya memungkinkan satu handler per fungsi Lambda. Itu sebabnya saya telah membuat mekanisme "routing" kecil dengan Java Generics (untuk pemeriksaan tipe yang lebih kuat pada waktu kompilasi). Pada contoh berikut ini Anda dapat memanggil beberapa metode dan mengirimkan objek yang berbeda ketik ke Lambda dan kembali melalui satu penangan Lambda:

Kelas Lambda dengan handler:

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

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

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

}

Contoh POJO Kelas pengguna:

public class User {
private String name;

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

Metode uji 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 .: jika Anda memiliki masalah deserialisation (LinkedTreeMap tidak dapat dilemparkan ke ...) dalam fungsi Lambda Anda (karena uf Generics/Gson ), gunakan pernyataan berikut:

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

Metode:

private <T> Object convertLambdaRequestData2Object(LambdaRequest<?> lambdaRequest, Class<T> clazz) {

    Gson gson = new Gson();
    String json = gson.toJson(lambdaRequest.getData());
    return gson.fromJson(json, clazz);
}
2
Jörg

Cara saya melihat, memilih API tunggal vs ganda adalah fungsi dari pertimbangan berikut:

  1. Keamanan: Saya pikir ini adalah tantangan terbesar untuk memiliki struktur API tunggal. Dimungkinkan untuk memiliki profil keamanan yang berbeda untuk bagian persyaratan yang berbeda

  2. Pikirkan model layanan mikro dari perspektif bisnis: Seluruh tujuan API apa pun harus melayani beberapa permintaan, oleh karena itu harus dipahami dengan baik dan mudah digunakan. Jadi API terkait harus digabungkan. Misalnya, jika Anda memiliki klien seluler dan memerlukan 10 hal untuk ditarik masuk dan keluar dari DB, masuk akal untuk memiliki 10 titik akhir ke dalam satu API. Tetapi ini harus masuk akal dan harus dilihat dalam konteks desain solusi keseluruhan. Misalnya, jika Anda mendesain produk penggajian, Anda mungkin berpikir untuk memiliki modul terpisah untuk manajemen cuti dan manajemen detail pengguna. Bahkan jika mereka sering digunakan oleh satu klien, mereka harus tetap menjadi API yang berbeda, karena makna bisnis mereka berbeda.

  3. Dapat digunakan kembali: Berlaku untuk penggunaan kembali kode dan fungsionalitas. Penggunaan kembali kode adalah masalah yang lebih mudah untuk dipecahkan, yaitu membangun modul umum untuk persyaratan bersama dan membangunnya sebagai perpustakaan. Penggunaan kembali fungsionalitas lebih sulit untuk dipecahkan. Dalam pikiran saya, sebagian besar kasus dapat diselesaikan dengan mendesain ulang cara titik akhir/fungsi diletakkan, karena jika Anda memerlukan duplikasi fungsi yang berarti desain awal Anda tidak cukup detail.

Baru saja menemukan tautan di pos lain SO pos yang merangkum lebih baik

0
Ayan Guha