大纲:

  1、paramiko模块

  2、开启进程的两种方式

    3、多进程实现并发的套接字通信

  4、Process对象的join方法和其他属性或方法

  5、守护进程

  6、同步锁

  7、进程之间通信

  8、生产者消费者模型

  9、进程池已经

  10、进程池之回调函数

一、paramiko模块

  paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。

下载安装:pip3 install paramiko #在python3中
pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto #在python2中
pip3 install pycrypto
pip3 install paramiko
注:如果在安装pycrypto2.0.1时发生如下错误
        command 'gcc' failed with exit status 1...
可能是缺少python-dev安装包导致
如果gcc没有安装,请事先安装gcc

ssh的使用

SSHClient
#基于用户名密码连接:

# import paramiko
#
# 创建SSH对象
# ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# # 连接服务器
#
# while True:
#     ssh.connect(hostname='10.0.0.20', port=22, username='root', password='123456')
#
#     cmd=input('>>').strip()
#     # 执行命令
#     stdin, stdout, stderr = ssh.exec_command(cmd)
#     # 获取命令结果
#     result = stdout.read()
#     print(result.decode('utf-8'),end='')
#     # 关闭连接
#     ssh.close()

基于公钥密钥连接:

客户端文件名:id_rsa

服务端必须有文件名:authorized_keys(在用ssh-keygen时,必须制作一个authorized_keys,可以用ssh-copy-id来制作)

# import paramiko
#
# private_key = paramiko.RSAKey.from_private_key_file('id_rsa')
#
# # 创建SSH对象
# ssh = paramiko.SSHClient()
# # 允许连接不在know_hosts文件中的主机
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#
# while True:
#     # 连接服务器
#     ssh.connect(hostname='10.0.0.20', port=22, username='root', pkey=private_key)
#
#     cmd = input('>>').strip()
#     # 执行命令
#     stdin, stdout, stderr = ssh.exec_command(cmd)
#     # 获取命令结果
#     result = stdout.read()
#     print(result.decode('utf-8'),end='')
#     # 关闭连接
#     ssh.close()

SFTPClient

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

基于用户名密码上传下载

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

################ SFTPClient
# import paramiko
#
# transport = paramiko.Transport(('10.0.0.20', 22))
# transport.connect(username='root', password='123456')
#
# sftp = paramiko.SFTPClient.from_transport(transport)
# # 将location.py 上传至服务器 /tmp/test.py
# sftp.put('id_rsa', '/tmp/id_rsa_test')
# # 将remove_path 下载到本地 local_path
# sftp.get('/tmp/AABB', 'AABB_test')
#
# transport.close()

基于用户名密码上传下载

 基于公钥密钥上传下载

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

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

# 基于公钥密钥上传下载

二、开启进程的两种方式

1、使用Process来启动进程

from multiprocessing import  Process
import  os,sys,time
def work(name):
    print('task is running  >%s' %name)
    time.sleep(2)
if __name__=='__main__':
    # Process(target=work,args=('sheng',),  kwargs={'name':'sheng'})
    p1=Process(target=work, args=('sheng',))  #在windows 下Process一定要写在__main__下面
    p2= Process(target=work, args=('lele',))
    p1.start()
    p2.start()
    print('主进程')

2、继承Process类来启动多进程

可以定义自己需要的属性。

from multiprocessing import  Process
import  os,sys,time

class Myprocess(Process):
    def __init__(self,name):
        super().__init__()  #使用super可以防止继承的勒种有相同的属性
        self.name=name
    def run(self):
        print('task is running  >%s' % self.name)
        time.sleep(2)
if __name__=='__main__':
    p=Myprocess('sheng')
    p.start()
    print('主进程')

三、多进程实现并发的套接字通信

 multiprocessing模块介绍  

   python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。
     multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

     multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

       需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

Process类的介绍:

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:

1. 需要使用关键字的方式来指定参数

2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍:

1、group参数未使用,值始终为None
2、target表示调用对象,即子进程要执行的任务
3、args表示调用对象的位置参数元组,args=(1,2,'egon',)
4、kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} 
5、name为子进程的名称

方法介绍:

1、p.start():启动进程,并调用该子进程中的p.run() 
2、p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法  
3、p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
4、p.is_alive():如果p仍然运行,返回True
5、p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

Process类的使用

注意:在windowsProcess()必须放到# if __name__ == '__main__':

