2008-08-25 10 views
5

En mi trabajo, tengo seis servicios de Windows de los cuales soy responsable, escritos en C# 2003. Cada uno de estos servicios contiene un temporizador que dispara cada minuto, donde la mayoría de sus el trabajo sucedeServicio de Windows Aumento del consumo de CPU

Mi problema es que, a medida que estos servicios se ejecutan, comienzan a consumir más y más tiempo de CPU en cada iteración del ciclo, incluso si no hay un trabajo significativo para ellos (es decir, están inactivos, buscando en la base de datos algo que hacer). Cuando se inician, cada servicio usa un promedio de (aproximadamente) 2-3% de 4 CPUs, lo cual está bien. Después de 24 horas, cada servicio consumirá un procesador completo mientras dure su ciclo.

¿Alguien puede ayudar? No sé qué podría estar causando esto. Nuestra solución actual es reiniciar los servicios una vez al día (se cierran, un script ve que están desconectados y los reinicia a las 3AM aproximadamente). Pero esta no es una solución a largo plazo; mi preocupación es que a medida que los servicios se vuelven más activos, reiniciarlos una vez al día puede no ser suficiente ... pero como hay una penalización de arranque significativa (todos usan NHibernate para acceso a datos), a medida que se vuelven más ocupados, exactamente lo que don ' t desea estar haciendo es reiniciarlos con más frecuencia.


@akmad: Es cierto, es muy difícil.

  1. Sí, un servicio ejecutado de forma aislada mostrará el mismo síntoma a lo largo del tiempo.
  2. No, no es así. Hemos visto eso. Esto puede suceder a las 10 AM o a las 6 PM o en el medio de la noche. No hay consistencia.
  3. Hacemos; y estan. Los servicios están haciendo exactamente lo que deberían ser, y nada más.
  4. Desafortunadamente, eso requiere conocimiento previo de cuándo los servicios van a estar maximizando las CPU, lo que ocurre en un cronograma impredecible, y nunca muy rápido ... lo que hace las cosas doblemente difíciles, porque mi jefe las ejecutará y reiniciará cuando comienzan a tener problemas sin pensar en problemas de depuración.
  5. No, están usando una cantidad bastante constante de RAM (aproximadamente 60-80 MB cada uno, de 4 GB en la máquina).

Buenas sugerencias, pero puede estar seguro, hemos probado todas las soluciones habituales. Lo que espero es que este sea un problema de .NET que alguien pueda saber, que podamos resolver. La solución de mi jefe (que enfáticamente no quiero implementar) es poner un campo en la base de datos que retiene varias veces para que los servicios se reinicien durante el día, para que pueda solucionar el problema y no pensar en ello . Estoy buscando desesperadamente la causa del problema real para poder solucionarlo, porque esa solución se convertirá en un desastre en unos seis meses.


@Yaakov Ellis: Cada uno de ellos tiene una función diferente. Uno lee los registros de una base de datos Oracle en otro lugar; otro procesa esos registros y transfiere archivos pertenecientes a esos registros a nuestro sistema; un tercero verifica esos archivos para asegurarse de que sean lo que esperamos que sean; otro es un servicio de mantenimiento que constantemente revisa cosas como el espacio en disco (que tenemos suficiente) y sondea otros servidores para asegurarse de que estén vivos; uno se está ejecutando solo para asegurarse de que todos estos otros estén ejecutándose y haciendo su trabajo, monitorea e informa errores, y reinicia todo lo que no haya podido mantener todo el sistema funcionando las 24 horas del día.

Por lo tanto, si me pregunta qué pienso que está preguntando, no, no hay nada en común con todos estos servicios (aparte del acceso a la base de datos a través de NHibernate) que pueda señalar como un posible problema . Desafortunadamente, si eso resulta ser el problema real (lo cual no me sorprendería mucho), todo podría estar arruinado, y terminaré reescribiéndolos en SQL simple. Espero que sea un problema de recolector de basura o algo más fácil de tratar que NHibernate.


@Joshdan: No secret. Como dije, probamos todos los problemas habituales. La creación de perfiles no fue útil: el generador de perfiles que utilizamos no pudo señalar ningún código que se estaba ejecutando cuando el uso de la CPU era alto. Estos servicios fueron destrozados hace aproximadamente un mes buscando este problema. Cada sección del código fue analizada para tratar de averiguar si nuestro código era el problema; No estoy aquí preguntando porque no he hecho mi tarea. Si este fuera un caso simple de los servicios haciendo más trabajo de lo previsto, eso es algo que habría sido atrapado.

