مدل برنامه نویسی
در این مستند یاد می گیریم چطور کد مربوط به یک تابع در سرویس رایانش را بنویسیم، چه امکاناتی هنگام اجرای درخواست داریم و چه نکاتی را بهتر است هنگام نوشتن توابع هندلر رعایت کنیم.
پیشنیازها
- در صورتی که با سرویس رایانش آشنایی ندارید، به معرفی سرویس رایانش مراجعه کنید.
- در صورتی که با برنامهنویسی به زبان NodeJs آشنایی ندارید، به آشنایی با NodeJs مراجعه کنید.
- در صورتی که هنوز با تنظیمات پنل سرویس رایانش آشنا نشدهاید و یک تابع در آن جا نساختهاید، به تنظیمات پنل رایانش مراجعه کنید.
تابع هندلر (HANDLER FUNCTION)
هنگامی که یک تابع در سرویس رایانش بکتوری تعریف می کنید، یک هندلر که در واقع تابعی در کد شماست که بکتوری فراخوانی را از آن اجرا آغاز می کند، تعریف می کنید. وقتی می خواهید در Node.js یک تابع هندلر ایجاد کنید، از سینتکس زیر پیروی کنید.
exports.myHandler = function(requestBody, context) {
...
}
در مورد این سینتکس به نکات زیر توجه کنید:
-
requestBody - منظور از این پارامتر بدنه درخواست است که همراه با درخواست HTTP برای سرویس رایانش ارسال کرده اید. در صورتی که هدر مربوط به Content-Type را برابر application/json ست کرده باشید، به صورت پارس شده میباشد. در این حالت اگر در پارس بدنه درخواستی مشکلی پیش آید، سرور کد 417 (Expectation Failed) بر می گرداند. ولی اگر این هدر را ارسال نکنید، به صورت رشته خام به تابع هندلر ارسال می شود و خودتون در صورت لزوم می بایست آن را پارس کنید.
-
context - بکتوری از طریق این پارامتر اطلاعات مربوط به درخواست جاری در حال اجرا و هم چنین متد هایی برای لاگ زدن، مشخص کردن خروجی و … برای شما فراهم می آورد.
-
myHandler - این نام تابع هندلری است که بکتوری صدا می زند. شما این تابع را export کردید. بنابراین برای بکتوری قابل مشاهده و صدا زدن می باشد. فرض کنید شما این کد را در فایل helloWorld.js ذخیره کرده و در فایل زیپ در بکتوری آپلود کرده باشید. آنگاه هندلری که هنگام تعریف تابع مشخص می کنید باید helloWorld.myHandler باشد.
اطلاعات زمان اجرا (Context Object)
- چه مقدار زمان باقی مانده است تا بکتوری اجرای تابعتان را متوقف کند. (تایم اوت یکی از تنظیمات تعریف تابع می باشد.)
- امکان لاگ زدن برای درخواست اجرای جاری
- اطلاعات کاربری که این تابع را فراخوانی کرده است.
- مشخص کردن خروجی درخواست
- و …
به عنوان مثال، کد نمونه ای که در آن از همه ی متدهای context استفاده شده است، در زیر آمده است.
exports.handler = function(requestBody, context) {
var securityContext = context.getSecurityContext();
context.log('received event:', requestBody);
context.log('value1 =', requestBody.key1);
context.log('value2 =', requestBody.key2);
context.log('remaining time =', context.getRemainingTimeInMillis());
context.log('request headers =', context.getRequestHeaders());
context.log('userId =', securityContext.userId);
context.log('userName =', securityContext.userName);
context.log('keyType =', context.keyType);
context.log('requestId =', context.requestId);
context.succeed(requestBody.key1); // Echo back the first key value (with 200 status code)
context.fail(requestBody.key1); // Echo back the first key value (with 420 status code)
};
در ذیل به بررسی تک تک متد های context می پردازیم:
-
()context.getRemainingTimeInMillis - به کمک این متد می توانید بفهمید که چه مقدار زمان از حداکثر زمان مجاز برای این درخواست باقی مانده است. (با توجه به مقدار تایم اوتی که هنگام تعریف تابع مشخص کرده اید.)
-
context.requestId - هر درخواستی که به سرویس رایانش بکتوری می زنید، دارای یک id منحصر به فرد می باشد که توسط context.requestId می توانید مقدارش را بخوانید.
-
()context.getSecurityContext - به کمک متد getSecurityContext می توانید اطلاعات کاربری که این درخواست را ایجاد کرده داشته باشید. این متد به عنوان خروجی یک شی json حاوی مقادیر userId، userName، keyType و authenticationId بر می گرداند. مقدار keyType برابر master یا client می باشد. دقت کنید که فقط در حالتی که keyType برابر client می باشد، مقادیر userName و userId معنا دارند.
-
()context.getRequestHeaders - به کمک این متد می توانید به هدر هایی که همراه با درخواست ارسال شدند و کلید آن ها با -X-backtory شروع شدهاند، دسترسی داشته باشید.
-
()context.succeed - شما به کمک این متد می توانید خروجی درخواست را مشخص کنید. به صورت دیفالت سرویس رایانش JSON.stringify را بر روی آن چه می خواهید به عنوان خروجی مشخص کنید، صدا می زند. شما موظفید وقتی کارتان تمام شد یکی از متد های succeed یا fail را صدا بزنید؛ وگرنه، سرویس رایانش آن درخواست را time-out محسوب می کند و مجبور می شود که درخواست های جاری را kill کند. هم چنین اگر بیش از یک بار به ازای یک درخواست succeed یا fail صدا زده شود، سرویس رایانش فقط بار اول را در نظر می گیرد و بقیه را نادیده می گیرد.
-
()context.fail - زمانی که می خواهید بگویید اجرای درخواست با مشکل روبرو شده است به جای succeed متد fail را صدا بزنید. تنها تفاوت آن با حالت قبلی در این است که کد status ای که بر می گرداند به جای 200 مقدار 420 خواهد بود.
-
()context.log - به وسیله این متد می توانید به ازای هر درخواست لاگ بزنید. این لاگ ها با استفاده از requestId از طریق پنل بکتوری قابل دسترسی می باشند. شما به ازای یک درخواست حداکثر می توانید ۵۰۰۰ کاراکتر لاگ بزنید. اگر به محدودیت لاگ برای یک درخواست برسید، بقیه لاگ هایی که می زنید، نادیده گرفته می شوند.
در زیر نمونه دیگری از نحوه استفاده صحیح از متد های context آمده است.
var Backtory = require("backtory-sdk");
exports.handler = function(requestBody, context) {
context.log("request started");
var userId = context.getSecurityContext().userId;
var Book = Backtory.Object.extend("Book");
var query = new Backtory.Query(Book);
query.set("userId", userId);
query.find({
success: function(list) {
context.succeed(list);
},
error: function(error) {
context.fail(error);
}
});
};
الگوی مناسب استفاده (Best-Practices)
در ذیل، توصیه هایی در مورد نحوه استفاده از سرویس رایانش آمده است:
-
صدا زدن توابع ()context.succeed و ()context.fail به معنای پایان اجرای توابع رایانش نیستند! بلکه کدهای بعد از اونها هم اجرا میشوند. بنابراین، به لحاظ معنایی بهتر است کدهای خود را به گونهای بنویسید که بعد از صدا زدن هریک از این دو تابع، دیگر کدی برای اجرا شدن وجود نداشته باشد.
-
کدهایتان را به صورت stateless بنویسید و مطمئن شوید که هیچ ارتباطی بین کدتان و زیرساختی که کدهایتان در آن اجرا می شود وجود ندارد. برای مثال سرویس رایانش ممکن است بسته به میزان استفاده تان در ۵ مکان مختلف پروژه تان را دیپلوی کند. بنابراین اگر درخواست فعلی در نود خاصی اجرا شود، دلیل نمی شود که درخواست بعدی هم همان جا اجرا شود.
-
از تعریف متغیر های توابعتان خارج از اسکوپ مربوط به تابع هندلر بپرهیزید. سرویس رایانش تضمین نمی کند به ازای هر درخواست اجرا این متغیر ها رفرش شوند.
-
مطمئن شوید در حالتی که فایل زیپ برای تابعتان آپلود می کنید، فایل ها دسترسی مناسب (rx+) داشته باشند تا سرویس رایانش در اجرایشان به مشکل بر نخورد.
-
تا حد امکان تلاش کنید که از timeout شدن درخواست هایتان اجتناب کنید و در همه ی حالات یکی از دو متد context.succeed یا context.fail را صدا زده و جواب را برگردانید. در استفاده از متدهایی از SDK که درخواستی به سمت سرور می فرستند، اگر کارهایتان را می خواهید در کالبک success انجام دهید (مثلا خروجی درخواست را آن جا تعیین می کنید)، حتما کالبک fail را هم بگذارید و در آن حالت هم چیزی بر گردانید.
مثالی از نحوه نامناسب فراخوانی دستور find در SDK:
var Backtory = require("backtory-sdk");
exports.handler = function(requestBody, context) {
var Book = Backtory.Object.extend("Book");
var query = new Backtory.Query(Book);
query.find({
success: function(list) {
context.succeed(list);
}
});
};
در مثال بالا، اگر به هر دلیلی مثلا خطا در اتصال درخواست مربوط به find با مشکل روبرو
شود، کالبک مربوط به error صدا زده می شود که شما مشخص نکرده اید. خوب مشکل این جاست که شما فقط در حالت
success خروجی درخواست را از طریق context.succeed مشخص کرده اید.
مثالی از نحوه درست فراخوانی:
var Backtory = require("backtory-sdk");
exports.handler = function(requestBody, context) {
var userId = context.getSecurityContext().userId;
var Book = Backtory.Object.extend("Book");
var query = new Backtory.Query(Book);
query.find({
success: function(list) {
context.succeed(list);
},
error: function(error) {
context.fail(error);
}
});
};
در این حالت اگر به هر دلیلی درخواست find به سرور با مشکل روبرو شود، کالبک مربوط به error صدا می شود و بلافاصله پاسخ از طریق context.fail بر گردانده می شود. در نتیجه درخواست دیگر timeout نمی شود.