Python RPC 远程调用脚本之 RPyC 实践

Stella981
• 阅读 1152

最近有个监控需求,需要远程执行集群每个节点上的脚本,并获取脚本执行结果,为了安全起见不需要账号密码登陆节点主机,要求只需要调用远程脚本模块的方法就能实现。

总结下python进行远程调用脚本方法:

  • 登陆主机执行脚本,python模块支持如 pssh、pexpect、paramiko、ansible

  • 以远程方法调用(不需要登陆主机),python模块 rpyc,支持分布式

  • socket 方式,稍显复杂,需要熟悉网络协议,起点比较高

rpyc支持远程调用、分布式计算,以较少代码量实现复杂socket编程,本文主要介绍 rpyc 并用它来实现一个 demo。

以代码方式介绍:

需求:分别执行集群每个节点上 server 端的脚本,并返回执行结果给 client 端

Monitor_RPC_Client.py

#!/usr/bin/env python
# coding=utf-8
# 测试utf-8编码
# python exec_cmd.py "ls -lrt /opt/data1/logs/nginx/pc/track/`date +'%Y%m%d'`|awk '{s+=\$5}END{print s}'"
# python exec_cmd.py "wc -l /opt/data1/logs/nginx/pc/track/`date +'%Y%m%d'`/*|awk '{s+=\$1}END{print s}'"
import sys

reload(sys)
sys.setdefaultencoding('utf-8')

import rpyc
from pyUtil import *
from multiprocessing.dummy import Pool as ThreadPool

hostDict = {
    '192.168.1.216': 12345,
    '192.168.1.217': 12345,
    '192.168.1.218': 12345
}

localResultDict = {}


def rpc_client(host_port_cmd):
    host = host_port_cmd[0]
    port = host_port_cmd[1]
    cmd = host_port_cmd[2]
    c = rpyc.connect(host, port)
    result = c.root.exposed_execCmd(cmd)
    localResultDict[host] = result
    c.close()


def exec_cmd(cmd_str):
    host_port_list = []
    for (host, port) in hostDict.items():
        host_port_list.append((host, port, cmd_str))

    pool = ThreadPool(len(hostDict))
    results = pool.map(rpc_client, host_port_list)
    pool.close()
    pool.join()
    for ip, result in sorted(localResultDict.iteritems(), key=lambda d: int(d[0].replace(".", ""))):
        print ip + ":\t" + result

if __name__ == "__main__":

    if len(sys.argv) == 2 and sys.argv[1] != "-h":
        print "======================"
        print "    Your command is:\t" + sys.argv[1]
        print "======================"
        cmd_str = sys.argv[1]
    else:
        print """
            该脚本可以在集群中批量执行任意命令并返回结果,但需注意以下几点:
            1、命令请先单机测试通过,然后提交给脚本批量执行;
            2、不要执行 rm 等危险 || 极其耗时 || 影响机器性能的命令;
            3、命令请用双引号引起来,另外命令中有 $ 符号需要转义成 \$ 否则会被 Shell 当做变量解析掉,具体请参见下面的例子。
            Usage && for example:
            python exec_cmd.py "ls -lrt /opt/data1/logs/nginx/pc/track/{}|awk '{{s+=\$5}}END{{print s}}'"
            python exec_cmd.py "wc -l /opt/data1/logs/nginx/pc/track/{}/*|awk '{{s+=\$1}}END{{print s}}'"
        """.format(yesterday, yesterday)
        sys.exit(1)

    exec_cmd(cmd_str)


Monitor_RPC_Server.py

#!/usr/bin/env python
# coding=utf-8
# 测试utf-8编码
# cd /opt/script/rpcMonitorFlume
# pkill -f flumeFileMonitor_RPC_Server.py
# nohup python -u flumeFileMonitor_RPC_Server.py >> logs/flumeFileMonitor_RPC_Server.log 2>&1 &
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import os, commands, glob, re
import datetime

from rpyc import Service
from rpyc.utils.server import ThreadedServer

from pyUtil import getNowTime, get_ip_address

class remote_call_func(Service):

    def on_connect(self):
        print "[{0}]\t--------------<<< on_connect".format(getNowTime())

    def on_disconnect(self):
        print "[{0}]\t-------------->>> on_disconnect".format(getNowTime())

    def exposed_execCmd(self, cmd):
        exitCode, execResult = commands.getstatusoutput(cmd)
        nowTime = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        print "[{0}] → {1} → {2}".format(nowTime, cmd, execResult)
        return execResult


rpycServer = ThreadedServer(remote_call_func, hostname=get_ip_address('eth0'), port=11111, auto_register=False)
rpycServer.start()

Python RPC 远程调用脚本之 RPyC 实践

官方文档中类似例子很多,就不详细介绍了,需注意3点:

  • server端定义方法需要被client调用,必须定义以exposed 开头的方法,不然会报错AttributeError: ‘remote_call_script’ object has no attribute ‘exposed_iamshell’

  • server端默认不设认证机制,如果需要认证有推荐两种方法: ThreadedServer的authenticator参数与SSL模块

  • pip install rpyc ,如果 import rpyc 报错则 yum install openssl-devel,然后重新编译、安装 python

当然还需要考虑很多异常处理,如超时、验证失败等。

Refer:

[1] python远程调用脚本(一)

http://www.dbunix.com/?p=3262

http://rpyc.readthedocs.org/en/latest/tutorial.html

[2] python学习——python中执行shell命令

http://zhou123.blog.51cto.com/4355617/1312791

[3] celery实现任务统一收集、分发执行 

http://blog.csdn.net/vintage_1/article/details/47664187

[4] Timeout function if it takes too long to finish [duplicate]

http://stackoverflow.com/questions/2281850/timeout-function-if-it-takes-too-long-to-finish

[5] 源码之Queue

http://www.cnblogs.com/liqxd/p/5104051.html

[6] python多线程编程(9) Queue模块

http://beginman.cn/python/2015/12/01/python-threading-queue/

[7] Python 并行任务技巧

http://my.oschina.net/leejun2005/blog/194270?fromerr=mNcoWQlp

[8] 利用 Python yield 创建协程将异步编程同步化

http://my.oschina.net/leejun2005/blog/501448?fromerr=ynpLsTXB

[9] Python 多线程教程:并发与并行

http://my.oschina.net/leejun2005/blog/398826

[10] 理解 Python 中的多线程

http://my.oschina.net/leejun2005/blog/179265

[11] paramiko小记

http://www.cnblogs.com/liqxd/p/5077803.html

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
暗箭伤人 暗箭伤人
1年前
【www.ithunter.club】 20230922下午
不容易的2023年,我们一起努力【www.ithunter.club】(2023092208:00:00.8872062023092216:00:00.887206)1.人事招聘专员数名(可选远程或入职)2.招聘向坐标东京Yahoo、Shift、L
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这