diff --git a/BMA.EHR.Leave/BMA.EHR.Leave.csproj b/BMA.EHR.Leave/BMA.EHR.Leave.csproj
index c462d37a..63814a03 100644
--- a/BMA.EHR.Leave/BMA.EHR.Leave.csproj
+++ b/BMA.EHR.Leave/BMA.EHR.Leave.csproj
@@ -37,6 +37,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
@@ -49,6 +50,7 @@
+
diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs
index 5d644061..eb51780d 100644
--- a/BMA.EHR.Leave/Controllers/LeaveController.cs
+++ b/BMA.EHR.Leave/Controllers/LeaveController.cs
@@ -1,5 +1,4 @@
-using Amazon.S3.Model;
-using BMA.EHR.Application.Repositories;
+using BMA.EHR.Application.Repositories;
using BMA.EHR.Application.Repositories.Commands;
using BMA.EHR.Application.Repositories.Leaves.LeaveRequests;
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
@@ -15,12 +14,10 @@ using BMA.EHR.Leave.Service.DTOs.CheckIn;
using BMA.EHR.Leave.Service.DTOs.DutyTime;
using BMA.EHR.Leave.Service.DTOs.LeaveRequest;
using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.ObjectPool;
using Newtonsoft.Json;
-using Org.BouncyCastle.Ocsp;
using RabbitMQ.Client;
-using Serilog;
using Swashbuckle.AspNetCore.Annotations;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
@@ -58,6 +55,10 @@ namespace BMA.EHR.Leave.Service.Controllers
private readonly string _bucketName = "check-in";
+ private readonly ObjectPool _objectPool;
+ private readonly string _fakeCheckInQueue = "fake-bma-checkin-queue";
+ private readonly string _realCheckInQueue = "bma-checkin-queue";
+
#endregion
#region " Constuctor and Destructor "
@@ -75,7 +76,8 @@ namespace BMA.EHR.Leave.Service.Controllers
AdditionalCheckRequestRepository additionalCheckRequestRepository,
UserCalendarRepository userCalendarRepository,
CommandRepository commandRepository,
- LeaveRequestRepository leaveRequestRepository)
+ LeaveRequestRepository leaveRequestRepository,
+ ObjectPool objectPool)
{
_dutyTimeRepository = dutyTimeRepository;
_context = context;
@@ -92,6 +94,7 @@ namespace BMA.EHR.Leave.Service.Controllers
_commandRepository = commandRepository;
_leaveRequestRepository = leaveRequestRepository;
+ _objectPool = objectPool;
}
#endregion
@@ -428,6 +431,7 @@ namespace BMA.EHR.Leave.Service.Controllers
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task> CheckInAsync([FromForm] CheckTimeDto data)
{
+ // prepare data and convert request body and send to queue
var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId);
var currentDate = DateTime.Now;
var checkFileBytes = new byte[0];
@@ -458,29 +462,48 @@ namespace BMA.EHR.Leave.Service.Controllers
Token = AccessToken ?? ""
};
- // create connection
- var factory = new ConnectionFactory()
+ var channel = _objectPool.Get();
+ try
{
- HostName = _configuration["Rabbit:Host"],
- UserName = _configuration["Rabbit:User"],
- Password = _configuration["Rabbit:Password"],
- };
+ channel.QueueDeclare(queue: _realCheckInQueue, durable: true, exclusive: false, autoDelete: false, arguments: null);
+ var serializedObject = JsonConvert.SerializeObject(checkData);
+ var body = Encoding.UTF8.GetBytes(serializedObject);
- // create channel
- using var connection = factory.CreateConnection();
- using var channel = connection.CreateModel();
- channel.QueueDeclare(queue: "checkin-queue", durable: false, exclusive: false, autoDelete: false, arguments: null);
+ channel.BasicPublish(exchange: "",
+ routingKey: _realCheckInQueue,
+ basicProperties: null,
+ body: body);
- // แปลง Object เป็น JSON สตริง
- var serializedObject = JsonConvert.SerializeObject(checkData);
+ return Success(new { date = currentDate });
+ }
+ finally
+ {
+ _objectPool.Return(channel);
+ }
- // แปลง JSON สตริงเป็น byte array
- var body = Encoding.UTF8.GetBytes(serializedObject);
+ //// create connection
+ //var factory = new ConnectionFactory()
+ //{
+ // HostName = _configuration["Rabbit:Host"],
+ // UserName = _configuration["Rabbit:User"],
+ // Password = _configuration["Rabbit:Password"],
+ //};
- channel.BasicPublish(exchange: "", routingKey: "checkin-queue", basicProperties: null, body: body);
- Console.WriteLine($"Send to Queue: {serializedObject}");
+ //// create channel
+ //using var connection = factory.CreateConnection();
+ //using var channel = connection.CreateModel();
+ //channel.QueueDeclare(queue: "checkin-queue", durable: false, exclusive: false, autoDelete: false, arguments: null);
- return Success(new { date = currentDate });
+ //// แปลง Object เป็น JSON สตริง
+ //var serializedObject = JsonConvert.SerializeObject(checkData);
+
+ //// แปลง JSON สตริงเป็น byte array
+ //var body = Encoding.UTF8.GetBytes(serializedObject);
+
+ //channel.BasicPublish(exchange: "", routingKey: "checkin-queue", basicProperties: null, body: body);
+ //Console.WriteLine($"Send to Queue: {serializedObject}");
+
+ //return Success(new { date = currentDate });
}
///
@@ -491,7 +514,7 @@ namespace BMA.EHR.Leave.Service.Controllers
/// เมื่อทำรายการสำเร็จ
/// ไม่ได้ Login เข้าระบบ
/// เมื่อเกิดข้อผิดพลาดในการทำงาน
- [HttpPost("check-in"), DisableRequestSizeLimit]
+ [HttpPost("fake-check-in"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
@@ -499,30 +522,26 @@ namespace BMA.EHR.Leave.Service.Controllers
public ActionResult FakeCheckIn([FromBody] FakeCheckTimeDto data)
{
var currentDate = DateTime.Now;
-
- // create connection
- var factory = new ConnectionFactory()
+ var channel = _objectPool.Get();
+ try
{
- HostName = _configuration["Rabbit:Host"],
- UserName = _configuration["Rabbit:User"],
- Password = _configuration["Rabbit:Password"],
- };
+ channel.QueueDeclare(queue: _fakeCheckInQueue, durable: true, exclusive: false, autoDelete: false, arguments: null);
- // create channel
- using var connection = factory.CreateConnection();
- using var channel = connection.CreateModel();
- channel.QueueDeclare(queue: "fake-checkin-queue", durable: false, exclusive: false, autoDelete: false, arguments: null);
+ var serializedObject = JsonConvert.SerializeObject(data);
- // แปลง Object เป็น JSON สตริง
- var serializedObject = JsonConvert.SerializeObject(data);
+ var body = Encoding.UTF8.GetBytes(serializedObject);
- // แปลง JSON สตริงเป็น byte array
- var body = Encoding.UTF8.GetBytes(serializedObject);
+ channel.BasicPublish(exchange: "",
+ routingKey: _fakeCheckInQueue,
+ basicProperties: null,
+ body: body);
- channel.BasicPublish(exchange: "", routingKey: "fake-checkin-queue", basicProperties: null, body: body);
- Console.WriteLine($"Send to Queue: {serializedObject}");
-
- return Success(new { date = currentDate });
+ return Success(new { date = currentDate });
+ }
+ finally
+ {
+ _objectPool.Return(channel);
+ }
}
///
diff --git a/BMA.EHR.Leave/Extensions/RabbitMqServiceCollectionExtensions.cs b/BMA.EHR.Leave/Extensions/RabbitMqServiceCollectionExtensions.cs
new file mode 100644
index 00000000..1dd9d3e8
--- /dev/null
+++ b/BMA.EHR.Leave/Extensions/RabbitMqServiceCollectionExtensions.cs
@@ -0,0 +1,20 @@
+using BMA.EHR.Leave.Service.Services;
+using Microsoft.Extensions.ObjectPool;
+using RabbitMQ.Client;
+
+namespace BMA.EHR.Leave.Service.Extensions
+{
+ public static class RabbitMqServiceCollectionExtensions
+ {
+ public static IServiceCollection AddRabbitMqConnectionPooling(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddSingleton();
+ services.AddSingleton>(sp =>
+ {
+ var provider = sp.GetRequiredService();
+ return provider.Create(new RabbitMqConnectionPoolPolicy(configuration));
+ });
+ return services;
+ }
+ }
+}
diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs
index 715a9c8b..217aedaa 100644
--- a/BMA.EHR.Leave/Program.cs
+++ b/BMA.EHR.Leave/Program.cs
@@ -21,6 +21,7 @@ using System.Transactions;
using BMA.EHR.Leave.Service.Filters;
using Hangfire.Common;
using BMA.EHR.Application.Repositories.Leaves.TimeAttendants;
+using BMA.EHR.Leave.Service.Extensions;
var builder = WebApplication.CreateBuilder(args);
{
@@ -100,6 +101,8 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
+ builder.Services.AddRabbitMqConnectionPooling(builder.Configuration);
+
// Add Hangfire services.
var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection");
diff --git a/BMA.EHR.Leave/Services/RabbitMqConnectionPoolPolicy.cs b/BMA.EHR.Leave/Services/RabbitMqConnectionPoolPolicy.cs
new file mode 100644
index 00000000..296c596c
--- /dev/null
+++ b/BMA.EHR.Leave/Services/RabbitMqConnectionPoolPolicy.cs
@@ -0,0 +1,53 @@
+using Microsoft.Extensions.ObjectPool;
+using RabbitMQ.Client;
+
+namespace BMA.EHR.Leave.Service.Services;
+
+public class RabbitMqConnectionPoolPolicy : IPooledObjectPolicy
+{
+ #region " Fields "
+
+ private readonly IConnection _connection;
+ private readonly IConfiguration _configuration;
+
+ #endregion
+
+ #region " Constructor and Destructor "
+
+ public RabbitMqConnectionPoolPolicy(IConfiguration configuration)
+ {
+ _configuration = configuration;
+
+ var factory = new ConnectionFactory()
+ {
+ HostName = _configuration["Rabbit:Host"],
+ UserName = _configuration["Rabbit:User"],
+ Password = _configuration["Rabbit:Password"]
+ };
+ _connection = factory.CreateConnection();
+ }
+
+ #endregion
+
+ #region " Methods "
+
+ public IModel Create()
+ {
+ return _connection.CreateModel();
+ }
+
+ public bool Return(IModel obj)
+ {
+ if (obj.IsOpen)
+ {
+ return true;
+ }
+ else
+ {
+ obj.Dispose();
+ return false;
+ }
+ }
+
+ #endregion
+}
diff --git a/BMA.EHR.Solution.sln b/BMA.EHR.Solution.sln
index a65e792b..44e71937 100644
--- a/BMA.EHR.Solution.sln
+++ b/BMA.EHR.Solution.sln
@@ -31,7 +31,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BMA.EHR.Insignia", "BMA.EHR
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BMA.EHR.Leave", "BMA.EHR.Leave\BMA.EHR.Leave.csproj", "{B06C5612-2346-44B1-9D50-F8373885BD88}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BMA.EHR.CheckInConsumer", "BMA.EHR.CheckInConsumer\BMA.EHR.CheckInConsumer.csproj", "{B36F7E5A-2745-42B5-8493-D648EF8CC43C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BMA.EHR.CheckInConsumer", "BMA.EHR.CheckInConsumer\BMA.EHR.CheckInConsumer.csproj", "{B36F7E5A-2745-42B5-8493-D648EF8CC43C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "k6_scripts", "k6_scripts", "{367D82BE-0AF9-4739-A45D-C88194E7820C}"
+ ProjectSection(SolutionItems) = preProject
+ checkin_load_test.js = checkin_load_test.js
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/checkin_load_test.js b/checkin_load_test.js
new file mode 100644
index 00000000..be5bbd0f
--- /dev/null
+++ b/checkin_load_test.js
@@ -0,0 +1,40 @@
+import http from 'k6/http';
+import { check, sleep } from 'k6';
+
+export let options = {
+ scenarios: {
+ constant_rate: {
+ executor: 'constant-arrival-rate',
+ rate: 1000, // จำกัดที่ 1000 requests ต่อวินาที
+ timeUnit: '1s',
+ duration: '5m', // ใช้เวลารันทดสอบ 5 นาที
+ preAllocatedVUs: 50,
+ },
+ },
+};
+
+export default function () {
+ // ข้อมูล payload ที่จะส่งไปยัง server
+ let payload = JSON.stringify({
+ lat: 1,
+ lon: 1,
+ poi: 'FPT',
+ isLocation: true
+ });
+
+ // ตัวเลือก headers
+ let headers = {
+ 'Content-Type': 'application/json',
+ };
+
+ // ส่ง POST request
+ let response = http.post('https://localhost:7283/api/v1/leave/fake-check-in', payload, { headers: headers });
+
+ // ตรวจสอบการตอบสนอง
+ check(response, {
+ 'is status 200': (r) => r.status === 200,
+ });
+
+ // หน่วงเวลา 1 วินาที
+ sleep(1);
+}