MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Cara Menggunakan Pengumpulan Koneksi MongoDB di AWS Lambda

Dalam postingan ini, kami akan menunjukkan cara menggunakan penggabungan koneksi MongoDB di AWS Lambda menggunakan driver Node.js dan Java.

Apa itu AWS Lambda?

AWS Lambda adalah layanan komputasi tanpa server berbasis peristiwa yang disediakan oleh Layanan Web Amazon . Ini memungkinkan pengguna menjalankan kode tanpa tugas administratif apa pun, tidak seperti instance EC2 di mana pengguna bertanggung jawab untuk menyediakan server, penskalaan, ketersediaan tinggi, dll. Sebagai gantinya, Anda hanya perlu mengunggah kode dan menyiapkan pemicu peristiwa, dan AWS Lambda secara otomatis menangani semua hal lainnya.

AWS Lambda mendukung berbagai waktu proses, termasuk Node.js , Python , Jawa , dan Pergi . Ini dapat dipicu langsung oleh layanan AWS seperti S3 , DynamoDB , Kinesis , SNS , dll. Dalam contoh kami, kami menggunakan gateway API AWS untuk memicu fungsi Lambda.

Apa itu Connection Pool?

Membuka dan menutup koneksi database adalah operasi yang mahal karena melibatkan waktu CPU dan memori. Jika aplikasi perlu membuka koneksi database untuk setiap operasi, maka itu akan berdampak buruk pada kinerja.

Bagaimana jika kita memiliki banyak koneksi database yang tetap hidup dalam cache? Setiap kali aplikasi perlu melakukan operasi database, aplikasi dapat meminjam koneksi dari cache, melakukan operasi yang diperlukan, dan mengembalikannya. Dengan menggunakan pendekatan ini, kita dapat menghemat waktu yang diperlukan untuk membuat koneksi baru setiap kali dan menggunakan kembali koneksi. Cache ini dikenal sebagai kumpulan koneksi .

Ukuran kumpulan koneksi dapat dikonfigurasi di sebagian besar driver MongoDB, dan ukuran kumpulan default bervariasi dari driver ke driver. Misalnya, 5 di driver Node.js, sedangkan 100 di driver Java. Ukuran kumpulan koneksi menentukan jumlah maksimum permintaan paralel yang dapat ditangani oleh driver Anda pada waktu tertentu. Jika batas kumpulan koneksi tercapai, setiap permintaan baru akan dibuat untuk menunggu hingga yang sudah ada selesai. Oleh karena itu, ukuran kumpulan perlu dipilih dengan hati-hati, dengan mempertimbangkan beban aplikasi dan konkurensi yang ingin dicapai.

Kumpulan Koneksi MongoDB di AWS Lambda

Dalam posting ini, kami akan menunjukkan kepada Anda contoh yang melibatkan driver Node.js dan Java untuk MongoDB. Untuk tutorial ini, kami menggunakan MongoDB yang dihosting di ScaleGrid menggunakan instans AWS EC2. Penyiapannya membutuhkan waktu kurang dari 5 menit, dan Anda dapat membuat uji coba gratis selama 30 hari di sini untuk memulai.
Cara Menggunakan Penyatuan Koneksi #MongoDB di AWS Lambda Menggunakan Node.js dan Driver LambdaKlik Untuk Tweet

Kolam Koneksi MongoDB Driver Java

Berikut kode untuk mengaktifkan kumpulan koneksi MongoDB menggunakan driver Java di fungsi handler AWS Lambda:


public class LambdaFunctionHandler
		implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

	private MongoClient sgMongoClient;
	private String sgMongoClusterURI;
	private String sgMongoDbName;

	@Override
	public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
		APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
		response.setStatusCode(200);

		try {
			context.getLogger().log("Input: " + new Gson().toJson(input));
			init(context);
			String body = getLastAlert(input, context);
			context.getLogger().log("Result body: " + body);
			response.setBody(body);
		} catch (Exception e) {
			response.setBody(e.getLocalizedMessage());
			response.setStatusCode(500);
		}

		return response;
	}

	private MongoDatabase getDbConnection(String dbName, Context context) {
		if (sgMongoClient == null) {
			context.getLogger().log("Initializing new connection");
			MongoClientOptions.Builder destDboptions = MongoClientOptions.builder();
			destDboptions.socketKeepAlive(true);
			sgMongoClient = new MongoClient(new MongoClientURI(sgMongoClusterURI, destDboptions));
			return sgMongoClient.getDatabase(dbName);
		}
		context.getLogger().log("Reusing existing connection");
		return sgMongoClient.getDatabase(dbName);
	}

	private String getLastAlert(APIGatewayProxyRequestEvent input, Context context) {
		String userId = input.getPathParameters().get("userId");
		MongoDatabase db = getDbConnection(sgMongoDbName, context);
		MongoCollection coll = db.getCollection("useralerts");
		Bson query = new Document("userId", Integer.parseInt(userId));
		Object result = coll.find(query).sort(Sorts.descending("$natural")).limit(1).first();
		context.getLogger().log("Result: " + result);
		return new Gson().toJson(result);
	}

	private void init(Context context) {
		sgMongoClusterURI = System.getenv("SCALEGRID_MONGO_CLUSTER_URI");
		sgMongoDbName = System.getenv("SCALEGRID_MONGO_DB_NAME");
	}

}

