python實(shí)現(xiàn)的一個(gè)p2p文件傳輸實(shí)例
考慮到我手上的服務(wù)器逐漸的增多,有時(shí)候需要大規(guī)模的部署同一個(gè)文件,例如因?yàn)榉奖闶褂胹ystemtap這個(gè)工具定位問(wèn)題,需要把手上幾百臺(tái)服務(wù)器同時(shí)安裝kernel-debuginfo這個(gè)包,原有的方式采用一個(gè)源服務(wù)器,采用rsync或者scp之類的文件傳輸方式只能做到一個(gè)點(diǎn)往下分發(fā)這個(gè)文件,這個(gè)時(shí)候下發(fā)的速度就會(huì)比較的慢,基于以上原因,我寫(xiě)了一個(gè)基于bt協(xié)議傳輸文件的小工具,實(shí)際測(cè)試,傳輸?shù)?0個(gè)機(jī)房,70多臺(tái)機(jī)器傳輸一個(gè)240M的這個(gè)內(nèi)核文件,到所有的機(jī)器,源采用限速2m/s的上傳速度,測(cè)試的結(jié)果大概只要140s,就可以全部傳輸完畢,這個(gè)效率是非常之高,如果不限速的情況下速度會(huì)更快,下面把這個(gè)程序開(kāi)源出來(lái)。
#!/usr/bin/env python import libtorrent as lt import sys import os import time from optparse import OptionParser import socket import struct import fcntl def get_interface_ip(ifname): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24]) def ip2long(ip): return reduce(lambda a,b:(a<<8)+b,[int(i) for i in ip.split('.')]) def get_wan_ip_address(): interfaces = set(['eth0', 'eth1', 'eth2', 'eth3', 'em1', 'em2', 'em3', 'em4']) ip = '' for i in interfaces: try: ip = get_interface_ip(i) if (ip2long(ip) < ip2long('10.0.0.0') or ip2long(ip) > ip2long('10.255.255.255')) \ and (ip2long(ip) < ip2long('172.16.0.0') or ip2long(ip) > ip2long('172.33.255.255')) \ and (ip2long(ip) < ip2long('192.168.0.0') or ip2long(ip) > ip2long('192.168.255.255')): return ip except: pass return ip def make_torrent(path, save): fs = lt.file_storage() lt.add_files(fs, path) if fs.num_files() == 0: print 'no files added' sys.exit(1) input = os.path.abspath(path) basename = os.path.basename(path) t = lt.create_torrent(fs, 0, 4 * 1024 * 1024) t.add_tracker("http://10.0.1.5:8760/announce") t.set_creator('libtorrent %s' % lt.version) lt.set_piece_hashes(t, os.path.split(input)[0], lambda x: sys.stderr.write('.')) sys.stderr.write('\n') save = os.path.dirname(input) save = "%s/%s.torrent" % (save, basename) f=open(save, "wb") f.write(lt.bencode(t.generate())) f.close() print "the bt torrent file is store at %s" % save def dl_status(handle): while not (handle.is_seed()): s = handle.status() state_str = ['queued', 'checking', 'downloading metadata', \ 'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume'] print '\ractive_time: %d, %.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d, seeds: %d) %s' % \ (s.active_time, s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \ s.num_peers, s.num_seeds, state_str[s.state]), sys.stdout.flush() time.sleep(1) def seed_status(handle, seedtime=100): seedtime = int(seedtime) if seedtime < 100: seedtime = 100 while seedtime > 0: seedtime -= 1 s = handle.status() state_str = ['queued', 'checking', 'downloading metadata', \ 'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume'] print '\rseed_time: %d, %.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d, seeds: %d) %s' % \ (s.active_time, s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \ s.num_peers, s.num_seeds, state_str[s.state]), sys.stdout.flush() time.sleep(1) def remove_torrents(torrent, session): session.remove_torrent(torrent) def read_alerts(session): alert = session.pop_alert() while alert: #print alert, alert.message() alert = session.pop_alert() def download(torrent, path, upload_rate_limit=0, seedtime=100): try: session = lt.session() session.set_alert_queue_size_limit(1024 * 1024) sts = lt.session_settings() sts.ssl_listen = False sts.user_agent = "Thunder deploy system" sts.tracker_completion_timeout = 5 sts.tracker_receive_timeout = 5 sts.stop_tracker_timeout = 5 sts.active_downloads = -1 sts.active_seeds = -1 sts.active_limit = -1 sts.auto_scrape_min_interval = 5 sts.udp_tracker_token_expiry = 120 sts.min_announce_interval = 1 sts.inactivity_timeout = 60 sts.connection_speed = 10 sts.allow_multiple_connections_per_ip = True sts.max_out_request_queue = 128 sts.request_queue_size = 3 sts.use_read_cache = False session.set_settings(sts) session.set_alert_mask(lt.alert.category_t.tracker_notification | lt.alert.category_t.status_notification) session.set_alert_mask(lt.alert.category_t.status_notification) ipaddr = get_wan_ip_address() #print ipaddr if ipaddr == "": session.listen_on(6881, 6881) else: session.listen_on(6881, 6881, ipaddr) limit = int(upload_rate_limit) if limit>=100: session.set_upload_rate_limit(limit*1024) session.set_local_upload_rate_limit(limit*1024) print session.upload_rate_limit() torrent_info = lt.torrent_info(torrent) add_params = { 'save_path': path, 'storage_mode': lt.storage_mode_t.storage_mode_sparse, 'paused': False, 'auto_managed': True, 'ti': torrent_info, } handle = session.add_torrent(add_params) read_alerts(session) st = time.time() dl_status(handle) et = time.time() - st print '\nall file download in %.2f\nstart to seeding\n' % et sys.stdout.write('\n') handle.super_seeding() seed_status(handle, seedtime) remove_torrents(handle, session) assert len(session.get_torrents()) == 0 finally: print 'download finished' if __name__ == '__main__': usage = "usage: %prog [options] \n \ %prog -d -f <torrent file=""> -s <file save="" path="">\n \ or \n \ %prog -m -p <file or="" dir=""> -s <torrent save="" path="">\n" parser = OptionParser(usage=usage) parser.add_option("-d", "--download", dest="download", help="start to download file", action="store_false", default=True) parser.add_option("-f", "--file", dest="file", help="torrent file") parser.add_option("-u", "--upload", dest="upload", help="set upload rate limit, default is not limit", default=0) parser.add_option("-t", "--time", dest="time", help="set seed time, default is 100s", default=100) parser.add_option("-p", "--path", dest="path", help="to make torrent with this path") parser.add_option("-m", "--make", dest="make", help="make torrent", action="store_false", default=True) parser.add_option("-s", "--save", dest="save", help="file save path, default is store to ./", default="./") (options, args) = parser.parse_args() #download(sys.argv[1]) if len(sys.argv) != 6 and len(sys.argv) != 4 and len(sys.argv) != 8 and len(sys.argv) != 10: parser.print_help() sys.exit() if options.download == False and options.file !="": download(options.file, options.save, options.upload, options.time) elif options.make == False and options.path != "": make_torrent(options.path, options.save) </torrent></file></file></torrent>
準(zhǔn)備環(huán)境:
需要在所有的os上面安裝一個(gè)libtorrent的庫(kù),下載地址:
http://code.google.com/p/libtorrent/downloads/list
記得編譯的時(shí)候帶上./configure –enable-python-binding,然后mak,make install,進(jìn)入binding目錄,make,make install就
可以運(yùn)行這個(gè)小的工具
當(dāng)然大規(guī)模部署不可能采用每一臺(tái)都去編譯安裝的方式,只要把編譯出來(lái)的libtorrent.so libtorrent-rasterbar.so.7的文件跟bt.py這個(gè)文件放到同一個(gè)目錄,另外寫(xiě)一個(gè)shell腳本
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$lib
python bt.py -d -f <種子文件> -s <文件保存路徑> -t <做種時(shí)間> -u <限制上傳速度>
使用方法:
首先在源服務(wù)器上面生成種子文件
發(fā)布文件
在源服務(wù)器上面,執(zhí)行
其中做種時(shí)間默認(rèn)設(shè)置的是100s,上傳速度默認(rèn)不限制,限制的速度單位是KB
下面的機(jī)器,直接可以
只要有一臺(tái)機(jī)器完成了,就自動(dòng)作為種子,在下載的過(guò)程中也會(huì)上傳,任何一臺(tái)機(jī)器都可以作為源服務(wù)器,當(dāng)然了這里面還有中心的tracker服務(wù)器,腳本當(dāng)中,我搭建了一個(gè)tracker源服務(wù)器,放到10.0.1.5端口是8760上面,當(dāng)然大家也可以采用opentracker這個(gè)軟件自己搭建一個(gè)tracker服務(wù)器,修改其中的源代碼對(duì)應(yīng)部分,另外考慮到發(fā)布都是私有文件,代碼當(dāng)作已經(jīng)禁止了dht,如果還想更安全,就自己搭建一個(gè)私有的tracker server,具體搭建方法就使用一下搜索引擎,查找一下搭建的方法!
目前基本做到可以使用,后續(xù)考慮更簡(jiǎn)單一點(diǎn),采用磁力鏈接的方式,這樣就可以做到不用每臺(tái)都要拷貝一個(gè)種子文件,采用一個(gè)單獨(dú)的命令行就可以發(fā)布整個(gè)文件
相關(guān)文章
Python利用多線程枚舉實(shí)現(xiàn)獲取wifi信息
這篇文章主要為大家詳細(xì)介紹了Python如何利用枚舉字典的方式來(lái)實(shí)現(xiàn)獲取wifi信息,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-12-12flask框架使用orm連接數(shù)據(jù)庫(kù)的方法示例
這篇文章主要介紹了flask框架使用orm連接數(shù)據(jù)庫(kù)的方法,結(jié)合實(shí)例形式分析了flask框架使用flask_sqlalchemy包進(jìn)行mysql數(shù)據(jù)庫(kù)連接操作的具體步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-07-07使用Python打造高效多進(jìn)程TCP服務(wù)器
這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)多進(jìn)程的TCP服務(wù)器,通過(guò)為每個(gè)連接進(jìn)來(lái)的客戶端分配一個(gè)進(jìn)程,實(shí)現(xiàn)并發(fā)處理多個(gè)客戶端請(qǐng)求的能力,感興趣的可以了解下2024-01-01python中itertools模塊zip_longest函數(shù)詳解
itertools模塊包含創(chuàng)建高效迭代器的函數(shù),這些函數(shù)的返回值不是list,而是iterator(可迭代對(duì)象),可以用各種方式對(duì)數(shù)據(jù)執(zhí)行循環(huán)操作,今天我們來(lái)詳細(xì)探討下zip_longest函數(shù)2018-06-06python引入requests報(bào)錯(cuò)could?not?be?resolved解決方案
這篇文章主要為大家介紹了python引入requests報(bào)錯(cuò)could?not?be?resolved解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Numpy數(shù)組的廣播機(jī)制的實(shí)現(xiàn)
這篇文章主要介紹了Numpy數(shù)組的廣播機(jī)制的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Python使用Shelve保存對(duì)象方法總結(jié)
在本篇文章里我們給大家分享的是關(guān)于Python使用Shelve保存對(duì)象的知識(shí)點(diǎn)總結(jié),有興趣的朋友們學(xué)習(xí)下。2019-01-01