• python运维开发(十二)rabbitMQ、pymysql、SQLAlchemy


    内容目录:

    • rabbitMQ
    • python操作mysql,pymysql模块
    • Python ORM框架,SQLAchemy模块
    • Paramiko
    • 其他with上下文切换

    rabbitMQ

    RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。

    MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

    1、基本安装配置使用

    安装

    ubuntu系统上安装rabbitmq
    sudo apt install erlang
    sudo apt install rabbitmq-server
    #拷贝配置文件,否则连接rabbitmq会报错
    cp /usr/share/doc/rabbitmq-server/rabbitmq.config.example.gz /etc/rabbitmq/
    cd  /etc/rabbitmq/
    gunzip rabbitmq.config.example.gz
    /etc/init.d/rabbitmq-server start #启动
     
    centos上
    安装配置epel源
       $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
      
     
    安装erlang
       $ yum -y install erlang
      
    安装RabbitMQ
       $ yum -y install rabbitmq-server
     
    service rabbitmq-server start/stop
     
    python操作的客户端需要安装pika模块来操作rabbitmq
     
    python3 -m pip install pika

    使用

    基于queue方式实现的消费者和生产者模型

    import queue
    import threading
    import time
    
    message = queue.Queue(10)
    
    
    def producer(i):#生产者方法
        while True:
            message.put('生产--'+str(i)) #添加到队列
            time.sleep(1)
    
    
    def consumer(i):#消费者方法
        msg = message.get() #从队列中取消息
        print(msg)
    
    
    
    
    for i in range(3):#创建2个任务线程来生产
        t = threading.Thread(target=producer, args=(i,))
        t.start()
    
    for i in range(10):#创建5个消费者任务来消费
        t = threading.Thread(target=consumer, args=(i,))
        t.start()
    

    对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是某台服务器上的RabbitMQ Server实现的消息队列。

     生产者

    import pika
    
    connection = pika.BlockingConnection(pika.ConnectionParameters
                                         (host='192.168.139.137')) #创建连接
    channel = connection.channel() #创建频道
    
    channel.queue_declare(queue='hello') #定义队列名,和订阅者的一致
    
    channel.basic_publish(exchange='',routing_key='hello', 
                          body='Hello World!')#发布消息
    print(" [x] Sent 'Hello World!'")
    connection.close()
    

      

    消费者

    import pika
    connection = pika.BlockingConnection(pika.ConnectionParameters
                                         (host='192.168.139.137')) #建立连接
    channel = connection.channel()#创建频道
    
    channel.queue_declare\
        (queue='hello') #定义一个rabbitMQ下的一个队列名,与发布者一致
    
    
    def callback(ch, method, properties, body):#回调函数
        print(" [x] Received %r" % body)
    
    
    channel.basic_consume(callback,
                          queue='hello',
                          no_ack=True)#指定队列名核函数准备执行接收消息
    
    print(' [*] Waiting for messages. To exit press CTRL+C')
    channel.start_consuming()#开始接收消息
    

      

    2、acknowledgment 消息不丢失

    no-ack = False,如果消费者遇到情况(its channel is closed, connection is closed, or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中。

     我们在操作中只需在订阅者上添加no-ack=False设置就行,生产者不变

    消费者代码:

    import pika
    import  time
    
    connection = pika.BlockingConnection(pika.ConnectionParameters
                                         (host='192.168.139.137')) #建立连接
    channel = connection.channel()#创建频道
    
    channel.queue_declare\
        (queue='hello') #定义一个rabbitMQ下的一个队列名
    
    
    def callback(ch, method, properties, body):#回调函数
        print(" [x] Received %r" % body)
        time.sleep(3)
        print('ok')
        ch.basic_ack(delivery_tag = method.delivery_tag)
    
    
    channel.basic_consume(callback,
                          queue='hello',
                          no_ack=False)#此处设置no_ack=False 
                                      # 表示中途断开rabbitmq会将将改任务添加到队列中
    
    print(' [*] Waiting for messages. To exit press CTRL+C')
    channel.start_consuming()#开始接收消息

    3、durable   消息不丢失

    生产者

    import pika
    
    connection = pika.BlockingConnection(pika.ConnectionParameters
                                         (host='192.168.139.137')) #创建连接
    channel = connection.channel() #创建频道
    
    channel.queue_declare(queue='hello',
                          durable=True) #定义队列名,和接收者的一致
    
    channel.basic_publish(exchange='',routing_key='hello',
                          properties=pika.BasicProperties
                          (delivery_mode=2),#make message persistent
                          body='Hello World!') #发布消息
    print(" [x] Sent 'Hello World!'")
    connection.close()

    消费者

    import pika
    import  time
    
    connection = pika.BlockingConnection(pika.ConnectionParameters
                                         (host='192.168.139.137')) #建立连接
    channel = connection.channel()#创建频道
    
    channel.queue_declare(queue='hello',
                          durable=True) #定义一个rabbitMQ下的一个队列名
    
    
    def callback(ch, method, properties, body):#回调函数
        print(" [x] Received %r" % body)
        time.sleep(3)
        print('ok')
        ch.basic_ack(delivery_tag = method.delivery_tag)
    
    
    channel.basic_consume(callback,
                          queue='hello',
                          no_ack=False)#此处设置no_ack=False 
                                      # 表示中途断开rabbitmq会将将改任务添加到队列中
    
    print(' [*] Waiting for messages. To exit press CTRL+C')
    channel.start_consuming()#开始接收消息
    

    4、消息获取顺序

    默认消息队列里的数据是按照顺序被消费者拿走,例如:消费者1 去队列中获取 奇数 序列的任务,消费者2去队列中获取 偶数 序列的任务。

    channel.basic_qos(prefetch_count=1) 表示谁来谁取,不再按照奇偶数排列

    订阅者代码和发布者均取消durable=True部分,测试时候只要开启多个订阅者执行就能看到第一个订阅者收第一个消息,第二个订阅者接收第二条消息。。。

    消费者

    import pika
    import  time
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.139.137')) #建立连接
    channel = connection.channel()#创建频道
    
    #channel.queue_declare(queue='hello',durable=True) #定义一个rabbitMQ下的一个队列名
    channel.queue_declare(queue='hello') #定义一个rabbitMQ下的一个队列名
    
    
    def callback(ch, method, properties, body):#回调函数
        print(" [x] Received %r" % body)
        time.sleep(3)
        print('ok')
        ch.basic_ack(delivery_tag = method.delivery_tag)
    
    channel.basic_qos(prefetch_count=1)
    
    channel.basic_consume(callback,queue='hello',no_ack=False)#此处设置no_ack=False 表示中途断开rabbitmq会将将改任务添加到队列中
    
    print(' [*] Waiting for messages. To exit press CTRL+C')
    channel.start_consuming()#开始接收消息
    消费者

    生产者

    import pika
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.139.137')) #创建连接
    channel = connection.channel() #创建频道
    
    # channel.queue_declare(queue='hello',durable=True) #定义队列名,和接收者的一致
    channel.queue_declare(queue='hello') #定义队列名,和接收者的一致
    
    channel.basic_publish(exchange='',routing_key='hello',
                          properties=pika.BasicProperties(delivery_mode=2),#make message persistent
                          body='Hello World!') #发布消息
    print(" [x] Sent 'Hello World!'")
    connection.close()
    生产者

    5、发布订阅

    发布订阅和简单的消息队列区别在于,发布订阅会将消息发送给所有的订阅者,而消息队列中的数据被消费一次便消失。所以,RabbitMQ实现发布和订阅时,会为每一个订阅者创建一个队列,而发布者发布消息时,会将消息放置在所有相关队列中。

     exchange type = fanout

    测试过程中可以执行多个订阅者代码窗口,执行发布者代码发布消息,所有打开的订阅者窗口都会收到消息。

    import pika
    import sys
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(
            host='192.168.62.168'))#建立连接
    channel = connection.channel()#创建频道
    
    channel.exchange_declare(exchange='logs',
                             type='fanout')#定义exchange,类型为fanout,fanout即为后端多个队列情况
    
    message = ' '.join(sys.argv[1:]) or "info: Hello World!"#发送的消息
    channel.basic_publish(exchange='logs',
                          routing_key='',
                          body=message)
    print(" [x] Sent %r" % message)
    connection.close()
    发布者
    import pika
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(
            host='192.168.62.168'))#建立rabbitMQ连接
    channel = connection.channel()
    
    channel.exchange_declare(exchange='logs',
                             type='fanout')#定义exchange由exchange来指定后端队列
    
    result = channel.queue_declare(exclusive=True)#后端队列随机命名
    queue_name = result.method.queue#队列名字
    
    channel.queue_bind(exchange='logs',
                       queue=queue_name)#将exchange和队列进行绑定
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r" % body)
    
    channel.basic_consume(callback,
                          queue=queue_name,
                          no_ack=True)#调用callback函数
    
    channel.start_consuming()#执行
    订阅者

    6、关键字发送

    上面的事例中,发送消息时明确指定某个队列并向其中发送消息,RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

    exchange type = direct

    import pika
    import sys
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='192.168.62.168'))  # 建立rabbitMQ连接
    channel = connection.channel()#创建频道
    
    channel.exchange_declare(exchange='direct_logs',
                             type='direct')#关键字匹配type类型为direct
    
    severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
    '''
    如果输入的参数长度大于1则将第一个参数传给serverity,否则小于1的话将inf传给serverity
    '''
    message = ' '.join(sys.argv[2:]) or 'Hello World!'
    #将第二个参数传做字符串拼接传给message,或者将Hello World传给message
    channel.basic_publish(exchange='direct_logs',
                          routing_key=severity,
                          body=message)
    print(" [x] Sent %r:%r" % (severity, message))
    connection.close()
    发布者
    import pika
    import sys
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='192.168.62.168'))  # 建立rabbitMQ连接
    channel = connection.channel()
    
    channel.exchange_declare(exchange='direct_logs',
                             type='direct')
    
    result = channel.queue_declare(exclusive=True)
    queue_name = result.method.queue
    
    severities = sys.argv[1:]#执行时候需要传代参数,如果不对的话提示如下说明并推出程序,输入的参数做为关键字
    if not severities:
        sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
        sys.exit(1)
    
    for severity in severities:#对输入的参数关键字进行循环,如果匹配上关键字就输出
        channel.queue_bind(exchange='direct_logs',
                           queue=queue_name,
                           routing_key=severity)
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r:%r" % (method.routing_key, body))
    
    channel.basic_consume(callback,
                          queue=queue_name,
                          no_ack=True)
    
    channel.start_consuming()
    订阅者

    7、模糊匹配

    在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。

    exchange type = topic

    • # 表示可以匹配 0 个 或 多个 单词
    • *  表示只能匹配 一个 单词
    发送者路由值              队列中
    test.jabe.com          test.*  -- 不匹配
    test.jabe.com          test.#  -- 匹配
    
    import pika
    import sys
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='192.168.62.168'))  # 建立rabbitMQ连接
    channel = connection.channel()#创建频道
    
    
    channel.exchange_declare(exchange='topic_logs',
                             type='topic')#模糊匹配type类型为topic
    
    routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
    message = ' '.join(sys.argv[2:]) or 'Hello World!'
    channel.basic_publish(exchange='topic_logs',
                          routing_key=routing_key,
                          body=message)
    print(" [x] Sent %r:%r" % (routing_key, message))
    connection.close()
    发布者
    import pika
    import sys
    
    connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='192.168.62.168'))  # 建立rabbitMQ连接
    channel = connection.channel()
    
    channel.exchange_declare(exchange='topic_logs',
                             type='topic')
    
    result = channel.queue_declare(exclusive=True)
    queue_name = result.method.queue
    
    binding_keys = sys.argv[1:]
    if not binding_keys:
        sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
        sys.exit(1)
    
    for binding_key in binding_keys:
        channel.queue_bind(exchange='topic_logs',
                           queue=queue_name,
                           routing_key=binding_key)
    
    print(' [*] Waiting for logs. To exit press CTRL+C')
    
    def callback(ch, method, properties, body):
        print(" [x] %r:%r" % (method.routing_key, body))
    
    channel.basic_consume(callback,
                          queue=queue_name,
                          no_ack=True)
    
    channel.start_consuming()
    订阅者

    pymysql模块

    pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb(py2中)几乎相同。

    安装pymysql模块,在py2中模块名为MySQLdb

    安装

    python3 -m pip install pymysql

    使用

    1、执行SQL

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import pymysql
     
    # 创建连接
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    # 创建游标
    cursor = conn.cursor()
     
    # 执行SQL,并返回收影响行数
    effect_row = cursor.execute("update hosts set host = '1.1.1.2'")
     
    # 执行SQL,并返回受影响行数
    #effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
     
    # 执行SQL,并返回受影响行数
    #effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
     
     
    # 提交,不然无法保存新建或者修改的数据
    conn.commit()
     
    # 关闭游标
    cursor.close()
    # 关闭连接
    conn.close()
    插入、修改

    2、获取新创建数据自增ID

    import pymysql
     
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    cursor = conn.cursor()
    cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
    conn.commit()
    cursor.close()
    conn.close()
     
    # 获取最新自增ID
    new_id = cursor.lastrowid
    auto_increment

    3、获取查询数据

    import pymysql
     
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
    cursor = conn.cursor()
    cursor.execute("select * from hosts")
     
    # 获取第一行数据
    row_1 = cursor.fetchone()
     
    # 获取前n行数据
    # row_2 = cursor.fetchmany(3)
    # 获取所有数据
    # row_3 = cursor.fetchall()
     
    conn.commit()
    cursor.close()
    conn.close()
    
    '''
    注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:
    cursor.scroll(1,mode='relative')  # 相对当前位置移动
    cursor.scroll(2,mode='absolute') # 相对绝对位置移动
    '''
    View Code

    4、fetch数据类型

    关于默认获取的数据是元组类型,如果想要或者字典类型的数据,即:

    import pymysql
     
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
     
    # 游标设置为字典类型
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    r = cursor.execute("call p1()")
     
    result = cursor.fetchone()
     
    conn.commit()
    cursor.close()
    conn.close()
    fetch

    SQLAchemy模块

    QLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

    Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

    MySQL-Python
        mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
     
    pymysql
        mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
     
    MySQL-Connector
        mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
     
    cx_Oracle
        oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
     
    更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
    dailect

    步骤一:

    使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。

    from sqlalchemy import create_engine
     
     
    engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)
     
    engine.execute(
        "INSERT INTO ts_test (a, b) VALUES ('2', 'v1')"
    )
     
    engine.execute(
         "INSERT INTO ts_test (a, b) VALUES (%s, %s)",
        ((555, "v1"),(666, "v1"),)
    )
    engine.execute(
        "INSERT INTO ts_test (a, b) VALUES (%(id)s, %(name)s)",
        id=999, name="v1"
    )
     
    result = engine.execute('select * from ts_test')
    result.fetchall()
    

    事务操作

    from sqlalchemy import create_engine
    
    
    engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)
    
    
    # 事务操作
    with engine.begin() as conn:
        conn.execute("insert into table (x, y, z) values (1, 2, 3)")
        conn.execute("my_special_procedure(5)")
        
        
    conn = engine.connect()
    # 事务操作 
    with conn.begin():
           conn.execute("some statement", {'x':5, 'y':10})
    事务操作

    注:查看数据库连接:show status like 'Threads%';

    步骤二:

    使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 进行数据库操作。Engine使用Schema Type创建一个特定的结构对象,之后通过SQL Expression Language将该对象转换成SQL语句,然后通过 ConnectionPooling 连接数据库,再然后通过 Dialect 执行SQL,并获取结果。

    from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
     
    metadata = MetaData()
     
    user = Table('user', metadata,
        Column('id', Integer, primary_key=True),
        Column('name', String(20)),
    )
     
    color = Table('color', metadata,
        Column('id', Integer, primary_key=True),
        Column('name', String(20)),
    )
    engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)
     
    metadata.create_all(engine)
    # metadata.clear()
    # metadata.remove()
    

    增删改查操作

    from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
    
    metadata = MetaData()
    
    user = Table('user', metadata,
        Column('id', Integer, primary_key=True),
        Column('name', String(20)),
    )
    
    color = Table('color', metadata,
        Column('id', Integer, primary_key=True),
        Column('name', String(20)),
    )
    engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)
    
    conn = engine.connect()
    
    # 创建SQL语句,INSERT INTO "user" (id, name) VALUES (:id, :name)
    conn.execute(user.insert(),{'id':7,'name':'seven'})
    conn.close()
    
    # sql = user.insert().values(id=123, name='wu')
    # conn.execute(sql)
    # conn.close()
    
    # sql = user.delete().where(user.c.id > 1)
    
    # sql = user.update().values(fullname=user.c.name)
    # sql = user.update().where(user.c.name == 'jack').values(name='ed')
    
    # sql = select([user, ])
    # sql = select([user.c.id, ])
    # sql = select([user.c.name, color.c.name]).where(user.c.id==color.c.id)
    # sql = select([user.c.name]).order_by(user.c.name)
    # sql = select([user]).group_by(user.c.name)
    
    # result = conn.execute(sql)
    # print result.fetchall()
    # conn.close()
    增删改查

    注:SQLAlchemy无法修改表结构,如果需要可以使用SQLAlchemy开发者开源的另外一个软件Alembic来完成。

    步骤三:

    使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有组件对数据进行操作。根据类创建对象,对象转换成SQL,执行SQL。

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy import create_engine
     
    engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)
     
    Base = declarative_base()
     
     
    class User(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)
        name = Column(String(50))
     
    # 寻找Base的所有子类,按照子类的结构在数据库中生成对应的数据表信息
    # Base.metadata.create_all(engine)
     
    Session = sessionmaker(bind=engine)
    session = Session()
     
     
    # ########## 增 ##########
    # u = User(id=2, name='sb')
    # session.add(u)
    # session.add_all([
    #     User(id=3, name='sb'),
    #     User(id=4, name='sb')
    # ])
    # session.commit()
     
    # ########## 删除 ##########
    # session.query(User).filter(User.id > 2).delete()
    # session.commit()
     
    # ########## 修改 ##########
    # session.query(User).filter(User.id > 2).update({'cluster_id' : 0})
    # session.commit()
    # ########## 查 ##########
    # ret = session.query(User).filter_by(name='sb').first()
     
    # ret = session.query(User).filter_by(name='sb').all()
    # print ret
     
    # ret = session.query(User).filter(User.name.in_(['sb','bb'])).all()
    # print ret
     
    # ret = session.query(User.name.label('name_label')).all()
    # print ret,type(ret)
     
    # ret = session.query(User).order_by(User.id).all()
    # print ret
     
    # ret = session.query(User).order_by(User.id)[1:3]
    # print ret
    # session.commit()
    operation

    Paramiko模块

    paramiko模块,基于SSH用于连接远程服务器并执行相关操作。

    1、安装模块

    python3 -m pip install paramiko

    2、使用

    SSHClient

    用于连接远程服务器并执行基本命令

    import paramiko
    ssh = paramiko.SSHClient() # 创建SSH对象
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())# 允许连接不在know_hosts文件中的主机
    
    ssh.connect(hostname='192.168.139.137',port=22,username='jabe',password='1234')# 连接服务器
    
    stdin,stdout,stderr = ssh.exec_command('ls')# 执行命令
    
    resault = stdout.read()# 获取命令结果
    
    print(str(resault,encoding='utf-8')) #打印
    ssh.close()
    ssh
    import paramiko
    
    transport = paramiko.Transport(('192.168.139.137', 22))
    transport.connect(username='jabe', password='1234')
    
    ssh = paramiko.SSHClient()
    ssh._transport = transport
    
    stdin, stdout, stderr = ssh.exec_command('df')
    resault = stdout.read()# 获取命令结果
    print(str(resault,encoding='utf-8'))
    
    transport.close()
    SSHClient 封装 Transport

    SFTPClient

    用于连接远程服务器并执行上传下载

    基于用户名密码上传下载:

    import paramiko
    
    transport = paramiko.Transport(('192.168.139.137',22))
    transport.connect(username='jabe',password='1234')
    
    sftp = paramiko.SFTPClient.from_transport(transport)
    
    #sftp.put('s1.py', '/tmp/test.py')  # 将s1.py 上传至服务器 /tmp/test.py
    
    sftp.get('/tmp/test.py', 'download.py') # 将remove_path 下载到本地 local_path
    
    transport.close()
    SFTP

    基于公钥密钥上传下载:

    import paramiko
      
    private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
      
    transport = paramiko.Transport(('hostname', 22))
    transport.connect(username='wupeiqi', pkey=private_key )
      
    sftp = paramiko.SFTPClient.from_transport(transport)
    # 将location.py 上传至服务器 /tmp/test.py
    sftp.put('/tmp/location.py', '/tmp/test.py')
    # 将remove_path 下载到本地 local_path
    sftp.get('remove_path', 'local_path')
      
    transport.close()
    key

    简单应用

    import paramiko
    import uuid
    
    class SSHConnection(object):
    
        def __init__(self, host='172.16.103.191', port=22, username='wupeiqi',pwd='123'):
            self.host = host
            self.port = port
            self.username = username
            self.pwd = pwd
            self.__k = None
    
        def create_file(self):
            file_name = str(uuid.uuid4())
            with open(file_name,'w') as f:
                f.write('sb')
            return file_name
    
        def run(self):
            self.connect()
            self.upload('/home/wupeiqi/tttttttttttt.py')
            self.rename('/home/wupeiqi/tttttttttttt.py', '/home/wupeiqi/ooooooooo.py)
            self.close()
    
        def connect(self):
            transport = paramiko.Transport((self.host,self.port))
            transport.connect(username=self.username,password=self.pwd)
            self.__transport = transport
    
        def close(self):
    
            self.__transport.close()
    
        def upload(self,target_path):
            # 连接,上传
            file_name = self.create_file()
    
            sftp = paramiko.SFTPClient.from_transport(self.__transport)
            # 将location.py 上传至服务器 /tmp/test.py
            sftp.put(file_name, target_path)
    
        def rename(self, old_path, new_path):
    
            ssh = paramiko.SSHClient()
            ssh._transport = self.__transport
            # 执行命令
            cmd = "mv %s %s" % (old_path, new_path,)
            stdin, stdout, stderr = ssh.exec_command(cmd)
            # 获取命令结果
            result = stdout.read()
    
        def cmd(self, command):
            ssh = paramiko.SSHClient()
            ssh._transport = self.__transport
            # 执行命令
            stdin, stdout, stderr = ssh.exec_command(command)
            # 获取命令结果
            result = stdout.read()
            return result
            
    
    
    ha = SSHConnection()
    ha.run()
    apply
    # 对于更多限制命令,需要在系统中设置
    /etc/sudoers
      
    Defaults    requiretty
    Defaults:cmdb    !requiretty

    with上下文切换

    with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。

    import contextlib
    
    
    @contextlib.contextmanager
    def worker_state(state_list, worker_thread):
    
    	state_list.append(worker_thread)
    	print(state_list)
    	try:
    		yield
    	finally:
    		state_list.remove(worker_thread)
    		print(state_list)
    
    free_list = []
    current_thread = "test_list"
    with worker_state(free_list, current_thread):
    	'''执行worker_stat函数,遇到yield跳出,执行下面的程序,执行完之后执行finally的代码块'''
    	print(123)
    	print(456)

    执行顺序如图所示

     

    我们可以将socket做成with形式,其他的类似功能都可以这样来实现,用完之后自动关闭

    import contextlib
    import socket
    
    @contextlib.contextmanager
    def context_socket(host, port):
        sk = socket.socket()
        sk.bind((host,port))
        sk.listen(5)
        try:
            yield sk
        finally:
            sk.close()
    
    with context_socket('127.0.0.1', 8888) as sock:#将函数的yield以前的部分集成到sock中,
        print(sock) 

    参考url:

    http://www.cnblogs.com/wupeiqi/articles/5132791.html

    http://www.cnblogs.com/wupeiqi/articles/5699254.html

  • 相关阅读:
    网站构建前期知识掌握
    Lucene学习之CURD
    Lucene学习之初步了解
    jQuery的extend详解
    jQuery和DOM对象之间的转换
    Tomcat教程
    Log4j配置
    Java学习之Java中常用对象
    Java学习之Comparable与Comparator的区别
    测试工具。
  • 原文地址:https://www.cnblogs.com/Jabe/p/5701993.html
Copyright © 2020-2023  润新知