Почему веб-сервис asp.net занимает так много памяти? - PullRequest
2 голосов
/ 16 апреля 2019

У меня есть веб-сервис .net (Web API 2), который принимает запросы на получение данных из базы данных. Возвращаемые данные не очень велики, обычно 1-3 кб (примерно 1-15 строк в базе данных). Я настроил его в IIS и провел несколько массовых тестов, где он много раз попадал в течение часа или около того, поэтому он получал несколько тысяч запросов. Когда я смотрю на w3wp.exe (рабочий процесс IIS), объем памяти продолжает увеличиваться, пока не достигнет более 5 ГБ, а загрузка ЦП не достигнет 100%, а затем веб-служба перестанет работать. Сервер имеет 16 ГБ ОЗУ, мы только что увеличили его с 8. Нормально ли 5 ​​ГБ? Это кажется большим, я бы предположил, что сборка мусора справится с этим лучше. Я не очень опытен в использовании монитора производительности или других вещей для устранения проблем, подобных этой.

Любые предложения о том, что я могу посмотреть? Может ли быть утечка памяти? Стоит ли пытаться перезапускать пул приложений при достижении определенного объема памяти?

Обновление - у нас есть другой веб-сервис .net, который даже не обращается к базе данных, внешним файлам или чему-либо еще и работает так же - память просто увеличивается. Наш план состоит в том, чтобы, вероятно, настроить пул приложений для каждого перезапуска каждые x минут или когда он достигает определенного объема памяти.

Вот пример типичной функции в API:

[Route("patient/{patientKey}/activations")]
public async Task<IHttpActionResult> GetActivations(int patientKey)
{
     try
     {
          ActivationList actList = new ActivationList();

          actList.Activations = await _actProc.GetByPatient(patientKey);

          return Ok<ActivationList>(actList);
     }
     catch (Exception ex)
     {
         return new BadRequestWithInfoResult(Translators.makeXML<ErrorReturn>(CreateError(ex)));
     }
}

public async Task<List<Activation>> GetByPatient(long patientKey)
{
     using (var dbConn = _database.CreateConnection())
     {
          List<DBActivation> lst_Activation = await dbConn.FetchAsync<DBActivation>("select * from fact.Activation where PatientKey = " + patientKey.ToString());

          List<Activation> Activations = lst_Activation.Select(x => _mapper.Map<Activation>(x)).ToList<Activation>();              

          return Activations;
     }
}

Ответы [ 3 ]

0 голосов
/ 17 апреля 2019

Кажется, что это тупик из-за неиспользования ConfigureAwait в библиотеке,

[Route("patient/{patientKey}/activations")]
public async Task<IHttpActionResult> GetActivations(int patientKey)
{
     try
     {
          ActivationList actList = new ActivationList();

          actList.Activations = await _actProc.GetByPatient(patientKey).ConfigureAwait(false);

          return Ok<ActivationList>(actList);
     }
     catch (Exception ex)
     {
         return new BadRequestWithInfoResult(Translators.makeXML<ErrorReturn>(CreateError(ex)));
     }
}

public async Task<List<Activation>> GetByPatient(long patientKey)
{
     using (var dbConn = _database.CreateConnection())
     {
          List<DBActivation> lst_Activation = await dbConn.FetchAsync<DBActivation>("select * from fact.Activation where PatientKey = " + patientKey.ToString()).ConfigureAwait(false);

          List<Activation> Activations = lst_Activation.Select(x => _mapper.Map<Activation>(x)).ToList<Activation>();              

          return Activations;
     }
}
0 голосов
/ 18 апреля 2019

OP answer - Я нашел какой-то оскорбительный код (и нет, для такого веб-сервиса, как этот, ненормально использовать больше 5 ГБ памяти). Некоторое время назад, пытаясь создать свои собственные пространства имен в xml, возвращенном веб-службой, я добавил класс CustomNamespaceXmlFormatter, указанный в этом посте, в ответе @Konamiman: Удалить пространство имен в xml Комментарий ниже это упоминает проблему утечки памяти. Хотя ANTS Memory Profiler никогда не показывал, что мой веб-сервис генерирует несколько динамических сборок, я, тем не менее, обновил код, чтобы использовать нечто похожее на одноэлементный шаблон для создания экземпляров XmlSerializer, как показано ниже, и теперь мое использование памяти находится под контролем (и фактически снижается). когда закончится обработка запросов!).

public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
    private readonly string defaultRootNamespace;

    public CustomNamespaceXmlFormatter() : this(string.Empty)
    {
    }

    public CustomNamespaceXmlFormatter(string defaultRootNamespace)
    {
        this.defaultRootNamespace = defaultRootNamespace;
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        if (type == typeof(String))
        {
            //If all we want to do is return a string, just send to output as <string>value</string>
            return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
        }
        else
        {
            XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)type.GetCustomAttributes(typeof(XmlRootAttribute), true)[0];
            if (xmlRootAttribute == null)
                xmlRootAttribute = new XmlRootAttribute(type.Name)
                {
                    Namespace = defaultRootNamespace
                };
            else if (xmlRootAttribute.Namespace == null)
                xmlRootAttribute = new XmlRootAttribute(xmlRootAttribute.ElementName)
                {
                    Namespace = defaultRootNamespace
                };

            var xns = new XmlSerializerNamespaces();
            xns.Add(string.Empty, xmlRootAttribute.Namespace);

            return Task.Factory.StartNew(() =>
            {
                //var serializer = new XmlSerializer(type, xmlRootAttribute); **OLD CODE**
                var serializer = XmlSerializerInstance.GetSerializer(type, xmlRootAttribute);
                serializer.Serialize(writeStream, value, xns);                    
            });
        }
    }
}

public static class XmlSerializerInstance
{
    public static object _lock = new object();
    public static Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
    public static XmlSerializer GetSerializer(Type type, XmlRootAttribute xra)
    {
        lock (_lock)
        {
            var key = $"{type}|{xra}";
            if (!_serializers.TryGetValue(key, out XmlSerializer serializer))
            {
                if (type != null && xra != null)
                {
                    serializer = new XmlSerializer(type, xra);
                }

                _serializers.Add(key, serializer);
            }

            return serializer;
        }
    }
}
0 голосов
/ 16 апреля 2019

Что находится в объекте активации? Если у активации много дочерних элементов, во время сопоставления она может попытаться заполнить все дочерние элементы и дочерние элементы детей и т. Д. Если вы используете отложенную загрузку EF, EF создаст подзапрос для каждой дочерней популяции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...