Cloudera Operational Database (COD) er en administreret dbPaaS-løsning tilgængelig som en oplevelse i Cloudera Data Platform (CDP). Det tilbyder multimodal klientadgang med NoSQL-nøgleværdi ved hjælp af Apache HBase API'er og relationel SQL med JDBC (via Apache Phoenix). Sidstnævnte gør COD tilgængelig for udviklere, der er vant til at bygge applikationer, der bruger MySQL, Postgres osv. De vigtigste fordele ved COD omfatter:
- Auto-skalering – baseret på arbejdsbyrdeudnyttelsen af klyngen og vil snart have mulighed for at skalere klyngen op/ned
- Auto-tune – bedre ydeevne inden for den eksisterende infrastrukturs fodaftryk.
- Auto-heal – løs driftsproblemer automatisk (kommer snart).
I denne blog vil jeg demonstrere, hvordan COD nemt kan bruges som et backend-system til at gemme data og billeder til en simpel webapplikation. For at bygge denne applikation vil vi bruge Phoenix, en af de underliggende komponenter i COD, sammen med Flask. Til lagring af billeder vil vi bruge en HBase (Apache Phoenix backend storage)-funktion kaldet MOB (medium objects). MOB giver os mulighed for at læse/skrive værdier fra 100k-10MB hurtigt.
*For at gøre det nemmere at bruge, kan du også bruge Phoenix-forespørgselsserveren i stedet for COD. Forespørgselsserveren er en lille build af phoenix, der kun er beregnet til udviklingsformål, og data slettes i hver build.
Al kode er i min github-repo.
Instruktioner:
1. Log ind på Cloudera Management Console, og vælg Operational Database-oplevelsen
2. Vælg dit miljø og navngiv din DB
3. Når DB'en er oppe, skal du tage URL'en fra den tynde JDBC-klient
4. Indstil din adgangskode til CDP-arbejdsbelastning
5. Klon projekt git repo og installationskrav:$ pip install -r requirements.txt
6. Gå til app-mappen og kør "setup.py" – dette vil oprette en tabel med 3 registreringer af brugere og deres billeder $ python setup.py
7. Kør flask-webserveren, så webapplikationen starter:$ FLASK_APP=app.py python -m flask run –port=8888 –host=127.0.0.1 –reload –with-threads –debugger
8. Gå til http://localhost:8888/users på din browser. Du burde kunne se programmet køre! Så enkelt er det.
Gennemgang af koden
1. Skema-klassen, indeholder grundlæggende forbindelsesdetaljerne og metoder til oprettelse og slip af tabel. Som du kan se, er "foto"-kolonnen en VARBINARY-type, som oversættes til et MOB-objekt i 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 Brugerklassen er ansvarlig for al applikationsdrift med Phoenix. Vi kan opdatere/indsætte (upsert på phoenix-sprog), slette, liste og håndtere billedtransaktioner:
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 er hovedrouteren for applikationen. Den indeholder al håndtering med brugerinput og routing af dem til tilslutningsmetoderne. Jeg adskilte håndteringen af billeder for at gøre det nemmere at bruge, og på den måde kan jeg få et specifikt billede til en bruger:
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)
Næste trin, du kan bruge denne github-repo til at teste din applikation.
Håber du finder det nyttigt, Happy coding!!