Penyatuan koneksi dicapai di sini dengan mendeklarasikan sgMongoClient variabel di luar fungsi handler. Variabel yang dideklarasikan di luar metode handler tetap diinisialisasi di seluruh panggilan, selama wadah yang sama digunakan kembali. Ini berlaku untuk bahasa pemrograman lain yang didukung oleh AWS Lambda.

Koleksi Koneksi MongoDB Driver Node.js

Untuk driver Node.js, mendeklarasikan variabel koneksi dalam lingkup global juga akan berhasil. Namun, ada pengaturan khusus yang tanpanya penyatuan koneksi tidak mungkin dilakukan. Parameter itu adalah callbackWaitsForEmptyEventLoop yang termasuk dalam objek konteks Lambda. Menyetel properti ini ke false akan membuat AWS Lambda membekukan proses dan data status apa pun. Ini dilakukan segera setelah panggilan balik dipanggil, meskipun ada peristiwa dalam loop peristiwa.

Berikut kode untuk mengaktifkan kumpulan koneksi MongoDB menggunakan driver Node.js di fungsi handler AWS Lambda:


'use strict'

var MongoClient = require('mongodb').MongoClient;

let mongoDbConnectionPool = null;
let scalegridMongoURI = null;
let scalegridMongoDbName = null;

exports.handler = (event, context, callback) => {

    console.log('Received event:', JSON.stringify(event));
    console.log('remaining time =', context.getRemainingTimeInMillis());
    console.log('functionName =', context.functionName);
    console.log('AWSrequestID =', context.awsRequestId);
    console.log('logGroupName =', context.logGroupName);
    console.log('logStreamName =', context.logStreamName);
    console.log('clientContext =', context.clientContext);
   
    // This freezes node event loop when callback is invoked
    context.callbackWaitsForEmptyEventLoop = false;

    var mongoURIFromEnv = process.env['SCALEGRID_MONGO_CLUSTER_URI'];
    var mongoDbNameFromEnv = process.env['SCALEGRID_MONGO_DB_NAME'];
    if(!scalegridMongoURI) {
	if(mongoURIFromEnv){
		scalegridMongoURI = mongoURIFromEnv;
	} else {
		var errMsg = 'Scalegrid MongoDB cluster URI is not specified.';
		console.log(errMsg);
		var errResponse = prepareResponse(null, errMsg);
		return callback(errResponse);
	}			
    }

    if(!scalegridMongoDbName) {
	if(mongoDbNameFromEnv) {
                scalegridMongoDbName = mongoDbNameFromEnv;
	} else {
		var errMsg = 'Scalegrid MongoDB name not specified.';
		console.log(errMsg);
		var errResponse = prepareResponse(null, errMsg);
		return callback(errResponse);
	}
    }

    handleEvent(event, context, callback);
};


function getMongoDbConnection(uri) {

    if (mongoDbConnectionPool && mongoDbConnectionPool.isConnected(scalegridMongoDbName)) {
        console.log('Reusing the connection from pool');
        return Promise.resolve(mongoDbConnectionPool.db(scalegridMongoDbName));
    }

    console.log('Init the new connection pool');
    return MongoClient.connect(uri, { poolSize: 10 })
        .then(dbConnPool => { 
                            mongoDbConnectionPool = dbConnPool; 
                            return mongoDbConnectionPool.db(scalegridMongoDbName); 
                          });
}

function handleEvent(event, context, callback) {
    getMongoDbConnection(scalegridMongoURI)
        .then(dbConn => {
			console.log('retrieving userId from event.pathParameters');
			var userId = event.pathParameters.userId;
			getAlertForUser(dbConn, userId, context);
		})
        .then(response => {
            console.log('getAlertForUser response: ', response);
            callback(null, response);
        })
        .catch(err => {
            console.log('=> an error occurred: ', err);
            callback(prepareResponse(null, err));
        });
}

function getAlertForUser(dbConn, userId, context) {

    return dbConn.collection('useralerts').find({'userId': userId}).sort({$natural:1}).limit(1)
        .toArray()
        .then(docs => { return prepareResponse(docs, null);})
        .catch(err => { return prepareResponse(null, err); });
}

function prepareResponse(result, err) {
	if(err) {
		return { statusCode:500, body: err };
	} else {
		return { statusCode:200, body: result };
	}
}

Analisis dan Pengamatan Kumpulan Koneksi Lambda AWS

Untuk memverifikasi kinerja dan pengoptimalan penggunaan kumpulan koneksi, kami menjalankan beberapa pengujian untuk fungsi Lambda Java dan Node.js. Dengan menggunakan gateway API AWS sebagai pemicu, kami memanggil fungsi dalam rentetan 50 permintaan per iterasi dan menentukan waktu respons rata-rata untuk permintaan di setiap iterasi. Pengujian ini diulang untuk fungsi Lambda tanpa menggunakan kumpulan koneksi pada awalnya, dan kemudian dengan kumpulan koneksi.

