Изначально отказаться от стандартных СУБД (like MySQL), ибо в базу ежесекундно (а то и чащще!) нужно скидывать текущие значения коэффициентов с тысяч событий. (К примеру - в субботний вечер в час пик на разных конторах транслируется 200 событий на разные виды спорта. Нужно например парсить 30 контор. 200*30 = 6 000 трансляций. А контор, необходимых для сканирования - гораздо больше 20). Конечно же коэффициенты обновляются не каждую секунду. Но на динамичных видах спорта - очень часто. И нужно рассчитывать на то что в такую базу будет прилетать 6000 запросов обновления в секунду.
Продолжение п.1: вместо стандартной БД использовать "In memory DB", т.е. что то, что висит в оперативке и обновляет данные максимально быстро. Сохранность данных здесь вообще не важна, ибо через 3 секунды актуальность данных уже пропадает.
С одной стороны в эту базу будут писать данные парсеры, с другой стороны ежесекундно к этой базе ежесекундно будет обращаться функция построения итоговой таблицы тех самых арбитражных ситуаций. И уже к этой итоговой таблице будет обращаться вебсервер и по выбранным фильтрам пользователя будет показывать ему таблицу интересующих его вилок/валуев (тех самых арбитражных ситуаций). Кстати - у пользователя будет открыта страница, которая будет рефрешиться тоже раз в секунду. А учитывая что пользователей может быть тысяча - то и таких запросов тоже будет прилетать 1 000 в секунду.
Что касается самого парсинга. Раньше каждую контору парсили по своему: какая то контора обновляла данные через сокеты, какие то - обычными http-запросами. И все существующие подобные сканеры посылали свои запросы через сокеты, или формировали свои http запросы. Но сегодня это все уже работает плохо, ибо конторы защищаются от парсинга разными методами. И самый простой и самый универсальный способ парсить данные - это парсинг браузером. Т.е. вы просто открываете в браузере страницу события и парсите ее. Но конечно же - за такую универсальность придется заплатить ресурсами. Каждая такая страница будет занимать мегабайты в оперативке. Предположим одна страница в среднем занимае 20 МБ оперативки. Тогда предполагаемые 6 000 открытых страниц займут 6 000 * 20 МБ = 120 000 МБ = 120 ГБ оперативки. Конечно, это нужно делать на
using EggCloud.Code;
using EggCloud.Data.Abstract;
using EggCloud.Model.Entities;
using EggCloud.Services.Abstraction;
using EggCloud.ViewModels.Auth;
using Microsoft.AspNetCore.Mvc;
namespace EggCloud.Controllers;
/// <summary>
/// Контроллер аутентификации
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IAuthService authService;
private readonly IUserRepository userRepository;
/// <summary>
/// Конструктор
/// </summary>
/// <param name="authService">сервис авторизации</param>
/// <param name="userRepository">пользовательский репозиторий</param>
public AuthController(IAuthService authService, IUserRepository userRepository)
{
this.authService = authService;
this.userRepository = userRepository;
}
/// <summary>
/// Вход в систему
/// </summary>
/// <param name="model">Модель входа в систему</param>
/// <returns></returns>
[HttpPost("login")]
public ActionResult<AuthData> Post([FromBody] LoginViewModel model)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var user = userRepository.GetSingle(u => u.Email == model.Email);
if (user == null)
{
return BadRequest(new { error = "no user with this email" });
}
var passwordValid = authService.VerifyPassword(model.Password, user.Password);
if (!passwordValid)
{
return BadRequest(new { error = "invalid password" });
}
return authService.GetAuthData(user.Id, user.IsAdmin,user.Username);
}
/// <summary>
/// Регистрация в системе
/// </summary>
/// <param name="model">Модель регистрации</param>
/// <returns></returns>
[HttpPost("register")]
public ActionResult<AuthData> Post([FromBody] RegisterViewModel model)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var emailUniq = userRepository.isEmailUniq(model.Email);
if (!emailUniq) return BadRequest(new { error = "user with this email already exists" });
var user = new User
{
Username = model.Username,
Email = model.Email,
Password = authService.HashPassword(model.Password),
RateId = Rate.Default.Id,
PaidUntil = Utils.GetTimeStamp()
};
userRepository.Add(user);
userRepository.Commit();
return authService.GetAuthData(user.Id, user.IsAdmin, user.Username);
}
}