关于BBR的介绍参见 BBR (很久以前写过详细文档,后来丢了,只找到了一小部分……)
在一些比较老的OpenVZ的vps(宿主机内核低于4.9版本)上,是无法开启bbr的。tcp的拥塞控制,在网络链路存在严重丢包时,会将拥塞窗口乘性减少,所以进行文件传输时,速度会越来越慢。以下介绍一下我面临scp速度慢的问题的解决办法
haproxy-lkl代理ssh端口
市面上有很多代替BBR的方案,大多都大差不差,都是使用lkl(linux kernal library)内核库绕过内核协议处理网络数据,再使用端口代理,实现在某几个端口的传输层能达到bbr的效果。
参考了ovz架构安装bbr内核一文,很方便,一建安装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | 恭喜!BBR 安装完成并成功启动
  已加速的端口: 2222
  你可以通过修改文件:     /usr/local/haproxy-lkl/etc/port-rules
  来配置需要加速的端口或端口范围。
  请使用 systemctl {start|stop|restart} haproxy-lkl 来 {开启|关闭|重启} 服务
  服务已自动加入开机启动,请放心使用。
  如果这个脚本帮到了你,你可以请作者喝瓶可乐:   https://blog.kuoruan.com/donate
  享受加速的快感吧!
   | 
 
但也有缺点,这个端口代理只能影响外部访问该端口的,不能影响由内向外的。我尝试过打开某个本地端口,再通过以下代码将文件scp到外面,但是并没有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   | 
  def transfer_and_delete_file_using_key_and_local_port(local_file_path, remote_host, remote_user, private_key_path,                                                       remote_file_path, local_port):     private_key = paramiko.RSAKey.from_private_key_file(private_key_path)     local_host = "0.0.0.0"  
           ssh = paramiko.SSHClient()          ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)     sock.bind((local_host, local_port))     sock.connect((remote_host, 22))     transport = paramiko.Transport(sock)     try:         transport.start_client()         transport.auth_publickey(remote_user, private_key)         sftp = paramiko.SFTPClient.from_transport(transport)                  remote_dir = os.path.dirname(remote_file_path)         try:             sftp.listdir(remote_dir)         except IOError:             sftp.mkdir(remote_dir)         sftp.put(local_file_path, remote_file_path)         sftp.close()         logging.info(f"local file scp finish, {local_file_path}")                  os.remove(local_file_path)         logging.info(f"local file deleted: {local_file_path}")     except Exception as e:         traceback.print_exc()         logging.error(f"exception on {local_file_path} with error {e}")     finally:                  transport.close()
 
  | 
 
http请求呼叫远端pull
我干脆就不push了,让远端pull。这里面只有一个问题,就是网络链路不稳定容易导致('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))错误
解决方案是pull接口幂等+重试
本地:
1 2 3 4 5 6 7 8 9 10 11
   | def ask_remote_scp_local_file_and_remote():     while True:         try:             resp = session.post("http://xxx", json=req,timeout=3600)             if resp:                 break         except Exception as e:             logging.error("ask remote scp fail," + e.__str__())     if resp.status_code == 200 and resp.text == 'success':         os.remove(file_path)     return
   | 
 
远端:
使用python-redis-lock包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   | def scp_from_remote_using_key(local_file_path, remote_host, remote_user, private_key_path, remote_file_path, port):          ssh = paramiko.SSHClient()     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())     try:                  private_key = paramiko.RSAKey.from_private_key_file(private_key_path)                  ssh.connect(remote_host, port=port, username=remote_user, pkey=private_key)                  sftp = ssh.open_sftp()                  local_dir = os.path.dirname(remote_file_path)         try:             os.listdir(local_dir)         except IOError:             os.mkdir(local_dir)
          sftp.get(remote_file_path, local_file_path)         sftp.close()         logging.info(f"remote file scp finish, {remote_file_path}")     except Exception as e:         logging.error(f"exception on {remote_file_path} with error {e}")     finally:                  ssh.close() def lock_and_scp(local_file_path, remote_host, remote_user, private_key_path, remote_file_path, port):     m2 = hashlib.md5()     m2.update(remote_file_path.encode('utf-8'))     md5 = m2.hexdigest()     lock = redis_lock.Lock(redis_connect, "lock_" + md5, expire=600)
      if lock.acquire(blocking=True):         if redis_connect.get("finished_" + md5) is None:             scp_from_remote_using_key(local_file_path, remote_host, remote_user, private_key_path, remote_file_path,                                       port)             redis_connect.set("finished_" + md5, "1")             redis_connect.expire("finished_" + md5, 600)         lock.release()
   |