服务端:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import  *
from  multiprocessing import  Process
import  os,sys
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('127.0.0.1',8080))
s.listen(5)
def work(conn,addr):
    print('===>进程号:%s'%os.getpid())
    while True:
        try:
            data=conn.recv(1024)
            if not data:continue
            conn.send(data.upper())
        except Exception as e:
            break
    conn.close()

if __name__=='__main__':
    while  True:
        conn,addr=s.accept()
        p=Process(target=work,args=(conn,addr))
        p.start()
    s.close

View Code

客户端:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import  *
c=socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8080))
while True:
    msg=input('>>').strip()
    if not msg:continue
    c.send(msg.encode('utf-8'))
    data=c.recv(1024)
    print(data.decode('utf-8'))
c.close()

View Code

四、Process对象的其他属性与join方法

属性介绍:

1、p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2、p.name:进程的名称
3、p.pid:进程的pid
4、p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
5、p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

例子:

from multiprocessing import  Process
import  os,sys,time
def work(name):
    print('task is running  >%s' %name)
    time.sleep(1)
if __name__=='__main__':
    # Process(target=work,args=('sheng',),  kwargs={'name':'sheng'})
    p1=Process(target=work, args=('sheng',))  #在windows 下Process一定要写在__main__下面
    p1.start()
    p1.terminate()  #终止p1进程
    time.sleep(1)  
    print(p1.is_alive())  #查看p1进程是否存在
    print(p1.name)   #Process-1  查看进程名字
    print(p1.pid)
    print('主进程',os.getpid())

  join函数等待主进程结束后在执行主函数。

from multiprocessing import  Process
import  os,sys,time
def work(name):
    print('task is running  >%s' %name)
    time.sleep(1)
if __name__=='__main__':
    # Process(target=work,args=('sheng',),  kwargs={'name':'sheng'})
    p1=Process(target=work, args=('sheng',))  #在windows 下Process一定要写在__main__下面
    p2= Process(target=work, args=('lele',))
    p1.start()
    p2.start()
    p1.join()  #等待子进程执行完,在执行主进程
    p2.join()
    print('主进程')

五、守护进程

主进程创建守护进程

  其一:守护进程会在主进程代码执行结束后就终止

  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

例子1:

from multiprocessing import  Process
import  os,sys,time
def work(name):
    print('task is running  >%s' %name)
    time.sleep(10)
if __name__=='__main__':
    # Process(target=work,args=('sheng',),  kwargs={'name':'sheng'})
    p1=Process(target=work, args=('sheng',))  #在windows 下Process一定要写在__main__下面
    p1.daemon =True  #p1.start开始之前设置daemon,
    p1.start()
    time.sleep(0.5)
    print('主进程',os.getpid())
# 特点:
    #1、子进程在主进程结束后结束子进程
    #2、子进程下面不能产生子进程

例子2:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

#主进程代码运行完毕,守护进程就会结束
from multiprocessing import Process
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")
def bar():
    print(456)
    time.sleep(3)
    print("end456")

if __name__=='__main__':
    p1=Process(target=foo)
    p2=Process(target=bar)
    p1.daemon=True
    p1.start()
    p2.start()
    print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止

# 迷惑人的例子

六、同步锁

例子1:

import  os,time
from  multiprocessing  import  Process,Lock
def work(name,mutex):
    mutex.acquire()  #锁开始
    print('%s task is running %d' %(name,os.getpid()))
    time.sleep(2)
    print('%s=> is done %d'%(name,os.getpid()))
    mutex.release()  #锁结束
if __name__=='__main__':
    mutex = Lock()
    p1=Process(target=work,args=('sheng',mutex))
    p2=Process(target=work,args=('leqi',mutex))
    p1.start()
    p2.start()
    print('===>主进程')

例子2:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

import json
import os
import time
from multiprocessing import Process,Lock
def search():
    dic=json.load(open('db.txt'))
    print('\033[32m[%s] 看到剩余票数<%s>\033[0m' %(os.getpid(),dic['count']))
def get_ticket():
    dic = json.load(open('db.txt'))
    time.sleep(0.5) #模拟读数据库的网络延迟
    if dic['count'] > 0:
        dic['count']-=1
        time.sleep(0.5)  # 模拟写数据库的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[31m%s 购票成功\033[0m' %os.getpid())
def task(mutex):
    search()
    mutex.acquire()
    get_ticket()
    mutex.release()