El problema aquí es que, la mayoría de las veces, los servicios no están haciendo nada en absoluto, y aún logran consumir el 25% o más de cuatro núcleos de CPU: no encuentran trabajo que hacer, y salir de su bucle y esperando la siguiente iteración. Esto debería, literalmente, tomar casi ningún tiempo de CPU en absoluto.

Aquí hay un ejemplo de comportamiento que estamos viendo, en un servicio sin trabajo durante dos días (en un entorno inmutable). Esto fue capturado la semana pasada:

Día 1, 8AM: Prom. Uso de CPU aproximadamente 3%
Día 1, 6 PM: Prom. Uso de CPU aproximadamente 8%
Día 2, 7AM: Prom. Uso de la CPU aproximadamente 20%
Día 2, 11AM: Prom. Uso de la CPU aproximadamente 30%

Después de haber analizado todas las posibles razones mundanas para esto, hice esta pregunta aquí porque pensé (con razón, resulta que) que obtendría respuestas más innovadoras (como Ubiguchi's), o punteros a cosas que yo no había pensado en (como la sugerencia de Ian).


Así ocurre el pico de la CPU inmediatamente anterior a la devolución de llamada temporizador , dentro de la devolución de llamada de temporizador, o inmediatamente después de que el temporizador devolución de llamada?

Lo malinterpreta. Esto no es un aumento. Si lo fuera, no habría ningún problema; Puedo lidiar con los picos. Pero no es ... el uso de la CPU está aumentando en general. Incluso cuando el servicio no está haciendo nada, esperando el próximo golpe de temporizador. Cuando se inicia el servicio, las cosas son agradables y tranquilas, y el gráfico se ve como lo que se espera ... generalmente, 0% de uso, con picos al 10% cuando NHibernate llega a la base de datos o el servicio realiza una cantidad de trabajo trivial . Pero esto aumenta a un uso general del 25% (más si lo dejo ir demasiado lejos) en todo momento mientras el proceso se está ejecutando.

Eso hizo que la sugerencia de Ian fuera la bala de plata lógica (NHibernate hace muchas cosas cuando no estás buscando). Por desgracia, he implementado su solución, pero no ha tenido ningún efecto (no tengo pruebas de esto, pero en realidad creo que empeoró las cosas ... el uso promedio es aparentando que sube mucho más rápido ahora).Tenga en cuenta que eliminar las "secciones" de NHibernate (como usted recomienda) no es factible, ya que eso eliminaría aproximadamente el 90% del código en el servicio, lo que me permitiría descartar el temporizador como un problema (que tengo la absoluta intención de intente), pero no puede ayudarme a descartar NHibernate como el problema, porque si NHibernate está causando esto, entonces la solución dudosa que se implementó (ver a continuación) simplemente tendrá que convertirse en The Way The System Works; dependemos tanto de NHibernate para este proyecto que el PM simplemente no aceptará que está causando un problema estructural irresolubles.

acabo de señalar un sentido de desesperación en la pregunta - que sus problemas continuarían salvo un pequeño milagro

no me refiero a que venga de esa manera. Por el momento, los servicios se reinician a diario (con la opción de ingresar cualquier cantidad de horas del día para que se apaguen y reinicien), lo que soluciona el problema pero no puede ser una solución a largo plazo una vez que ingresan en la máquina de producción. y comienza a estar ocupado Los problemas no continuarán, ya sea que los solucione o el PM mantiene esta restricción sobre ellos. Obviamente, preferiría implementar una solución real, pero dado que las pruebas iniciales no revelaron ninguna razón para esto, y los servicios ya han sido ampliamente revisados, el PM prefiere simplemente reiniciarlos varias veces antes de pasar más tiempo tratando de arreglarlos. . Eso está completamente fuera de mi control y hace que el milagro del que hablas sea más importante de lo que sería de otra manera.

Eso es extremadamente intrigante (en cuanto a ya que confía en su generador de perfiles).

I do not. Pero entonces, estos son servicios de Windows escritos en .NET 1.1 que se ejecuta en una máquina con Windows 2000, implementados por una secuencia de comandos Nant poco fiable, utilizando una versión anterior de NHibernate para el acceso a la base de datos. Hay poco en esa máquina en la que realmente diría que confío.

+0

¿Es posible que su temporizador de servicio esté activo más de lo necesario? Digamos, el temporizador se activa y el ciclo no se ejecuta aún, y pasan 2 minutos. ¿Se activará el temporizador nuevamente? Muchas cosas pueden salir mal, pero todas están relacionadas específicamente con tu código. Este no es el comportamiento normal de un servicio. – Jaywalker

+0

¿Tiene algún código que detecte si el servicio todavía está "funcionando" en un ciclo de trabajo anterior? – hova

Respuesta

2

Obviamente es muy difícil de depurar de forma remota estás aplicación desconocida ... pero aquí hay algunas cosas que me gustaría tener en cuenta:

  1. ¿Qué ocurre cuando sólo se ejecuta uno de los servicios a la vez? ¿Todavía ves la desaceleración? Esto puede indicar que hay cierta disputa entre los servicios.
  2. ¿El problema siempre ocurre al mismo tiempo, independientemente de cuánto tiempo se ha estado ejecutando el servicio? Esto puede indicar que algo más (una copia de seguridad, un análisis de virus, etc.) está causando que la máquina (o db) en su conjunto se desacelere.
  3. ¿Tiene algún tipo de registro o algún otro mecanismo para asegurarse de que el servicio solo está trabajando tantas veces como cree que debería?
  4. Si puede ver la degradación del rendimiento durante un corto período de tiempo, intente ejecutar el servicio por un tiempo y luego adjunte un generador de perfiles para ver exactamente qué está vinculando la CPU.
  5. No menciona nada sobre el uso de la memoria. ¿Tiene alguna de esta información para los servicios? Es posible que consuma la mayor parte de la memoria RAM y ocasione la basura en el disco, o algún problema similar.

Best of luck!

1

'Resolver esta respuesta solo sugerirá algunas instrucciones para que usted pueda ver, pero después de haber visto problemas similares en .NET Windows Services, tengo un par de ideas que pueden ser útiles.

Mi primera sugerencia es que sus servicios pueden tener algunos errores, ya sea en la forma en que manejan la memoria, o tal vez en la forma en que manejan la memoria no administrada. La última vez que rastreé un problema similar, resultó que una biblioteca de OSS de terceros estaba usando identificadores almacenados para objetos no administrados en la memoria estática. Cuanto más duraba el servicio, más controladores manejaba el servicio, lo que hacía que el rendimiento de la CPU del proceso se disparara rápidamente. La forma de tratar de resolver este tipo de problema para garantizar que sus servicios no almacenen nada en la memoria entre las invocaciones del temporizador, aunque si sus bibliotecas de terceros usan memoria estática es posible que tenga que hacer algo inteligente como crear un dominio de aplicación para la invocación del temporizador. la aplicación doamin (y su memoria estática) una vez que se completa el procesamiento.

El otro problema que he visto en circunstancias similares fue que el código de sincronización del temporizador era sospechoso, lo que de hecho permitió que más de un hilo ejecutara el código de procesamiento a la vez. Cuando depuramos el código, encontramos que el primer hilo estaba bloqueando el 2do, y para el momento en que el 2do comenzó, había un tercero bloqueado. Con el tiempo, el bloqueo fue durando cada vez más y el uso de la CPU se dirigió hacia la cima. La solución que usamos para solucionar el problema fue implementar un código de sincronización apropiado para que el temporizador solo iniciara otro hilo si no se bloqueara.

Espero que esto ayude, pero me disculpo por adelantado si mis dos pensamientos son pistas falsas.

+0

Lo de enhebrar no es un problema; eso es algo con lo que lidié hace 18 meses, y estoy seguro de que solo se está ejecutando una invocación de temporizador en un momento dado. Buena idea, gracias.Buscaré en el bit de AppDomain. Aunque no estoy al tanto de ningún problema de memoria, si puedo deshacerme de todo el contexto de ejecución después de cada golpe de temporizador, eso puede matar cualquier problema que tengamos. Gracias por las sugerencias. – TheSmurf

1

Suena como un problema de enhebrado con el temporizador. Es posible que tenga una unidad de trabajo bloqueando otra ejecución en diferentes subprocesos de trabajo, haciendo que se acumulen cada vez que se dispara el temporizador. O puede que tenga instancias que viven y trabajan más de lo esperado.

Sugiero refacturar el temporizador. Reemplácelo con un solo hilo que ponga en cola el trabajo en ThreadPool. Puede suspender() el hilo para controlar la frecuencia con que busca un nuevo trabajo. Asegúrese de que este sea el único lugar donde su código sea multiproceso. Todos los demás objetos deben crearse instancias a medida que el trabajo se prepara para el procesamiento y se destruye después de que se complete el trabajo. STATE IS THE ENEMY en código multiproceso.

Otra área donde falta el diseño parece ser que tiene múltiples servicios que son recursos de sondeo para hacer algo. Sugiero unificarlos bajo un solo servicio. Podrían separar cosas, pero están trabajando al unísono; solo está usando el sistema de archivos, la base de datos, etc. como una sustitución de llamadas a métodos. Además, 2003? Me siento mal por ti.

+0

Gracias por eso. Desafortunadamente, no tengo control sobre cuántos servicios se están ejecutando. Estoy totalmente de acuerdo en que la mayor parte de esto debería consolidarse. Desafortunadamente, el PM está convencido de que ejecutar seis servicios para hacer este trabajo es más eficiente. : S También he estado insistiendo acerca de la actualización a una nueva versión de .NET durante aproximadamente dos años. Eso no ha tenido ningún efecto. Y ahora tenemos aún más código (la mayoría ASP.NET), lo que hará que la actualización sea aún más difícil ahora que cuando la sugerí por primera vez. Estoy bastante seguro de que el trabajo no se está acumulando. Si un servicio ya está funcionando cuando se dispara el temporizador, th – TheSmurf

2

Sugiero hackear el problema en pedazos.
Primero, encuentre una forma de reproducir el problema el 100% de las veces y rápidamente. Baje el temporizador para que los servicios se activen con mayor frecuencia (por ejemplo, 10 veces más rápido que lo normal). Si el problema surge 10 veces más rápido, entonces está relacionado con el número de iteraciones y no con el tiempo real o el trabajo real realizado por los servicios). Y podrá hacer los próximos pasos más rápido que una vez al día.
En segundo lugar, comente todo el código de trabajo real y deje solo los servicios, los temporizadores y el mecanismo de sincronización. Si el problema aún aparece, entonces estará en esa parte del código. Si no es así, comience a agregar nuevamente el código que ha comentado, de a una por vez. Finalmente, debe averiguar qué parte del código está causando el problema.

