You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SSH批量采集设备信息时序列号重复写入问题求助

解决多线程SSH采集设备信息时序列号重复及文件写入问题

看起来你遇到的核心问题是多线程共享全局变量导致的数据覆盖,再加上文件写入逻辑的小疏漏,才会出现序列号重复、数据混乱的情况。毕竟要处理9000多台设备,线程安全和正确的IO操作至关重要,我来帮你一步步修复:

问题分析

  1. 全局变量的线程安全隐患:你定义的serial_number、hardware_address、ip_address、result都是全局变量,多个线程会同时读写这些变量。比如线程A刚获取到设备X的序列号,线程B马上把这个变量改成设备Y的,当线程A准备写入时,就会用错线程B的序列号,导致重复。
  2. 文件写入逻辑错误:
    • 每次处理一个IP都执行with open(..., 'w')写表头,这会清空文件并重复写入表头,多线程下还会互相覆盖内容。
    • result变量没有在每次处理完一个IP后重置,会不断累积所有设备的数据,导致后续写入时重复追加之前的内容。
  3. 多线程写入文件的竞态条件:多个线程同时追加写入文件,可能会导致内容错乱(比如两条记录挤在同一行)。

修复后的代码

我调整了代码逻辑,去掉全局变量,改用局部变量存储单台设备的信息,添加文件写入锁,并且只初始化一次表头:

import paramiko
import time
import threading
from queue import Queue

stb_ips = "stb_ip_alive_test.txt"
stb_serial_number_filename = 'stb_serial_numbers.txt'
# 添加文件写入锁,避免多线程同时写入冲突
file_lock = threading.Lock()

def init_output_file():
    # 只初始化一次表头,放在主线程执行
    with open(stb_serial_number_filename, 'w') as f:
        f.write('ip_address,serial_number,hardware_address\n')

def stb_serial_number(q):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        while True:
            ip = q.get()
            print('Connecting to ', ip)
            # 每个设备的信息用局部变量存储,避免线程间干扰
            serial_number = ''
            hardware_address = ''
            ip_address = ''
            
            ssh.connect(ip, port=10022, username='xxxx', password='xxxx', banner_timeout=70, timeout=70)
            # 获取序列号
            stdin, stdout, stderr = ssh.exec_command('cat /tmp/hostname\n')
            for line in stdout.readlines():
                serial_number = line.strip()
                break  # 假设hostname文件只有一行序列号
            
            # 获取MAC和IP
            stdin, stdout, stderr = ssh.exec_command('/sbin/ifconfig eth0\n')
            for mac_address_line in stdout.readlines():
                if mac_address_line.startswith('eth0'):
                    tmp = mac_address_line.split()
                    hardware_address = tmp[4]
                if 'inet' in mac_address_line:
                    tmp = mac_address_line.split()
                    ip_address = tmp[1].lstrip('addr:')
            
            # 生成当前设备的记录
            device_record = f"{ip_address},{serial_number},{hardware_address}\n"
            # 加锁写入,避免多线程冲突
            with file_lock:
                with open(stb_serial_number_filename, 'a') as f:
                    f.write(device_record)
            
            q.task_done()
    except (TimeoutError, ConnectionResetError, paramiko.SSHException) as e:
        # 错误日志也加锁,避免多线程写入混乱
        with file_lock:
            with open(f'STB_Log_File_{time.strftime("%m%d%Y%H%M%S")}.txt', 'a') as file_index:
                file_index.write(f"Error connecting to {ip}: {str(e)}\n")
    finally:
        # 确保SSH连接关闭,避免资源泄漏
        ssh.close()

def queue_input(q):
    with open(stb_ips, 'r') as f:
        ip_addresses = f.read().splitlines()
        for ip in ip_addresses:
            q.put(ip)
    q.join()

if __name__ == '__main__':
    # 先初始化输出文件,写表头
    init_output_file()
    q = Queue(maxsize=5)
    thread_count = 5
    for i in range(thread_count):
        t = threading.Thread(target=stb_serial_number, args=(q,), daemon=True)
        t.start()
    producer_thread = threading.Thread(target=queue_input, args=(q,))
    producer_thread.start()
    q.join()

关键改进点

  • 去掉全局变量:每个线程处理单台设备时,用局部变量存储该设备的序列号、MAC、IP,完全避免线程间的数据覆盖。
  • 文件写入锁:用threading.Lock()确保同一时间只有一个线程写入文件,解决多线程IO的竞态问题。
  • 表头只写一次:在主线程初始化时就写入表头,不会重复覆盖或重复写入。
  • 及时关闭SSH连接:用finally块确保无论是否出现异常,SSH连接都会被关闭,避免资源泄漏。
  • 错误日志优化:错误信息也加锁写入,同时记录具体的异常内容,方便排查问题。

这样修改后,应该就能正确采集每台设备的唯一序列号、MAC和IP,而且文件写入也不会出现混乱了,处理9000多台设备也能稳定运行。

内容的提问来源于stack exchange,提问作者Shane Heupel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:38:48