if __name__ == '__main__':
    mutex=Lock()
    for i in range(3):
        p=Process(target=task,args=(mutex,))
        p.start()

#例子:模拟抢票

七、进程之间通信

1、共享内存:

启动进程来减同一个内存数据:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

from  multiprocessing import  Process,Manager,Lock
def work(dic):
    dic['count']-=1

if __name__=='__main__':
    m=Manager()
    dic=m.dict({'count':100})   #共享字典dic,
    print(dic)
    p_l=[]
    for i in range(100):
        p=Process(target=work,args=(dic,))
        p_l.append(p)
        p.start()

    for p in p_l:
        p.join()
    print(dic)
# 结果:
    # {'count': 100}
    # {'count': 2}
# 子进程同时修改共享字典dic,

View Code

使用加锁的机制:控制资源的抢占保证数据的正确性。

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

from  multiprocessing import  Process,Manager,Lock

def work(dic,mutex):
    mutex.acquire()
    dic['count']-=1
    mutex.release()
if __name__=='__main__':
    mutex=Lock()
    m=Manager()
    dic=m.dict({'count':100})   #共享字典dic,
    print(dic)
    p_l=[]
    for i in range(100):
        p=Process(target=work,args=(dic,mutex))
        p_l.append(p)
        p.start()

    for p in p_l:
        p.join()
    print(dic)
# 结果
    # {'count': 100}
    # {'count': 0}
# 自己处理加锁的方式。
#缺点降低了运行的效率、

View Code

2、队列

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

from  multiprocessing import  Queue

q=Queue(4)
q.put('fist') 
q.put('scond')
q.put('third') #put放入队列
q.put('fourth',block=True)  #队列满了就抛异常(不卡)

print(q.get())#get取出队列
print(q.get())
print(q.get())

View Code

八、生产者消费者模型

1、队列的应用

from multiprocessing import  Process,Lock,Queue
import  time,os
def producer(q):
    '''生产者程序'''
    for i in range(10):
        res='包子 %s' %i
        q.put(res)
        time.sleep(0.7)
        print('< %s生产了[%s]>' %(os.getpid(),res))
def consumer(q):
    '''消费者程序'''
    while True:
        res=q.get()
        time.sleep(1)
        print('< %s消费了[%s]>' %(os.getpid(),res))
if __name__=='__main__':
    q=Queue()
    p1=Process(target=producer,args=(q,))
    c1=Process(target=consumer,args=(q,))
    # 启动生产者
    p1.start()
    # 启动消费者
    c1.start()
    print('==>main')

上个例子中有问题:

       生产者p1循环完之后,已经结束生产者进程,消费者c1一直等待就会卡住。消费者无法得知生产者什么时候完成。

解决方法:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

from multiprocessing import Process, JoinableQueue
import time, os

def producer(q, name):
    for i in range(3):
        time.sleep(2)
        res = '%s%s' % (name, i)
        q.put(res)
        print('\033[45m<%s> 生产了 [%s]\033[0m' % (os.getpid(), res))
    q.join()   #生产者等待q的结束之后在结束(q的结束就是q为空的时候)

def consumer(q):
    while True:
        res = q.get()
        # time.sleep(1.5)
        print('\033[34m<%s> 吃了 [%s]\033[0m' % (os.getpid(), res))
        q.task_done()

if __name__ == '__main__':
    q = JoinableQueue()

    # 生产者们:即厨师们
    p1 = Process(target=producer, args=(q, '包子'))
    p2 = Process(target=producer, args=(q, '饺子'))
    p3 = Process(target=producer, args=(q, '馄饨'))

    # 消费者们:即吃货们
    c1 = Process(target=consumer, args=(q,))
    c2 = Process(target=consumer, args=(q,))

    c1.daemon=True
    c2.daemon=True  #把消费者设置为守护进程当主进程结束之后同时结束
    p1.start()
    p2.start()
    p3.start()
    c1.start()
    c2.start()
    p1.join() #主进程等待p1的结束之后继续执行后面

    print('主')

View Code

九、进程池

1、进程池的使用:

import  os,time
from multiprocessing import  Pool

def work(n):
    print('task is running  >%s' %os.getpid())
    time.sleep(1)
    return  n**2

