2011-08-31 7 views
11

Estoy tratando con una aplicación de Python que consiste en múltiples componentes livianos distribuidos que se comunican usando RabbitMQ & Kombu.Agregar comportamiento REST a una clase con matraz, caso de planos?

Un componente escucha en dos colas y puede recibir múltiples tipos de mensajes en cada cola. Las subclases pueden anular la forma en que se procesa cada tipo de mensaje registrando controladores personalizados. Todo esto funciona bien.

Ahora tengo el requisito adicional de que cada componente debe tener una interfaz REST/HTML básica. La idea es que apunte su navegador al componente en ejecución y obtenga información en tiempo real sobre lo que está haciendo actualmente (qué mensajes está procesando, uso de la CPU, información de estado, registro, etc.)

Tiene que ser liviano, por lo que después de algunas investigaciones me he decidido por Flask (pero estoy abierto a sugerencias). En pseudocódigo esto significa tomar:

class Component: 
    Queue A 
    Queue B 
    ... 
    def setup(..): 
    # connect to the broker & other initialization 

    def start(..): 
    # start the event loop and wait for work 

    def handle_msg_on_A(self,msg): 
    # dispatch a msg to a handler depending on the msg type 

    def handle_msg_on_B(self,msg): 
    ... 

    ... 

y la adición de una serie de métodos de vista:

@app.route('/') 
    def web_ui(self): 
     # render to a template 

    @app.route('/state') 
    def get_state(self): 
     # REST method to return some internal state info as JSON 

    ... 

Sin embargo, las gasas y una interfaz web a una clase como esta se rompe SOLID principios y trae problemas con la herencia (una subclase puede querer mostrar más/menos información). Los decoradores no se heredan, por lo que todos los métodos de vista deberían anularse y redefinirse explícitamente. Tal vez usar una reflexión mixin + podría funcionar de alguna manera, pero se siente hackish.

En su lugar, el uso de la composición podría funcionar: coloque el elemento web en una clase separada que delegue las rutas url en un conjunto fijo y predefinido de métodos polimórficos en el componente anidado. De esta forma, los componentes permanecen ajenos a Flask a costa de una pérdida de flexibilidad (el conjunto de métodos disponibles es fijo).

Ahora he descubierto Flask blueprints y Application Dispatching y parece que podrían traer una solución mejor y más extensible. Sin embargo, todavía tengo que envolver mi cabeza alrededor de ellos.

Siento que me falta un patrón de diseño aquí y espero que alguien con más flask-fu o experiencia con este tipo de problema pueda comentar.

Respuesta

11

Algo más se introdujo silenciosamente en Flask 0.7 que podría interesarte: Pluggable Views. Estos son basados ​​en clase en lugar de puntos finales basados ​​en funciones, por lo que puede utilizar el método dispatch_request para administrar sus transiciones de estado (solo anulándolo cuando sea necesario).

El beneficio de hacerlo de esta manera, en lugar de usar Despacho de aplicaciones, es que obtienes compatibilidad con url_for en toda tu aplicación (en lugar de tener que codificar en URLs que cruzan los límites de la aplicación). decida si esto es algo que probablemente sea un problema para su aplicación.

En pseudo-código:

# File: Components.py 
from flask.views import View 

class Component(View): 
    # Define your Component-specific application logic here 

    dispatch_request(self, *url_args, **url_kwargs): 
     # Define route-specific logic that all Components should have here. 
     # Call Component-specific methods as necessary 

class Tool_1(Component): 
    pass 

class Tool_2(Component): 
    # Override methods here 

# File: app.py 
from flask import Flask 
from yourapplication import Tool_1, Tool_2 

app = Flask() 

# Assuming you want to pass all additional parameters as one argument 
app.add_url_rule("/tool_1/<path:options>", "tool1", view_func=Tool_1.as_view()) 

# Assuming you want to pass additional parameters separately 
tool_2_view = Tool_2.as_view() 
app.add_url_rule("/tool_2/", "tool2", view_func=tool_2_view) 
app.add_url_rule("/tool_2/<option>", "tool2", view_func=tool_2_view) 
app.add_url_rule("/tool_2/<option>/<filter>", "tool2", view_func=tool_2_view) 

Usted puede añadir planos a la mezcla si usted tiene una serie de componentes que están todos conectados entre sí y lógicamente no desea tener que recordar poner /prefix delante de la llamada de cada uno add_url_rule. Pero si solo tiene una serie de componentes que son en su mayoría independientes uno del otro, este es el patrón que usaría *.

*. Por otro lado, si necesitan ser aislados uno del otro usaría el patrón de Despacho de la aplicación recomendado en los documentos.

+0

Enfriar, gracias por la respuesta, definitivamente lo analizará y aprobará la respuesta si funciona – dgorissen

+0

¿Funcionó @dgorissen –

Cuestiones relacionadas