参考:
c#端2021/11/29更新
须要引用一个库:CookComputing.XmlRpcV2.dll(这个我放在资源里去了,找不到的也可以私信我)
ProxyInterface.cs
using CookComputing.XmlRpc;
namespace RemoteServerSample
{
[XmlRpcUrl("http://IP地址:端口")]
public interface ProxyInterface:IXmlRpcProxy
{ // getData就是远程调用的Python函数
[XmlRpcMethod("getData")]
string getData(string seq);
}
}
JsonNetResult.cs
using Newtonsoft.Json;
namespace RemoteServerSample
{
public static class JsonNetResult
{
///
/// Json序列化设置
///
public static JsonSerializerSettings Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
///
/// 执行Json序列化
///
///
///
public static string SerializeObject(object obj)
{
return JsonConvert.SerializeObject(obj, Settings);
}
///
/// 反序列化对象
///
///
public static T DeserializeObject<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, Settings);
}
}
}
Program.cs
using CookComputing.XmlRpc;
using System;
using System.Collections.Generic;
namespace RemoteServerSample
{
class Program
{
static void Main(string[] args)
{
Program program = new Program();
// 这个类的定义和对象的赋值我省略了
GeneOrder order = new GeneOrder();
...
// 序列化
var seqName = JsonNetResult.SerializeObject(order);
//远程调用
program.CallAlphags_tat(seqName);
}
///
/// 远程调用
///
private void CallAlphags_tat(string seqName)
{
ProxyInterface proxy = (ProxyInterface)XmlRpcProxyGen.Create(typeof(ProxyInterface));
Console.WriteLine("BEGIN------------------");
Console.WriteLine(proxy.getData(seqName));
Console.WriteLine("END---------------------");
Console.ReadLine();
}
原始做法的问题是:1.要保持脚本仍然在后台运行;2.没有考虑多用户访问,即须要并发处理恳求的情况。
还有我自己的小问题3:跑脚本的环境是miniconda里的一个环境,每次要先condaactivate环境名。
3是最容易解决的,去miniconda的路径下边,找到那种环境的类库,其路径类似:
/XXXXX/miniconda2/envs/环境名/bin/python
用这个路径取代原先的condaactivate环境名|python即可。
**2的解决也很简单。**参考这儿:
SimpleXMLRPCServer是单线程访问的,所以有多个用户恳求的时侯,要排队64位linux,次序执行。
改多线程的代码如下:
# 不需要额外装,已经内置了
from socketserver import ThreadingMixIn
# 新建一个继承了ThreadingMixIn(支持多线程)和SimpleXMLRPCServer(支持远程调用)的类
class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
pass
if __name__ == '__main__':
# 只改一下sercer即可
server = ThreadXMLRPCServer(('ip地址', 6060))
# 事实上可以注册多个函数
server.register_function(getData, "getData")
server.serve_forever()
python本地远程调用服务器上的代码
from xmlrpc.client import ServerProxy
if __name__ == "__main__":
data = 'xxxxxx'
server = ServerProxy("http://ip地址:6060")
print(server.getData(data))
对于1,我考虑了两种做法。
把py脚本放在一个docker容器里,之后让容器开着,在容器里保持py运行;用nohup,把脚本放在后台运行。
暂时没考虑把脚本弄成服务,由于服务器暂时不能重启。
我最终选了解法2,由于我的代码须要本地调用一个软件,尽管我在docker容器中安装了那种软件,而且容器的算力有限,还是服务器跑上去更快。句子如下:
nohup /XXXXX/miniconda2/envs/环境名/bin/python 脚本名.py &
此时,假如退出当前终端,再用jobs虽然是不会显示这条命令的,并且可以用psaux|grep-ipython来查:
root 1x 0.0 0.1 160 568 ? S 16:41 0:02 /xx/miniconda2/envs/xx/bin/python xx.py
大约有此类信息。中间的S表示脚本在等待中断,例如这儿就是等待顾客的恳求(是post,不是get),这就表示脚本确实还在后台持续运行中。
对于解法1,我的步骤大约如下:
开启一个共享文件夹、开启端口映射(可以回看我的docker系列文章)
docker run -itd --privileged -v /宿主机路径:/容器内路径 -p 6060:6060 镜像名:标签
把要跑的脚本置于共享文件夹下,之后dockerattach容器id进容器,拷贝到别处(在共享文件夹可能有权限问题,我通常放容器内部)nohup跑脚本
若果此时不能访问服务
这么先检测在容器里能不能ping通宿主机、8.8.8.8啥的(还有一些我忘了,之后想上去再说吧),倘若都通,但就是难以访问服务,那检测下main函数里server=SimpleXMLRPCServer((‘localhost’,6060))这一句有没有改成容器的ip。在容器里用ifconfig,看eth0的inet值。用这个值取代原先的localhost,应当就可以了。
还有一种雷人的是iptables那儿缺乏规则。在宿主机linux 执行python脚本,用下边这条命令看一下,假若没有容器ip和宿主机ip的映射,要添加规则的。(尽管正常情况下docker会手动创建linux服务器代维,而且万一没有,可以考虑这个方式)
iptables -vnL -t nat
添加
iptables -t nat -A PREROUTING -m tcp -p tcp --dport 6060 -j DNAT --to-destination 容器ip:6060
iptables -t nat -A POSTROUTING -m tcp -p tcp --dport 6060 -d 172.17.0.2 -j SNAT --to-source 宿主机ip
原始做法
json和xmlrpc都是标准库的linux 执行python脚本,不用安装
import json
from xmlrpc.server import SimpleXMLRPCServer
# 输入输出都是json字符串
# json.loads要求的格式为:'{"name":"XXX", "List":[]}'
# 即最外层是单引号,里面都是双引号
def getData(data):
info = json.loads(data)
...# 修改info
return json.dumps(info)
if __name__ == '__main__':
server = SimpleXMLRPCServer(('localhost', 6060))
# 不管带不带参数,都是只写函数名的
server.register_function(getData, "getData")
server.serve_forever()
在服务器上执行python脚本
保持运行,c#调用即可
端口问题
参考:
Centos7默认安装了firewalld,而非iptables
查看防火墙状态
systemctl status firewalld
启动
systemctl start firewalld.service
开启某个端口:--permanent //--permanent永久生效,没有此参数防火墙重启便失效
firewall-cmd --zone=public --add-port=6060/tcp
关闭某个端口:--permanent 同理
firewall-cmd --zone=public --remove-port=6060/tcp
开启端口
iptables -I INPUT -p TCP --dport 6060 -j ACCEPT
查看端口占用情况的命令:lsof -i
netstat -lntu
查看某一端口的占用情况: lsof -i:端口号
# 如果报错某个端口
结束占用端口的进程:kill -9 进程号
报错:OSError:[Errno98]Addressalreadyinuse
找到占用该端口的进程,杀掉即可
[root@localhost xxx]# lsof -i:6060
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 150140 root 3u IPv6 704733 0t0 TCP *:6060 (LISTEN)
nc 150140 root 4u IPv4 704734 0t0 TCP *:6060 (LISTEN)
[root@localhost alphags]# kill -9 150140