Télégramme comme entrepôt de données pour les projets informatiques

Bonjour, aujourd'hui, je voudrais partager avec vous les problèmes et leurs solutions inhabituelles qui se sont rencontrés lors de la rédaction de petits projets informatiques. Je dois dire tout de suite que l'article s'adresse à ceux qui connaissent même un peu le développement des télégrammes pour les bots, les bases de données, le SQL et le langage de programmation python.

L'ensemble du projet est posté sur github, le lien sera à la fin de l'article.

image

Problème principal


Au départ, je voulais écrire pour moi un simple compteur de calories de télégramme bot, qui reçoit un nombre de l'utilisateur et renvoie le nombre de calories laissées à la normale pour la journée. Autrement dit, vous devez stocker environ deux variables pour chaque utilisateur.

En fin de compte, vous avez dû choisir un moyen de stocker ces données.

  1. Option - variables globales , mémoire vive. L'option est immédiatement un échec, car lorsque le programme plante, nous perdons tout
  2. Option - écrire dans un fichier sur le disque. Cela pourrait fonctionner pour un tel projet, mais j'avais prévu de déployer un bot sur heroku, qui efface toutes les données du disque tous les jours. Donc, cette option ne convenait pas
  3. Option - Feuilles de calcul Google . Au départ, je voulais m'attarder sur cette option, mais j'ai commencé à comprendre et à réaliser qu'il y avait une limite au nombre de requêtes dans la table, et pour commencer à utiliser la table, vous devez écrire un tas de lignes de code et comprendre leur api pas si simple
  4. L'option est une base de données . Oui, c'est la meilleure option en tout. Mais pour un tel projet, c'est même amusant à utiliser. De plus, le déploiement et la prise en charge d'une base de données sur un serveur tiers coûtera un joli centime.

En conséquence, aucune de ces options n'est apparue. Bien sûr, il existe des dizaines d'autres façons, mais je voudrais que ce soit gratuit, rapide et au moins du code.

Solution


L'idée est très simple, pour le stockage de données, nous utiliserons la base de données sqllite en mémoire, car elle est déjà intégrée à python 3 et nous sauvegardons notre table sur les serveurs Telegram avec un petit intervalle (environ toutes les 30 secondes) et sauvegardons lorsque le processus du programme est fermé.

Si le serveur tombe en panne, à la première demande, nous téléchargerons automatiquement notre table depuis le serveur Telegram et restaurerons les données sur sqllite.

Vous pouvez utiliser n'importe quelle autre base de données en mémoire comme vous le souhaitez.

