мне кажется, что производительность внедренного EF внутри сервисной области действительно низкая.
Существует высокая вероятность того, что я неправильно использую EF в этом случае, но я не смог найти правильное описание / документацию о том, как использовать EF в этом случае (добавить его в настраиваемую размещенную службу).
В EasyRabbit / startup.cs (ConfigureServices) зарегистрированы следующие сервисы:
// db
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer("Server=localhost; Database=RabbitTest; MultipleActiveResultSets=true; User ID=sa; Password=Admin1234");
});
// default REST Api
services.AddMvc();
// Configuration for Rabbit connector
services.Configure<RabbitConfig>(Configuration.GetSection("RabbitConfig"));
// Rabbit Connector
services.AddSingleton<RabbitConnector>();
// Subscriber which listens if some new message arrives
services.AddSingleton<IHostedService, GenericHostedSubscriber<CalculatorInputs>>();
// Every arival message is then processed in following scope
// ApplicationDbContext dbContext is injected into this RabbitSubscribers.Adder
services.AddScoped<IScopedProcessingService<CalculatorInputs>, RabbitSubscribers.Adder>();
Теперь, если я правильно понимаю, он должен автоматически создавать область контекста БД для каждой новой области RabbitSubscribeers.Adder.
Проблема в том, что таким образом он может потреблять / обрабатывать в среднем только около 50 сообщений в секунду.
Когда я комментирую все операции с db (AddAsync и SaveChangesAsync) из следующего кода, тогда он может обрабатывать около 2000 сообщений в секунду, что неплохо, но без db для меня бесполезно: (
namespace EasyRabbit.RabbitSubscribers
{
public class Adder : IScopedProcessingService<CalculatorInputs>
{
private ILogger<Adder> _logger;
private ApplicationDbContext _db;
public Adder(ApplicationDbContext dbContext, ILogger<Adder> logger)
{
_logger = logger;
_db = dbContext;
}
public async Task HandleMessageAsync(CalculatorInputs message)
{
Console.WriteLine($"Calculator: [{message.FirstNumber}] + [{message.SecondNumber}] = {message.FirstNumber + message.SecondNumber}");
await _db.Calculations.AddAsync(new Calculation()
{
FirstNumber = message.FirstNumber,
SecondNumber = message.SecondNumber,
Result = message.FirstNumber + message.SecondNumber
});
await _db.SaveChangesAsync();
}
}
}
Когда я попытался заменить EF на System.Data.SqlClient (следующий фрагмент кода, непосредственно используемый в Adder.cs), он мог обрабатывать в среднем 1100 сообщений в секунду. Но работать с БД, как это, действительно неудобно: - /
public static class DB
{
private static string _connectionString = "Server=localhost; Database=RabbitTest; MultipleActiveResultSets=true; User ID=sa; Password=Admin1234";
public static void AddRecord(MyDBObject myDBObject)
{
using (SqlConnection con = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("insert into test (FirstNumber, SecondNumber, Result) values (@FirstNumber, @SecondNumber, @Result)", con))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("@FirstNumber", myDBObject.FirstNumber);
cmd.Parameters.AddWithValue("@SecondNumber", myDBObject.SecondNumber);
cmd.Parameters.AddWithValue("@Result", myDBObject.Result);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
}
}
public class MyDBObject
{
public int FirstNumber { get; set; }
public int SecondNumber { get; set; }
public int Result { get; set; }
}
Весь код можно найти на https://github.com/suchoss/uServiceChasis
Спасибо.
Шаги для воспроизведения
- Текущий репозиторий клонов из: https://github.com/suchoss/uServiceChasis
- Установить RabbitMQ из: https://www.rabbitmq.com/#getstarted
- Установить MSSQL
- Измените соединитель на DB в EasyRabbit / startup.cs (строка 31)
- Перейдите в папку RandomNumberPairGenerator и выполните команду dotnet run на несколько секунд, затем вы можете отменить ее с помощью ctrl + c (она создает некоторые сообщения в очередь RabbitMQ)
- Запустите проект EasyRabbit и посмотрите, сколько сообщений в секунду обрабатывается
*. Если у вас установлено управление RabbitMQ, вы можете посмотреть производительность на http://localhost:15672 (логин по умолчанию: гость; пароль: гость)
Отредактировано:
Вот текущие измеренные характеристики:
- EF async внутри adder.cs - 230 / с
- EF без асинхронности внутри adder.cs - 100 / с
- ADO.NET async - 1300 / с
- ADO.NET без асинхронной работы - 150 / с
- EF вставляет контекст асинхронно - 40 / с
- Внедренный EF контекст без асинхронности - 65 / с
сценарии 1., 2., 3., 4. иметь adder.cs как синглтон
сценарии 5., 6. у них есть adder.cs в качестве сервиса