if __name__=='__main__':
    p=Pool()
    # for i in range(10):
    #     res=p.apply(work,args=(i,))  #同步执行,等待任务执行完毕
    #     print(res)
    res_l = []
    for i in range(10):
        res=p.apply_async(work,args=(i,))  #异步执行
        res_l.append(res)
    p.close()  #不允许往进程池提交新的任务
    p.join()  #等待进程池的任务全部完成

    for res in res_l:
        print(res.get())  #取出进程的返回值

2、进程池控制并发的套接字通信

服务端:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import  *
from  multiprocessing import Pool
import  os,sys
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('127.0.0.1',8080))
s.listen(5)

def work(conn,addr):
    print('===>进程号:%s'%os.getpid())
    while True:
        try:
            data=conn.recv(1024)
            if not data:continue
            conn.send(data.upper())
        except Exception as e:
            break
    conn.close()

if __name__=='__main__':
    p=Pool(3)
    while  True:
        conn,addr=s.accept()
        p.apply_async(work,args=(conn,addr))

    s.close

#服务端:

客户端:

python 模拟并发 python paramiko并发_子进程

python 模拟并发 python paramiko并发_守护进程_02

# -*- coding: utf-8 -*-
__author__ = 'ShengLeQi'
from socket import  *

c=socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8080))

while True:
    msg=input('>>').strip()
    if not msg:continue
    c.send(msg.encode('utf-8'))
    data=c.recv(1024)
    print(data.decode('utf-8'))

c.close()

#客户端

十、进程池之回调函数

回调函数的应用的例子:

当没有使用回调函数:如下

import  requests,os,time
from multiprocessing import  Pool
def get_page(url):  #get网页内容的方法
    print('<%s> get :%s' %(os.getpid(),url))
    request=requests.get(url)
    if request.status_code==200:
        return {'url':url,'text':request.text}   #返回一个字典
def parse_page(dic):
    print('<%s> parse :%s' %(os.getpid(),res))
    print('url:%s size :%s' %(dic['url'],len(dic['text'])))
if __name__=='__main__':
    p = Pool(4)
    urls=[
        'https://www.baidu.com',
        'https://www.python.org',
        'http://www.sina.com.cn',
        'https://www.openstack.org',
        'https://about.gitlab.com'
    ]
    res_l=[]
    for url in urls:
        res=p.apply_async(get_page,args=(url,))  #apply_async使用异步启动进程(get_page)
        res_l.append(res)  

    p.close()   #先close()保证没有新的进程添加到进程池
    p.join()  #等待钱需要close()

    for res in res_l:
        parse_page(res.get())

  上面的例子中有问题: =》

  效率问题:需要等待进程池里面的函数都执行完毕,才能继续执行主进程(当进程池所有的进程把网页下载完成后才能分析提取页面内容。)

  使用回调函数来提高程序的效率。当进程池的一个进程把url的网页下载下来之后就开始分析提取此网页内容。

import  requests,os,time
from multiprocessing import  Pool

def get_page(url):  #get页面函数
    print('<%s> get :%s' %(os.getpid(),url))
    request=requests.get(url)
    if request.status_code==200:
        return {'url':url,'text':request.text}

def parse_page(dic):  #分析页面内容函
    print('<%s> parse: %s' %(os.getpid(),dic['url']))
    print('url:%s size :%s' %(dic['url'],len(dic['text'])))


if __name__=='__main__':
    p = Pool(4)  #数值4是意味可以并发4个进程,不写默认是系统cpu内核数
    urls=[
        'https://www.baidu.com',
        'https://www.python.org',
        'http://www.sina.com.cn',
        'https://www.openstack.org',
        'https://about.gitlab.com'
    ]

    res_l=[]
    for url in urls:
        p.apply_async(get_page,args=(url,),callback=parse_page) #通知主进程回调parse_page函数  使用回调函数

    p.close()
    p.join()

运行结果:
# <30700> get :https://www.baidu.com
# <26740> get :https://www.python.org
# <10516> get :http://www.sina.com.cn
# <10516> get :https://www.openstack.org
# <29920> parse: http://www.sina.com.cn
# url:http://www.sina.com.cn size :600911
# <29780> get :https://about.gitlab.com
# <29920> parse: https://www.baidu.com
# url:https://www.baidu.com size :2443
# <29920> parse: https://www.python.org
# url:https://www.python.org size :48708
# <29920> parse: https://about.gitlab.com
# url:https://about.gitlab.com size :81129
# <29920> parse: https://www.openstack.org
# url:https://www.openstack.org size :60365