一、分布式进程
Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。
Python的multiprocessing模块不但支持多进程,其中managers子模块还支持把多进程分布到多台机器上。一个服务进程可以作为调度者,将任务分布到其他多个进程中,依靠网络通信。由于managers模块封装很好,不必了解网络通信的细节,就可以很容易地编写分布式多进程程序。
二、举例实现分布式进程:
举个例子:如果我们已经有一个通过Queue通信的多进程程序在同一台机器上运行,现在,由于处理任务的进程任务繁重,希望把发送任务的进程和处理任务的进程分布到两台机器上。怎么用分布式进程实现?
该例子首先要解决:如何让其他机器的进程访问Queue的问题
问题解决方法:原有的Queue可以继续使用,通过managers模块把Queue通过网络暴露出去,则可以让其他机器的进程访问Queue
2.1实现分布式进程代码
1、服务进程,命名为task_master.py
服务进程作用:负责启动Queue,把Queue注册到网络上,然后往Queue里面写入任务
服务进程代码内容:1、创建发送任务的队列、接收结果的队列
2、创建继承BaseManager的子类
3、将”1“中创建的两个队列注册到网络上
(注意:这边callable的参数必须是可调用的,如果直接给变量名是不可以的,所以老师在例子中就通过最简单lambda表达式匿名函数的形式)
4、绑定端口,设置验证码
(注意:authkey的用处是为了保证两台机器正常通信,不被其他机器恶意干扰。如果task_worker.py的authkey和task_master.py的authkey不一致,肯定连接不上。)
5、启动队列
6、使用manager.get_task_queue()分别获取通过网络访问的Queue对象包括任务队列和结果队列
(注意:当我们在一台机器上写多进程程序时,创建的Queue可以直接拿来用,但是,在分布式多进程环境下,添加任务到Queue不可以直接对原始的task_queue进行操作,那样就绕过QueueManager 的封装,必须通过manager.get_task_queue()获得的Queue接口添加。)
7、使用put()函数将任务放入任务队列
8、使用get()函数在结果队列中读取结果
(注意:master里result.get(timeout=10),这里需要在10s获取到信息,否则就抛出Queue.empty)
9、使用shutdown()函数关闭
#task_master.py
import random, time, queue
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support
#send queue 发送队列
task_queue = queue.Queue()
#receiver queue 接收队列
result_queue = queue.Queue()
class QueueManager(BaseManager):
pass
#注册2个queue到网络上 使用callable和匿名函数关联了Queue对象
'''仅适用Linux Windows下callable不能使用lambda表达式赋值
QueueManager.register('get_task_queue', callable=lambda: task_queue)
QueueManager.register('get_result_queue', callable=lambda: result_queue)
'''
def return_task_queue():
global task_queue
return task_queue
def return_result_queue():
global result_queue
return result_queue
def runf():
QueueManager.register('get_task_queue', callable=return_task_queue)
QueueManager.register('get_result_queue', callable=return_result_queue)
#绑定端口5000,设置验证密码'abc'
manager = QueueManager(address=('127.0.0.1', 5000), authkey=b'abc')
#Linux下address留空等于本机 Windows下不能留空 127.0.0.0即本机的地址
#启动Queue
manager.start()
#通过网络获取Queue对象
task = manager.get_task_queue()
result = manager.get_result_queue()
#开启示例任务
for i in range(10):
n = random.randint(0, 10000)
print('Put task %d to run...' %n)
task.put(n)
#读取任务结果
print('Try to get results...')
for i in range(10):
r = result.get(timeout=10)
print('Results: %s' %r)
manager.shutdown()
print('master has been shoutdown')
if __name__ == '__main__':
freeze_support()
runf()
以上代码转载自:https://blog.youkuaiyun.com/Solo95/article/details/789137092、任务进程,命名为启动任务进程 (可以在本机或另一个机器上运行)
任务进程作用:连接到服务器,即运行task_master.py的机器,从task队列取任务,并把结果写入result队列。
任务进程代码内容:1、创建继承自BaseManager的类似的子类QueueManager
2、注册任务队列和结果队列
(注意:由于这个QueueManager只从网络上获取Queue,所以注册时只提供名字)
3、连接到服务器,也就是运行task_master.py的机器
(注意:端口和验证码注意保持与task_master.py设置的完全一致)
4、使用get()从任务队列取任务,使用put()把结果写入result队列
2.2实现分布式进程代码运行
1、首先运行task_master.py,如下所示
$ python3 task_master.py
Put task 3411...
Put task 1605...
Put task 1398...
Put task 4729...
Put task 5300...
Put task 7471...
Put task 68...
Put task 4219...
Put task 339...
Put task 7866...
Try get results..
2、其次运行 task_worker.py
$ python3 task_worker.py
Connect to server 127.0.0.1...
run task 3411 * 3411...
run task 1605 * 1605...
run task 1398 * 1398...
run task 4729 * 4729...
run task 5300 * 5300...
run task 7471 * 7471...
run task 68 * 68...
run task 4219 * 4219...
run task 339 * 339...
run task 7866 * 7866...
worker exit.
3、task_worker.py进程结束,在task_master.py进程中会继续打印出结果
Result: 3411 * 3411 = 11634921
Result: 1605 * 1605 = 2576025
Result: 1398 * 1398 = 1954404
Result: 4729 * 4729 = 22363441
Result: 5300 * 5300 = 28090000
Result: 7471 * 7471 = 55815841
Result: 68 * 68 = 4624
Result: 4219 * 4219 = 17799961
Result: 339 * 339 = 114921
Result: 7866 * 7866 = 61873956
2.3总结
廖老师的一张图反映了运行task_master.py 和task_worker.py 的流程