3

Usted ha mencionado que está utilizando NHibernate - (? Como al final de cada iteración) está cerrando sus sesiones de NHibernate en los puntos apropiados

Si no es así, entonces el tamaño del objeto de mapa cargado en la memoria se aumentar gradualmente con el tiempo, y cada descarga de la sesión llevará cada vez más tiempo de CPU.

+0

no creo que lo estemos. Ese es un patrón en nuestro desarrollo relacionado con NHibernate. Las sesiones permanecieron por mucho tiempo. Esa es una posibilidad interesante, gracias. – TheSmurf

0

Buenas sugerencias, pero puede estar seguro, hemos probado todas las soluciones habituales. Lo que espero es que este sea un problema de .NET que alguien pueda saber, que podamos resolver.

Mi sensación es que no importa cuán extraña sea la causa subyacente, los pasos habituales de solución de problemas son su mejor opción para localizar el problema.

Dado que esto es un problema de rendimiento, las buenas medidas son invaluables. El uso general de la CPU del proceso es una medida demasiado amplia. ¿Dónde está gastando su tiempo? Puede usar un generador de perfiles para medir esto, o simplemente registrar varias secciones de inicio y detención. Si no puede hacer eso, utilice la sugerencia de Andrea Bertani: aísle las secciones eliminando otras.

