Telegramm als Data Warehouse für IT-Projekte

Guten Tag, heute möchte ich Ihnen die Probleme und ihre ungewöhnlichen Lösungen mitteilen, die sich beim Schreiben kleiner IT-Projekte ergeben haben. Ich muss gleich sagen, dass der Artikel für diejenigen gedacht ist, die sich sogar mit der Entwicklung von Telegrammen für Bots, Datenbanken, SQL und die Programmiersprache Python auskennen.

Das gesamte Projekt ist auf github veröffentlicht, der Link befindet sich am Ende des Artikels.

Bild

Hauptproblem


Anfangs wollte ich mir einen einfachen Telegramm-Bot-Kalorienzähler schreiben, der eine Nummer vom Benutzer erhält und zurückgibt, wie viele Kalorien für den Tag noch normal sind. Das heißt, Sie müssen ungefähr ein paar Variablen für jeden Benutzer speichern.

Am Ende mussten Sie einen Weg wählen, um diese Daten zu speichern.

  1. Option - globale Variablen , Direktzugriffsspeicher. Die Option ist sofort ein Fehler, denn wenn das Programm abstürzt, verlieren wir alles
  2. Option - In eine Datei auf der Festplatte schreiben . Es könnte für ein solches Projekt funktionieren, aber ich hatte vor, einen Bot auf Heroku bereitzustellen, der jeden Tag alle Daten von der Festplatte löscht. Diese Option passte also nicht
  3. Option - Google-Tabellen . Anfangs wollte ich mich mit dieser Option befassen, aber ich begann zu verstehen und erkannte, dass die Anzahl der Abfragen für die Tabelle begrenzt war. Um die Tabelle nur zu verwenden, müssen Sie eine Reihe von Codezeilen schreiben und ihre nicht so einfache API herausfinden
  4. Option ist eine Datenbank . Ja, dies ist die beste Option in allem. Aber für ein solches Projekt ist es sogar lustig, es zu verwenden. Außerdem kostet die Bereitstellung und Unterstützung einer Datenbank auf einem Server eines Drittanbieters einen hübschen Cent.

Infolgedessen kam keine dieser Optionen in Frage. Natürlich gibt es Dutzende anderer Möglichkeiten, aber ich möchte, dass es kostenlos, schnell und mit einem Minimum an Code ist.

Lösung


Die Idee ist sehr einfach: Für die Datenspeicherung verwenden wir die SQLite-Datenbank im Speicher, da sie bereits in Python 3 integriert ist, und sichern unsere Tabelle in einem kleinen Intervall (ungefähr alle 30 Sekunden) auf Telegrammservern und sichern sie, wenn der Programmprozess geschlossen wird.

Wenn der Server abstürzt, laden wir bei der ersten Anforderung automatisch unsere Tabelle vom Telegrammserver herunter und stellen die Daten auf sqllite wieder her.

Sie können jede andere in der Speicherdatenbank verwenden, wie Sie möchten.

Vorteile


  1. Leistung - Aufgrund der Arbeit mit Daten im Hauptspeicher ist die Programmausführungsgeschwindigkeit sogar noch höher als bei Verwendung der Datenbank auf einem Drittanbieter-Server (Diagramme der Ausführungs- und Testgeschwindigkeit werden am Ende angezeigt).
  2. Kostenlos - Sie müssen keine Server von Drittanbietern für Datenbanken kaufen und alle Daten werden kostenlos als Backup auf Telegrammservern gespeichert
  3. Relativ zuverlässig - Wenn der Server aus unbekannten Gründen abstürzt, verlieren wir die maximalen Daten für die letzten 30 Sekunden (Sicherungsintervall). Für einen funktionierenden Prototyp oder ein kleines Projekt ist dies ausreichend.
  4. Minimale Kosten beim Wechsel zu einer regulären Datenbank - Sie müssen die Verbindungsdaten ersetzen, den Sicherungscode entfernen und die Tabellendaten aus der Sicherung in eine neue Datenbank übertragen.

Nachteile


  1. Fehlende horizontale Skalierung
  2. Sie benötigen zwei Konten in Telegramm (eines für den Administrator, das andere zum Testen des Benutzers).
  3. Der Server funktioniert in Russland aufgrund von Sperren nicht
  4. Ich denke, in den Kommentaren finden Sie ein Dutzend anderer Nuancen.

Zeit zu scheißen


Schreiben wir einen einfachen Clicker und führen Geschwindigkeitstests durch.

Der Bot wird in der Programmiersprache Python unter Verwendung einer asynchronen Bibliothek der Interaktion mit dem API-Telegramm-Aiogramm geschrieben.

Zunächst müssen Sie die Bot-Einstellungen ausfüllen. Ich werde Ihnen nicht sagen, wie Sie ein Token von BotFather erhalten. Es gibt bereits Hunderte von Artikeln zu diesem Thema.

Wir benötigen außerdem ein zweites Konto in einem Telegramm für den Administrator, in dem unsere Backups gespeichert werden.

