Cloudera Operational Database (COD) to zarządzane rozwiązanie dbPaaS dostępne jako doświadczenie w Cloudera Data Platform (CDP). Oferuje wielomodalny dostęp klienta za pomocą klucza i wartości NoSQL przy użyciu interfejsów API Apache HBase i relacyjnego SQL z JDBC (poprzez Apache Phoenix). Ten ostatni sprawia, że COD jest dostępny dla programistów, którzy są przyzwyczajeni do tworzenia aplikacji korzystających z MySQL, Postgres itp. Główne zalety COD to:
- Automatyczne skalowanie – w oparciu o wykorzystanie obciążenia klastra i wkrótce będzie mieć możliwość skalowania w górę/w dół klastra
- Automatyczne dostrajanie – lepsza wydajność w ramach istniejącej infrastruktury.
- Auto-naprawa – automatycznie rozwiąż problemy operacyjne (wkrótce).
W tym blogu pokażę, jak COD można łatwo wykorzystać jako system zaplecza do przechowywania danych i obrazów dla prostej aplikacji internetowej. Do budowy tej aplikacji będziemy używać Phoenix, jednego z podstawowych komponentów COD, wraz z Flask. Do przechowywania obrazów będziemy używać funkcji HBase (magazyn zaplecza Apache Phoenix) o nazwie MOB (obiekty średnie). MOB pozwala nam na szybki odczyt/zapis wartości od 100k-10MB.
*W celu ułatwienia programowania można również użyć serwera zapytań Phoenix zamiast COD. Serwer zapytań to niewielka wersja programu Phoenix, która jest przeznaczona wyłącznie do celów programistycznych, a dane są usuwane w każdej wersji.
Cały kod znajduje się w moim repozytorium github.
Instrukcje:
1. Zaloguj się do Konsoli Zarządzania Cloudera i wybierz doświadczenie operacyjnej bazy danych
2. Wybierz swoje środowisko i nazwij bazę danych
3. Po uruchomieniu bazy danych pobierz adres URL z cienkiego klienta JDBC
4. Ustaw hasło obciążenia CDP
5. Sklonuj repozytorium git projektu i wymagania instalacyjne:$ pip install -r wymagania.txt
6. Przejdź do folderu aplikacji i uruchom „setup.py” – spowoduje to utworzenie tabeli z 3 rekordami użytkowników i ich obrazami $ python setup.py
7. Uruchom serwer sieciowy flask, aby uruchomić aplikację sieciową:$ FLASK_APP=app.py python -m flask run –port=8888 –host=127.0.0.1 –reload –with-threads –debugger
8. Przejdź do http://localhost:8888/users w swojej przeglądarce. Powinieneś być w stanie zobaczyć uruchomioną aplikację! To proste.
Przeglądanie Kodeksu
1. Klasa Schema zasadniczo przechowuje szczegóły połączenia oraz tworzy i usuwa metody tabel. Jak widać, kolumna „photo” jest typem VARBINARY, co przekłada się na obiekt MOB w 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 Klasa users odpowiada za wszystkie operacje aplikacji w Phoenix. Możemy aktualizować/wstawiać (upsert w języku feniksa), usuwać, wyświetlać i obsługiwać transakcje dotyczące obrazów:
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 jest głównym routerem aplikacji. Zawiera całą obsługę danych wejściowych użytkownika i kierowanie ich do metod połączenia. Oddzieliłem obsługę obrazów dla łatwości użytkowania, dzięki czemu mogę uzyskać konkretny obraz dla użytkownika:
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)
Kolejne kroki, możesz użyć tego repozytorium github do przetestowania swojej aplikacji.
Mam nadzieję, że okaże się to przydatne, miłego kodowania!