Reputation: 472
I have this ASP.NET Core 2.1 web application. The admin gets online game codes from the page below. Selecting the game (Razer Gold Pin), quantity and email to send those codes.
A Rest API call is made for the purchase. There is a limit for purchasing, a maximum of 200 purchases can be made at one time.
At first, there wasn't much to buy so there was no problem. But the demand has increased, when there are 10 thousand, 20 thousand purchases, it is necessary to purchase from this screen for hours. I wonder can we make a large number of purchases without waiting in front of the screen with scheduling?
Here is the the Javascript in the cshtml:
$("#btn_add").html(
'<span class="spinner-border spinner-border-sm" role="status" id="spinner" aria-hidden="true"></span> Loading...'
);
var selText = $("#validationCustom04").val();
var gameName = $("#validationCustom04 option:selected").text();
var quantity = $("#quantity").val();
var email = $("#email").val();
var price = $("#total").val();
var company = $("#validationCustom05").val();
if ($("#total").val() !== '') {
price = $("#total").val();
}
var serviceUrl = '/GameBanks/A101PinAsync?quantity=' + quantity + '&game=' + selText + '&email=' + email + '&prc=' + price + '&gameName=' + gameName + '&company=' + company;
$.ajax({
type: "GET",
url: serviceUrl,
dataType: 'json',
success: function (data) {
//alert(JSON.stringify(data));
ShowResult(data);
$("#spinner").remove();
$("#btn_add").html('Add');
}, error: function (xhr, status, error) {
$("#spinner").remove();
$("#btn_add").html('Add');
var errorMessage = xhr.responseText;
alert('Error - ' + errorMessage);
}
});
Here is the controller method:
[HttpGet]
public async Task<IActionResult> A101PinAsync(int quantity, string game, string email, int prc, string gameName, string company)
{
var price = 0;
try
{
string result = null;
var dList = new DemoList();
if (prc > 0)
{
price = prc;
}
var games = new Game { Product = game, Quantity = quantity };
var content = await games.CallGameAsync("Test", "12345", game, quantity, company);
var deserializedResult = JObject.Parse(content);
var root = JsonConvert.DeserializeObject<Root>(content);
if ((string)deserializedResult["coupons"]?[0]?["Serial"] == null)
{
result = result + gameName + ":" + (string)deserializedResult["Message"] + "\n";
}
else
{
foreach (var coupon in root.coupons)
{
result = result + gameName + " Serial:" + coupon.Serial + " Pin:" + coupon.Pin + "\n";
}
}
dList.gameList = result;
// NLOG
NLogPin(logger, User.Identity.Name, DateTime.Now, result, email);
return new JsonResult(dList);
}
catch (Exception e)
{
// NLOG
NLog(logger2, "OyunPalas " + e.Message, DateTime.UtcNow,0);
return StatusCode(500,e.Message);
}
}
Here is the Web API calling method:
public class Game
{
public string Username { get; set; }
public string Password { get; set; }
public string Product { get; set; }
public int Quantity { get; set; }
public string ShopNo { get; set; }
private static readonly Logger logger2 = LogManager.GetLogger("exceptionFile");
public async Task<string> CallGameAsync(string username, string password, string product, int quantity, string company)
{
try
{
HttpWebRequest request = null;
request = (HttpWebRequest)WebRequest.Create("http://111.111.111.111:1907//api/v2/web/purchase");
var svcCredentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(username + ":" + password));
request.Headers.Add("Authorization", "Basic " + svcCredentials);
request.Method = "POST";
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("productCode", product),
new KeyValuePair<string, string>("quantity", quantity.ToString()),
new KeyValuePair<string, string>("shopNo", company),
new KeyValuePair<string, string>("safeNo", company),
new KeyValuePair<string, string>("cashierNo", company)
});
var urlEncodedString = await content.ReadAsStringAsync();
using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
await streamWriter.WriteAsync(urlEncodedString);
}
var httpResponse = (HttpWebResponse) (await request.GetResponseAsync());
var response = new HttpResponseMessage
{
StatusCode = httpResponse.StatusCode,
Content = new StreamContent(httpResponse.GetResponseStream()),
};
//Read response
var htmlResponse = await response.Content.ReadAsStringAsync();
return htmlResponse;
}
catch (Exception e)
{
//NLOG
NLog(logger2, "Test" + e.Message);
throw;
}
}
public void NLog(Logger logger, string user)
{
var sb = new StringBuilder();
sb.AppendLine("Test: " + user);
logger.Info(sb.ToString());
}
}
I have to pass product (game type) to the job. I read the https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/more-about-jobs.html#jobdatamap documentation but not fully understand how to use in my case. And I wonder if I can stop/cancel the job from this page before its end time?
I really appreciate any help you can provide. I'm sorry if the question is too silly as I have no experience with this scheduling.
I thought of a solution like this, but I'm not sure if it's viable.
The user will enter which game, how many they want to buy, and their e-mail address on the screen.
This data will be saved in a table (BulkRequest) containing this information and the status field in the SQL database. (game: ABC, quantity:20000, status:0)
One IHostedService will get the saved data in a schedule which status=0 from the BulkRequest table and the save requests with status information in a new table (GameRequests) in multiples of 100. (game: ABC, quantity:100, status:0)
Another IHostedService will get the records in 10 minutes schedule which status=0 from the GameRequests table and make the 3rd party API call. Write a response to another table (GameResponses) and set the status=1 if there is no error.
When all requests are completed (status = 1), the status in the BulkRequest table will also be updated to 1.
A third IHostedService will check the status of BulkRequest, if status=1 then maybe a fourth service will prepare a file and send the responses to the e-mail.
Upvotes: 0
Views: 1274
Reputation: 2605
I do suggest reviewing message queuing tools such RabbitMQ. With this architecture you will do all the jobs using messaging protocols with no need for "scheduling" and any "x minutes schedule" s would be ommited. Also it is more stable and error handling could be done using best practice standards. More important, it is scalable and you can have multiple hostedServices even on different servers process items in queue:
The solution you proposed is doing the same job with an ad-hoc process. In fact you are reinventing the wheel. Hope it helps.
Upvotes: 1