Um admin_id und config_id zu erhalten, müssen wir den Bot vom Administratorkonto aus starten und "admin" in den Bot schreiben. Danach wird die erste Sicherung erstellt und Ihre admin_id, config_id, geschrieben. Ersetzen Sie den Bot und starten Sie ihn erneut.

#-------------------- ------------------------- #    BotFather TOKEN = '1234567:your_token' #  logging.basicConfig(level=logging.INFO) bot = Bot(token=TOKEN) dp = Dispatcher(bot) #             admin_id=12345678 config_id=12345 conn = sqlite3.connect(":memory:") #  in memory  cursor = conn.cursor() 

Lassen Sie uns nun die Hauptlogik des Bots durchgehen


Wenn der Bot eine Nachricht mit dem Wort "admin" erhält, erstellen wir eine Benutzertabelle mit dem folgenden Datenmodell:

  • chatid - eindeutiger Chat-ID-Benutzer
  • Name - Benutzername
  • click - Anzahl der Klicks
  • state - Der Wert für die Zustandsmaschine wird in diesem Projekt nicht verwendet, aber Sie können nicht auf komplexere verzichten

Fügen Sie einen Testbenutzer hinzu und senden Sie das Dokument mit unserer Tabelle an den Telegrammserver. Wir senden dem Administrator auch admin_id und config_id in Form von Nachrichten. Nach Erhalt der IDs muss dieser Code auskommentiert werden.

 #    if message.text == 'admin': cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)") cursor.execute("INSERT INTO users VALUES (1234, 'eee', 1,0)") conn.commit() sql = "SELECT * FROM users " cursor.execute(sql) data = cursor.fetchall() str_data = json.dumps(data) await bot.send_document(message.chat.id, io.StringIO(str_data)) await bot.send_message(message.chat.id, 'admin_id = {}'.format(message.chat.id)) await bot.send_message(message.chat.id, 'config_id = {}'.format(message.message_id+1)) 

Benutzerlogik


Zunächst versuchen wir, die Daten des Benutzers, der die Nachricht gesendet hat, aus der In-Memory-Datenbank abzurufen. Wenn wir einen Fehler feststellen, laden Sie die Daten aus der Telergam-Server-Sicherung, füllen Sie unsere Datenbank mit Daten aus der Sicherung und versuchen Sie erneut, den Benutzer zu finden.

 #    try: sql = "SELECT * FROM users where chatid={}".format(message.chat.id) cursor.execute(sql) data = cursor.fetchone() # or use fetchone() except Exception: data = await get_data() cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)") cursor.executemany("INSERT INTO users VALUES (?,?,?,?)", data) conn.commit() sql = "SELECT * FROM users where chatid={}".format(message.chat.id) cursor.execute(sql) data = cursor.fetchone() # or use fetchone() 

Wenn wir den Benutzer in der Datenbank gefunden haben, verarbeiten wir die Schaltflächen:

  • Durch Klicken auf "Klicken" aktualisieren wir die Anzahl der Klicks für diesen Benutzer
  • Wenn Sie auf "Bewertung" klicken, wird eine Liste mit fünfzehn Personen angezeigt, die die meisten Klicks haben.

Wenn Sie den Benutzer nicht gefunden haben, schreiben Sie ihm einen Fehler.

  #      click     if data is not None: if message.text == '': sql = "UPDATE users SET click = {} WHERE chatid = {}".format(data[2]+1,message.chat.id) cursor.execute(sql) conn.commit() await bot.send_message(message.chat.id, ': {} '.format(data[2]+1)) #        10 if message.text == '': sql = "SELECT * FROM users ORDER BY click DESC LIMIT 15" cursor.execute(sql) newlist = cursor.fetchall() # or use fetchone() sql_count = "SELECT COUNT(chatid) FROM users" cursor.execute(sql_count) count=cursor.fetchone() rating=': {}\n'.format(count[0]) i=1 for user in newlist: rating=rating+str(i)+': '+user[1]+' - '+str(user[2])+'\n' i+=1 await bot.send_message(message.chat.id, rating) else: await bot.send_message(message.chat.id, '  ') 

Schreiben wir die Logik für die Benutzerregistrierung


Wir versuchen, den Benutzer in der Datenbank zu finden. Wenn er nicht vorhanden ist, fügen Sie der Tabelle eine neue Zeile hinzu und erstellen Sie eine Sicherung.

Wenn wir einen Fehler feststellen, laden wir die letzte Sicherung, füllen die Tabelle aus und wiederholen den Registrierungsversuch.

 sql_select = "SELECT * FROM users where chatid={}".format(message.chat.id) sql_insert = "INSERT INTO users VALUES ({}, '{}', {},{})".format(message.chat.id,message.chat.first_name, 0, 0) try: cursor.execute(sql_select) data = cursor.fetchone() if data is None: cursor.execute(sql_insert) conn.commit() await save_data() except Exception: data = await get_data() cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)") cursor.executemany("INSERT INTO users VALUES (?,?,?,?)", data) conn.commit() cursor.execute(sql_select) data = cursor.fetchone() if data is None: cursor.execute(sql_insert) conn.commit() await save_data() #   button = KeyboardButton('') button2 = KeyboardButton('') #  kb = ReplyKeyboardMarkup(resize_keyboard=True).add(button).add(button2) #     await bot.send_message(message.chat.id,' {}'.format(message.chat.first_name),reply_markup=kb) 

