2011-10-27 32 views
0

我试图为dar设置一个远程备份服务器,along these lines。如果可能的话,我真的想用python来做所有的管道工作,但是我已经问过separate question这个问题。恶魔化服务器中的python子进程死锁

subprocess.Popen(cmd, shell=True)中使用netcat,我成功地进行了差异备份,就像dar网站上的示例一样。仅有的两个问题是:

  1. 我不知道如何动态地这样
  2. 分配端口号。如果我在后台执行的服务器,它失败。为什么?

更新:这似乎与netcat没有关系;即使没有netcat,它也会挂起。

这里是我的代码:

from socket import socket, AF_INET, SOCK_STREAM 
import os, sys 
import SocketServer 
import subprocess 

class DarHandler(SocketServer.BaseRequestHandler): 
    def handle(self): 
     print('entering handler') 
     data = self.request.recv(1024).strip() 
     print('got: ' + data) 
     if data == 'xform': 
      cmd1 = 'nc -dl 41201 | dar_slave archives/remotehost | nc -l 41202' 
      print(cmd1) 
      cmd2 = 'nc -dl 41200 | dar_xform -s 10k - archives/diffbackup' 
      print(cmd2) 
      proc1 = subprocess.Popen(cmd1, shell=True) 
      proc2 = subprocess.Popen(cmd2, shell=True) 
      print('sending port number') 
      self.request.send('41200') 
      print('waiting') 
      result = str(proc1.wait()) 
      print('nc-dar_slave-nc returned ' + result) 
      result = str(proc2.wait()) 
      print('nc-dar_xform returned ' + result) 
     else: 
      result = 'bad request' 
     self.request.send(result) 
     print('send result, exiting handler') 

myaddress = ('localhost', 18010) 
def server(): 
    server = SocketServer.TCPServer(myaddress, DarHandler) 
    print('listening') 
    server.serve_forever() 

def client(): 
    sock = socket(AF_INET, SOCK_STREAM) 
    print('connecting') 
    sock.connect(('localhost', 18010)) 
    print('connected, sending request') 
    sock.send('xform') 
    print('waiting for response') 
    port = sock.recv(1024) 
    print('got: ' + port) 
    try: 
     os.unlink('toslave') 
    except: 
     pass 
    os.mkfifo('toslave') 
    cmd1 = 'nc -w3 localhost 41201 < toslave' 
    cmd2 = 'nc -w3 localhost 41202 | dar -B config/test.dcf -A - -o toslave -c - | nc -w3 localhost ' + port 
    print(cmd2) 
    proc1 = subprocess.Popen(cmd1, shell=True) 
    proc2 = subprocess.Popen(cmd2, shell=True) 
    print('waiting') 
    result2 = proc2.wait() 
    result1 = proc1.wait() 
    print('nc<fifo returned: ' + str(result1)) 
    print('nc-dar-nc returned: ' + str(result2)) 
    result = sock.recv(1024) 
    print('received: ' + result) 
    sock.close() 
    print('socket closed, exiting') 

if __name__ == "__main__": 
    if sys.argv[1].startswith('serv'): 
     server() 
    else: 
     client() 

这里的服务器上会发生什么:

$ python clientserver.py serve & 
[1] 4651 
$ listening 
entering handler 
got: xform 
nc -dl 41201 | dar_slave archives/remotehost | nc -l 41202 
nc -dl 41200 | dar_xform -s 10k - archives/diffbackup 
sending port number 
waiting 

[1]+ Stopped     python clientserver.py serve 

这里的客户端上会发生什么:

$ python clientserver.py client 
connecting 
connected, sending request 
waiting for response 
got: 41200 
nc -w3 localhost 41202 | dar -B config/test.dcf -A - -o toslave -c - | nc -w3 localhost 41200 
waiting 
FATAL error, aborting operation 
Corrupted data read on pipe 
nc<fifo returned: 1 
nc-dar-nc returned: 1 

客户端也挂了,我必须用键盘中断来杀死它。

+0

为什么python需要参与? 'dar - | nc'和'nc -l |达尔似乎简单得多。 –

+0

@gringo,它_seems_更简单,但我不想手动调用所有备份。克朗不够聪明。在bash中编写备份服务器会非常痛苦。那么你究竟在暗示什么? –

+0

cron有什么问题?我没有看到为套接字使用nc和python,为什么不是其中一个? –

回答

1
  1. 使用Popen.communicate()而不是Popen.wait()

    python documentation for wait()状态:

    警告:如果子进程产生足够的输出到标准输出或标准错误管道,使得它阻止等待操作系统管缓冲区,接受更多的数据这将死锁。使用通信()来避免这种情况。

  2. Dar及其相关的可执行文件should get a -Q如果它们没有交互式运行。

  3. 当同步处理资料多个进程,请确保调用communicate()的“最薄弱环节”先上:dar_slave之前dar_xformdarcat之前。这在问题中是正确的,但值得注意。

  4. 清理共享资源。客户端进程正在打开一个dar_xform仍在读取的套接字。尝试在之后尝试发送/ recv数据在之后的初始套接字上,并且在没有关闭该套接字的情况下完成朋友将因此导致死锁。

Here is a working example其中不使用shell=True或netcat。这样做的好处是我可以动态分配辅助端口,因此可以想象同时为多个备份客户端提供服务。

1

我会减少我的损失,重新开始。这个解决方案的尝试非常复杂和复杂。有许多现成的解决方案,在该地区:如果你想采取简单的方法,rsync的+ SSH的硬核

Fwbackups听起来不错。

+0

+1为务实的方法。当我遇到这样的技术挑战时,我不幸睡不着。我认为bacula和amanda,但我想它会花费我几乎一样的时间来理解它们,因为它会推出我自己的。 rsync的问题也是诱人的,它不会像dar那样做正确的事情,再加上dar我可以加密我的备份并在amazon s3上轻松地粘贴它们。 –