Pregunta

Estoy trabajando en una biblioteca de Python que interactúa con una API de servicios web. Al igual que muchos servicios web que he encontrado, este solicita limitar la tasa de solicitudes. Me gustaría proporcionar un parámetro opcional, límite , a la instanciación de clase que, si se proporciona, mantendrá las solicitudes salientes hasta que transcurran los segundos especificados.

Entiendo que el escenario general es el siguiente: una instancia de la clase realiza una solicitud a través de un método. Cuando lo hace, el método emite una señal que establece una variable de bloqueo en algún lugar, y comienza un temporizador de cuenta regresiva para el número de segundos en límite . (Con toda probabilidad, el bloqueo es el propio temporizador de cuenta regresiva.) Si se realiza otra solicitud dentro de este marco de tiempo, debe ponerse en cola hasta que el temporizador de cuenta regresiva llegue a cero y el bloqueo se desactive. en este punto, se envía la solicitud más antigua de la cola, y se reinicia el temporizador de cuenta regresiva y se vuelve a activar el bloqueo.

¿Es este un caso para enhebrar? ¿Hay otro enfoque que no estoy viendo?

¿El temporizador de cuenta regresiva y el bloqueo deben ser variables de instancia, o deben pertenecer a la clase, de modo que todas las instancias de la clase contengan solicitudes?

Además, ¿es generalmente una mala idea proporcionar una funcionalidad de limitación de velocidad dentro de una biblioteca? Razoné porque, de manera predeterminada, la cuenta regresiva es de cero segundos, la biblioteca aún permite a los desarrolladores usar la biblioteca y proporcionar sus propios esquemas de limitación de velocidad. Sin embargo, dado que los desarrolladores que utilicen el servicio necesitarán solicitudes de límite de velocidad de todos modos, creo que sería conveniente para la biblioteca proporcionar un medio de limitación de velocidad.

Independientemente de colocar o no un esquema de limitación de velocidad en la biblioteca, desearé escribir una aplicación utilizando la biblioteca, por lo que las técnicas sugeridas serán útiles.

¡Muchas gracias por tus sugerencias!

Chris

¿Fue útil?

Solución

Esto funciona mejor con una cola y un despachador.

Dividiste tu procesamiento en dos lados: fuente y envío . Estos pueden ser hilos separados (o procesos separados si es más fácil).

El lado Fuente crea y pone en cola las solicitudes a cualquier velocidad que las haga felices.

El lado Despacho hace esto.

  1. Obtenga la hora de inicio de la solicitud, s.

  2. Encola una solicitud, procesa la solicitud a través del servicio remoto.

  3. Obtén la hora actual, t . Duerma durante tasa - ( t - s ) segundos.

Si desea ejecutar el lado Fuente conectado directamente al servicio remoto, puede hacerlo y omitir la limitación de velocidad. Esto es bueno para las pruebas internas con una versión simulada del servicio remoto.

La parte difícil de esto es crear una representación para cada solicitud que puede poner en cola. Ya que la Cola de Python se encargará de casi cualquier cosa, no tienes que hacer mucho.

Si está utilizando multiprocesamiento, deberá pickle tus objetos para ponerlos en una tubería.

Otros consejos

Las colas pueden ser demasiado complicadas. Una solución más simple es darle a su clase una variable para el momento en que se llamó por última vez al servicio. Cuando se llame al servicio (! 1), configure waitTime en delay - Now + lastcalltime . delay debe ser igual al tiempo mínimo permitido entre solicitudes. Si este número es positivo, duerma durante tanto tiempo antes de hacer la llamada (! 2). La desventaja / ventaja de este enfoque es que considera que las solicitudes de servicio web son síncronas. La ventaja es que es absurdamente simple y fácil de implementar.

  • (! 1): Debería suceder justo después de recibir una respuesta del servicio, dentro de la envoltura (probablemente en la parte inferior de la envoltura).
  • (! 2): Debería suceder cuando se llama a la envoltura de python que rodea el servicio web, en la parte superior de la envoltura.

La solución de S.Lott es más elegante, por supuesto.

Su esquema de limitación de velocidad debe estar muy influenciado por las convenciones de llamada del código subyacente (sincrónico o asíncrono), así como por el alcance (subproceso, proceso, máquina, grupo?) en el que funcionará la limitación de velocidad.

Sugeriría mantener todas las variables dentro de la instancia, para que pueda implementar fácilmente múltiples períodos / tasas de control.

Por último, parece que quieres ser un componente de middleware. No trates de ser una aplicación e introduce hilos por tu cuenta. Solo bloquee / duerma si está sincronizado y use el marco de envío asíncrono si uno de ellos lo llama.

Si su biblioteca está diseñada para ser sincrónica, entonces recomendaría que no se aplique el límite (aunque puede hacer un seguimiento de las tarifas y al menos ayudar a la persona que llama a decidir cómo respetar los límites).

Uso twisted para interactuar con casi todo en la actualidad. Facilita hacer ese tipo de cosas al tener un modelo que separa el envío de solicitudes del manejo de respuestas. Si no desea que los usuarios de su API tengan que usar trenzado, al menos sería mejor que entendieran su API para la ejecución diferida.

Por ejemplo, tengo una interfaz de twitter que envía un número bastante absurdo de solicitudes en nombre de usuarios de xmpp . No tengo límite de tarifas, pero tuve que hacer un poco de trabajo para evitar que todas las solicitudes sucedieran al mismo tiempo.

No reinventes la rueda, a menos que sea necesario. Consulte la impresionante biblioteca ratelimit . Perfecto si solo quieres limitar las llamadas a una API de descanso por cualquier motivo y seguir con tu vida.

Así que estoy asumiendo algo simple como tiempo de importación time.sleep (2) no funcionará por esperar 2 segundos entre solicitudes

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top