Also gut und das interessanteste.

Speichern und Abrufen von Daten vom Telergam-Server


Wir entladen alle Daten aus der Benutzertabelle, übersetzen das Wörterbuch in eine Zeichenfolge und ändern unsere Datei, die auf Telegrammservern gespeichert ist.

  #-------------------- ------------------------- async def save_data(): sql = "SELECT * FROM users " cursor.execute(sql) data = cursor.fetchall() # or use fetchone() try: #     str_data=json.dumps(data) #      await bot.edit_message_media(InputMediaDocument(io.StringIO(str_data)), admin_id, config_id) except Exception as ex: print(ex) 

Um ein Backup zu erhalten, müssen wir die Nachricht mit der Datei vom Administrator an den Administrator weiterleiten. Holen Sie sich dann den Pfad zur Datei, lesen Sie die Daten per URL und geben Sie die gesamte Sicherung zurück.

 # #-------------------- ------------------------- async def get_data(): #         forward_data = await bot.forward_message(admin_id, admin_id, config_id) #    ,   file_data = await bot.get_file(forward_data.document.file_id) #    url file_url_data = bot.get_file_url(file_data.file_path) #     json_file= urlopen(file_url_data).read() #    json     return json.loads(json_file) 

Nun, das ist fast alles, es bleibt nur ein Timer zu schreiben, um Backups zu erstellen und den Bot zu testen.
Erstellen Sie einen Thread, der alle 30 Sekunden unsere Methode save_data () ausführt.

 def timer_start(): threading.Timer(30.0, timer_start).start() try: asyncio.run_coroutine_threadsafe(save_data(),bot.loop) except Exception as exc: pass 

Nun, im Hauptprogramm starten wir den Timer und den Bot selbst.

 #-------------------- ------------------------- if __name__ == '__main__': timer_start() executor.start_polling(dp, skip_updates=True) 

Der Code scheint also aussortiert zu sein. Hier ist der Link des Arbeitsentwurfs zu Github .

Wie man läuft


  1. Laden Sie das Projekt von github herunter. Wir starten das Projekt in jeder Entwicklungsumgebung für Python (zum Beispiel: PyCharm).
  2. Die Entwicklungsumgebung lädt automatisch die erforderlichen Bibliotheken aus der Anforderungsdatei.
  3. Ersetzen Sie das Token von BotFather in main.py.

  4. Wir starten das Projekt
  5. Klicken / starten Sie im zweiten Konto und schreiben Sie das Wort "admin".

  6. Schalten Sie das Projekt aus und geben Sie admin_id und config_id in die Datei main.py ein
  7. Wir starten das Projekt und drücken über das Benutzerkonto auf Start

  8. Gewinn

Tests und Grafiken


Tests wurden auf Heroku-Servern mit minimalen Instanzeigenschaften durchgeführt. Wir können also davon ausgehen, dass alle Tests unter mehr oder weniger gleichen Bedingungen durchgeführt wurden.

Die Grafiken werden aus Beispielen von ~ 100 Anfrage-Antworten erstellt. Und der Durchschnitt der Stichprobe wird dargestellt.

PostgreSQL auf Amazon RDS mit minimalen Merkmalen wurde als Datenbank auf einem Server eines Drittanbieters verwendet.



Bei einer Million Benutzern wird die Sicherungszeit zum Problem.



Die Größe der Sicherung hängt vollständig von Ihrem Datenmodell ab. In meinem Fall wurde mit einer Million Benutzern eine Datendatei mit 21 Megabyte abgerufen.



Fazit


Diese Methode der Datenspeicherung ist für Projekte mit bis zu einer Million Benutzern sinnvoll. Das heißt, für einen Prototyp oder ein persönliches Startup hat diese Methode das Recht auf Leben.

Als Ergebnis haben wir einen vollständig eigenständigen Clicker erhalten, der von entfernten Datenbanken unabhängig ist.

Hier ist das oben beschriebene Projekt, das für Heroku bereitgestellt wurde: @Clicker_fast_bot

Ich habe auch ein komplexeres Projekt mit dieser Ideologie implementiert: @Random_friend_bot

Die Ähnlichkeit eines Zwei-Personen-Chatrooms und eines Chatroulettes, jedoch nur in einem Telegramm.

Er sucht im Umkreis von 100 km um eine Person des anderen Geschlechts nach Kommunikation und realisiert einen geschlossenen Chat mit einem neuen Gesprächspartner.

Wenn es interessant ist, kann ich den Quellcode des Projekts wegwerfen. Wenn dieses Thema relevant ist, kann ich im nächsten Artikel die Erstellung der Rest-API ohne externe Datenbanken beschreiben. Das ist so ein Django-Sqllite-Telegramm-Stack.

Ich freue mich über jede Kritik, danke für Ihre Aufmerksamkeit!

Source: https://habr.com/ru/post/de464865/


All Articles