Avantages


  1. Performances - en raison du travail avec les données dans la mémoire principale, la vitesse d'exécution du programme est encore plus rapide que lors de l'utilisation de la base de données sur un serveur tiers (les graphiques de la vitesse d'exécution et de test seront à la fin)
  2. Gratuit - pas besoin d'acheter des serveurs tiers pour les bases de données et toutes les données sont stockées gratuitement en tant que sauvegarde sur les serveurs Telegram
  3. Relativement fiable - si le serveur plante pour des raisons inconnues, alors nous perdons le maximum de données pour les 30 dernières secondes (intervalle de sauvegarde), pour un prototype fonctionnel ou un petit projet ce sera suffisant.
  4. Coûts minimaux lors du passage à une base de données standard - vous devez remplacer les données de connexion, supprimer le code de sauvegarde et transférer les données de la table de la sauvegarde vers une nouvelle base de données.

Inconvénients


  1. Absence de mise à l'échelle horizontale
  2. Vous avez besoin de deux comptes dans Telegram (un pour l'administrateur, l'autre pour tester l'utilisateur)
  3. Le serveur ne fonctionnera pas en Russie en raison de verrous
  4. Dans les commentaires, je pense que vous trouverez une douzaine d'autres nuances.

Il est temps de chier


Écrivons un simple clicker et exécutons des tests de vitesse.

Le bot sera écrit dans le langage de programmation python en utilisant une bibliothèque d'interaction asynchrone avec l'aïogramme du télégramme api.

La première chose à faire est de remplir les paramètres du bot, je ne vous dirai pas comment obtenir un token de BotFather, il y a déjà des centaines d'articles sur ce sujet.

Nous avons également besoin d'un deuxième compte dans un télégramme pour l'administrateur, dans lequel nos sauvegardes seront enregistrées.

Afin d'obtenir admin_id et config_id, nous devons démarrer le bot depuis le compte administrateur et écrire «admin» dans le bot, après quoi il va créer la première sauvegarde et écrire votre admin_id, config_id. Remplacez et redémarrez le bot.

#-------------------- ------------------------- #    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() 

Passons maintenant à la logique principale du bot


Si le bot reçoit un message avec le mot "admin", alors nous créons une table utilisateur avec le modèle de données suivant:

  • chatid - utilisateur d'identifiant de chat unique
  • nom - nom d'utilisateur
  • clic - nombre de clics
  • état - la valeur de la machine d'état n'est pas utilisée dans ce projet, mais vous ne pouvez pas vous en passer de plus complexes

Ajoutez un utilisateur test et envoyez le document au serveur Telegram avec notre tableau. Nous envoyons également admin_id et config_id à l'administrateur sous forme de messages. Après avoir reçu les identifiants, ce code doit être mis en commentaire.

 #    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)) 

Logique utilisateur


Tout d'abord, nous essayons d'obtenir de la base de données en mémoire les données de l'utilisateur qui a envoyé le message. Si nous détectons une erreur, chargez les données de la sauvegarde du serveur Telergam, remplissez notre base de données avec les données de la sauvegarde et essayez à nouveau de trouver l'utilisateur.

 #    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() 

Si nous avons trouvé l'utilisateur dans la base de données, nous traitons les boutons:

  • En cliquant sur «Cliquer», nous mettons à jour le nombre de clics pour cet utilisateur
  • Lorsque vous cliquez sur "Évaluation", nous affichons une liste de quinze personnes qui ont enregistré le plus de clics.

Si vous n'avez pas trouvé l'utilisateur, écrivez-lui une erreur.

  #      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, '  ') 

Écrivons la logique d'enregistrement des utilisateurs


Nous essayons de trouver l'utilisateur dans la base de données, s'il n'est pas là, puis d'ajouter une nouvelle ligne à la table et de faire une sauvegarde.

Si nous détectons une erreur, nous chargeons la dernière sauvegarde, remplissons le tableau et répétons la tentative d'enregistrement.

 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) 

Alors, eh bien, et le plus intéressant.

Sauvegarde et récupération des données du serveur Telergam


Nous déchargeons toutes les données de la table utilisateur, traduisons le dictionnaire en chaîne et modifions notre fichier, qui est stocké sur les serveurs Telegram.

  #-------------------- ------------------------- 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) 

Afin d'obtenir une sauvegarde, nous devons transmettre le message avec le fichier de l'administrateur à l'administrateur. Ensuite, obtenez le chemin d'accès au fichier, lisez les données par URL et renvoyez l'intégralité de la sauvegarde.

 # #-------------------- ------------------------- 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) 

Eh bien, c'est presque tout, il ne reste plus qu'à écrire un minuteur pour faire des sauvegardes et tester le bot.
Créez un thread qui exécute notre méthode save_data () toutes les 30 secondes.

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

Eh bien, dans le programme principal, nous démarrons le chronomètre et le bot lui-même.

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

Donc, le code semble être réglé, voici le lien du brouillon de travail vers github .

Comment courir


  1. Téléchargez le projet depuis github. Nous démarrons le projet dans n'importe quel environnement de développement pour python (par exemple: PyCharm).
  2. L'environnement de développement chargera automatiquement les bibliothèques nécessaires à partir du fichier d'exigences.
  3. Remplacer le jeton de BotFather dans main.py

  4. Nous commençons le projet
  5. Depuis le deuxième compte, cliquez / démarrez et écrivez le mot «admin»

  6. Désactivez le projet et remplissez admin_id et config_id dans le fichier main.py
  7. Nous commençons le projet et à partir du compte utilisateur, nous appuyons sur start

  8. Bénéfice

Tests et graphiques


Des tests ont été effectués sur des serveurs Heroku avec des caractéristiques d'instance minimales. On peut donc supposer que tous les tests ont été effectués dans des conditions plus ou moins égales.

Les graphiques sont fabriqués à partir d'échantillons de ~ 100 demandes-réponses. Et la moyenne de l'échantillon est présentée.

PostgreSQL sur Amazon RDS avec des caractéristiques minimales a été utilisé comme base de données sur un serveur tiers.



Avec un million d'utilisateurs, le temps de sauvegarde devient un problème.



La taille de la sauvegarde dépend entièrement de votre modèle de données, dans mon cas, avec un million d'utilisateurs, un fichier de données de 21 mégaoctets a été obtenu.



Conclusion


Cette méthode de stockage de données a du sens pour les projets jusqu'à un million d'utilisateurs. Autrement dit, pour un prototype ou une startup personnelle, cette méthode a droit à la vie.

En conséquence, nous avons obtenu un clicker entièrement autonome, indépendant des bases de données distantes.

Voici le projet décrit ci-dessus déployé sur heroku: @Clicker_fast_bot

J'ai également mis en œuvre un projet plus complexe avec cette idéologie: @Random_friend_bot

La ressemblance d'un salon de discussion à deux et d'un chatroulette, mais uniquement dans un télégramme.

Il cherche dans un rayon de 100 km d'une personne du sexe opposé à communiquer et réalise une conversation fermée avec un nouvel interlocuteur.

Si c'est intéressant, je peux me débarrasser du code source du projet. De plus, si ce sujet est pertinent, dans l'article suivant, je peux décrire la création d'API Rest sans bases de données externes. C'est une telle pile django-sqllite-Telegram.

Je serai heureux de toute critique, merci de votre attention!

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


All Articles