HBase
 sql >> Database >  >> NoSQL >> HBase

Creazione di un'applicazione Web CRUD semplice e di un archivio di immagini utilizzando Cloudera Operational Database e Flask

Il Cloudera Operational Database (COD) è una soluzione dbPaaS gestita disponibile come esperienza in Cloudera Data Platform (CDP). Offre accesso client multimodale con valore-chiave NoSQL utilizzando le API HBase di Apache e SQL relazionale con JDBC (tramite Apache Phoenix). Quest'ultimo rende COD accessibile agli sviluppatori che sono abituati a creare applicazioni che utilizzano MySQL, Postgres, ecc. I principali vantaggi di COD includono:

  • Ridimensionamento automatico: basato sull'utilizzo del carico di lavoro del cluster e presto avrà la possibilità di aumentare/ridurre il cluster
  • Auto-tuning:prestazioni migliori all'interno dell'impronta dell'infrastruttura esistente.
  • Auto-heal:risolve automaticamente i problemi operativi (disponibile a breve).

In questo blog dimostrerò come COD può essere facilmente utilizzato come sistema back-end per archiviare dati e immagini per una semplice applicazione web. Per costruire questa applicazione, utilizzeremo Phoenix, uno dei componenti alla base di COD, insieme a Flask. Per archiviare le immagini, utilizzeremo una funzionalità HBase (Apache Phoenix backend storage) chiamata MOB (oggetti medi). MOB ci consente di leggere/scrivere rapidamente valori da 100k-10MB.

*Per semplificare lo sviluppo, puoi anche utilizzare il server di query Phoenix invece di COD. Il server di query è una piccola build di phoenix destinata esclusivamente a scopi di sviluppo e i dati vengono eliminati in ogni build.

Tutto il codice è nel mio repository github.

Istruzioni:

1. Accedi a Cloudera Management Console e seleziona l'esperienza Database operativo

2. Scegli il tuo ambiente e assegna un nome al tuo DB

3. Una volta che il DB è attivo, prendi l'URL dal thin client JDBC

4. Imposta la password del carico di lavoro CDP

5. Clona il repository git del progetto e installa i requisiti:$ pip install -r Requirements.txt

6. Vai alla cartella dell'app ed esegui "setup.py":questo creerà una tabella con 3 record di utenti e le loro immagini $ python setup.py

7. Eseguire il server Web di flask per l'avvio dell'applicazione Web:$ FLASK_APP=app.py python -m flask run –port=8888 –host=127.0.0.1  –reload –with-threads –debugger

8. Vai a http://localhost:8888/users sul tuo browser. Dovresti essere in grado di vedere l'applicazione in esecuzione! Semplice come quello.

Esame del codice

1. La classe Schema, in pratica contiene i dettagli della connessione e crea e rilascia i metodi della tabella. Come puoi vedere, la colonna "foto" è di tipo VARBINARY, che si traduce in un oggetto MOB in HBase:

import phoenixdb
import phoenixdb.cursor
class Schema:
    def __init__(self):
        opts = {}
        opts['authentication'] = 'BASIC'
        opts['avatica_user'] = '<cod workload username>'
        opts['avatica_password'] = '<cod workload pw>'
        database_url = "<cod thin jdbc url>"
        self.TABLENAME = "users"
        self.conn = phoenixdb.connect(database_url, autocommit=True,**opts)
        self.curs = self.conn.cursor()

    def create_users_table(self):
        query = """
        CREATE TABLE IF NOT EXISTS """+self.TABLENAME+""" (
        username VARCHAR NOT NULL,
        firstname VARCHAR,
        lastname  VARCHAR,
        telephone VARCHAR,
        message VARCHAR,
        email VARCHAR,
        photo VARBINARY,
        photo_name VARCHAR,
        photo_type VARCHAR,
        photo_chars VARCHAR
        CONSTRAINT my_pk PRIMARY KEY (username))
        """
        self.curs.execute(query)

    def drop_users_table(self):
        query = "DROP TABLE "+self.TABLENAME
        self.curs.execute(query)

2 La classe users è responsabile di tutte le operazioni dell'applicazione con Phoenix. Possiamo aggiornare/inserire (upsert in phoenix language), eliminare, elencare e gestire le transazioni di immagini:

import phoenixdb
from schema import Schema
import json
class UsersModel:
    TABLENAME = "users"

    def __init__(self):
        db = Schema()
        self.conn=db.conn
        self.curs=db.curs

    def upsert(self, params):

        sql = "upsert into " + self.TABLENAME + \
            " (username ,message,telephone,firstname,lastname,email) \
             values (?,?,?,?,?,?)"
        data = (params.get('username'),params.get('message'),\
            params.get('telephone'),params.get('firstname'),\
            params.get('lastname'),params.get('email'))
        results = self.curs.execute(sql,data)
        return results

    def upsert_photo(self, params):
        if params.get('photo') is None:
            photo = bytes('','utf-8')
        else:
            photo = params.get('photo')

        sql = "upsert into " + self.TABLENAME + \
            " (username, photo,photo_name) values (?,?,?)"

        data = (params.get('username'),photo, params.get('photo_name'))
        results = self.curs.execute(sql,data)
        return results

    def delete(self, username):
        query = f"DELETE from {self.TABLENAME} " \
                f"WHERE username = {username}"

        self.curs.execute(query)

    def list_items(self, where_clause="",format="json"):
        query = f"SELECT username ,email,message,telephone,firstname,\
            lastname,photo_name " \
            f"from {self.TABLENAME} WHERE  " + where_clause

        self.curs.execute(query)
        if format=="json":
            r = [dict((self.curs.description[i][0].lower(), value) \
                   for i, value in enumerate(row)) for row in \
                   self.curs.fetchall()]
            self.conn.close()
            data={'data': r }
            return json.dumps(data)

        result_set=self.curs.fetchall()
        result = [{column: row[i]
            for i, column in enumerate(result_set[0].keys())}
                for row in result_set]
        return result
    def get_image(self, username):
        query = f"SELECT photo,photo_name " \
                f"from {self.TABLENAME} WHERE  username='"+username+"'"

        self.curs.execute(query)
        row = self.curs.fetchone()
        return row

3. App.py è il router principale dell'applicazione. Contiene tutta la gestione degli input dell'utente e l'instradamento ai metodi di connessione. Ho separato la gestione delle immagini per facilità d'uso e in questo modo posso ottenere un'immagine specifica per un utente:

from flask import Flask, request, send_file ,jsonify,render_template
import phoenixdb
import io
from users import UsersModel
from schema import Schema
import json

app = Flask(__name__)

@app.after_request
def add_headers(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Headers'] =  \
        "Content-Type, Access-Control-Allow-Headers, Authorization, \
        X-Requested-With"
    response.headers['Access-Control-Allow-Methods']=  "POST, GET, PUT, \
    DELETE, OPTIONS"
    response.headers['Allow']=  "POST, GET, PUT, OPTIONS"
    return response

@app.route("/")
def hello():
    return "Hello World!"

@app.route("/users")
def return_form():
    return render_template("users.html")

@app.route("/handle_data",methods=['POST'])
def handle_data():
    if request.method == 'POST':
        username = request.form['username']
        firstname = request.form['firstname']
        lastname = request.form['lastname']
        email = request.form['email']
        telephone = request.form['telephone']
        message = request.form['message']
        photo = request.files['photo']
        photo_bytes = photo.read()
        model=Schema()
        usersmodel=UsersModel()
        data = {'username':f"{username}",'firstname':f"{firstname}",\
            'lastname':f"{lastname}",'telephone':f"{telephone}",\
            'message':f"{message}"}
        photo_data = {'username':f"{username}",\
            'photo':photo_bytes,\
            'photo_name':f"{photo.filename}"}
        usersmodel.upsert(data)
        usersmodel.upsert_photo(photo_data)
        return render_template('users.html')
    else:
        return render_template('users.html')

@app.route("/get_users",methods=['GET'])
def get_users():
    if request.method == 'GET':
        usersmodel=UsersModel()
        users = usersmodel.list_items("1=1")
        return users

@app.route("/get_image",methods=['GET'])
def get_image():
    if request.method == 'GET':
        username = request.args.get('username')
        usersmodel=UsersModel()
        imagedb = usersmodel.get_image(username)
        return send_file(io.BytesIO(imagedb[0]),mimetype='image/png', \
            attachment_filename=imagedb[1])

if __name__ == "__main__":
    Schema()
    app.run(debug=True, port=8888)

Passaggi successivi, puoi utilizzare questo repository github per testare la tua applicazione.

Spero che lo trovi utile, buona codifica!!