Una vez que haya localizado el área general, podrá hacer mediciones aún más precisas, hasta que determine la fuente del uso de la CPU. Si no es obvio cómo solucionarlo en ese punto, al menos tienes munición para una pregunta mucho más específica.

Si de hecho ya ha hecho toda esta solución de problemas habitual, por favor, háganos saber el secreto.

3

Aquí es donde me gustaría empezar:

  1. Obtener Process Explorer y mostrar% Tiempo en JIT,% Tiempo en GC, los ciclos de CPU Delta, tiempo de CPU, CPU%, y Temas.
  2. También querrás kernel y tiempo de usuario, y un par de seguimientos de pila representativos, pero creo que debes presionar Propiedades para obtener instantáneas.
  3. Compara antes y después de las tomas.

Un par de reflexiones sobre las posibilidades:

  • GC excesiva (.% Tiempo en GC subiendo Además, Monitor de rendimiento GC y contadores CPU corresponderían)
  • hilos excesivas y cambios de contexto asociados (# de hilos subiendo)
  • sondeo (los rastreos de pila se capturan de forma consistente en una sola función)
  • excesivo tiempo de kernel (los tiempos de kernel son altos - El Administrador de tareas muestra números de tiempo de kernel grandes cuando la CPU es alta)
  • excepciones (pestaña PE .NET Las excepciones lanzadas son altas y cada vez más. También hay un contador Perfmon)
  • virus/rootkit (OK, esta es una última opción, pero es posible construir un rootkit que se esconde de TaskManager. Sospecho que entonces podría asignar su inevitable uso de CPU a otro proceso si fue lo suficientemente astuto. Además, si ha descartado todo lo anterior, me he quedado sin ideas en este momento)
Cuestiones relacionadas