Grafik di atas mewakili waktu respons rata-rata permintaan di setiap iterasi. Anda dapat melihat di sini perbedaan waktu respons saat kumpulan koneksi digunakan untuk melakukan operasi database. Waktu respons menggunakan kumpulan koneksi secara signifikan lebih rendah karena fakta bahwa kumpulan koneksi diinisialisasi sekali dan menggunakan kembali koneksi alih-alih membuka dan menutup koneksi untuk setiap operasi database.

Satu-satunya perbedaan mencolok antara fungsi Java dan Node.js Lambda adalah waktu mulai yang dingin.

Apa yang dimaksud dengan Waktu Mulai Dingin?

Waktu mulai dingin mengacu pada waktu yang dibutuhkan oleh fungsi AWS Lambda untuk inisialisasi. Ketika fungsi Lambda menerima permintaan pertamanya, itu akan menginisialisasi wadah dan lingkungan proses yang diperlukan. Dalam grafik di atas, waktu respons permintaan 1 mencakup waktu mulai dingin, yang berbeda secara signifikan berdasarkan bahasa pemrograman yang digunakan untuk fungsi AWS Lambda.

Apakah Saya Perlu Khawatir dengan Waktu Mulai Dingin?

Jika Anda menggunakan gateway API AWS sebagai pemicu untuk fungsi Lambda, Anda harus mempertimbangkan waktu mulai yang dingin. Respons gateway API akan error jika fungsi integrasi AWS Lambda tidak diinisialisasi dalam rentang waktu tertentu. Batas waktu integrasi gateway API berkisar dari 50 milidetik hingga 29 detik.

Dalam grafik untuk fungsi Java AWS Lambda, Anda dapat melihat bahwa permintaan pertama membutuhkan waktu lebih dari 29 detik, oleh karena itu, respons gateway API mengalami error. Waktu mulai dingin untuk fungsi AWS Lambda yang ditulis menggunakan Java lebih tinggi dibandingkan dengan bahasa pemrograman lain yang didukung. Untuk mengatasi masalah waktu mulai dingin ini, Anda dapat mengaktifkan permintaan inisialisasi sebelum pemanggilan yang sebenarnya. Alternatif lain adalah mencoba lagi di sisi klien. Dengan begitu, jika permintaan gagal karena waktu mulai yang dingin, percobaan ulang akan berhasil.

Apa yang Terjadi pada Fungsi AWS Lambda Selama Tidak Aktif?

Dalam pengujian kami, kami juga mengamati bahwa wadah hosting AWS Lambda dihentikan saat tidak aktif untuk sementara waktu. Interval ini bervariasi dari 7 hingga 20 menit. Jadi, jika fungsi Lambda Anda tidak sering digunakan, maka Anda perlu mempertimbangkan untuk mempertahankannya dengan mengaktifkan permintaan detak jantung atau menambahkan percobaan ulang di sisi klien.

Apa Yang Terjadi Saat Saya Memanggil Fungsi Lambda Secara Bersamaan?

Jika fungsi Lambda dipanggil secara bersamaan, maka Lambda akan menggunakan banyak wadah untuk melayani permintaan tersebut. Secara default, AWS Lambda memberikan konkurensi tanpa syarat dari 1000 permintaan dan dapat dikonfigurasi untuk fungsi Lambda tertentu.

Di sinilah Anda perlu berhati-hati dengan ukuran kumpulan koneksi karena permintaan bersamaan dapat membuka terlalu banyak koneksi. Jadi, Anda harus menjaga ukuran kolam koneksi tetap optimal untuk fungsi Anda. Namun, setelah kontainer dihentikan, koneksi akan dilepaskan berdasarkan batas waktu dari server MongoDB.

Kesimpulan Penyatuan Koneksi Lambda AWS

Fungsi Lambda tidak memiliki status dan asinkron, dan dengan menggunakan kumpulan koneksi database, Anda akan dapat menambahkan status ke dalamnya. Namun, ini hanya akan membantu saat penampung digunakan kembali, sehingga Anda dapat menghemat banyak waktu. Penggabungan koneksi menggunakan AWS EC2 lebih mudah dikelola karena satu instans dapat melacak status kumpulan koneksinya tanpa masalah. Jadi, menggunakan AWS EC2 secara signifikan mengurangi risiko kehabisan koneksi database. AWS Lambda dirancang untuk bekerja lebih baik saat dapat mencapai API dan tidak harus terhubung ke mesin database.


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Mengonfigurasi Otentikasi MongoDB-CR sebagai Default di MongoDB 3.x

  2. Mongo DB Java 3.x Driver - Kelompokkan Berdasarkan Kueri

  3. Urutkan menggunakan MongoEngine?

  4. Perbarui objek array bersarang berdasarkan properti di MongoDB

  5. 'Field membutuhkan jenis kacang yang tidak dapat ditemukan.' error spring restful API menggunakan mongodb