cga*_*uss 9 javascript ajax object node.js
当我们启动交易时,我会运行一个可以看到50-70个请求/秒的优惠券网站(我们每天多次推出20多个交易).当交易上线时,我们的用户按下按钮即可获得特定产品的优惠券,该优惠券通过ajax https请求提供独特的优惠券代码.每张优惠券只能兑换一次.
我的问题是,在这些时候如此高的流量,可以将相同的优惠券分发给多个用户.这是不好的,因为只有其中一个人能够实际兑换优惠券,留给另一方的糟糕用户体验.
我将所有优惠券信息存储在IBM Bluemix托管的node.js服务器上的内存中的对象中.我想这可以让我快速处理请求.
我如何存储优惠券信息:
global.coupons = {};
//the number of coupons given for each product
global.given = {};
/* Setting the coupon information */
//....I query my database for the products to be given today
for(var i = 0; i < results.length; i++){
var product = results[i];
//add only the coupons to give today to the array
var originalCoups = product.get('coupons');
var numToTake = product.get('toGivePerDay');
if(product.get('givenToday') > 0){
numToTake = numToTake - product.get('givenToday');
}
// Example coupon array [["VVXM-Q577J2-XRGHCC","VVLE-JJR364-5G5Q6B"]]
var couponArray = originalCoups[0].splice(product.get('given'), numToTake);
//set promo info
global.coupons[product.id] = couponArray;
global.given[product.id] = 0;
}
Run Code Online (Sandbox Code Playgroud)
处理优惠券要求:
app.post('/getCoupon', urlencodedParser, function(req, res){
if (!req.body) return res.status(400).send("Bad Request");
if (!req.body.category) return res.status(200).send("Please Refresh the Page.");
//Go grab a coupon
var coupon = getUserACoupon(req.body.objectId);
res.type('text/plain');
res.status(200).send(coupon);
if(coupon != "Sold Out!" && coupon != "Bad Request: Object does not exist."){
//Update user & product analytics
setStatsAfterCouponsSent(req.body.objectId, req.body.sellerProduct, req.body.userEmail, req.body.purchaseProfileId, coupon, req.body.category);
}
});
//getCoupon logic
function getUserACoupon(objectId){
var coupToReturn;
// coupon array for the requseted product
var coupsArray = global.coupons[objectId];
if(typeof coupsArray != 'undefined'){
// grab the number of coupons already given for this product and increase by one
var num = global.given[objectId];
global.given[objectId] = num+1;
if(num < coupsArray.length){
if(coupsArray[num] != '' && typeof coupsArray[num] != 'undefined' && coupsArray[num] != 'undefined'){
coupToReturn = coupsArray[num];
}else{
console.log("Error with the coupon for "+objectId + " the num is " + num);
coupToReturn = "Sold Out!";
wasSoldOut(objectId);
}
}else{
console.log("Sold out "+objectId+" with num " + num);
coupToReturn = "Sold Out!";
wasSoldOut(objectId);
}
}else{
coupToReturn = "Bad Request: Object does not exist.";
wasSoldOut(objectId);
}
return coupToReturn;
}
Run Code Online (Sandbox Code Playgroud)
我对node.js服务器及其运行方式没有太多了解.
一如既往,感谢您的帮助!
问题在于Node的非阻塞/异步特性.从同时请求调用相同的函数不会等待彼此完成.许多请求进入并同时访问全局代码数组.
您多次发出相同的代码,因为计数器会同时增加多个请求,因此可能会发生多个请求看到相同的计数器状态.
管理并发问题的一种方法是一次只允许一次访问(getUserACoupon
在您的情况下),因此使用优惠券的执行部分是同步的或互斥的.实现此目的的一种方法是锁定机制,因此当一个请求获得对锁的访问权时,进一步的请求将等待直到锁被释放.在伪代码中,它看起来像这样:
wait until lock exists
create lock
if any left, consume one coupon
remove lock
Run Code Online (Sandbox Code Playgroud)
但是这种方法违背了Node的非阻塞性质,并且还引入了在多个请求等待时释放时获取锁定的问题.
更好的方法是更有可能是队列系统.它应该工作,因此代码不会在请求时被消耗,而是作为可调用的方式放入队列中,等待启动.您可以读取队列的长度并停止接受新请求("售罄"),但是,这仍然会在全局队列/计数器上并发,因此您最终可能会获得比优惠券更多的排队项目,但这是这不是问题,因为队列将被同步处理,因此可以准确地确定何时达到分配的优惠券的数量,并且如果有的话,只是给其余部分"售罄",更重要的是,确保每个代码仅被提供一次.
使用temporal,可以很容易地创建线性,延迟的任务列表:
var temporal = require("temporal");
global.queues = {};
Run Code Online (Sandbox Code Playgroud)
app.post('/getCoupon', urlencodedParser, function(req, res){
if (!req.body) return res.status(400).send("Bad Request");
if (!req.body.category) return res.status(200).send("Please Refresh the Page.");
// Create global queue at first request or return to it.
var queue;
if( !global.queues[req.body.objectId] ) {
queue = global.queues[req.body.objectId] = temporal.queue([]);
}
else {
queue = global.queues[req.body.objectId];
}
// Prevent queuing after limit
// This will be still concurrent access so in case of large
// number of requests a few more may end up queued
if( global.given[objectId] >= global.coupons[objectId].length ) {
res.type('text/plain');
res.status(200).send("Sold out!");
return;
}
queue.add([{
delay: 200,
task: function() {
//Go grab a coupon
var coupon = getUserACoupon(req.body.objectId);
res.type('text/plain');
res.status(200).send(coupon);
if(coupon != "Sold Out!" && coupon != "Bad Request: Object does not exist."){
//Update user & product analytics
setStatsAfterCouponsSent(req.body.objectId, req.body.sellerProduct, req.body.userEmail, req.body.purchaseProfileId, coupon, req.body.category);
}
}
}]);
});
Run Code Online (Sandbox Code Playgroud)
这里的一个关键点是temporal执行任务顺序累加延迟,因此如果延迟更多则需要运行任务,则一次只能访问一个计数器/代码数组.
您可以使用定时队列处理基于此逻辑实现您自己的解决方案,但是时间似乎值得一试.
归档时间: |
|
查看次数: |
1126 次 |
最近记录: |