Cloudera Operational Database (COD) är en hanterad dbPaaS-lösning tillgänglig som en upplevelse i Cloudera Data Platform (CDP). Den erbjuder multimodal klientåtkomst med NoSQL-nyckelvärde med hjälp av Apache HBase API:er och relationell SQL med JDBC (via Apache Phoenix). Det senare gör COD tillgängligt för utvecklare som är vana vid att bygga applikationer som använder MySQL, Postgres, etc. Huvudfördelarna med COD inkluderar:
- Automatisk skalning – baserat på klustrets arbetsbelastning och kommer snart att ha möjlighet att skala upp/ned i klustret
- Auto-tune – bättre prestanda inom den befintliga infrastrukturen.
- Autoläkning – lös driftsproblem automatiskt (kommer snart).
I den här bloggen kommer jag att demonstrera hur COD enkelt kan användas som ett backend-system för att lagra data och bilder för en enkel webbapplikation. För att bygga denna applikation kommer vi att använda Phoenix, en av de underliggande komponenterna i COD, tillsammans med Flask. För att lagra bilder kommer vi att använda en HBase-funktion (Apache Phoenix backend-lagring) som kallas MOB (medium objects). MOB låter oss läsa/skriva värden från 100k-10MB snabbt.
*För enkel utveckling kan du också använda Phoenix frågeserver istället för COD. Frågeservern är en liten version av Phoenix som endast är avsedd för utvecklingsändamål, och data raderas i varje version.
All kod finns i min github-repo.
Instruktioner:
1. Logga in på Cloudera Management Console och välj Operational Database-upplevelse
2. Välj din miljö och namnge din DB
3. När DB är uppe, ta URL:en från den tunna JDBC-klienten
4. Ställ in ditt lösenord för CDP-arbetsbelastning
5. Klona projekt git repo och installationskrav:$ pip install -r requirements.txt
6. Gå till app-mappen och kör “setup.py” – detta skapar en tabell med tre poster över användare och deras bilder $ python setup.py
7. Kör flaskwebbservern för att webbapplikationen ska starta:$ FLASK_APP=app.py python -m flask run –port=8888 –host=127.0.0.1 –reload –with-threads –debugger
8. Gå till http://localhost:8888/users i din webbläsare. Du bör kunna se programmet köra! Enkelt är det.
Gå igenom koden
1. Schema-klassen, innehåller i princip anslutningsdetaljerna och skapa och släpp tabellmetoder. Som du kan se är kolumnen "foto" en VARBINARY-typ, som översätts till ett 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 Användarklassen ansvarar för all applikationsoperation med Phoenix. Vi kan uppdatera/infoga (upsert in phoenix language), radera, lista och hantera bildtransaktioner:
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 är huvudroutern för applikationen. Den innehåller all hantering med användaringångar och dirigering av dem till anslutningsmetoderna. Jag separerade hanteringen av bilder för att underlätta användningen, och på så sätt kan jag få en specifik bild för en användare:
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ästa steg, du kan använda denna github-repo för att testa din applikation.
Hoppas du finner det användbart, Happy coding!!