使用Python libtorrent创建守护进程以获取100k +种子的元数据

时间:2015-09-30 13:04:54

标签: python bittorrent libtorrent magnet-uri libtorrent-rasterbar

我正在尝试使用python libtorrent每天获取大约10k +种子的元数据。

这是当前的代码流

  1. 启动libtorrent会话。
  2. 获取我们需要在过去1天内上传的元数据的总数。
  3. 从块中获取数据库中的洪流哈希
  4. 使用这些哈希创建磁力链接,并通过为每个磁铁URI创建句柄,在会话中添加这些磁力URI。
  5. 在获取元数据的同时休眠一秒,并继续检查是否找到元数据。
  6. 如果收到元数据,请将其添加到数据库中,否则检查我们是否一直在寻找大约10分钟的元数据,如果是,则删除句柄,即暂时不再查找元数据。
  7. 无限期地完成上述工作。并保存会话状态以供将来使用。
  8. 到目前为止,我已经尝试过了。

    #!/usr/bin/env python
    # this file will run as client or daemon and fetch torrent meta data i.e. torrent files from magnet uri
    
    import libtorrent as lt # libtorrent library
    import tempfile # for settings parameters while fetching metadata as temp dir
    import sys #getting arguiments from shell or exit script
    from time import sleep #sleep
    import shutil # removing directory tree from temp directory 
    import os.path # for getting pwd and other things
    from pprint import pprint # for debugging, showing object data
    import MySQLdb # DB connectivity 
    import os
    from datetime import date, timedelta
    
    session = lt.session(lt.fingerprint("UT", 3, 4, 5, 0), flags=0)
    session.listen_on(6881, 6891)
    session.add_extension('ut_metadata')
    session.add_extension('ut_pex')
    session.add_extension('smart_ban')
    session.add_extension('metadata_transfer')
    
    session_save_filename = "/magnet2torrent/magnet_to_torrent_daemon.save_state"
    
    if(os.path.isfile(session_save_filename)):
    
        fileread = open(session_save_filename, 'rb')
        session.load_state(lt.bdecode(fileread.read()))
        fileread.close()
        print('session loaded from file')
    else:
        print('new session started')
    
    session.add_dht_router("router.utorrent.com", 6881)
    session.add_dht_router("router.bittorrent.com", 6881)
    session.add_dht_router("dht.transmissionbt.com", 6881)
    session.add_dht_router("dht.aelitis.com", 6881)
    
    session.start_dht()
    session.start_lsd()
    session.start_upnp()
    session.start_natpmp()
    
    alive = True
    while alive:
    
        db_conn = MySQLdb.connect(  host = '',  user = '',  passwd = '',    db = '',    unix_socket='/mysql/mysql.sock') # Open database connection
        #print('reconnecting')
        #get all records where enabled = 0 and uploaded within yesterday 
        subset_count = 100 ;
    
        yesterday = date.today() - timedelta(1)
        yesterday = yesterday.strftime('%Y-%m-%d %H:%M:%S')
        #print(yesterday)
    
        total_count_query = ("SELECT COUNT(*) as total_count FROM content WHERE upload_date > '"+ yesterday +"' AND enabled = '0' ")
        #print(total_count_query)
        try:
            total_count_cursor = db_conn.cursor()# prepare a cursor object using cursor() method
            total_count_cursor.execute(total_count_query) # Execute the SQL command
            total_count_results = total_count_cursor.fetchone() # Fetch all the rows in a list of lists.
            total_count = total_count_results[0]
            print(total_count)
        except:
                print "Error: unable to select data"
    
        total_pages = total_count/subset_count
        #print(total_pages)
    
        current_page = 1
        while(current_page <= total_pages):
            from_count = (current_page * subset_count) - subset_count
    
            #print(current_page)
            #print(from_count)
    
            hashes = []
    
            get_mysql_data_query = ("SELECT hash FROM content WHERE upload_date > '" + yesterday +"' AND enabled = '0' ORDER BY record_num DESC LIMIT "+ str(from_count) +" , " + str(subset_count) +" ")
            #print(get_mysql_data_query)
            try:
                get_mysql_data_cursor = db_conn.cursor()# prepare a cursor object using cursor() method
                get_mysql_data_cursor.execute(get_mysql_data_query) # Execute the SQL command
                get_mysql_data_results = get_mysql_data_cursor.fetchall() # Fetch all the rows in a list of lists.
                for row in get_mysql_data_results:
                    hashes.append(row[0].upper())
            except:
                print "Error: unable to select data"
    
            #print(hashes)
    
            handles = []
    
            for hash in hashes:
                tempdir = tempfile.mkdtemp()
                add_magnet_uri_params = {
                    'save_path': tempdir,
                    'duplicate_is_error': True,
                    'storage_mode': lt.storage_mode_t(2),
                    'paused': False,
                    'auto_managed': True,
                    'duplicate_is_error': True
                }
                magnet_uri = "magnet:?xt=urn:btih:" + hash.upper() + "&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.ccc.de%3A80"
                #print(magnet_uri)
                handle = lt.add_magnet_uri(session, magnet_uri, add_magnet_uri_params)
                handles.append(handle) #push handle in handles list
    
            #print("handles length is :")
            #print(len(handles))
    
            while(len(handles) != 0):
                for h in handles:
                    #print("inside handles for each loop")
                    if h.has_metadata():
                        torinfo = h.get_torrent_info()
                        final_info_hash = str(torinfo.info_hash())
                        final_info_hash = final_info_hash.upper()
                        torfile = lt.create_torrent(torinfo)
                        torcontent = lt.bencode(torfile.generate())
                        tfile_size = len(torcontent)
                        try:
                            insert_cursor = db_conn.cursor()# prepare a cursor object using cursor() method
                            insert_cursor.execute("""INSERT INTO dht_tfiles (hash, tdata) VALUES (%s, %s)""",  [final_info_hash , torcontent] )
                            db_conn.commit()
                            #print "data inserted in DB"
                        except MySQLdb.Error, e:
                            try:
                                print "MySQL Error [%d]: %s" % (e.args[0], e.args[1])
                            except IndexError:
                                print "MySQL Error: %s" % str(e)    
    
    
                        shutil.rmtree(h.save_path())    #   remove temp data directory
                        session.remove_torrent(h) # remove torrnt handle from session   
                        handles.remove(h) #remove handle from list
    
                    else:
                        if(h.status().active_time > 600):   # check if handle is more than 10 minutes old i.e. 600 seconds
                            #print('remove_torrent')
                            shutil.rmtree(h.save_path())    #   remove temp data directory
                            session.remove_torrent(h) # remove torrnt handle from session   
                            handles.remove(h) #remove handle from list
                    sleep(1)        
                    #print('sleep1')
    
            #print('sleep10')
            #sleep(10)
            current_page = current_page + 1
    
            #save session state
            filewrite = open(session_save_filename, "wb")
            filewrite.write(lt.bencode(session.save_state()))
            filewrite.close()
    
    
        print('sleep60')
        sleep(60)
    
        #save session state
        filewrite = open(session_save_filename, "wb")
        filewrite.write(lt.bencode(session.save_state()))
        filewrite.close()
    

    我试过保持上面的脚本一夜之间运行,发现在隔夜会话中只发现了大约1200个torrent的元数据。 所以我正在寻找提高脚本性能的方法。

    我甚至尝试过对save_state文件进行解码,并注意到我连接了700多个DHT nodes。所以它不像DHT那样没有运行,

    我计划在{@ 1}}无限期地在会话中执行,而未提取元数据。如果在10分钟内没有提取元数据,10分钟后就不会删除句柄,就像我现在正在这样做。

    关于lib-torrent python绑定我几乎没有问题。

    1. 我可以继续运行多少个手柄?运行句柄有限制吗?
    2. 运行10k +或100k的句柄会减慢我的系统速度吗?或吃掉资源?如果是,那么哪些资源?我的意思是RAM,NETWORK?
    3. 我在防火墙后面,可能是阻塞的传入端口导致元数据获取速度慢?
    4. DHT服务器可以像router.bittorrent.com或任何其他BAN我的IP地址发送太多请求吗?
    5. 其他同行是否可以通过我的IP地址查看我发出的请求只是获取太多请求获取元数据?
    6. 我可以运行此脚本的多个实例吗?或者可能是多线程?它会提供更好的表现吗?
    7. 如果使用同一脚本的多个实例,每个脚本将获得唯一的node-id,具体取决于我使用的ip和端口,这是否可行?
    8. 有没有更好的方法?为了实现我的目标?

