Pyro4 介绍,利用其实现进程间类方法的互相调用
笔者需要实现一个进程调用另外一个进程中类的方法,通过python库Pyro4解决此问题。
实际需求是这样的,有一个测试中心控制类,负责处理其他进程对其类方法的调用。其他进程通过Pyro4库调用测试中心类的方法。
Pyro4:以socket通信为基础,利用类序列化和反序列化方法,实现本机进程间通信,亦可实现局域网内不同机器进程间的通信。
http://pythonhosted.org/Pyro4/ 这是官网地址,想要深入学习的可以瞅瞅。
简单的说,Pyro4分为server和client两端,server一般是提供方法调用的一方,pyro4 server启动后,会使用局域网广播将本机注册到pyro4 server上的类方法广播
给其他pc或本机其他进程。client 根据特定的uri连接到特定的pyro4 server,client调用pyro4 server上注册的方法,就跟调用本地代码一样,如果调用方法阻塞,便会等pyro4 server上
的方法执行完后,client取到结果后,才会继续往下执行。
利用uri实现server和client间通信
client code #!/usr/bin/python # -*- coding: utf-8 -*- import Pyro4 uri=raw_input(" Pyro uri : ").strip() //输入server端uri name=raw_input("Your name: ").strip() greeting_maker=Pyro4.Proxy(uri) print greeting_maker.get_fortune(name) server code #!/usr/bin/python # -*- coding: utf-8 -*- import Pyro4 class GreetingMaker(object): def get_fortune(self, name): return "Hello, {0}. \n" .format(name) greeting_maker=GreetingMaker() daemon=Pyro4.Daemon() uri=daemon.register(greeting_maker) print "Ready. Object uri =", uri //此处的uri需要告知client daemon.requestLoop()
以上的代码便是一个说明pyro4的简单demo,但在实际项目中,server端每次生成的uri都是变化的,client不能够通过uri调用server端的代码。
其实,pyro4 提供了一个nameserver的方法,其类似于ip地址的域名解析,client 只需要将uri替换成一个pyro4 server声明的字符串即可,client回到nameserver中找寻此字符串对应
的类方法。server 将需要被调用的类方法暴露出去,并注册到nameserver中即可。
以下是通过nameserver中介的程序
server端
#!/usr/bin/python # coding:utf-8 import Pyro4 @Pyro4.expose #将此类暴露给client class GreetingMaker(object): def __init__(self,name): print ‘this is %s‘ % name def get_fortune(self, name): return "Hello, {0}. Here is your fortune message:\n" "Tomorrow‘s lucky number is 12345678.".format(name) daemon = Pyro4.Daemon() # make a Pyro daemon ns = Pyro4.locateNS() # find the name server uri = daemon.register(GreetingMaker(‘lyh‘)) # register the greeting maker as a Pyro object ns.register("example.greeting", uri) # register the object with a name in the name server print("Ready.") daemon.requestLoop() # start the event loop of the server to wait for calls
client 端
#!/usr/bin/python # coding:utf-8 import Pyro4 name = input("What is your name? ").strip() greeting_maker = Pyro4.Proxy("PYRONAME:example.greeting") # use name server object lookup uri shortcut print(greeting_maker.get_fortune(name))
命令行启动nameserver:python -m Pyro4.naming
以下是笔者项目中实际的rmi_server
#!/usr/bin/python # -*- coding: utf-8 -*- import Pyro4 import psutil import subprocess import traceback import time import threading import multiprocessing import os import common_func from awlib import atc_net_serial_com from slave_logger import Logger logger = Logger("RmiServer") MAINCOM = [‘COM11‘, ‘COM21‘] UARTCOM = [‘COM29‘, ‘COM28‘, ‘COM27‘, ‘COM26‘, ‘COM25‘, ‘COM24‘, ‘COM23‘, ‘COM22‘, ‘COM19‘, ‘COM18‘, ‘COM17‘, ‘COM16‘, ‘COM15‘, ‘COM14‘, ‘COM13‘, ‘COM12‘] class RmiServer(multiprocessing.Process): """Remote classmethod call server @register net uart COM11 COM21 @brocast port 9091, listen port 9090 """ def start(self): # start rmi nameserver net_ip = common_func.get_netcard()[0][1] exists_python_pids_before = common_func.get_pids_by_name(‘python‘) # broadcast method, all local area network pc can connect to it # pyro4_cmd = ‘python -m Pyro4.naming -n %s‘ % net_ip # start pyro4 server on localhost pyro4_cmd = ‘python -m Pyro4.naming‘ # set Pyro4 server threadpool size unlimit Pyro4.config.SERVERTYPE="multiplex" p_pyro4 = subprocess.Popen(pyro4_cmd, shell=True) time.sleep(1) exists_python_pids_after = common_func.get_pids_by_name(‘python‘) self.kill_pids = [] for pid_after in exists_python_pids_after: if pid_after not in exists_python_pids_before: self.kill_pids.append(pid_after) # self.pyro4_daemon = Pyro4.Daemon(host=net_ip) # make a Pyro daemon self.pyro4_daemon = Pyro4.Daemon() try: # find the broadcast name server use specify ip # self.ns = Pyro4.locateNS(host=net_ip) # find the name server self.ns = Pyro4.locateNS() except: traceback.print_exc() else: logger.info(‘connect to RmiNameServer successfully‘) # register classmethod self._register_net_uart() # run rmiserver thread t_run = threading.Thread(target=self._run) t_run.setDaemon(True) t_run.start() def stop(self): try: self.pyro4_daemon.shutdown() self.pyro4_daemon.close() for kill_pid in self.kill_pids: os.kill(kill_pid, 9) except: pass logger.info(‘stop RmiServer successfully‘) def _run(self): self.pyro4_daemon.requestLoop() # start the event loop of the server to wait for calls def _register(self, cls_md, cls_md_str): uri = self.pyro4_daemon.register(cls_md) self.ns.register(cls_md_str, uri) def _register_net_uart(self, wait_connect_time=6): logger.warn(‘********************Init MainCOM and DUT uart will take more time, please wait***********************‘) for maincom in MAINCOM: self._register(atc_net_serial_com.MainComCaller(maincom), maincom) time.sleep(wait_connect_time) logger.info(‘****************************************Init MainCOM and DUT uart OK!!!******************************‘) if __name__ == ‘__main__‘: rmi_server = RmiServer() rmi_server.start()
上面是一个本地进程间通信的例子,其中注释掉的
# broadcast method, all local area network pc can connect to it # pyro4_cmd = ‘python -m Pyro4.naming -n %s‘ % net_ip # self.pyro4_daemon = Pyro4.Daemon(host=net_ip) # make a Pyro daemon # find the broadcast name server use specify ip # self.ns = Pyro4.locateNS(host=net_ip)
实现局域网内不同PC进程间通信的例子
一个利用pyro4 client 例子
class NetSerialUSBAllInOneSwitch(): """support operate atc_g1 without virtual serial """ def __init__(self, maincom=None): self.main_com = Pyro4.Proxy("PYRONAME:" + maincom) def switch2power(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_usb_power‘, lines=[line]) return cmd_result def switch2usb(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_usb_power_signal‘, lines=[line]) return cmd_result def switch2off(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘close_usb‘, lines=[line]) return cmd_result def switch2dcoff(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_off_ext_power‘, lines=[line]) return cmd_result def switch2dc5v(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_ext_power‘, lines=[line], dict_arg={‘voltage‘: 5}) return cmd_result def switch2dc12v(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_ext_power‘, lines=[line], dict_arg={‘voltage‘: 12}) return cmd_result def switch2otg(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_otg_signal‘, lines=[line]) return cmd_result def switch2otg5v(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_otg_5v‘, lines=[line]) return cmd_result def keypress(self, line, number, value): cmd_result = None line = int(line) number = int(number) value = int(value) cmd_result = self.main_com.run_cmd(maincom_method=‘key_control‘, lines=[ line], dict_arg={‘num‘: number, ‘status‘: value}) return cmd_result def switch2ext12v(self, line): cmd_result = None line = int(line) cmd_result = self.main_com.run_cmd(maincom_method=‘turn_on_ext_12vdc‘, lines=[line]) return cmd_result def get_pyro4_maincom(self): return self.main_com def close(self): self.main_com._pyroRelease() # 调用pyro4内置方法,关闭pyro4 client 连接