1 个答案:

答案 0 :(得分:3)

我无法回答针对libtorrent API的具体问题,但您的一些问题一般适用于bittorrent。

  

运行10k +或100k的句柄会减慢我的系统速度吗?或吃掉资源?如果是,那么哪些资源?我的意思是RAM,NETWORK?

元数据下载不应该使用太多资源,因为它们尚未完全下载torrent,即它们无法分配实际文件或类似内容。但是一旦获取元数据中的第一块,它们就需要一些ram /磁盘空间用于元数据。

  

我在防火墙后面,可能是阻塞的传入端口导致元数据获取速度慢?

是的,通过减少可以建立连接的对等体的数量,在具有低对等体数的群组上获取元数据(或者根本建立任何连接)变得更加困难。

NAT可能会导致同样的问题。

  

DHT服务器可以像router.bittorrent.com或任何其他BAN我的IP地址发送太多请求吗?

router.bittorrent.com是一个引导节点,而不是服务器本身。查找不查询单个节点,它们查询许多不同的(数百万)。但是,是的,个别节点可以禁止,或者更可能限制你。

这可以通过查找随机分布的ID来扩展DHT密钥空间的负载来缓解。

  

我可以运行此脚本的多个实例吗?或者可能是多线程?它会提供更好的表现吗?

AIUI libtorrent足够无阻塞或多线程,您可以一次安排多个种子。

我不知道libtorrent是否对传出的DHT请求有速率限制。

  

如果使用相同脚本的多个实例,每个脚本将根据我使用的ip和端口获得唯一的node-id,这是否可行?

如果您指的是DHT节点ID,那么它们来自IP(根据BEP 42),而不是端口。虽然包含了一些随机元素,但每个IP可以获得有限数量的ID。

其中一些可能也适用于您的方案:http://blog.libtorrent.org/2012/01/seeding-a-million-torrents/

另一个选项是my own DHT implementation,其中包括一个批量获取种子的CLI。